diff options
author | Jeremy Benoist <jeremy.benoist@gmail.com> | 2016-11-03 16:41:29 +0100 |
---|---|---|
committer | Jeremy Benoist <jeremy.benoist@gmail.com> | 2016-11-03 16:41:29 +0100 |
commit | 5a619812ca3eb05a82a023ccdaee13501eb8d45f (patch) | |
tree | a1541999a3e13f9bb8b45d3a61320ee61aa4eb3c | |
parent | da4136557963018287cae61226e9006c3c741747 (diff) | |
parent | 84795d015b3c7e1af48a3dda3cb33cf080b66e8f (diff) | |
download | wallabag-5a619812ca3eb05a82a023ccdaee13501eb8d45f.tar.gz wallabag-5a619812ca3eb05a82a023ccdaee13501eb8d45f.tar.zst wallabag-5a619812ca3eb05a82a023ccdaee13501eb8d45f.zip |
Merge remote-tracking branch 'origin/master' into 2.2
60 files changed, 2477 insertions, 1936 deletions
diff --git a/.travis.yml b/.travis.yml index c7bb05fb..a8f6a744 100644 --- a/.travis.yml +++ b/.travis.yml | |||
@@ -75,4 +75,6 @@ script: | |||
75 | - if [[ $VALIDATE_TRANSLATION_FILE = '' ]]; then phpunit -v ; fi; | 75 | - if [[ $VALIDATE_TRANSLATION_FILE = '' ]]; then phpunit -v ; fi; |
76 | - if [[ $CS_FIXER = run ]]; then php bin/php-cs-fixer fix src/ --verbose --dry-run ; fi; | 76 | - if [[ $CS_FIXER = run ]]; then php bin/php-cs-fixer fix src/ --verbose --dry-run ; fi; |
77 | - if [[ $VALIDATE_TRANSLATION_FILE = run ]]; then php bin/console lint:yaml src/Wallabag/CoreBundle/Resources/translations -v ; fi; | 77 | - if [[ $VALIDATE_TRANSLATION_FILE = run ]]; then php bin/console lint:yaml src/Wallabag/CoreBundle/Resources/translations -v ; fi; |
78 | - if [[ $VALIDATE_TRANSLATION_FILE = run ]]; then php bin/console lint:yaml app/Resources/CraueConfigBundle/translations -v ; fi; | ||
79 | - if [[ $VALIDATE_TRANSLATION_FILE = run ]]; then php bin/console lint:yaml app/Resources/FOSUserBundle/translations -v ; fi; | ||
78 | - if [[ $ASSETS = build ]]; then ./node_modules/grunt-cli/bin/grunt tests; fi; | 80 | - if [[ $ASSETS = build ]]; then ./node_modules/grunt-cli/bin/grunt tests; fi; |
@@ -7,6 +7,8 @@ require 'capistrano/setup' | |||
7 | # Include default deployment tasks | 7 | # Include default deployment tasks |
8 | require 'capistrano/deploy' | 8 | require 'capistrano/deploy' |
9 | 9 | ||
10 | require 'capistrano/composer' | ||
11 | require 'capistrano/file-permissions' | ||
10 | require 'capistrano/symfony' | 12 | require 'capistrano/symfony' |
11 | 13 | ||
12 | # Load custom tasks from `lib/capistrano/tasks` if you have any defined | 14 | # Load custom tasks from `lib/capistrano/tasks` if you have any defined |
@@ -1,5 +1,6 @@ | |||
1 | source "https://rubygems.org" | 1 | source "https://rubygems.org" |
2 | 2 | ||
3 | gem 'capistrano', '~> 3.4' | 3 | gem 'capistrano', '~> 3.4' |
4 | gem 'capistrano-composer' | ||
4 | gem 'capistrano-symfony', '~> 1.0.0.rc1' | 5 | gem 'capistrano-symfony', '~> 1.0.0.rc1' |
5 | gem 'capistrano-composer', '~> 0.0.3' | 6 | gem 'capistrano-file-permissions' |
diff --git a/Gemfile.lock b/Gemfile.lock index aebbeba2..7b13b399 100644 --- a/Gemfile.lock +++ b/Gemfile.lock | |||
@@ -29,8 +29,9 @@ PLATFORMS | |||
29 | 29 | ||
30 | DEPENDENCIES | 30 | DEPENDENCIES |
31 | capistrano (~> 3.4) | 31 | capistrano (~> 3.4) |
32 | capistrano-composer (~> 0.0.3) | 32 | capistrano-composer |
33 | capistrano-file-permissions | ||
33 | capistrano-symfony (~> 1.0.0.rc1) | 34 | capistrano-symfony (~> 1.0.0.rc1) |
34 | 35 | ||
35 | BUNDLED WITH | 36 | BUNDLED WITH |
36 | 1.11.2 | 37 | 1.13.5 |
@@ -41,6 +41,6 @@ travis: ## Make some stuff for Travis-CI | |||
41 | deploy: ## Deploy wallabag | 41 | deploy: ## Deploy wallabag |
42 | @bundle exec cap staging deploy | 42 | @bundle exec cap staging deploy |
43 | 43 | ||
44 | .PHONY: help clean install update build test release travis deploy | 44 | .PHONY: help clean install update build test release travis deploy run dev |
45 | 45 | ||
46 | .DEFAULT_GOAL := install | 46 | .DEFAULT_GOAL := install |
diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pt.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pt.yml new file mode 100644 index 00000000..e8260422 --- /dev/null +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pt.yml | |||
@@ -0,0 +1,29 @@ | |||
1 | download_pictures: Download imagens no seu servidor | ||
2 | carrot: Habilitar compartilhamento para o Carrot | ||
3 | diaspora_url: URL Diaspora, se o serviço está habilitado | ||
4 | export_epub: Habilita exportação para ePub | ||
5 | export_mobi: Habilita exportação para .mobi | ||
6 | export_pdf: Habilita exportação para PDF | ||
7 | export_csv: Habilita exportação para CSV | ||
8 | export_json: Habilita exportação para JSON | ||
9 | export_txt: Habilita exportação para TXT | ||
10 | export_xml: Habilita exportação para XML | ||
11 | pocket_consumer_key: Chave de consumidor do Pocket para importar conteúdo (https://getpocket.com/developer/docs/authentication) | ||
12 | shaarli_url: URL Shaarli, se o serviço está habilitado | ||
13 | share_diaspora: Habilitar compartilhamento para o Diaspora | ||
14 | share_mail: Habilitar compartilhamento por e-mail | ||
15 | share_shaarli: Habilitar compartilhamento para o Shaarli | ||
16 | share_twitter: Habilitar compartilhamento para o Twitter | ||
17 | show_printlink: Mostrar um link para imprimir o conteúdo | ||
18 | wallabag_support_url: URL de Suporte do wallabag | ||
19 | wallabag_url: URL de *sua* instância do wallabag | ||
20 | entry: "artigo" | ||
21 | export: "exportar" | ||
22 | import: "importar" | ||
23 | misc: "misc" | ||
24 | modify_settings: "aplicar" | ||
25 | piwik_host: Host de seu website Piwik | ||
26 | piwik_site_id: ID de seu website Piwik | ||
27 | piwik_enabled: Habilitar Piwik | ||
28 | demo_mode_enabled: "Habilitar modo demo? (somente usado para o demo público do wallabag)" | ||
29 | demo_mode_username: "Usuário demo" | ||
diff --git a/app/Resources/FOSUserBundle/translations/FOSUserBundle.fr.yml b/app/Resources/FOSUserBundle/translations/FOSUserBundle.fr.yml index 1ccad137..1c5ea640 100644 --- a/app/Resources/FOSUserBundle/translations/FOSUserBundle.fr.yml +++ b/app/Resources/FOSUserBundle/translations/FOSUserBundle.fr.yml | |||
@@ -1,2 +1,2 @@ | |||
1 | Login: "Se connecter" | 1 | Login: "Se connecter" |
2 | Enter your email address below and we'll send you password reset instructions.: "Renseignez votre adresse email, nous vous enverrons les instructions pour réinitialiser votre mot de passe." | 2 | Enter your email address below and we'll send you password reset instructions.: "Renseignez votre adresse courriel, nous vous enverrons les instructions pour réinitialiser votre mot de passe." |
diff --git a/app/Resources/FOSUserBundle/translations/FOSUserBundle.pt.yml b/app/Resources/FOSUserBundle/translations/FOSUserBundle.pt.yml new file mode 100644 index 00000000..85eadfd8 --- /dev/null +++ b/app/Resources/FOSUserBundle/translations/FOSUserBundle.pt.yml | |||
@@ -0,0 +1,2 @@ | |||
1 | Login: "Login" | ||
2 | Enter your email address below and we'll send you password reset instructions.: "Digite seu endereço de e-mail para enviarmos as instruções de recupeção de sua senha." | ||
diff --git a/app/config/capistrano/deploy.rb b/app/config/capistrano/deploy.rb index f15eef30..fee04620 100644 --- a/app/config/capistrano/deploy.rb +++ b/app/config/capistrano/deploy.rb | |||
@@ -1,9 +1,4 @@ | |||
1 | # config valid only for current version of Capistrano | 1 | # config valid only for current version of Capistrano |
2 | lock '3.4.0' | ||
3 | |||
4 | set :log_path, "var/logs" | ||
5 | set :cache_path, "var/cache" | ||
6 | set :symfony_console_path, 'bin/console' | ||
7 | 2 | ||
8 | set :application, 'wallabag' | 3 | set :application, 'wallabag' |
9 | set :repo_url, 'git@github.com:wallabag/wallabag.git' | 4 | set :repo_url, 'git@github.com:wallabag/wallabag.git' |
@@ -11,8 +6,6 @@ set :repo_url, 'git@github.com:wallabag/wallabag.git' | |||
11 | set :ssh_user, 'framasoft_bag' | 6 | set :ssh_user, 'framasoft_bag' |
12 | server '78.46.248.87', user: fetch(:ssh_user), roles: %w{web app db} | 7 | server '78.46.248.87', user: fetch(:ssh_user), roles: %w{web app db} |
13 | 8 | ||
14 | set :scm, :git | ||
15 | |||
16 | set :format, :pretty | 9 | set :format, :pretty |
17 | set :log_level, :info | 10 | set :log_level, :info |
18 | # set :log_level, :debug | 11 | # set :log_level, :debug |
@@ -23,4 +16,4 @@ set :linked_files, %w{app/config/parameters.yml} | |||
23 | set :linked_dirs, [fetch(:log_path), "var/sessions", "web/uploads", "data"] | 16 | set :linked_dirs, [fetch(:log_path), "var/sessions", "web/uploads", "data"] |
24 | set :keep_releases, 3 | 17 | set :keep_releases, 3 |
25 | 18 | ||
26 | after 'deploy:finishing', 'deploy:cleanup' | 19 | after 'deploy:updated', 'symfony:cache:clear' |
diff --git a/app/config/config.yml b/app/config/config.yml index dfb0e3b2..7f24244d 100644 --- a/app/config/config.yml +++ b/app/config/config.yml | |||
@@ -44,13 +44,15 @@ wallabag_core: | |||
44 | es: 'Español' | 44 | es: 'Español' |
45 | oc: 'Occitan' | 45 | oc: 'Occitan' |
46 | it: 'Italiano' | 46 | it: 'Italiano' |
47 | pt: 'Português' | ||
47 | items_on_page: 12 | 48 | items_on_page: 12 |
48 | theme: material | 49 | theme: material |
49 | language: '%locale%' | 50 | language: '%locale%' |
50 | rss_limit: 50 | 51 | rss_limit: 50 |
51 | reading_speed: 1 | 52 | reading_speed: 1 |
52 | cache_lifetime: 10 | 53 | cache_lifetime: 10 |
53 | fetching_error_message: "wallabag can't retrieve contents for this article. Please report this issue to us." | 54 | fetching_error_message: | |
55 | wallabag can't retrieve contents for this article. Please <a href="http://doc.wallabag.org/en/master/user/errors_during_fetching.html#how-can-i-help-to-fix-that">troubleshoot this issue</a>. | ||
54 | 56 | ||
55 | wallabag_user: | 57 | wallabag_user: |
56 | registration_enabled: "%fosuser_registration%" | 58 | registration_enabled: "%fosuser_registration%" |
diff --git a/docs/de/developer/api.rst b/docs/de/developer/api.rst index f8911181..bb21154d 100644 --- a/docs/de/developer/api.rst +++ b/docs/de/developer/api.rst | |||
@@ -264,7 +264,8 @@ Drittanbieter Ressourcen | |||
264 | 264 | ||
265 | Einige Applikationen oder Bibliotheken nutzen unsere API. Hier ist eine nicht abschließende Aufzählung von ihnen: | 265 | Einige Applikationen oder Bibliotheken nutzen unsere API. Hier ist eine nicht abschließende Aufzählung von ihnen: |
266 | 266 | ||
267 | - `Java wrapper for the wallabag API <https://github.com/Strubbl/wallabag-java>`_ von Strubbl. | 267 | - `Java wrapper for the wallabag API <https://github.com/Strubbl/jWallabag>`_ von Strubbl. |
268 | - `.NET library for the wallabag v2 API <https://github.com/jlnostr/wallabag-api>`_ von Julian Oster. | 268 | - `.NET library for the wallabag v2 API <https://github.com/jlnostr/wallabag-api>`_ von Julian Oster. |
269 | - `Python API for wallabag <https://github.com/foxmask/wallabag_api>`_ von FoxMaSk, für sein Projekt `Trigger Happy <https://blog.trigger-happy.eu/>`_. | 269 | - `Python API for wallabag <https://github.com/foxmask/wallabag_api>`_ von FoxMaSk, für sein Projekt `Trigger Happy <https://blog.trigger-happy.eu/>`_. |
270 | - `A plugin <https://github.com/joshp23/ttrss-to-wallabag-v2>`_ entworfen für `Tiny Tiny RSS <https://tt-rss.org/gitlab/fox/tt-rss/wikis/home>`_, das die wallabag v2 API nutzt. Von Josh Panter. | 270 | - `A plugin <https://github.com/joshp23/ttrss-to-wallabag-v2>`_ entworfen für `Tiny Tiny RSS <https://tt-rss.org/gitlab/fox/tt-rss/wikis/home>`_, das die wallabag v2 API nutzt. Von Josh Panter. |
271 | - `Golang wrapper for the wallabag API <https://github.com/Strubbl/wallabago>`_ von Strubbl, für sein Projekt `wallabag-stats Graph <https://github.com/Strubbl/wallabag-stats>`_. | ||
diff --git a/docs/de/developer/rabbitmq.rst b/docs/de/developer/rabbitmq.rst index f81e07e3..143b64a1 100644 --- a/docs/de/developer/rabbitmq.rst +++ b/docs/de/developer/rabbitmq.rst | |||
@@ -37,7 +37,7 @@ RabbitMQ stoppen | |||
37 | Konfigure RabbitMQ in wallabag | 37 | Konfigure RabbitMQ in wallabag |
38 | ------------------------------ | 38 | ------------------------------ |
39 | 39 | ||
40 | Bearbeite die Datei ``parameters.yml``, um die RabbitMQ Konfiguration einzurichten. Die Standardkonfiguration sollte ok sein: | 40 | Bearbeite die Datei ``app/config/parameters.yml``, um die RabbitMQ Konfiguration einzurichten. Die Standardkonfiguration sollte ok sein: |
41 | 41 | ||
42 | .. code:: yaml | 42 | .. code:: yaml |
43 | 43 | ||
diff --git a/docs/de/developer/redis.rst b/docs/de/developer/redis.rst index 57b41550..2505bf24 100644 --- a/docs/de/developer/redis.rst +++ b/docs/de/developer/redis.rst | |||
@@ -28,7 +28,7 @@ Der Redis Service läuft eventuell schon direkt nach der Installation. Falls nic | |||
28 | Konfigure Redis in wallabag | 28 | Konfigure Redis in wallabag |
29 | --------------------------- | 29 | --------------------------- |
30 | 30 | ||
31 | Bearbeite die Datei ``parameters.yml``, um die RabbitMQ Konfiguration einzurichten. Die Standardkonfiguration sollte ok sein: | 31 | Bearbeite die Datei ``app/config/parameters.yml``, um die RabbitMQ Konfiguration einzurichten. Die Standardkonfiguration sollte ok sein: |
32 | 32 | ||
33 | .. code:: yaml | 33 | .. code:: yaml |
34 | 34 | ||
diff --git a/docs/de/user/faq.rst b/docs/de/user/faq.rst index 1a199c1c..c14cb3ef 100644 --- a/docs/de/user/faq.rst +++ b/docs/de/user/faq.rst | |||
@@ -43,3 +43,10 @@ Ich habe mein Passwort vergessen | |||
43 | 43 | ||
44 | Du kannst dein Passwort zurücksetzen, indem du auf den Link ``Kennwort vergessen?`` auf der Loginseite klickst. Fülle dann das Formular mit deiner E-Mail-Adresse oder deinem Nutzernamen aus | 44 | Du kannst dein Passwort zurücksetzen, indem du auf den Link ``Kennwort vergessen?`` auf der Loginseite klickst. Fülle dann das Formular mit deiner E-Mail-Adresse oder deinem Nutzernamen aus |
45 | und du wirst eine E-Mail zum Passwort zurücksetzen erhalten. | 45 | und du wirst eine E-Mail zum Passwort zurücksetzen erhalten. |
46 | |||
47 | Ich erhalte den Fehler ``failed to load external entity``, wenn ich wallabag installiere | ||
48 | ---------------------------------------------------------------------------------------- | ||
49 | |||
50 | Wie `hier <https://github.com/wallabag/wallabag/issues/2529>`_ beschrieben, bearbeite bitte deine Datei ``web/app.php`` und füge ihr diese Zeile ``libxml_disable_entity_loader(false);`` in Zeile 5 hinzu. | ||
51 | |||
52 | Dies ist ein Doctrine / PHP Fehler - nichts, woran wir etwas ändern können. | ||
diff --git a/docs/en/developer/api.rst b/docs/en/developer/api.rst index 4828cddd..b6c9ed3f 100644 --- a/docs/en/developer/api.rst +++ b/docs/en/developer/api.rst | |||
@@ -263,7 +263,8 @@ Third party resources | |||
263 | 263 | ||
264 | Some applications or libraries use our API. Here is a non-exhaustive list of them: | 264 | Some applications or libraries use our API. Here is a non-exhaustive list of them: |
265 | 265 | ||
266 | - `Java wrapper for the wallabag API <https://github.com/Strubbl/wallabag-java>`_ by Strubbl. | 266 | - `Java wrapper for the wallabag API <https://github.com/Strubbl/jWallabag>`_ by Strubbl. |
267 | - `.NET library for the wallabag v2 API <https://github.com/jlnostr/wallabag-api>`_ by Julian Oster. | 267 | - `.NET library for the wallabag v2 API <https://github.com/jlnostr/wallabag-api>`_ by Julian Oster. |
268 | - `Python API for wallabag <https://github.com/foxmask/wallabag_api>`_ by FoxMaSk, for his project `Trigger Happy <https://blog.trigger-happy.eu/>`_. | 268 | - `Python API for wallabag <https://github.com/foxmask/wallabag_api>`_ by FoxMaSk, for his project `Trigger Happy <https://blog.trigger-happy.eu/>`_. |
269 | - `A plugin <https://github.com/joshp23/ttrss-to-wallabag-v2>`_ designed for `Tiny Tiny RSS <https://tt-rss.org/gitlab/fox/tt-rss/wikis/home>`_ that makes use of the wallabag v2 API. By Josh Panter. | 269 | - `A plugin <https://github.com/joshp23/ttrss-to-wallabag-v2>`_ designed for `Tiny Tiny RSS <https://tt-rss.org/gitlab/fox/tt-rss/wikis/home>`_ that makes use of the wallabag v2 API. By Josh Panter. |
270 | - `Golang wrapper for the wallabag API <https://github.com/Strubbl/wallabago>`_ by Strubbl, for his project `wallabag-stats graph <https://github.com/Strubbl/wallabag-stats>`_. | ||
diff --git a/docs/en/developer/rabbitmq.rst b/docs/en/developer/rabbitmq.rst index 673228e9..7ee8a5ce 100644 --- a/docs/en/developer/rabbitmq.rst +++ b/docs/en/developer/rabbitmq.rst | |||
@@ -37,7 +37,7 @@ Stop RabbitMQ | |||
37 | Configure RabbitMQ in wallabag | 37 | Configure RabbitMQ in wallabag |
38 | ------------------------------ | 38 | ------------------------------ |
39 | 39 | ||
40 | Edit your ``parameters.yml`` file to edit RabbitMQ configuration. The default one should be ok: | 40 | Edit your ``app/config/parameters.yml`` file to edit RabbitMQ configuration. The default one should be ok: |
41 | 41 | ||
42 | .. code:: yaml | 42 | .. code:: yaml |
43 | 43 | ||
diff --git a/docs/en/developer/redis.rst b/docs/en/developer/redis.rst index 2e2bbbea..ea084e66 100644 --- a/docs/en/developer/redis.rst +++ b/docs/en/developer/redis.rst | |||
@@ -28,7 +28,7 @@ The server might be already running after installing, if not you can launch it u | |||
28 | Configure Redis in wallabag | 28 | Configure Redis in wallabag |
29 | --------------------------- | 29 | --------------------------- |
30 | 30 | ||
31 | Edit your ``parameters.yml`` file to edit Redis configuration. The default one should be ok: | 31 | Edit your ``app/config/parameters.yml`` file to edit Redis configuration. The default one should be ok: |
32 | 32 | ||
33 | .. code:: yaml | 33 | .. code:: yaml |
34 | 34 | ||
diff --git a/docs/en/user/faq.rst b/docs/en/user/faq.rst index 61303604..0f995ce5 100644 --- a/docs/en/user/faq.rst +++ b/docs/en/user/faq.rst | |||
@@ -46,3 +46,10 @@ I forgot my password | |||
46 | You can reset your password by clicking on ``Forgot your password?`` link, | 46 | You can reset your password by clicking on ``Forgot your password?`` link, |
47 | on the login page. Then, fill the form with your email address or your username, | 47 | on the login page. Then, fill the form with your email address or your username, |
48 | you'll receive an email to reset your password. | 48 | you'll receive an email to reset your password. |
49 | |||
50 | I've got the ``failed to load external entity`` error when I try to install wallabag | ||
51 | ------------------------------------------------------------------------------------ | ||
52 | |||
53 | As described `here <https://github.com/wallabag/wallabag/issues/2529>`_, please edit your ``web/app.php`` file and add this line: ``libxml_disable_entity_loader(false);`` on line 5. | ||
54 | |||
55 | This is a Doctrine / PHP bug, nothing we can do about it. | ||
diff --git a/docs/fr/developer/api.rst b/docs/fr/developer/api.rst index a0710a96..8a6e2a13 100644 --- a/docs/fr/developer/api.rst +++ b/docs/fr/developer/api.rst | |||
@@ -263,7 +263,8 @@ Ressources tierces | |||
263 | 263 | ||
264 | Certaines applications ou bibliothèques utilisent notre API. En voici une liste non exhaustive : | 264 | Certaines applications ou bibliothèques utilisent notre API. En voici une liste non exhaustive : |
265 | 265 | ||
266 | - `Java wrapper for the wallabag API <https://github.com/Strubbl/wallabag-java>`_ par Strubbl. | 266 | - `Java wrapper for the wallabag API <https://github.com/Strubbl/jWallabag>`_ par Strubbl. |
267 | - `.NET library for the wallabag v2 API <https://github.com/jlnostr/wallabag-api>`_ par Julian Oster. | 267 | - `.NET library for the wallabag v2 API <https://github.com/jlnostr/wallabag-api>`_ par Julian Oster. |
268 | - `Python API for wallabag <https://github.com/foxmask/wallabag_api>`_ par FoxMaSk, pour son projet `Trigger Happy <https://blog.trigger-happy.eu/>`_. | 268 | - `Python API for wallabag <https://github.com/foxmask/wallabag_api>`_ par FoxMaSk, pour son projet `Trigger Happy <https://blog.trigger-happy.eu/>`_. |
269 | - `Un plugin <https://github.com/joshp23/ttrss-to-wallabag-v2>`_ conçu pour `Tiny Tiny RSS <https://tt-rss.org/gitlab/fox/tt-rss/wikis/home>`_ qui utilise l'API wallabag v2. Par Josh Panter. | 269 | - `Un plugin <https://github.com/joshp23/ttrss-to-wallabag-v2>`_ conçu pour `Tiny Tiny RSS <https://tt-rss.org/gitlab/fox/tt-rss/wikis/home>`_ qui utilise l'API wallabag v2. Par Josh Panter. |
270 | - `Golang wrapper for the wallabag API <https://github.com/Strubbl/wallabago>`_ par Strubbl, pour son projet `wallabag-stats graphe <https://github.com/Strubbl/wallabag-stats>`_. | ||
diff --git a/docs/fr/developer/rabbitmq.rst b/docs/fr/developer/rabbitmq.rst index 92db5a28..b534a48b 100644 --- a/docs/fr/developer/rabbitmq.rst +++ b/docs/fr/developer/rabbitmq.rst | |||
@@ -37,7 +37,7 @@ Arrêter RabbitMQ | |||
37 | Configurer RabbitMQ dans wallabag | 37 | Configurer RabbitMQ dans wallabag |
38 | --------------------------------- | 38 | --------------------------------- |
39 | 39 | ||
40 | Modifiez votre fichier ``parameters.yml`` pour éditer la configuration RabbitMQ. Celle par défaut devrait convenir : | 40 | Modifiez votre fichier ``app/config/parameters.yml`` pour éditer la configuration RabbitMQ. Celle par défaut devrait convenir : |
41 | 41 | ||
42 | .. code:: yaml | 42 | .. code:: yaml |
43 | 43 | ||
diff --git a/docs/fr/developer/redis.rst b/docs/fr/developer/redis.rst index 8a212e8a..58204d57 100644 --- a/docs/fr/developer/redis.rst +++ b/docs/fr/developer/redis.rst | |||
@@ -28,7 +28,7 @@ Le serveur devrait déjà être démarré après l'installation. Si ce n'est pas | |||
28 | Configurer Redis dans wallabag | 28 | Configurer Redis dans wallabag |
29 | ------------------------------- | 29 | ------------------------------- |
30 | 30 | ||
31 | Modifiez votre fichier ``parameters.yml`` pour éditer la configuration Redis. Celle par défaut devrait convenir : | 31 | Modifiez votre fichier ``app/config/parameters.yml`` pour éditer la configuration Redis. Celle par défaut devrait convenir : |
32 | 32 | ||
33 | .. code:: yaml | 33 | .. code:: yaml |
34 | 34 | ||
diff --git a/docs/fr/user/faq.rst b/docs/fr/user/faq.rst index e220c8a7..49aa94ba 100644 --- a/docs/fr/user/faq.rst +++ b/docs/fr/user/faq.rst | |||
@@ -33,3 +33,10 @@ J'ai oublié mon mot de passe | |||
33 | Vous pouvez réinitialiser votre mot de passe en cliquant sur ``Mot de passe oublié ?``, | 33 | Vous pouvez réinitialiser votre mot de passe en cliquant sur ``Mot de passe oublié ?``, |
34 | sur la page de connexion. Ensuite, renseignez votre adresse email ou votre nom d'utilisateur, | 34 | sur la page de connexion. Ensuite, renseignez votre adresse email ou votre nom d'utilisateur, |
35 | un email vous sera envoyé. | 35 | un email vous sera envoyé. |
36 | |||
37 | J'ai l'erreur ``failed to load external entity`` quand j'essaie d'installer wallabag | ||
38 | ------------------------------------------------------------------------------------ | ||
39 | |||
40 | Comme décrit `ici <https://github.com/wallabag/wallabag/issues/2529>`_, modifiez le fichier ``web/app.php`` et ajoutez la ligne ``libxml_disable_entity_loader(false);`` à la ligne 5. | ||
41 | |||
42 | C'est un bug lié à PHP et Doctrine, nous ne pouvons rien faire de notre côté. | ||
diff --git a/scripts/dev.sh b/scripts/dev.sh index 9b89da35..0703ced1 100644 --- a/scripts/dev.sh +++ b/scripts/dev.sh | |||
@@ -1,7 +1,13 @@ | |||
1 | #! /usr/bin/env bash | 1 | #!/usr/bin/env bash |
2 | # You can execute this file to install wallabag dev environmnet | 2 | # You can execute this file to install wallabag dev environment |
3 | # eg: `sh install.sh prod` | 3 | # eg: `sh dev.sh` |
4 | 4 | ||
5 | composer install | 5 | COMPOSER_COMMAND='composer' |
6 | |||
7 | DIR="${BASH_SOURCE}" | ||
8 | if [ ! -d "$DIR" ]; then DIR="$PWD/scripts"; fi | ||
9 | . "$DIR/require.sh" | ||
10 | |||
11 | $COMPOSER_COMMAND install | ||
6 | php bin/console wallabag:install | 12 | php bin/console wallabag:install |
7 | php bin/console server:run | 13 | php bin/console server:run |
diff --git a/scripts/install.sh b/scripts/install.sh index 54d0bb78..62a46f4f 100644 --- a/scripts/install.sh +++ b/scripts/install.sh | |||
@@ -1,10 +1,16 @@ | |||
1 | #! /usr/bin/env bash | 1 | #!/usr/bin/env bash |
2 | # You can execute this file to install wallabag | 2 | # You can execute this file to install wallabag |
3 | # eg: `sh install.sh prod` | 3 | # eg: `sh install.sh prod` |
4 | 4 | ||
5 | COMPOSER_COMMAND='composer' | ||
6 | |||
7 | DIR="${BASH_SOURCE}" | ||
8 | if [ ! -d "$DIR" ]; then DIR="$PWD/scripts"; fi | ||
9 | . "$DIR/require.sh" | ||
10 | |||
5 | ENV=$1 | 11 | ENV=$1 |
6 | TAG=$(git describe --tags $(git rev-list --tags --max-count=1)) | 12 | TAG=$(git describe --tags $(git rev-list --tags --max-count=1)) |
7 | 13 | ||
8 | git checkout $TAG | 14 | git checkout $TAG |
9 | SYMFONY_ENV=$ENV composer install --no-dev -o --prefer-dist | 15 | SYMFONY_ENV=$ENV $COMPOSER_COMMAND install --no-dev -o --prefer-dist |
10 | php bin/console wallabag:install --env=$ENV | 16 | php bin/console wallabag:install --env=$ENV |
diff --git a/scripts/require.sh b/scripts/require.sh new file mode 100644 index 00000000..ddfb3dd2 --- /dev/null +++ b/scripts/require.sh | |||
@@ -0,0 +1,9 @@ | |||
1 | #! /usr/bin/env bash | ||
2 | # File used to check dependencies | ||
3 | |||
4 | if [ ! -f composer.phar ]; then | ||
5 | echo "composer.phar not found, we'll see if composer is installed globally." | ||
6 | command -v composer >/dev/null 2>&1 || { echo >&2 "wallabag requires composer but it's not installed (see http://doc.wallabag.org/en/master/user/installation.html). Aborting."; exit 1; } | ||
7 | else | ||
8 | COMPOSER_COMMAND='composer.phar' | ||
9 | fi | ||
diff --git a/scripts/update.sh b/scripts/update.sh index b920a829..f43c4f24 100644 --- a/scripts/update.sh +++ b/scripts/update.sh | |||
@@ -1,7 +1,13 @@ | |||
1 | #! /usr/bin/env bash | 1 | #!/usr/bin/env bash |
2 | # You can execute this file to update wallabag | 2 | # You can execute this file to update wallabag |
3 | # eg: `sh update.sh prod` | 3 | # eg: `sh update.sh prod` |
4 | 4 | ||
5 | COMPOSER_COMMAND='composer' | ||
6 | |||
7 | DIR="${BASH_SOURCE}" | ||
8 | if [ ! -d "$DIR" ]; then DIR="$PWD/scripts"; fi | ||
9 | . "$DIR/require.sh" | ||
10 | |||
5 | ENV=$1 | 11 | ENV=$1 |
6 | TAG=$(git describe --tags $(git rev-list --tags --max-count=1)) | 12 | TAG=$(git describe --tags $(git rev-list --tags --max-count=1)) |
7 | 13 | ||
@@ -9,5 +15,5 @@ rm -rf var/cache/* | |||
9 | git fetch origin | 15 | git fetch origin |
10 | git fetch --tags | 16 | git fetch --tags |
11 | git checkout $TAG --force | 17 | git checkout $TAG --force |
12 | SYMFONY_ENV=$ENV composer install --no-dev -o --prefer-dist | 18 | SYMFONY_ENV=$ENV $COMPOSER_COMMAND install --no-dev -o --prefer-dist |
13 | php bin/console cache:clear --env=$ENV | 19 | php bin/console cache:clear --env=$ENV |
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php new file mode 100644 index 00000000..b3622c62 --- /dev/null +++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php | |||
@@ -0,0 +1,374 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\ApiBundle\Controller; | ||
4 | |||
5 | use Hateoas\Configuration\Route; | ||
6 | use Hateoas\Representation\Factory\PagerfantaFactory; | ||
7 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | ||
8 | use Symfony\Component\HttpFoundation\Request; | ||
9 | use Symfony\Component\HttpFoundation\JsonResponse; | ||
10 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
11 | use Wallabag\CoreBundle\Entity\Entry; | ||
12 | use Wallabag\CoreBundle\Entity\Tag; | ||
13 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
14 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; | ||
15 | |||
16 | class EntryRestController extends WallabagRestController | ||
17 | { | ||
18 | /** | ||
19 | * Check if an entry exist by url. | ||
20 | * | ||
21 | * @ApiDoc( | ||
22 | * parameters={ | ||
23 | * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"}, | ||
24 | * {"name"="urls", "dataType"="string", "required"=false, "format"="An array of urls (?urls[]=http...&urls[]=http...)", "description"="Urls (as an array) to check if it exists"} | ||
25 | * } | ||
26 | * ) | ||
27 | * | ||
28 | * @return JsonResponse | ||
29 | */ | ||
30 | public function getEntriesExistsAction(Request $request) | ||
31 | { | ||
32 | $this->validateAuthentication(); | ||
33 | |||
34 | $urls = $request->query->get('urls', []); | ||
35 | |||
36 | // handle multiple urls first | ||
37 | if (!empty($urls)) { | ||
38 | $results = []; | ||
39 | foreach ($urls as $url) { | ||
40 | $res = $this->getDoctrine() | ||
41 | ->getRepository('WallabagCoreBundle:Entry') | ||
42 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | ||
43 | |||
44 | $results[$url] = false === $res ? false : true; | ||
45 | } | ||
46 | |||
47 | $json = $this->get('serializer')->serialize($results, 'json'); | ||
48 | |||
49 | return (new JsonResponse())->setJson($json); | ||
50 | } | ||
51 | |||
52 | // let's see if it is a simple url? | ||
53 | $url = $request->query->get('url', ''); | ||
54 | |||
55 | if (empty($url)) { | ||
56 | throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId()); | ||
57 | } | ||
58 | |||
59 | $res = $this->getDoctrine() | ||
60 | ->getRepository('WallabagCoreBundle:Entry') | ||
61 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | ||
62 | |||
63 | $exists = false === $res ? false : true; | ||
64 | |||
65 | $json = $this->get('serializer')->serialize(['exists' => $exists], 'json'); | ||
66 | |||
67 | return (new JsonResponse())->setJson($json); | ||
68 | } | ||
69 | |||
70 | /** | ||
71 | * Retrieve all entries. It could be filtered by many options. | ||
72 | * | ||
73 | * @ApiDoc( | ||
74 | * parameters={ | ||
75 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by archived status."}, | ||
76 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by starred status."}, | ||
77 | * {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."}, | ||
78 | * {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."}, | ||
79 | * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."}, | ||
80 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, | ||
81 | * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, | ||
82 | * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, | ||
83 | * } | ||
84 | * ) | ||
85 | * | ||
86 | * @return JsonResponse | ||
87 | */ | ||
88 | public function getEntriesAction(Request $request) | ||
89 | { | ||
90 | $this->validateAuthentication(); | ||
91 | |||
92 | $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive'); | ||
93 | $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred'); | ||
94 | $sort = $request->query->get('sort', 'created'); | ||
95 | $order = $request->query->get('order', 'desc'); | ||
96 | $page = (int) $request->query->get('page', 1); | ||
97 | $perPage = (int) $request->query->get('perPage', 30); | ||
98 | $tags = $request->query->get('tags', ''); | ||
99 | $since = $request->query->get('since', 0); | ||
100 | |||
101 | $pager = $this->getDoctrine() | ||
102 | ->getRepository('WallabagCoreBundle:Entry') | ||
103 | ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags); | ||
104 | |||
105 | $pager->setCurrentPage($page); | ||
106 | $pager->setMaxPerPage($perPage); | ||
107 | |||
108 | $pagerfantaFactory = new PagerfantaFactory('page', 'perPage'); | ||
109 | $paginatedCollection = $pagerfantaFactory->createRepresentation( | ||
110 | $pager, | ||
111 | new Route( | ||
112 | 'api_get_entries', | ||
113 | [ | ||
114 | 'archive' => $isArchived, | ||
115 | 'starred' => $isStarred, | ||
116 | 'sort' => $sort, | ||
117 | 'order' => $order, | ||
118 | 'page' => $page, | ||
119 | 'perPage' => $perPage, | ||
120 | 'tags' => $tags, | ||
121 | 'since' => $since, | ||
122 | ], | ||
123 | UrlGeneratorInterface::ABSOLUTE_URL | ||
124 | ) | ||
125 | ); | ||
126 | |||
127 | $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); | ||
128 | |||
129 | return (new JsonResponse())->setJson($json); | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * Retrieve a single entry. | ||
134 | * | ||
135 | * @ApiDoc( | ||
136 | * requirements={ | ||
137 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
138 | * } | ||
139 | * ) | ||
140 | * | ||
141 | * @return JsonResponse | ||
142 | */ | ||
143 | public function getEntryAction(Entry $entry) | ||
144 | { | ||
145 | $this->validateAuthentication(); | ||
146 | $this->validateUserAccess($entry->getUser()->getId()); | ||
147 | |||
148 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
149 | |||
150 | return (new JsonResponse())->setJson($json); | ||
151 | } | ||
152 | |||
153 | /** | ||
154 | * Create an entry. | ||
155 | * | ||
156 | * @ApiDoc( | ||
157 | * parameters={ | ||
158 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, | ||
159 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, | ||
160 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
161 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, | ||
162 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, | ||
163 | * } | ||
164 | * ) | ||
165 | * | ||
166 | * @return JsonResponse | ||
167 | */ | ||
168 | public function postEntriesAction(Request $request) | ||
169 | { | ||
170 | $this->validateAuthentication(); | ||
171 | |||
172 | $url = $request->request->get('url'); | ||
173 | $title = $request->request->get('title'); | ||
174 | $isArchived = $request->request->get('archive'); | ||
175 | $isStarred = $request->request->get('starred'); | ||
176 | |||
177 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId()); | ||
178 | |||
179 | if (false === $entry) { | ||
180 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry( | ||
181 | new Entry($this->getUser()), | ||
182 | $url | ||
183 | ); | ||
184 | } | ||
185 | |||
186 | if (!is_null($title)) { | ||
187 | $entry->setTitle($title); | ||
188 | } | ||
189 | |||
190 | $tags = $request->request->get('tags', ''); | ||
191 | if (!empty($tags)) { | ||
192 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
193 | } | ||
194 | |||
195 | if (!is_null($isStarred)) { | ||
196 | $entry->setStarred((bool) $isStarred); | ||
197 | } | ||
198 | |||
199 | if (!is_null($isArchived)) { | ||
200 | $entry->setArchived((bool) $isArchived); | ||
201 | } | ||
202 | |||
203 | $em = $this->getDoctrine()->getManager(); | ||
204 | $em->persist($entry); | ||
205 | $em->flush(); | ||
206 | |||
207 | // entry saved, dispatch event about it! | ||
208 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
209 | |||
210 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
211 | |||
212 | return (new JsonResponse())->setJson($json); | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * Change several properties of an entry. | ||
217 | * | ||
218 | * @ApiDoc( | ||
219 | * requirements={ | ||
220 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
221 | * }, | ||
222 | * parameters={ | ||
223 | * {"name"="title", "dataType"="string", "required"=false}, | ||
224 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
225 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."}, | ||
226 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, | ||
227 | * } | ||
228 | * ) | ||
229 | * | ||
230 | * @return JsonResponse | ||
231 | */ | ||
232 | public function patchEntriesAction(Entry $entry, Request $request) | ||
233 | { | ||
234 | $this->validateAuthentication(); | ||
235 | $this->validateUserAccess($entry->getUser()->getId()); | ||
236 | |||
237 | $title = $request->request->get('title'); | ||
238 | $isArchived = $request->request->get('archive'); | ||
239 | $isStarred = $request->request->get('starred'); | ||
240 | |||
241 | if (!is_null($title)) { | ||
242 | $entry->setTitle($title); | ||
243 | } | ||
244 | |||
245 | if (!is_null($isArchived)) { | ||
246 | $entry->setArchived((bool) $isArchived); | ||
247 | } | ||
248 | |||
249 | if (!is_null($isStarred)) { | ||
250 | $entry->setStarred((bool) $isStarred); | ||
251 | } | ||
252 | |||
253 | $tags = $request->request->get('tags', ''); | ||
254 | if (!empty($tags)) { | ||
255 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
256 | } | ||
257 | |||
258 | $em = $this->getDoctrine()->getManager(); | ||
259 | $em->flush(); | ||
260 | |||
261 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
262 | |||
263 | return (new JsonResponse())->setJson($json); | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * Delete **permanently** an entry. | ||
268 | * | ||
269 | * @ApiDoc( | ||
270 | * requirements={ | ||
271 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
272 | * } | ||
273 | * ) | ||
274 | * | ||
275 | * @return JsonResponse | ||
276 | */ | ||
277 | public function deleteEntriesAction(Entry $entry) | ||
278 | { | ||
279 | $this->validateAuthentication(); | ||
280 | $this->validateUserAccess($entry->getUser()->getId()); | ||
281 | |||
282 | $em = $this->getDoctrine()->getManager(); | ||
283 | $em->remove($entry); | ||
284 | $em->flush(); | ||
285 | |||
286 | // entry deleted, dispatch event about it! | ||
287 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | ||
288 | |||
289 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
290 | |||
291 | return (new JsonResponse())->setJson($json); | ||
292 | } | ||
293 | |||
294 | /** | ||
295 | * Retrieve all tags for an entry. | ||
296 | * | ||
297 | * @ApiDoc( | ||
298 | * requirements={ | ||
299 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
300 | * } | ||
301 | * ) | ||
302 | * | ||
303 | * @return JsonResponse | ||
304 | */ | ||
305 | public function getEntriesTagsAction(Entry $entry) | ||
306 | { | ||
307 | $this->validateAuthentication(); | ||
308 | $this->validateUserAccess($entry->getUser()->getId()); | ||
309 | |||
310 | $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); | ||
311 | |||
312 | return (new JsonResponse())->setJson($json); | ||
313 | } | ||
314 | |||
315 | /** | ||
316 | * Add one or more tags to an entry. | ||
317 | * | ||
318 | * @ApiDoc( | ||
319 | * requirements={ | ||
320 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
321 | * }, | ||
322 | * parameters={ | ||
323 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
324 | * } | ||
325 | * ) | ||
326 | * | ||
327 | * @return JsonResponse | ||
328 | */ | ||
329 | public function postEntriesTagsAction(Request $request, Entry $entry) | ||
330 | { | ||
331 | $this->validateAuthentication(); | ||
332 | $this->validateUserAccess($entry->getUser()->getId()); | ||
333 | |||
334 | $tags = $request->request->get('tags', ''); | ||
335 | if (!empty($tags)) { | ||
336 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
337 | } | ||
338 | |||
339 | $em = $this->getDoctrine()->getManager(); | ||
340 | $em->persist($entry); | ||
341 | $em->flush(); | ||
342 | |||
343 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
344 | |||
345 | return (new JsonResponse())->setJson($json); | ||
346 | } | ||
347 | |||
348 | /** | ||
349 | * Permanently remove one tag for an entry. | ||
350 | * | ||
351 | * @ApiDoc( | ||
352 | * requirements={ | ||
353 | * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"}, | ||
354 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
355 | * } | ||
356 | * ) | ||
357 | * | ||
358 | * @return JsonResponse | ||
359 | */ | ||
360 | public function deleteEntriesTagsAction(Entry $entry, Tag $tag) | ||
361 | { | ||
362 | $this->validateAuthentication(); | ||
363 | $this->validateUserAccess($entry->getUser()->getId()); | ||
364 | |||
365 | $entry->removeTag($tag); | ||
366 | $em = $this->getDoctrine()->getManager(); | ||
367 | $em->persist($entry); | ||
368 | $em->flush(); | ||
369 | |||
370 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
371 | |||
372 | return (new JsonResponse())->setJson($json); | ||
373 | } | ||
374 | } | ||
diff --git a/src/Wallabag/ApiBundle/Controller/TagRestController.php b/src/Wallabag/ApiBundle/Controller/TagRestController.php new file mode 100644 index 00000000..4e7ddc66 --- /dev/null +++ b/src/Wallabag/ApiBundle/Controller/TagRestController.php | |||
@@ -0,0 +1,171 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\ApiBundle\Controller; | ||
4 | |||
5 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | ||
6 | use Symfony\Component\HttpFoundation\Request; | ||
7 | use Symfony\Component\HttpFoundation\JsonResponse; | ||
8 | use Wallabag\CoreBundle\Entity\Entry; | ||
9 | use Wallabag\CoreBundle\Entity\Tag; | ||
10 | |||
11 | class TagRestController extends WallabagRestController | ||
12 | { | ||
13 | /** | ||
14 | * Retrieve all tags. | ||
15 | * | ||
16 | * @ApiDoc() | ||
17 | * | ||
18 | * @return JsonResponse | ||
19 | */ | ||
20 | public function getTagsAction() | ||
21 | { | ||
22 | $this->validateAuthentication(); | ||
23 | |||
24 | $tags = $this->getDoctrine() | ||
25 | ->getRepository('WallabagCoreBundle:Tag') | ||
26 | ->findAllTags($this->getUser()->getId()); | ||
27 | |||
28 | $json = $this->get('serializer')->serialize($tags, 'json'); | ||
29 | |||
30 | return (new JsonResponse())->setJson($json); | ||
31 | } | ||
32 | |||
33 | /** | ||
34 | * Permanently remove one tag from **every** entry. | ||
35 | * | ||
36 | * @ApiDoc( | ||
37 | * requirements={ | ||
38 | * {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"} | ||
39 | * } | ||
40 | * ) | ||
41 | * | ||
42 | * @return JsonResponse | ||
43 | */ | ||
44 | public function deleteTagLabelAction(Request $request) | ||
45 | { | ||
46 | $this->validateAuthentication(); | ||
47 | $label = $request->request->get('tag', ''); | ||
48 | |||
49 | $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label); | ||
50 | |||
51 | if (empty($tag)) { | ||
52 | throw $this->createNotFoundException('Tag not found'); | ||
53 | } | ||
54 | |||
55 | $this->getDoctrine() | ||
56 | ->getRepository('WallabagCoreBundle:Entry') | ||
57 | ->removeTag($this->getUser()->getId(), $tag); | ||
58 | |||
59 | $this->cleanOrphanTag($tag); | ||
60 | |||
61 | $json = $this->get('serializer')->serialize($tag, 'json'); | ||
62 | |||
63 | return (new JsonResponse())->setJson($json); | ||
64 | } | ||
65 | |||
66 | /** | ||
67 | * Permanently remove some tags from **every** entry. | ||
68 | * | ||
69 | * @ApiDoc( | ||
70 | * requirements={ | ||
71 | * {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"} | ||
72 | * } | ||
73 | * ) | ||
74 | * | ||
75 | * @return JsonResponse | ||
76 | */ | ||
77 | public function deleteTagsLabelAction(Request $request) | ||
78 | { | ||
79 | $this->validateAuthentication(); | ||
80 | |||
81 | $tagsLabels = $request->request->get('tags', ''); | ||
82 | |||
83 | $tags = []; | ||
84 | |||
85 | foreach (explode(',', $tagsLabels) as $tagLabel) { | ||
86 | $tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel); | ||
87 | |||
88 | if (!empty($tagEntity)) { | ||
89 | $tags[] = $tagEntity; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | if (empty($tags)) { | ||
94 | throw $this->createNotFoundException('Tags not found'); | ||
95 | } | ||
96 | |||
97 | $this->getDoctrine() | ||
98 | ->getRepository('WallabagCoreBundle:Entry') | ||
99 | ->removeTags($this->getUser()->getId(), $tags); | ||
100 | |||
101 | $this->cleanOrphanTag($tags); | ||
102 | |||
103 | $json = $this->get('serializer')->serialize($tags, 'json'); | ||
104 | |||
105 | return (new JsonResponse())->setJson($json); | ||
106 | } | ||
107 | |||
108 | /** | ||
109 | * Permanently remove one tag from **every** entry. | ||
110 | * | ||
111 | * @ApiDoc( | ||
112 | * requirements={ | ||
113 | * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"} | ||
114 | * } | ||
115 | * ) | ||
116 | * | ||
117 | * @return JsonResponse | ||
118 | */ | ||
119 | public function deleteTagAction(Tag $tag) | ||
120 | { | ||
121 | $this->validateAuthentication(); | ||
122 | |||
123 | $this->getDoctrine() | ||
124 | ->getRepository('WallabagCoreBundle:Entry') | ||
125 | ->removeTag($this->getUser()->getId(), $tag); | ||
126 | |||
127 | $this->cleanOrphanTag($tag); | ||
128 | |||
129 | $json = $this->get('serializer')->serialize($tag, 'json'); | ||
130 | |||
131 | return (new JsonResponse())->setJson($json); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Retrieve version number. | ||
136 | * | ||
137 | * @ApiDoc() | ||
138 | * | ||
139 | * @return JsonResponse | ||
140 | */ | ||
141 | public function getVersionAction() | ||
142 | { | ||
143 | $version = $this->container->getParameter('wallabag_core.version'); | ||
144 | |||
145 | $json = $this->get('serializer')->serialize($version, 'json'); | ||
146 | |||
147 | return (new JsonResponse())->setJson($json); | ||
148 | } | ||
149 | |||
150 | /** | ||
151 | * Remove orphan tag in case no entries are associated to it. | ||
152 | * | ||
153 | * @param Tag|array $tags | ||
154 | */ | ||
155 | private function cleanOrphanTag($tags) | ||
156 | { | ||
157 | if (!is_array($tags)) { | ||
158 | $tags = [$tags]; | ||
159 | } | ||
160 | |||
161 | $em = $this->getDoctrine()->getManager(); | ||
162 | |||
163 | foreach ($tags as $tag) { | ||
164 | if (count($tag->getEntries()) === 0) { | ||
165 | $em->remove($tag); | ||
166 | } | ||
167 | } | ||
168 | |||
169 | $em->flush(); | ||
170 | } | ||
171 | } | ||
diff --git a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php index 50652b77..544c1ea9 100644 --- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php +++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php | |||
@@ -3,627 +3,11 @@ | |||
3 | namespace Wallabag\ApiBundle\Controller; | 3 | namespace Wallabag\ApiBundle\Controller; |
4 | 4 | ||
5 | use FOS\RestBundle\Controller\FOSRestController; | 5 | use FOS\RestBundle\Controller\FOSRestController; |
6 | use Hateoas\Configuration\Route as HateoasRoute; | ||
7 | use Hateoas\Representation\Factory\PagerfantaFactory; | ||
8 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | ||
9 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | ||
10 | use Symfony\Component\HttpFoundation\Request; | ||
11 | use Symfony\Component\HttpFoundation\JsonResponse; | ||
12 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | ||
13 | use Symfony\Component\Security\Core\Exception\AccessDeniedException; | 6 | use Symfony\Component\Security\Core\Exception\AccessDeniedException; |
14 | use Wallabag\CoreBundle\Entity\Entry; | 7 | use Wallabag\CoreBundle\Entity\Entry; |
15 | use Wallabag\CoreBundle\Entity\Tag; | ||
16 | use Wallabag\AnnotationBundle\Entity\Annotation; | ||
17 | use Wallabag\CoreBundle\Event\EntrySavedEvent; | ||
18 | use Wallabag\CoreBundle\Event\EntryDeletedEvent; | ||
19 | 8 | ||
20 | class WallabagRestController extends FOSRestController | 9 | class WallabagRestController extends FOSRestController |
21 | { | 10 | { |
22 | private function validateAuthentication() | ||
23 | { | ||
24 | if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) { | ||
25 | throw new AccessDeniedException(); | ||
26 | } | ||
27 | } | ||
28 | |||
29 | /** | ||
30 | * Check if an entry exist by url. | ||
31 | * | ||
32 | * @ApiDoc( | ||
33 | * parameters={ | ||
34 | * {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"}, | ||
35 | * {"name"="urls", "dataType"="string", "required"=false, "format"="An array of urls (?urls[]=http...&urls[]=http...)", "description"="Urls (as an array) to check if it exists"} | ||
36 | * } | ||
37 | * ) | ||
38 | * | ||
39 | * @return JsonResponse | ||
40 | */ | ||
41 | public function getEntriesExistsAction(Request $request) | ||
42 | { | ||
43 | $this->validateAuthentication(); | ||
44 | |||
45 | $urls = $request->query->get('urls', []); | ||
46 | |||
47 | // handle multiple urls first | ||
48 | if (!empty($urls)) { | ||
49 | $results = []; | ||
50 | foreach ($urls as $url) { | ||
51 | $res = $this->getDoctrine() | ||
52 | ->getRepository('WallabagCoreBundle:Entry') | ||
53 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | ||
54 | |||
55 | $results[$url] = false === $res ? false : true; | ||
56 | } | ||
57 | |||
58 | $json = $this->get('serializer')->serialize($results, 'json'); | ||
59 | |||
60 | return (new JsonResponse())->setJson($json); | ||
61 | } | ||
62 | |||
63 | // let's see if it is a simple url? | ||
64 | $url = $request->query->get('url', ''); | ||
65 | |||
66 | if (empty($url)) { | ||
67 | throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId()); | ||
68 | } | ||
69 | |||
70 | $res = $this->getDoctrine() | ||
71 | ->getRepository('WallabagCoreBundle:Entry') | ||
72 | ->findByUrlAndUserId($url, $this->getUser()->getId()); | ||
73 | |||
74 | $exists = false === $res ? false : true; | ||
75 | |||
76 | $json = $this->get('serializer')->serialize(['exists' => $exists], 'json'); | ||
77 | |||
78 | return (new JsonResponse())->setJson($json); | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * Retrieve all entries. It could be filtered by many options. | ||
83 | * | ||
84 | * @ApiDoc( | ||
85 | * parameters={ | ||
86 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by archived status."}, | ||
87 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by starred status."}, | ||
88 | * {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."}, | ||
89 | * {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."}, | ||
90 | * {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."}, | ||
91 | * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, | ||
92 | * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, | ||
93 | * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, | ||
94 | * } | ||
95 | * ) | ||
96 | * | ||
97 | * @return JsonResponse | ||
98 | */ | ||
99 | public function getEntriesAction(Request $request) | ||
100 | { | ||
101 | $this->validateAuthentication(); | ||
102 | |||
103 | $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive'); | ||
104 | $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred'); | ||
105 | $sort = $request->query->get('sort', 'created'); | ||
106 | $order = $request->query->get('order', 'desc'); | ||
107 | $page = (int) $request->query->get('page', 1); | ||
108 | $perPage = (int) $request->query->get('perPage', 30); | ||
109 | $tags = $request->query->get('tags', ''); | ||
110 | $since = $request->query->get('since', 0); | ||
111 | |||
112 | $pager = $this->getDoctrine() | ||
113 | ->getRepository('WallabagCoreBundle:Entry') | ||
114 | ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags); | ||
115 | |||
116 | $pager->setCurrentPage($page); | ||
117 | $pager->setMaxPerPage($perPage); | ||
118 | |||
119 | $pagerfantaFactory = new PagerfantaFactory('page', 'perPage'); | ||
120 | $paginatedCollection = $pagerfantaFactory->createRepresentation( | ||
121 | $pager, | ||
122 | new HateoasRoute( | ||
123 | 'api_get_entries', | ||
124 | [ | ||
125 | 'archive' => $isArchived, | ||
126 | 'starred' => $isStarred, | ||
127 | 'sort' => $sort, | ||
128 | 'order' => $order, | ||
129 | 'page' => $page, | ||
130 | 'perPage' => $perPage, | ||
131 | 'tags' => $tags, | ||
132 | 'since' => $since, | ||
133 | ], | ||
134 | UrlGeneratorInterface::ABSOLUTE_URL | ||
135 | ) | ||
136 | ); | ||
137 | |||
138 | $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); | ||
139 | |||
140 | return (new JsonResponse())->setJson($json); | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * Retrieve a single entry. | ||
145 | * | ||
146 | * @ApiDoc( | ||
147 | * requirements={ | ||
148 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
149 | * } | ||
150 | * ) | ||
151 | * | ||
152 | * @return JsonResponse | ||
153 | */ | ||
154 | public function getEntryAction(Entry $entry) | ||
155 | { | ||
156 | $this->validateAuthentication(); | ||
157 | $this->validateUserAccess($entry->getUser()->getId()); | ||
158 | |||
159 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
160 | |||
161 | return (new JsonResponse())->setJson($json); | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * Retrieve a single entry as a predefined format. | ||
166 | * | ||
167 | * @ApiDoc( | ||
168 | * requirements={ | ||
169 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
170 | * } | ||
171 | * ) | ||
172 | * | ||
173 | * @return Response | ||
174 | */ | ||
175 | public function getEntryExportAction(Entry $entry, Request $request) | ||
176 | { | ||
177 | $this->validateAuthentication(); | ||
178 | $this->validateUserAccess($entry->getUser()->getId()); | ||
179 | |||
180 | return $this->get('wallabag_core.helper.entries_export') | ||
181 | ->setEntries($entry) | ||
182 | ->updateTitle('entry') | ||
183 | ->exportAs($request->attributes->get('_format')); | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * Create an entry. | ||
188 | * | ||
189 | * @ApiDoc( | ||
190 | * parameters={ | ||
191 | * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, | ||
192 | * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, | ||
193 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
194 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, | ||
195 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, | ||
196 | * } | ||
197 | * ) | ||
198 | * | ||
199 | * @return JsonResponse | ||
200 | */ | ||
201 | public function postEntriesAction(Request $request) | ||
202 | { | ||
203 | $this->validateAuthentication(); | ||
204 | |||
205 | $url = $request->request->get('url'); | ||
206 | $title = $request->request->get('title'); | ||
207 | $isArchived = $request->request->get('archive'); | ||
208 | $isStarred = $request->request->get('starred'); | ||
209 | |||
210 | $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId()); | ||
211 | |||
212 | if (false === $entry) { | ||
213 | $entry = $this->get('wallabag_core.content_proxy')->updateEntry( | ||
214 | new Entry($this->getUser()), | ||
215 | $url | ||
216 | ); | ||
217 | } | ||
218 | |||
219 | if (!is_null($title)) { | ||
220 | $entry->setTitle($title); | ||
221 | } | ||
222 | |||
223 | $tags = $request->request->get('tags', ''); | ||
224 | if (!empty($tags)) { | ||
225 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
226 | } | ||
227 | |||
228 | if (!is_null($isStarred)) { | ||
229 | $entry->setStarred((bool) $isStarred); | ||
230 | } | ||
231 | |||
232 | if (!is_null($isArchived)) { | ||
233 | $entry->setArchived((bool) $isArchived); | ||
234 | } | ||
235 | |||
236 | $em = $this->getDoctrine()->getManager(); | ||
237 | $em->persist($entry); | ||
238 | $em->flush(); | ||
239 | |||
240 | // entry saved, dispatch event about it! | ||
241 | $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); | ||
242 | |||
243 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
244 | |||
245 | return (new JsonResponse())->setJson($json); | ||
246 | } | ||
247 | |||
248 | /** | ||
249 | * Change several properties of an entry. | ||
250 | * | ||
251 | * @ApiDoc( | ||
252 | * requirements={ | ||
253 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
254 | * }, | ||
255 | * parameters={ | ||
256 | * {"name"="title", "dataType"="string", "required"=false}, | ||
257 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
258 | * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."}, | ||
259 | * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, | ||
260 | * } | ||
261 | * ) | ||
262 | * | ||
263 | * @return JsonResponse | ||
264 | */ | ||
265 | public function patchEntriesAction(Entry $entry, Request $request) | ||
266 | { | ||
267 | $this->validateAuthentication(); | ||
268 | $this->validateUserAccess($entry->getUser()->getId()); | ||
269 | |||
270 | $title = $request->request->get('title'); | ||
271 | $isArchived = $request->request->get('archive'); | ||
272 | $isStarred = $request->request->get('starred'); | ||
273 | |||
274 | if (!is_null($title)) { | ||
275 | $entry->setTitle($title); | ||
276 | } | ||
277 | |||
278 | if (!is_null($isArchived)) { | ||
279 | $entry->setArchived((bool) $isArchived); | ||
280 | } | ||
281 | |||
282 | if (!is_null($isStarred)) { | ||
283 | $entry->setStarred((bool) $isStarred); | ||
284 | } | ||
285 | |||
286 | $tags = $request->request->get('tags', ''); | ||
287 | if (!empty($tags)) { | ||
288 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
289 | } | ||
290 | |||
291 | $em = $this->getDoctrine()->getManager(); | ||
292 | $em->flush(); | ||
293 | |||
294 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
295 | |||
296 | return (new JsonResponse())->setJson($json); | ||
297 | } | ||
298 | |||
299 | /** | ||
300 | * Delete **permanently** an entry. | ||
301 | * | ||
302 | * @ApiDoc( | ||
303 | * requirements={ | ||
304 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
305 | * } | ||
306 | * ) | ||
307 | * | ||
308 | * @return JsonResponse | ||
309 | */ | ||
310 | public function deleteEntriesAction(Entry $entry) | ||
311 | { | ||
312 | $this->validateAuthentication(); | ||
313 | $this->validateUserAccess($entry->getUser()->getId()); | ||
314 | |||
315 | // entry deleted, dispatch event about it! | ||
316 | $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); | ||
317 | |||
318 | $em = $this->getDoctrine()->getManager(); | ||
319 | $em->remove($entry); | ||
320 | $em->flush(); | ||
321 | |||
322 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
323 | |||
324 | return (new JsonResponse())->setJson($json); | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * Retrieve all tags for an entry. | ||
329 | * | ||
330 | * @ApiDoc( | ||
331 | * requirements={ | ||
332 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
333 | * } | ||
334 | * ) | ||
335 | * | ||
336 | * @return JsonResponse | ||
337 | */ | ||
338 | public function getEntriesTagsAction(Entry $entry) | ||
339 | { | ||
340 | $this->validateAuthentication(); | ||
341 | $this->validateUserAccess($entry->getUser()->getId()); | ||
342 | |||
343 | $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); | ||
344 | |||
345 | return (new JsonResponse())->setJson($json); | ||
346 | } | ||
347 | |||
348 | /** | ||
349 | * Add one or more tags to an entry. | ||
350 | * | ||
351 | * @ApiDoc( | ||
352 | * requirements={ | ||
353 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
354 | * }, | ||
355 | * parameters={ | ||
356 | * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, | ||
357 | * } | ||
358 | * ) | ||
359 | * | ||
360 | * @return JsonResponse | ||
361 | */ | ||
362 | public function postEntriesTagsAction(Request $request, Entry $entry) | ||
363 | { | ||
364 | $this->validateAuthentication(); | ||
365 | $this->validateUserAccess($entry->getUser()->getId()); | ||
366 | |||
367 | $tags = $request->request->get('tags', ''); | ||
368 | if (!empty($tags)) { | ||
369 | $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); | ||
370 | } | ||
371 | |||
372 | $em = $this->getDoctrine()->getManager(); | ||
373 | $em->persist($entry); | ||
374 | $em->flush(); | ||
375 | |||
376 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
377 | |||
378 | return (new JsonResponse())->setJson($json); | ||
379 | } | ||
380 | |||
381 | /** | ||
382 | * Permanently remove one tag for an entry. | ||
383 | * | ||
384 | * @ApiDoc( | ||
385 | * requirements={ | ||
386 | * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"}, | ||
387 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
388 | * } | ||
389 | * ) | ||
390 | * | ||
391 | * @return JsonResponse | ||
392 | */ | ||
393 | public function deleteEntriesTagsAction(Entry $entry, Tag $tag) | ||
394 | { | ||
395 | $this->validateAuthentication(); | ||
396 | $this->validateUserAccess($entry->getUser()->getId()); | ||
397 | |||
398 | $entry->removeTag($tag); | ||
399 | $em = $this->getDoctrine()->getManager(); | ||
400 | $em->persist($entry); | ||
401 | $em->flush(); | ||
402 | |||
403 | $json = $this->get('serializer')->serialize($entry, 'json'); | ||
404 | |||
405 | return (new JsonResponse())->setJson($json); | ||
406 | } | ||
407 | |||
408 | /** | ||
409 | * Retrieve all tags. | ||
410 | * | ||
411 | * @ApiDoc() | ||
412 | * | ||
413 | * @return JsonResponse | ||
414 | */ | ||
415 | public function getTagsAction() | ||
416 | { | ||
417 | $this->validateAuthentication(); | ||
418 | |||
419 | $tags = $this->getDoctrine() | ||
420 | ->getRepository('WallabagCoreBundle:Tag') | ||
421 | ->findAllTags($this->getUser()->getId()); | ||
422 | |||
423 | $json = $this->get('serializer')->serialize($tags, 'json'); | ||
424 | |||
425 | return (new JsonResponse())->setJson($json); | ||
426 | } | ||
427 | |||
428 | /** | ||
429 | * Permanently remove one tag from **every** entry. | ||
430 | * | ||
431 | * @ApiDoc( | ||
432 | * requirements={ | ||
433 | * {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"} | ||
434 | * } | ||
435 | * ) | ||
436 | * | ||
437 | * @return JsonResponse | ||
438 | */ | ||
439 | public function deleteTagLabelAction(Request $request) | ||
440 | { | ||
441 | $this->validateAuthentication(); | ||
442 | $label = $request->request->get('tag', ''); | ||
443 | |||
444 | $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label); | ||
445 | |||
446 | if (empty($tag)) { | ||
447 | throw $this->createNotFoundException('Tag not found'); | ||
448 | } | ||
449 | |||
450 | $this->getDoctrine() | ||
451 | ->getRepository('WallabagCoreBundle:Entry') | ||
452 | ->removeTag($this->getUser()->getId(), $tag); | ||
453 | |||
454 | $this->cleanOrphanTag($tag); | ||
455 | |||
456 | $json = $this->get('serializer')->serialize($tag, 'json'); | ||
457 | |||
458 | return (new JsonResponse())->setJson($json); | ||
459 | } | ||
460 | |||
461 | /** | ||
462 | * Permanently remove some tags from **every** entry. | ||
463 | * | ||
464 | * @ApiDoc( | ||
465 | * requirements={ | ||
466 | * {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"} | ||
467 | * } | ||
468 | * ) | ||
469 | * | ||
470 | * @return JsonResponse | ||
471 | */ | ||
472 | public function deleteTagsLabelAction(Request $request) | ||
473 | { | ||
474 | $this->validateAuthentication(); | ||
475 | |||
476 | $tagsLabels = $request->request->get('tags', ''); | ||
477 | |||
478 | $tags = []; | ||
479 | |||
480 | foreach (explode(',', $tagsLabels) as $tagLabel) { | ||
481 | $tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel); | ||
482 | |||
483 | if (!empty($tagEntity)) { | ||
484 | $tags[] = $tagEntity; | ||
485 | } | ||
486 | } | ||
487 | |||
488 | if (empty($tags)) { | ||
489 | throw $this->createNotFoundException('Tags not found'); | ||
490 | } | ||
491 | |||
492 | $this->getDoctrine() | ||
493 | ->getRepository('WallabagCoreBundle:Entry') | ||
494 | ->removeTags($this->getUser()->getId(), $tags); | ||
495 | |||
496 | $this->cleanOrphanTag($tags); | ||
497 | |||
498 | $json = $this->get('serializer')->serialize($tags, 'json'); | ||
499 | |||
500 | return (new JsonResponse())->setJson($json); | ||
501 | } | ||
502 | |||
503 | /** | ||
504 | * Permanently remove one tag from **every** entry. | ||
505 | * | ||
506 | * @ApiDoc( | ||
507 | * requirements={ | ||
508 | * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"} | ||
509 | * } | ||
510 | * ) | ||
511 | * | ||
512 | * @return JsonResponse | ||
513 | */ | ||
514 | public function deleteTagAction(Tag $tag) | ||
515 | { | ||
516 | $this->validateAuthentication(); | ||
517 | |||
518 | $this->getDoctrine() | ||
519 | ->getRepository('WallabagCoreBundle:Entry') | ||
520 | ->removeTag($this->getUser()->getId(), $tag); | ||
521 | |||
522 | $this->cleanOrphanTag($tag); | ||
523 | |||
524 | $json = $this->get('serializer')->serialize($tag, 'json'); | ||
525 | |||
526 | return (new JsonResponse())->setJson($json); | ||
527 | } | ||
528 | |||
529 | /** | ||
530 | * Retrieve annotations for an entry. | ||
531 | * | ||
532 | * @ApiDoc( | ||
533 | * requirements={ | ||
534 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
535 | * } | ||
536 | * ) | ||
537 | * | ||
538 | * @param Entry $entry | ||
539 | * | ||
540 | * @return JsonResponse | ||
541 | */ | ||
542 | public function getAnnotationsAction(Entry $entry) | ||
543 | { | ||
544 | $this->validateAuthentication(); | ||
545 | |||
546 | return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:getAnnotations', [ | ||
547 | 'entry' => $entry, | ||
548 | ]); | ||
549 | } | ||
550 | |||
551 | /** | ||
552 | * Creates a new annotation. | ||
553 | * | ||
554 | * @ApiDoc( | ||
555 | * requirements={ | ||
556 | * {"name"="ranges", "dataType"="array", "requirement"="\w+", "description"="The range array for the annotation"}, | ||
557 | * {"name"="quote", "dataType"="string", "required"=false, "description"="Optional, quote for the annotation"}, | ||
558 | * {"name"="text", "dataType"="string", "required"=true, "description"=""}, | ||
559 | * } | ||
560 | * ) | ||
561 | * | ||
562 | * @param Request $request | ||
563 | * @param Entry $entry | ||
564 | * | ||
565 | * @return JsonResponse | ||
566 | */ | ||
567 | public function postAnnotationAction(Request $request, Entry $entry) | ||
568 | { | ||
569 | $this->validateAuthentication(); | ||
570 | |||
571 | return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:postAnnotation', [ | ||
572 | 'request' => $request, | ||
573 | 'entry' => $entry, | ||
574 | ]); | ||
575 | } | ||
576 | |||
577 | /** | ||
578 | * Updates an annotation. | ||
579 | * | ||
580 | * @ApiDoc( | ||
581 | * requirements={ | ||
582 | * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"} | ||
583 | * } | ||
584 | * ) | ||
585 | * | ||
586 | * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") | ||
587 | * | ||
588 | * @param Annotation $annotation | ||
589 | * @param Request $request | ||
590 | * | ||
591 | * @return JsonResponse | ||
592 | */ | ||
593 | public function putAnnotationAction(Annotation $annotation, Request $request) | ||
594 | { | ||
595 | $this->validateAuthentication(); | ||
596 | |||
597 | return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:putAnnotation', [ | ||
598 | 'annotation' => $annotation, | ||
599 | 'request' => $request, | ||
600 | ]); | ||
601 | } | ||
602 | |||
603 | /** | ||
604 | * Removes an annotation. | ||
605 | * | ||
606 | * @ApiDoc( | ||
607 | * requirements={ | ||
608 | * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"} | ||
609 | * } | ||
610 | * ) | ||
611 | * | ||
612 | * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") | ||
613 | * | ||
614 | * @param Annotation $annotation | ||
615 | * | ||
616 | * @return JsonResponse | ||
617 | */ | ||
618 | public function deleteAnnotationAction(Annotation $annotation) | ||
619 | { | ||
620 | $this->validateAuthentication(); | ||
621 | |||
622 | return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:deleteAnnotation', [ | ||
623 | 'annotation' => $annotation, | ||
624 | ]); | ||
625 | } | ||
626 | |||
627 | /** | 11 | /** |
628 | * Retrieve version number. | 12 | * Retrieve version number. |
629 | * | 13 | * |
@@ -634,32 +18,15 @@ class WallabagRestController extends FOSRestController | |||
634 | public function getVersionAction() | 18 | public function getVersionAction() |
635 | { | 19 | { |
636 | $version = $this->container->getParameter('wallabag_core.version'); | 20 | $version = $this->container->getParameter('wallabag_core.version'); |
637 | |||
638 | $json = $this->get('serializer')->serialize($version, 'json'); | 21 | $json = $this->get('serializer')->serialize($version, 'json'); |
639 | |||
640 | return (new JsonResponse())->setJson($json); | 22 | return (new JsonResponse())->setJson($json); |
641 | } | 23 | } |
642 | 24 | ||
643 | /** | 25 | protected function validateAuthentication() |
644 | * Remove orphan tag in case no entries are associated to it. | ||
645 | * | ||
646 | * @param Tag|array $tags | ||
647 | */ | ||
648 | private function cleanOrphanTag($tags) | ||
649 | { | 26 | { |
650 | if (!is_array($tags)) { | 27 | if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) { |
651 | $tags = [$tags]; | 28 | throw new AccessDeniedException(); |
652 | } | ||
653 | |||
654 | $em = $this->getDoctrine()->getManager(); | ||
655 | |||
656 | foreach ($tags as $tag) { | ||
657 | if (count($tag->getEntries()) === 0) { | ||
658 | $em->remove($tag); | ||
659 | } | ||
660 | } | 29 | } |
661 | |||
662 | $em->flush(); | ||
663 | } | 30 | } |
664 | 31 | ||
665 | /** | 32 | /** |
@@ -668,7 +35,7 @@ class WallabagRestController extends FOSRestController | |||
668 | * | 35 | * |
669 | * @param int $requestUserId User id from the requested source | 36 | * @param int $requestUserId User id from the requested source |
670 | */ | 37 | */ |
671 | private function validateUserAccess($requestUserId) | 38 | protected function validateUserAccess($requestUserId) |
672 | { | 39 | { |
673 | $user = $this->get('security.token_storage')->getToken()->getUser(); | 40 | $user = $this->get('security.token_storage')->getToken()->getUser(); |
674 | if ($requestUserId != $user->getId()) { | 41 | if ($requestUserId != $user->getId()) { |
diff --git a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml index 35f8b2c1..8e1886ac 100644 --- a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml +++ b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml | |||
@@ -1,4 +1,14 @@ | |||
1 | api: | 1 | entry: |
2 | type: rest | 2 | type: rest |
3 | resource: "WallabagApiBundle:WallabagRest" | 3 | resource: "WallabagApiBundle:EntryRest" |
4 | name_prefix: api_ | 4 | name_prefix: api_ |
5 | |||
6 | tag: | ||
7 | type: rest | ||
8 | resource: "WallabagApiBundle:TagRest" | ||
9 | name_prefix: api_ | ||
10 | |||
11 | misc: | ||
12 | type: rest | ||
13 | resource: "WallabagApiBundle:WallabagRest" | ||
14 | name_prefix: api_ | ||
diff --git a/src/Wallabag/CoreBundle/Controller/ExportController.php b/src/Wallabag/CoreBundle/Controller/ExportController.php index 6191d5d7..79653cfe 100644 --- a/src/Wallabag/CoreBundle/Controller/ExportController.php +++ b/src/Wallabag/CoreBundle/Controller/ExportController.php | |||
@@ -4,8 +4,10 @@ namespace Wallabag\CoreBundle\Controller; | |||
4 | 4 | ||
5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; | 5 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; | 6 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
7 | use Symfony\Component\HttpFoundation\Request; | ||
7 | use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | 8 | use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
8 | use Wallabag\CoreBundle\Entity\Entry; | 9 | use Wallabag\CoreBundle\Entity\Entry; |
10 | use Wallabag\CoreBundle\Entity\Tag; | ||
9 | 11 | ||
10 | /** | 12 | /** |
11 | * The try/catch can be removed once all formats will be implemented. | 13 | * The try/catch can be removed once all formats will be implemented. |
@@ -51,15 +53,24 @@ class ExportController extends Controller | |||
51 | * | 53 | * |
52 | * @return \Symfony\Component\HttpFoundation\Response | 54 | * @return \Symfony\Component\HttpFoundation\Response |
53 | */ | 55 | */ |
54 | public function downloadEntriesAction($format, $category) | 56 | public function downloadEntriesAction(Request $request, $format, $category) |
55 | { | 57 | { |
56 | $method = ucfirst($category); | 58 | $method = ucfirst($category); |
57 | $methodBuilder = 'getBuilderFor'.$method.'ByUser'; | 59 | $methodBuilder = 'getBuilderFor'.$method.'ByUser'; |
58 | $entries = $this->getDoctrine() | 60 | |
59 | ->getRepository('WallabagCoreBundle:Entry') | 61 | if ($category == 'tag_entries') { |
60 | ->$methodBuilder($this->getUser()->getId()) | 62 | $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneBySlug($request->query->get('tag')); |
61 | ->getQuery() | 63 | |
62 | ->getResult(); | 64 | $entries = $this->getDoctrine() |
65 | ->getRepository('WallabagCoreBundle:Entry') | ||
66 | ->findAllByTagId($this->getUser()->getId(), $tag->getId()); | ||
67 | } else { | ||
68 | $entries = $this->getDoctrine() | ||
69 | ->getRepository('WallabagCoreBundle:Entry') | ||
70 | ->$methodBuilder($this->getUser()->getId()) | ||
71 | ->getQuery() | ||
72 | ->getResult(); | ||
73 | } | ||
63 | 74 | ||
64 | try { | 75 | try { |
65 | return $this->get('wallabag_core.helper.entries_export') | 76 | return $this->get('wallabag_core.helper.entries_export') |
diff --git a/src/Wallabag/CoreBundle/Controller/TagController.php b/src/Wallabag/CoreBundle/Controller/TagController.php index 4542d484..a3e70fd0 100644 --- a/src/Wallabag/CoreBundle/Controller/TagController.php +++ b/src/Wallabag/CoreBundle/Controller/TagController.php | |||
@@ -143,6 +143,7 @@ class TagController extends Controller | |||
143 | 'form' => null, | 143 | 'form' => null, |
144 | 'entries' => $entries, | 144 | 'entries' => $entries, |
145 | 'currentPage' => $page, | 145 | 'currentPage' => $page, |
146 | 'tag' => $tag->getLabel(), | ||
146 | ]); | 147 | ]); |
147 | } | 148 | } |
148 | } | 149 | } |
diff --git a/src/Wallabag/CoreBundle/Helper/EntriesExport.php b/src/Wallabag/CoreBundle/Helper/EntriesExport.php index e50c68a6..4bf292a4 100644 --- a/src/Wallabag/CoreBundle/Helper/EntriesExport.php +++ b/src/Wallabag/CoreBundle/Helper/EntriesExport.php | |||
@@ -8,7 +8,6 @@ use JMS\Serializer\SerializerBuilder; | |||
8 | use PHPePub\Core\EPub; | 8 | use PHPePub\Core\EPub; |
9 | use PHPePub\Core\Structure\OPF\DublinCore; | 9 | use PHPePub\Core\Structure\OPF\DublinCore; |
10 | use Symfony\Component\HttpFoundation\Response; | 10 | use Symfony\Component\HttpFoundation\Response; |
11 | use Craue\ConfigBundle\Util\Config; | ||
12 | 11 | ||
13 | /** | 12 | /** |
14 | * This class doesn't have unit test BUT it's fully covered by a functional test with ExportControllerTest. | 13 | * This class doesn't have unit test BUT it's fully covered by a functional test with ExportControllerTest. |
@@ -27,12 +26,12 @@ class EntriesExport | |||
27 | </div>'; | 26 | </div>'; |
28 | 27 | ||
29 | /** | 28 | /** |
30 | * @param Config $craueConfig CraueConfig instance to get wallabag instance url from database | 29 | * @param string $wallabagUrl Wallabag instance url |
31 | * @param string $logoPath Path to the logo FROM THE BUNDLE SCOPE | 30 | * @param string $logoPath Path to the logo FROM THE BUNDLE SCOPE |
32 | */ | 31 | */ |
33 | public function __construct(Config $craueConfig, $logoPath) | 32 | public function __construct($wallabagUrl, $logoPath) |
34 | { | 33 | { |
35 | $this->wallabagUrl = $craueConfig->get('wallabag_url'); | 34 | $this->wallabagUrl = $wallabagUrl; |
36 | $this->logoPath = $logoPath; | 35 | $this->logoPath = $logoPath; |
37 | } | 36 | } |
38 | 37 | ||
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index 56d776ad..9786ac27 100644 --- a/src/Wallabag/CoreBundle/Resources/config/services.yml +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml | |||
@@ -55,6 +55,7 @@ services: | |||
55 | '.fok.nl': 'Googlebot/2.1' | 55 | '.fok.nl': 'Googlebot/2.1' |
56 | 'getpocket.com': 'PHP/5.2' | 56 | 'getpocket.com': 'PHP/5.2' |
57 | 'iansommerville.com': 'PHP/5.2' | 57 | 'iansommerville.com': 'PHP/5.2' |
58 | '.slashdot.org': 'PHP/5.2' | ||
58 | calls: | 59 | calls: |
59 | - [ setLogger, [ "@logger" ] ] | 60 | - [ setLogger, [ "@logger" ] ] |
60 | tags: | 61 | tags: |
@@ -91,7 +92,7 @@ services: | |||
91 | wallabag_core.helper.entries_export: | 92 | wallabag_core.helper.entries_export: |
92 | class: Wallabag\CoreBundle\Helper\EntriesExport | 93 | class: Wallabag\CoreBundle\Helper\EntriesExport |
93 | arguments: | 94 | arguments: |
94 | - "@craue_config" | 95 | - '@=service(''craue_config'').get(''wallabag_url'')' |
95 | - src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-152.png | 96 | - src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-152.png |
96 | 97 | ||
97 | wallabag.operator.array.matches: | 98 | wallabag.operator.array.matches: |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml index 21c26079..aeae6bcf 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml | |||
@@ -150,7 +150,7 @@ entry: | |||
150 | # starred: 'Starred entries' | 150 | # starred: 'Starred entries' |
151 | # archived: 'Archived entries' | 151 | # archived: 'Archived entries' |
152 | # filtered: 'Filtered entries' | 152 | # filtered: 'Filtered entries' |
153 | # filtered_tags: 'Filtered by tags' | 153 | # filtered_tags: 'Filtered by tags:' |
154 | # untagged: 'Untagged entries' | 154 | # untagged: 'Untagged entries' |
155 | list: | 155 | list: |
156 | # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.' | 156 | # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.' |
@@ -472,7 +472,6 @@ flashes: | |||
472 | rss_updated: 'RSS-oplysninger opdateret' | 472 | rss_updated: 'RSS-oplysninger opdateret' |
473 | # tagging_rules_updated: 'Tagging rules updated' | 473 | # tagging_rules_updated: 'Tagging rules updated' |
474 | # tagging_rules_deleted: 'Tagging rule deleted' | 474 | # tagging_rules_deleted: 'Tagging rule deleted' |
475 | # user_added: 'User "%username%" added' | ||
476 | # rss_token_updated: 'RSS token updated' | 475 | # rss_token_updated: 'RSS token updated' |
477 | # annotations_reset: Annotations reset | 476 | # annotations_reset: Annotations reset |
478 | # tags_reset: Tags reset | 477 | # tags_reset: Tags reset |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml index ff70cbee..2105d02d 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml | |||
@@ -150,7 +150,7 @@ entry: | |||
150 | starred: 'Favorisierte Einträge' | 150 | starred: 'Favorisierte Einträge' |
151 | archived: 'Archivierte Einträge' | 151 | archived: 'Archivierte Einträge' |
152 | filtered: 'Gefilterte Einträge' | 152 | filtered: 'Gefilterte Einträge' |
153 | filtered_tags: 'Gefiltert nach Tags' | 153 | filtered_tags: 'Gefiltert nach Tags:' |
154 | untagged: 'Nicht getaggte Einträge' | 154 | untagged: 'Nicht getaggte Einträge' |
155 | list: | 155 | list: |
156 | number_on_the_page: '{0} Es gibt keine Einträge.|{1} Es gibt einen Eintrag.|]1,Inf[ Es gibt %count% Einträge.' | 156 | number_on_the_page: '{0} Es gibt keine Einträge.|{1} Es gibt einen Eintrag.|]1,Inf[ Es gibt %count% Einträge.' |
@@ -472,7 +472,6 @@ flashes: | |||
472 | rss_updated: 'RSS-Informationen aktualisiert' | 472 | rss_updated: 'RSS-Informationen aktualisiert' |
473 | tagging_rules_updated: 'Tagging-Regeln aktualisiert' | 473 | tagging_rules_updated: 'Tagging-Regeln aktualisiert' |
474 | tagging_rules_deleted: 'Tagging-Regel gelöscht' | 474 | tagging_rules_deleted: 'Tagging-Regel gelöscht' |
475 | user_added: 'Benutzer "%username%" erstellt' | ||
476 | rss_token_updated: 'RSS-Token aktualisiert' | 475 | rss_token_updated: 'RSS-Token aktualisiert' |
477 | # annotations_reset: Annotations reset | 476 | # annotations_reset: Annotations reset |
478 | # tags_reset: Tags reset | 477 | # tags_reset: Tags reset |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml index 36382b6f..2bb95728 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml | |||
@@ -150,7 +150,7 @@ entry: | |||
150 | starred: 'Starred entries' | 150 | starred: 'Starred entries' |
151 | archived: 'Archived entries' | 151 | archived: 'Archived entries' |
152 | filtered: 'Filtered entries' | 152 | filtered: 'Filtered entries' |
153 | filtered_tags: 'Filtered by tags' | 153 | filtered_tags: 'Filtered by tags:' |
154 | untagged: 'Untagged entries' | 154 | untagged: 'Untagged entries' |
155 | list: | 155 | list: |
156 | number_on_the_page: '{0} There are no entries.|{1} There is one entry.|]1,Inf[ There are %count% entries.' | 156 | number_on_the_page: '{0} There are no entries.|{1} There is one entry.|]1,Inf[ There are %count% entries.' |
@@ -472,7 +472,6 @@ flashes: | |||
472 | rss_updated: 'RSS information updated' | 472 | rss_updated: 'RSS information updated' |
473 | tagging_rules_updated: 'Tagging rules updated' | 473 | tagging_rules_updated: 'Tagging rules updated' |
474 | tagging_rules_deleted: 'Tagging rule deleted' | 474 | tagging_rules_deleted: 'Tagging rule deleted' |
475 | # user_added: 'User "%username%" added' | ||
476 | rss_token_updated: 'RSS token updated' | 475 | rss_token_updated: 'RSS token updated' |
477 | annotations_reset: Annotations reset | 476 | annotations_reset: Annotations reset |
478 | tags_reset: Tags reset | 477 | tags_reset: Tags reset |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml index 2c80fe8f..ca3db487 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml | |||
@@ -150,7 +150,7 @@ entry: | |||
150 | starred: 'Artículos favoritos' | 150 | starred: 'Artículos favoritos' |
151 | archived: 'Artículos archivados' | 151 | archived: 'Artículos archivados' |
152 | filtered: 'Artículos filtrados' | 152 | filtered: 'Artículos filtrados' |
153 | # filtered_tags: 'Filtered by tags' | 153 | # filtered_tags: 'Filtered by tags:' |
154 | # untagged: 'Untagged entries' | 154 | # untagged: 'Untagged entries' |
155 | list: | 155 | list: |
156 | number_on_the_page: '{0} No hay artículos.|{1} Hay un artículo.|]1,Inf[ Hay %count% artículos.' | 156 | number_on_the_page: '{0} No hay artículos.|{1} Hay un artículo.|]1,Inf[ Hay %count% artículos.' |
@@ -472,7 +472,6 @@ flashes: | |||
472 | rss_updated: 'La configuración de los feeds RSS ha sido actualizada' | 472 | rss_updated: 'La configuración de los feeds RSS ha sido actualizada' |
473 | tagging_rules_updated: 'Regla de etiquetado borrada' | 473 | tagging_rules_updated: 'Regla de etiquetado borrada' |
474 | tagging_rules_deleted: 'Regla de etiquetado actualizada' | 474 | tagging_rules_deleted: 'Regla de etiquetado actualizada' |
475 | user_added: 'Usuario "%username%" añadido' | ||
476 | rss_token_updated: 'RSS token actualizado' | 475 | rss_token_updated: 'RSS token actualizado' |
477 | # annotations_reset: Annotations reset | 476 | # annotations_reset: Annotations reset |
478 | # tags_reset: Tags reset | 477 | # tags_reset: Tags reset |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml index 6b6211d6..1914215a 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml | |||
@@ -150,7 +150,7 @@ entry: | |||
150 | starred: 'مقالههای برگزیده' | 150 | starred: 'مقالههای برگزیده' |
151 | archived: 'مقالههای بایگانیشده' | 151 | archived: 'مقالههای بایگانیشده' |
152 | filtered: 'مقالههای فیلترشده' | 152 | filtered: 'مقالههای فیلترشده' |
153 | # filtered_tags: 'Filtered by tags' | 153 | # filtered_tags: 'Filtered by tags:' |
154 | # untagged: 'Untagged entries' | 154 | # untagged: 'Untagged entries' |
155 | list: | 155 | list: |
156 | number_on_the_page: '{0} هیج مقالهای نیست.|{1} یک مقاله هست.|]1,Inf[ %count% مقاله هست.' | 156 | number_on_the_page: '{0} هیج مقالهای نیست.|{1} یک مقاله هست.|]1,Inf[ %count% مقاله هست.' |
@@ -472,7 +472,6 @@ flashes: | |||
472 | rss_updated: 'اطلاعات آر-اس-اس بهروز شد' | 472 | rss_updated: 'اطلاعات آر-اس-اس بهروز شد' |
473 | tagging_rules_updated: 'برچسبگذاری خودکار بهروز شد' | 473 | tagging_rules_updated: 'برچسبگذاری خودکار بهروز شد' |
474 | tagging_rules_deleted: 'قانون برچسبگذاری پاک شد' | 474 | tagging_rules_deleted: 'قانون برچسبگذاری پاک شد' |
475 | user_added: 'کابر "%username%" افزوده شد' | ||
476 | rss_token_updated: 'کد آر-اس-اس بهروز شد' | 475 | rss_token_updated: 'کد آر-اس-اس بهروز شد' |
477 | # annotations_reset: Annotations reset | 476 | # annotations_reset: Annotations reset |
478 | # tags_reset: Tags reset | 477 | # tags_reset: Tags reset |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml index 74d59e1a..60fa9a39 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml | |||
@@ -1,94 +1,94 @@ | |||
1 | security: | 1 | security: |
2 | login: | 2 | login: |
3 | page_title: 'Bienvenue sur wallabag !' | 3 | page_title: "Bienvenue sur wallabag !" |
4 | keep_logged_in: 'Rester connecté' | 4 | keep_logged_in: "Rester connecté" |
5 | forgot_password: 'Mot de passe oublié ?' | 5 | forgot_password: "Mot de passe oublié ?" |
6 | submit: 'Se connecter' | 6 | submit: "Se connecter" |
7 | register: 'Créer un compte' | 7 | register: "Créer un compte" |
8 | username: "Nom d'utilisateur" | 8 | username: "Nom d’utilisateur" |
9 | password: 'Mot de passe' | 9 | password: "Mot de passe" |
10 | cancel: 'Annuler' | 10 | cancel: "Annuler" |
11 | resetting: | 11 | resetting: |
12 | description: "Saisissez votre adresse e-mail ci-dessous, nous vous enverrons les instructions pour réinitialiser votre mot de passe." | 12 | description: "Saisissez votre adresse courriel ci-dessous, nous vous enverrons les instructions pour réinitialiser votre mot de passe." |
13 | register: | 13 | register: |
14 | page_title: 'Se créer un compte' | 14 | page_title: "Se créer un compte" |
15 | go_to_account: 'Aller sur votre compte' | 15 | go_to_account: "Aller sur votre compte" |
16 | 16 | ||
17 | menu: | 17 | menu: |
18 | left: | 18 | left: |
19 | unread: 'Non lus' | 19 | unread: "Non lus" |
20 | starred: 'Favoris' | 20 | starred: "Favoris" |
21 | archive: 'Lus' | 21 | archive: "Lus" |
22 | all_articles: 'Tous les articles' | 22 | all_articles: "Tous les articles" |
23 | config: 'Configuration' | 23 | config: "Configuration" |
24 | tags: 'Tags' | 24 | tags: "Tags" |
25 | internal_settings: 'Configuration interne' | 25 | internal_settings: "Configuration interne" |
26 | import: 'Importer' | 26 | import: "Importer" |
27 | howto: 'Aide' | 27 | howto: "Aide" |
28 | developer: 'Développeur' | 28 | developer: "Développeur" |
29 | logout: 'Déconnexion' | 29 | logout: "Déconnexion" |
30 | about: 'À propos' | 30 | about: "À propos" |
31 | search: 'Recherche' | 31 | search: "Recherche" |
32 | save_link: 'Sauvegarder un nouvel article' | 32 | save_link: "Sauvegarder un nouvel article" |
33 | back_to_unread: 'Retour aux articles non lus' | 33 | back_to_unread: "Retour aux articles non lus" |
34 | users_management: 'Gestion des utilisateurs' | 34 | users_management: "Gestion des utilisateurs" |
35 | top: | 35 | top: |
36 | add_new_entry: 'Sauvegarder un nouvel article' | 36 | add_new_entry: "Sauvegarder un nouvel article" |
37 | search: 'Rechercher' | 37 | search: "Rechercher" |
38 | filter_entries: 'Filtrer les articles' | 38 | filter_entries: "Filtrer les articles" |
39 | export: 'Exporter' | 39 | export: "Exporter" |
40 | search_form: | 40 | search_form: |
41 | input_label: 'Saisissez votre terme de recherche' | 41 | input_label: "Saisissez votre terme de recherche" |
42 | 42 | ||
43 | footer: | 43 | footer: |
44 | wallabag: | 44 | wallabag: |
45 | elsewhere: 'Emportez wallabag avec vous' | 45 | elsewhere: "Emportez wallabag avec vous" |
46 | social: 'Social' | 46 | social: "Social" |
47 | powered_by: 'propulsé par' | 47 | powered_by: "propulsé par" |
48 | about: 'À propos' | 48 | about: "À propos" |
49 | stats: Depuis le %user_creation% vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour ! | 49 | stats: Depuis le %user_creation% vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour ! |
50 | 50 | ||
51 | config: | 51 | config: |
52 | page_title: 'Configuration' | 52 | page_title: "Configuration" |
53 | tab_menu: | 53 | tab_menu: |
54 | settings: 'Paramètres' | 54 | settings: "Paramètres" |
55 | rss: 'RSS' | 55 | rss: "RSS" |
56 | user_info: 'Mon compte' | 56 | user_info: "Mon compte" |
57 | password: 'Mot de passe' | 57 | password: "Mot de passe" |
58 | rules: 'Règles de tag automatiques' | 58 | rules: "Règles de tag automatiques" |
59 | new_user: 'Créer un compte' | 59 | new_user: "Créer un compte" |
60 | form: | 60 | form: |
61 | save: 'Enregistrer' | 61 | save: "Enregistrer" |
62 | form_settings: | 62 | form_settings: |
63 | theme_label: 'Thème' | 63 | theme_label: "Thème" |
64 | items_per_page_label: "Nombre d'articles par page" | 64 | items_per_page_label: "Nombre d’articles par page" |
65 | language_label: 'Langue' | 65 | language_label: "Langue" |
66 | reading_speed: | 66 | reading_speed: |
67 | label: 'Vitesse de lecture' | 67 | label: "Vitesse de lecture" |
68 | help_message: 'Vous pouvez utiliser un outil en ligne pour estimer votre vitesse de lecture :' | 68 | help_message: "Vous pouvez utiliser un outil en ligne pour estimer votre vitesse de lecture :" |
69 | 100_word: 'Je lis environ 100 mots par minute' | 69 | 100_word: "Je lis environ 100 mots par minute" |
70 | 200_word: 'Je lis environ 200 mots par minute' | 70 | 200_word: "Je lis environ 200 mots par minute" |
71 | 300_word: 'Je lis environ 300 mots par minute' | 71 | 300_word: "Je lis environ 300 mots par minute" |
72 | 400_word: 'Je lis environ 400 mots par minute' | 72 | 400_word: "Je lis environ 400 mots par minute" |
73 | pocket_consumer_key_label: Clé d'authentification Pocket pour importer les données | 73 | pocket_consumer_key_label: Clé d’authentification Pocket pour importer les données |
74 | android_configuration: Configurez votre application Android | 74 | android_configuration: Configurez votre application Android |
75 | form_rss: | 75 | form_rss: |
76 | description: "Les flux RSS fournis par wallabag vous permettent de lire vos articles sauvegardés dans votre lecteur de flux préféré. Pour pouvoir les utiliser, vous devez d'abord créer un jeton." | 76 | description: "Les flux RSS fournis par wallabag vous permettent de lire vos articles sauvegardés dans votre lecteur de flux préféré. Pour pouvoir les utiliser, vous devez d’abord créer un jeton." |
77 | token_label: 'Jeton RSS' | 77 | token_label: "Jeton RSS" |
78 | no_token: 'Aucun jeton généré' | 78 | no_token: "Aucun jeton généré" |
79 | token_create: 'Créez votre jeton' | 79 | token_create: "Créez votre jeton" |
80 | token_reset: 'Réinitialisez votre jeton' | 80 | token_reset: "Réinitialisez votre jeton" |
81 | rss_links: 'URL de vos flux RSS' | 81 | rss_links: "Adresse de vos flux RSS" |
82 | rss_link: | 82 | rss_link: |
83 | unread: 'non lus' | 83 | unread: "non lus" |
84 | starred: 'favoris' | 84 | starred: "favoris" |
85 | archive: 'lus' | 85 | archive: "lus" |
86 | rss_limit: "Nombre d'articles dans le flux" | 86 | rss_limit: "Nombre d’articles dans le flux" |
87 | form_user: | 87 | form_user: |
88 | two_factor_description: "Activer l'authentification double-facteur veut dire que vous allez recevoir un code par email à chaque nouvelle connexion non approuvée." | 88 | two_factor_description: "Activer l’authentification double-facteur veut dire que vous allez recevoir un code par courriel à chaque nouvelle connexion non approuvée." |
89 | name_label: 'Nom' | 89 | name_label: "Nom" |
90 | email_label: 'Adresse e-mail' | 90 | email_label: "Adresse courriel" |
91 | twoFactorAuthentication_label: 'Double authentification' | 91 | twoFactorAuthentication_label: "Double authentification" |
92 | delete: | 92 | delete: |
93 | title: Supprimer mon compte (attention danger !) | 93 | title: Supprimer mon compte (attention danger !) |
94 | description: Si vous confirmez la suppression de votre compte, TOUS les articles, TOUS les tags, TOUTES les annotations et votre compte seront DÉFINITIVEMENT supprimé (c'est IRRÉVERSIBLE). Vous serez ensuite déconnecté. | 94 | description: Si vous confirmez la suppression de votre compte, TOUS les articles, TOUS les tags, TOUTES les annotations et votre compte seront DÉFINITIVEMENT supprimé (c'est IRRÉVERSIBLE). Vous serez ensuite déconnecté. |
@@ -102,335 +102,335 @@ config: | |||
102 | entries: Supprimer TOUS les articles | 102 | entries: Supprimer TOUS les articles |
103 | confirm: Êtes-vous vraiment vraiment sûr ? (C'EST IRRÉVERSIBLE) | 103 | confirm: Êtes-vous vraiment vraiment sûr ? (C'EST IRRÉVERSIBLE) |
104 | form_password: | 104 | form_password: |
105 | old_password_label: 'Mot de passe actuel' | 105 | old_password_label: "Mot de passe actuel" |
106 | new_password_label: 'Nouveau mot de passe' | 106 | new_password_label: "Nouveau mot de passe" |
107 | repeat_new_password_label: 'Confirmez votre nouveau mot de passe' | 107 | repeat_new_password_label: "Confirmez votre nouveau mot de passe" |
108 | form_rules: | 108 | form_rules: |
109 | if_label: 'si' | 109 | if_label: "si" |
110 | then_tag_as_label: 'alors attribuer les tags' | 110 | then_tag_as_label: "alors attribuer les tags" |
111 | delete_rule_label: 'supprimer' | 111 | delete_rule_label: "supprimer" |
112 | edit_rule_label: 'éditer' | 112 | edit_rule_label: "éditer" |
113 | rule_label: 'Règle' | 113 | rule_label: "Règle" |
114 | tags_label: 'Tags' | 114 | tags_label: "Tags" |
115 | faq: | 115 | faq: |
116 | title: 'FAQ' | 116 | title: "FAQ" |
117 | tagging_rules_definition_title: 'Que signifient les règles de tag automatiques ?' | 117 | tagging_rules_definition_title: "Que signifient les règles de tag automatiques ?" |
118 | tagging_rules_definition_description: "Ce sont des règles utilisées par wallabag pour classer automatiquement vos nouveaux articles.<br />À chaque fois qu'un nouvel article est ajouté, toutes les règles de tag automatiques seront utilisées afin d'ajouter les tags que vous avez configurés, vous épargnant ainsi l'effort de classifier vos articles manuellement." | 118 | tagging_rules_definition_description: "Ce sont des règles utilisées par wallabag pour classer automatiquement vos nouveaux articles.<br />À chaque fois qu’un nouvel article est ajouté, toutes les règles de tag automatiques seront utilisées afin d’ajouter les tags que vous avez configurés, vous épargnant ainsi l’effort de classifier vos articles manuellement." |
119 | how_to_use_them_title: 'Comment les utiliser ?' | 119 | how_to_use_them_title: "Comment les utiliser ?" |
120 | how_to_use_them_description: 'Imaginons que voulez attribuer aux nouveaux articles le tag « <i>lecture courte</i> » lorsque le temps de lecture est inférieur à 3 minutes.<br />Dans ce cas, vous devriez mettre « readingTime <= 3 » dans le champ <i>Règle</i> et « <i>lecture courte</i> » dans le champ <i>Tag</i>.<br />Plusieurs tags peuvent être ajoutés simultanément en les séparant par des virgules : « <i>lecture courte, à lire</i> »<br />Des règles complexes peuvent être créées en utilisant des opérateurs prédéfinis: si « <i>readingTime >= 5 AND domainName = \"github.com\"</i> » alors attribuer les tags « <i>lecture longue, github </i> »' | 120 | how_to_use_them_description: "Imaginons que voulez attribuer aux nouveaux articles le tag « <i>lecture courte</i> » lorsque le temps de lecture est inférieur à 3 minutes.<br />Dans ce cas, vous devriez mettre « readingTime <= 3 » dans le champ <i>Règle</i> et « <i>lecture courte</i> » dans le champ <i>Tag</i>.<br />Plusieurs tags peuvent être ajoutés simultanément en les séparant par des virgules : « <i>lecture courte, à lire</i> »<br />Des règles complexes peuvent être créées en utilisant des opérateurs prédéfinis: si « <i>readingTime >= 5 AND domainName = \"github.com\"</i> » alors attribuer les tags « <i>lecture longue, github</i> »" |
121 | variables_available_title: 'Quelles variables et opérateurs puis-je utiliser pour écrire des règles ?' | 121 | variables_available_title: "Quelles variables et opérateurs puis-je utiliser pour écrire des règles ?" |
122 | variables_available_description: 'Les variables et opérateurs suivants peuvent être utilisés pour écrire des règles de tag automatiques :' | 122 | variables_available_description: "Les variables et opérateurs suivants peuvent être utilisés pour écrire des règles de tag automatiques :" |
123 | meaning: 'Signification' | 123 | meaning: "Signification" |
124 | variable_description: | 124 | variable_description: |
125 | label: 'Variable' | 125 | label: "Variable" |
126 | title: "Titre de l'article" | 126 | title: "Titre de l’article" |
127 | url: "URL de l'article" | 127 | url: "Adresse de l’article" |
128 | isArchived: "Si l'article est archivé ou non" | 128 | isArchived: "Si l’article est archivé ou non" |
129 | isStarred: "Si l'article est favori ou non" | 129 | isStarred: "Si l’article est favori ou non" |
130 | content: "Le contenu de l'article" | 130 | content: "Le contenu de l’article" |
131 | language: "La langue de l'article" | 131 | language: "La langue de l’article" |
132 | mimetype: "Le type MIME de l'article" | 132 | mimetype: "Le type MIME de l’article" |
133 | readingTime: "Le temps de lecture estimé de l'article, en minutes" | 133 | readingTime: "Le temps de lecture estimé de l’article, en minutes" |
134 | domainName: "Le nom de domaine de l'article" | 134 | domainName: "Le nom de domaine de l’article" |
135 | operator_description: | 135 | operator_description: |
136 | label: 'Opérateur' | 136 | label: "Opérateur" |
137 | less_than: 'Moins que…...' | 137 | less_than: "Moins que…..." |
138 | strictly_less_than: 'Strictement moins que…' | 138 | strictly_less_than: "Strictement moins que…" |
139 | greater_than: 'Plus que…' | 139 | greater_than: "Plus que…" |
140 | strictly_greater_than: 'Strictement plus que…' | 140 | strictly_greater_than: "Strictement plus que…" |
141 | equal_to: 'Égal à…' | 141 | equal_to: "Égal à…" |
142 | not_equal_to: 'Différent de…' | 142 | not_equal_to: "Différent de…" |
143 | or: "Une règle OU l'autre" | 143 | or: "Une règle OU l’autre" |
144 | and: "Une règle ET l'autre" | 144 | and: "Une règle ET l’autre" |
145 | matches: 'Teste si un <i>sujet</i> correspond à une <i>recherche</i> (non sensible à la casse).<br />Exemple : <code>title matches "football"</code>' | 145 | matches: "Teste si un <i>sujet</i> correspond à une <i>recherche</i> (non sensible à la casse).<br />Exemple : <code>title matches \"football\"</code>" |
146 | 146 | ||
147 | entry: | 147 | entry: |
148 | page_titles: | 148 | page_titles: |
149 | unread: 'Articles non lus' | 149 | unread: "Articles non lus" |
150 | starred: 'Articles favoris' | 150 | starred: "Articles favoris" |
151 | archived: 'Articles lus' | 151 | archived: "Articles lus" |
152 | filtered: 'Articles filtrés' | 152 | filtered: "Articles filtrés" |
153 | filtered_tags: 'Articles filtrés par tags' | 153 | filtered_tags: "Articles filtrés par tags :" |
154 | untagged: 'Article sans tag' | 154 | untagged: "Article sans tag" |
155 | list: | 155 | list: |
156 | number_on_the_page: "{0} Il n'y a pas d'articles.|{1} Il y a un article.|]1,Inf[ Il y a %count% articles." | 156 | number_on_the_page: "{0} Il n’y a pas d’articles.|{1} Il y a un article.|]1,Inf[ Il y a %count% articles." |
157 | reading_time: 'durée de lecture' | 157 | reading_time: "durée de lecture" |
158 | reading_time_minutes: 'durée de lecture: %readingTime% min' | 158 | reading_time_minutes: "durée de lecture: %readingTime% min" |
159 | reading_time_less_one_minute: 'durée de lecture: <small class="inferieur"><</small> 1 min' | 159 | reading_time_less_one_minute: "durée de lecture: <small class=\"inferieur\"><</small> 1 min" |
160 | number_of_tags: '{1}et un autre tag|]1,Inf[et %count% autres tags' | 160 | number_of_tags: "{1}et un autre tag|]1,Inf[et %count% autres tags" |
161 | reading_time_minutes_short: '%readingTime% min' | 161 | reading_time_minutes_short: "%readingTime% min" |
162 | reading_time_less_one_minute_short: '<small class="inferieur"><</small> 1 min' | 162 | reading_time_less_one_minute_short: "<small class=\"inferieur\"><</small> 1 min" |
163 | original_article: 'original' | 163 | original_article: "original" |
164 | toogle_as_read: 'Marquer comme lu/non lu' | 164 | toogle_as_read: "Marquer comme lu/non lu" |
165 | toogle_as_star: 'Marquer comme favori' | 165 | toogle_as_star: "Marquer comme favori" |
166 | delete: 'Supprimer' | 166 | delete: "Supprimer" |
167 | export_title: 'Exporter' | 167 | export_title: "Exporter" |
168 | filters: | 168 | filters: |
169 | title: 'Filtres' | 169 | title: "Filtres" |
170 | status_label: 'Status' | 170 | status_label: "Status" |
171 | archived_label: 'Lus' | 171 | archived_label: "Lus" |
172 | starred_label: 'Favoris' | 172 | starred_label: "Favoris" |
173 | unread_label: 'Non lus' | 173 | unread_label: "Non lus" |
174 | preview_picture_label: 'A une photo' | 174 | preview_picture_label: "A une photo" |
175 | preview_picture_help: 'Photo' | 175 | preview_picture_help: "Photo" |
176 | language_label: 'Langue' | 176 | language_label: "Langue" |
177 | reading_time: | 177 | reading_time: |
178 | label: 'Durée de lecture en minutes' | 178 | label: "Durée de lecture en minutes" |
179 | from: 'de' | 179 | from: "de" |
180 | to: 'à' | 180 | to: "à" |
181 | domain_label: 'Nom de domaine' | 181 | domain_label: "Nom de domaine" |
182 | created_at: | 182 | created_at: |
183 | label: 'Date de création' | 183 | label: "Date de création" |
184 | from: 'de' | 184 | from: "de" |
185 | to: 'à' | 185 | to: "à" |
186 | action: | 186 | action: |
187 | clear: 'Effacer' | 187 | clear: "Effacer" |
188 | filter: 'Filtrer' | 188 | filter: "Filtrer" |
189 | view: | 189 | view: |
190 | left_menu: | 190 | left_menu: |
191 | back_to_top: 'Revenir en haut' | 191 | back_to_top: "Revenir en haut" |
192 | back_to_homepage: 'Retour' | 192 | back_to_homepage: "Retour" |
193 | set_as_read: 'Marquer comme lu' | 193 | set_as_read: "Marquer comme lu" |
194 | set_as_unread: 'Marquer comme non lu' | 194 | set_as_unread: "Marquer comme non lu" |
195 | set_as_starred: 'Mettre en favori' | 195 | set_as_starred: "Mettre en favori" |
196 | view_original_article: 'Article original' | 196 | view_original_article: "Article original" |
197 | re_fetch_content: 'Recharger le contenu' | 197 | re_fetch_content: "Recharger le contenu" |
198 | delete: 'Supprimer' | 198 | delete: "Supprimer" |
199 | add_a_tag: 'Ajouter un tag' | 199 | add_a_tag: "Ajouter un tag" |
200 | share_content: 'Partager' | 200 | share_content: "Partager" |
201 | share_email_label: 'Email' | 201 | share_email_label: "Courriel" |
202 | public_link: 'Lien public' | 202 | public_link: "Lien public" |
203 | delete_public_link: 'Supprimer lien public' | 203 | delete_public_link: "Supprimer le lien public" |
204 | download: 'Télécharger' | 204 | download: "Télécharger" |
205 | print: 'Imprimer' | 205 | print: "Imprimer" |
206 | problem: | 206 | problem: |
207 | label: 'Un problème ?' | 207 | label: "Un problème ?" |
208 | description: "Est-ce que cet article s'affiche mal ?" | 208 | description: "Est-ce que cet article s’affiche mal ?" |
209 | edit_title: 'Modifier le titre' | 209 | edit_title: "Modifier le titre" |
210 | original_article: 'original' | 210 | original_article: "original" |
211 | annotations_on_the_entry: '{0} Aucune annotation|{1} Une annotation|]1,Inf[ %count% annotations' | 211 | annotations_on_the_entry: "{0} Aucune annotation|{1} Une annotation|]1,Inf[ %count% annotations" |
212 | created_at: 'Date de création' | 212 | created_at: "Date de création" |
213 | new: | 213 | new: |
214 | page_title: 'Sauvegarder un nouvel article' | 214 | page_title: "Sauvegarder un nouvel article" |
215 | placeholder: 'http://website.com' | 215 | placeholder: "http://website.com" |
216 | form_new: | 216 | form_new: |
217 | url_label: Url | 217 | url_label: "Adresse" |
218 | edit: | 218 | edit: |
219 | page_title: 'Éditer un article' | 219 | page_title: "Éditer un article" |
220 | title_label: 'Titre' | 220 | title_label: "Titre" |
221 | url_label: 'Url' | 221 | url_label: "Adresse" |
222 | is_public_label: 'Public' | 222 | is_public_label: "Public" |
223 | save_label: 'Enregistrer' | 223 | save_label: "Enregistrer" |
224 | public: | 224 | public: |
225 | shared_by_wallabag: "Cet article a été partagé par <a href='%wallabag_instance%'>wallabag</a>" | 225 | shared_by_wallabag: "Cet article a été partagé par <a href=\"%wallabag_instance%\">wallabag</a>" |
226 | 226 | ||
227 | about: | 227 | about: |
228 | page_title: 'À propos' | 228 | page_title: "À propos" |
229 | top_menu: | 229 | top_menu: |
230 | who_behind_wallabag: "L'équipe derrière wallabag" | 230 | who_behind_wallabag: "L’équipe derrière wallabag" |
231 | getting_help: "Besoin d'aide" | 231 | getting_help: "Besoin d’aide" |
232 | helping: 'Aider wallabag' | 232 | helping: "Aider wallabag" |
233 | contributors: 'Contributeurs' | 233 | contributors: "Contributeurs" |
234 | third_party: 'Librairies tierces' | 234 | third_party: "Librairies tierces" |
235 | who_behind_wallabag: | 235 | who_behind_wallabag: |
236 | developped_by: 'Développé par' | 236 | developped_by: "Développé par" |
237 | website: 'Site web' | 237 | website: "Site web" |
238 | many_contributors: 'Et plein de contributeurs ♥ <a href="https://github.com/wallabag/wallabag/graphs/contributors">sur Github</a>' | 238 | many_contributors: "Et plein de contributeurs ♥ <a href=\"https://github.com/wallabag/wallabag/graphs/contributors\">sur Github</a>" |
239 | project_website: 'Site web du projet' | 239 | project_website: "Site web du projet" |
240 | license: 'Licence' | 240 | license: "Licence" |
241 | version: 'Version' | 241 | version: "Version" |
242 | getting_help: | 242 | getting_help: |
243 | documentation: 'Documentation' | 243 | documentation: "Documentation" |
244 | bug_reports: 'Rapport de bugs' | 244 | bug_reports: "Rapport de bogue" |
245 | support: '<a href="https://support.wallabag.org">Sur notre site de support</a> ou <a href="https://github.com/wallabag/wallabag/issues">sur GitHub</a>' | 245 | support: "<a href=\"https://support.wallabag.org\">Sur notre site de support</a> ou <a href=\"https://github.com/wallabag/wallabag/issues\">sur GitHub</a>" |
246 | helping: | 246 | helping: |
247 | description: 'wallabag est gratuit et opensource. Vous pouvez nous aider :' | 247 | description: "wallabag est gratuit et opensource. Vous pouvez nous aider :" |
248 | by_contributing: 'en contribuant au projet :' | 248 | by_contributing: "en contribuant au projet :" |
249 | by_contributing_2: 'un ticket recense tous nos besoins' | 249 | by_contributing_2: "un ticket recense tous nos besoins" |
250 | by_paypal: 'via Paypal' | 250 | by_paypal: "via Paypal" |
251 | contributors: | 251 | contributors: |
252 | description: "Merci aux contributeurs de l'application web de wallabag" | 252 | description: "Merci aux contributeurs de l’application web de wallabag" |
253 | third_party: | 253 | third_party: |
254 | description: 'Voici la liste des dépendances utilisées dans wallabag (et leur license) :' | 254 | description: "Voici la liste des dépendances utilisées dans wallabag (et leur license) :" |
255 | package: 'Dépendance' | 255 | package: "Dépendance" |
256 | license: 'Licence' | 256 | license: "Licence" |
257 | 257 | ||
258 | howto: | 258 | howto: |
259 | page_title: 'Aide' | 259 | page_title: "Aide" |
260 | page_description: "Il y a plusieurs façon d'enregistrer un article :" | 260 | page_description: "Il y a plusieurs façon d’enregistrer un article :" |
261 | top_menu: | 261 | top_menu: |
262 | browser_addons: 'Extensions de navigateur' | 262 | browser_addons: "Extensions de navigateur" |
263 | mobile_apps: 'Applications smartphone' | 263 | mobile_apps: "Applications smartphone" |
264 | bookmarklet: 'Bookmarklet' | 264 | bookmarklet: "Bookmarklet" |
265 | form: | 265 | form: |
266 | description: 'Grâce à ce formulaire' | 266 | description: "Grâce à ce formulaire" |
267 | browser_addons: | 267 | browser_addons: |
268 | firefox: 'Extension Firefox' | 268 | firefox: "Extension Firefox" |
269 | chrome: 'Extension Chrome' | 269 | chrome: "Extension Chrome" |
270 | mobile_apps: | 270 | mobile_apps: |
271 | android: | 271 | android: |
272 | via_f_droid: 'via F-Droid' | 272 | via_f_droid: "via F-Droid" |
273 | via_google_play: 'via Google Play' | 273 | via_google_play: "via Google Play" |
274 | ios: 'sur iTunes Store' | 274 | ios: "sur iTunes Store" |
275 | windows: 'sur Microsoft Store' | 275 | windows: "sur Microsoft Store" |
276 | bookmarklet: | 276 | bookmarklet: |
277 | description: 'Glissez et déposez ce lien dans votre barre de favoris :' | 277 | description: "Glissez et déposez ce lien dans votre barre de favoris :" |
278 | 278 | ||
279 | quickstart: | 279 | quickstart: |
280 | page_title: 'Pour bien débuter' | 280 | page_title: "Pour bien débuter" |
281 | more: 'Et plus encore…' | 281 | more: "Et plus encore…" |
282 | intro: | 282 | intro: |
283 | title: 'Bienvenue sur wallabag !' | 283 | title: "Bienvenue sur wallabag !" |
284 | paragraph_1: "Nous allons vous accompagner pour vous faire faire le tour de la maison et vous présenter quelques fonctionnalités qui pourraient vous intéresser pour vous approprier cet outil." | 284 | paragraph_1: "Nous allons vous accompagner pour vous faire faire le tour de la maison et vous présenter quelques fonctionnalités qui pourraient vous intéresser pour vous approprier cet outil." |
285 | paragraph_2: 'Suivez-nous !' | 285 | paragraph_2: "Suivez-nous !" |
286 | configure: | 286 | configure: |
287 | title: "Configurez l'application" | 287 | title: "Configurez l’application" |
288 | description: 'Pour voir une application qui vous correspond, allez voir du côté de la configuration de wallabag.' | 288 | description: "Pour voir une application qui vous correspond, allez voir du côté de la configuration de wallabag." |
289 | language: "Changez la langue et le design de l'application" | 289 | language: "Changez la langue et le design de l’application" |
290 | rss: 'Activez les flux RSS' | 290 | rss: "Activez les flux RSS" |
291 | tagging_rules: 'Écrivez des règles pour classer automatiquement vos articles' | 291 | tagging_rules: "Écrivez des règles pour classer automatiquement vos articles" |
292 | admin: | 292 | admin: |
293 | title: 'Administration' | 293 | title: "Administration" |
294 | description: "En tant qu'administrateur sur wallabag, vous avez des privilèges qui vous permettent de :" | 294 | description: "En tant qu’administrateur sur wallabag, vous avez des privilèges qui vous permettent de :" |
295 | new_user: 'Créer un nouvel utilisateur' | 295 | new_user: "Créer un nouvel utilisateur" |
296 | analytics: 'Configurer les statistiques' | 296 | analytics: "Configurer les statistiques" |
297 | sharing: 'Activer des paramètres de partages' | 297 | sharing: "Activer des paramètres de partages" |
298 | export: "Configurer les formats d'export" | 298 | export: "Configurer les formats d’export" |
299 | import: "Configurer l'import" | 299 | import: "Configurer l’import" |
300 | first_steps: | 300 | first_steps: |
301 | title: 'Premiers pas' | 301 | title: "Premiers pas" |
302 | description: "Maintenant que wallabag est bien configuré, il est temps d'archiver le web. Vous pouvez cliquer sur le signe + dans le coin en haut à droite." | 302 | description: "Maintenant que wallabag est bien configuré, il est temps d’archiver le web. Vous pouvez cliquer sur le signe + dans le coin en haut à droite." |
303 | new_article: 'Ajoutez votre premier article' | 303 | new_article: "Ajoutez votre premier article" |
304 | unread_articles: 'Et rangez-le !' | 304 | unread_articles: "Et rangez-le !" |
305 | migrate: | 305 | migrate: |
306 | title: 'Migrer depuis un service existant' | 306 | title: "Migrer depuis un service existant" |
307 | description: "Vous êtes un ancien utilisateur d'un service existant ? Nous allons vous aider à récupérer vos données sur wallabag." | 307 | description: "Vous êtes un ancien utilisateur d’un service existant ? Nous allons vous aider à récupérer vos données sur wallabag." |
308 | pocket: 'Migrer depuis Pocket' | 308 | pocket: "Migrer depuis Pocket" |
309 | wallabag_v1: 'Migrer depuis wallabag v1' | 309 | wallabag_v1: "Migrer depuis wallabag v1" |
310 | wallabag_v2: 'Migrer depuis wallabag v2' | 310 | wallabag_v2: "Migrer depuis wallabag v2" |
311 | readability: 'Migrer depuis Readability' | 311 | readability: "Migrer depuis Readability" |
312 | instapaper: 'Migrer depuis Instapaper' | 312 | instapaper: "Migrer depuis Instapaper" |
313 | developer: | 313 | developer: |
314 | title: 'Pour les développeurs' | 314 | title: "Pour les développeurs" |
315 | description: 'Nous avons aussi pensé aux développeurs : Docker, API, traductions, etc.' | 315 | description: "Nous avons aussi pensé aux développeurs : Docker, API, traductions, etc." |
316 | create_application: 'Créer votre application tierce' | 316 | create_application: "Créer votre application tierce" |
317 | use_docker: 'Utiliser Docker pour installer wallabag' | 317 | use_docker: "Utiliser Docker pour installer wallabag" |
318 | docs: | 318 | docs: |
319 | title: 'Documentation complète' | 319 | title: "Documentation complète" |
320 | description: "Il y a tellement de fonctionnalités dans wallabag. N'hésitez pas à lire le manuel pour les connaitre et apprendre comment les utiliser." | 320 | description: "Il y a tellement de fonctionnalités dans wallabag. N’hésitez pas à lire le manuel pour les connaitre et apprendre comment les utiliser." |
321 | annotate: 'Annoter votre article' | 321 | annotate: "Annoter votre article" |
322 | export: 'Convertissez vos articles en ePub ou en PDF' | 322 | export: "Convertissez vos articles en ePub ou en PDF" |
323 | search_filters: "Apprenez à utiliser le moteur de recherche et les filtres pour retrouver l'article qui vous intéresse" | 323 | search_filters: "Apprenez à utiliser le moteur de recherche et les filtres pour retrouver l’article qui vous intéresse" |
324 | fetching_errors: "Que faire si mon article n'est pas correctement récupéré ?" | 324 | fetching_errors: "Que faire si mon article n’est pas correctement récupéré ?" |
325 | all_docs: "Et encore plein d'autres choses !" | 325 | all_docs: "Et encore plein d’autres choses !" |
326 | support: | 326 | support: |
327 | title: 'Support' | 327 | title: "Support" |
328 | description: 'Parce que vous avez peut-être besoin de nous poser une question, nous sommes disponibles pour vous.' | 328 | description: "Parce que vous avez peut-être besoin de nous poser une question, nous sommes disponibles pour vous." |
329 | github: 'Sur GitHub' | 329 | github: "Sur GitHub" |
330 | email: 'Par e-mail' | 330 | email: "Par courriel" |
331 | gitter: 'Sur Gitter' | 331 | gitter: "Sur Gitter" |
332 | 332 | ||
333 | tag: | 333 | tag: |
334 | page_title: 'Tags' | 334 | page_title: "Tags" |
335 | list: | 335 | list: |
336 | number_on_the_page: "{0} Il n'y a pas de tag.|{1} Il y a un tag.|]1,Inf[ Il y a %count% tags." | 336 | number_on_the_page: "{0} Il n’y a pas de tag.|{1} Il y a un tag.|]1,Inf[ Il y a %count% tags." |
337 | see_untagged_entries: 'Voir les articles sans tag' | 337 | see_untagged_entries: "Voir les articles sans tag" |
338 | 338 | ||
339 | import: | 339 | import: |
340 | page_title: 'Importer' | 340 | page_title: "Importer" |
341 | page_description: "Bienvenue dans l'outil de migration de wallabag. Choisissez ci-dessous le service depuis lequel vous souhaitez migrer." | 341 | page_description: "Bienvenue dans l’outil de migration de wallabag. Choisissez ci-dessous le service depuis lequel vous souhaitez migrer." |
342 | action: | 342 | action: |
343 | import_contents: 'Importer les contenus' | 343 | import_contents: "Importer les contenus" |
344 | form: | 344 | form: |
345 | mark_as_read_title: 'Marquer tout comme lu ?' | 345 | mark_as_read_title: "Marquer tout comme lu ?" |
346 | mark_as_read_label: 'Marquer tous les contenus importés comme lus' | 346 | mark_as_read_label: "Marquer tous les contenus importés comme lus" |
347 | file_label: 'Fichier' | 347 | file_label: "Fichier" |
348 | save_label: 'Importer le fichier' | 348 | save_label: "Importer le fichier" |
349 | pocket: | 349 | pocket: |
350 | page_title: 'Importer > Pocket' | 350 | page_title: "Importer > Pocket" |
351 | description: "Cet outil va importer toutes vos données de Pocket. Pocket ne nous autorise pas à récupérer le contenu depuis leur service, donc wallabag doit reparcourir chaque article pour récupérer son contenu." | 351 | description: "Cet outil va importer toutes vos données de Pocket. Pocket ne nous autorise pas à récupérer le contenu depuis leur service, donc wallabag doit reparcourir chaque article pour récupérer son contenu." |
352 | config_missing: | 352 | config_missing: |
353 | description: "L'import à partir de Pocket n'est pas configuré." | 353 | description: "L’import à partir de Pocket n’est pas configuré." |
354 | admin_message: "Vous devez définir %keyurls%une clé pour l'API Pocket%keyurle%." | 354 | admin_message: "Vous devez définir %keyurls%une clé pour l’API Pocket%keyurle%." |
355 | user_message: "L'administrateur de votre serveur doit définir une clé pour l'API Pocket." | 355 | user_message: "L’administrateur de votre serveur doit définir une clé pour l’API Pocket." |
356 | authorize_message: "Vous pouvez importer vos données depuis votre compte Pocket. Vous n'avez qu'à cliquer sur le bouton ci-dessous et à autoriser wallabag à se connecter à getpocket.com." | 356 | authorize_message: "Vous pouvez importer vos données depuis votre compte Pocket. Vous n’avez qu’à cliquer sur le bouton ci-dessous et à autoriser wallabag à se connecter à getpocket.com." |
357 | connect_to_pocket: 'Se connecter à Pocket et importer les données' | 357 | connect_to_pocket: "Se connecter à Pocket et importer les données" |
358 | wallabag_v1: | 358 | wallabag_v1: |
359 | page_title: 'Importer > Wallabag v1' | 359 | page_title: "Importer > wallabag v1" |
360 | description: 'Cet outil va importer toutes vos données de wallabag v1. Sur votre page de configuration de wallabag v1, cliquez sur "Export JSON" dans la section "Exporter vos données de wallabag". Vous allez récupérer un fichier "wallabag-export-1-xxxx-xx-xx.json".' | 360 | description: "Cet outil va importer toutes vos données de wallabag v1. Sur votre page de configuration de wallabag v1, cliquez sur « Export JSON » dans la section « Exporter vos données de wallabag ». Vous allez récupérer un fichier « wallabag-export-1-xxxx-xx-xx.json »." |
361 | how_to: "Choisissez le fichier de votre export wallabag v1 et cliquez sur le bouton ci-dessous pour l'importer." | 361 | how_to: "Choisissez le fichier de votre export wallabag v1 et cliquez sur le bouton ci-dessous pour l’importer." |
362 | wallabag_v2: | 362 | wallabag_v2: |
363 | page_title: 'Importer > Wallabag v2' | 363 | page_title: "Importer > wallabag v2" |
364 | description: "Cet outil va importer tous vos articles d'une autre instance de wallabag v2. Allez dans tous vos articles, puis, sur la barre latérale, cliquez sur \"JSON\". Vous allez récupérer un fichier \"All articles.json\"" | 364 | description: "Cet outil va importer tous vos articles d’une autre instance de wallabag v2. Allez dans tous vos articles, puis, sur la barre latérale, cliquez sur « JSON ». Vous allez récupérer un fichier « All articles.json »" |
365 | readability: | 365 | readability: |
366 | page_title: 'Importer > Readability' | 366 | page_title: "Importer > Readability" |
367 | description: 'Cet outil va importer toutes vos données de Readability. Sur la page des outils (https://www.readability.com/tools/), cliquez sur "Export your data" dans la section "Data Export". Vous allez recevoir un email avec un lien pour télécharger le json.' | 367 | description: "Cet outil va importer toutes vos données de Readability. Sur la page des outils (https://www.readability.com/tools/), cliquez sur « Export your data » dans la section « Data Export ». Vous allez recevoir un courriel avec un lien pour télécharger le json." |
368 | how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l'importer." | 368 | how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." |
369 | worker: | 369 | worker: |
370 | enabled: "Les imports sont asynchrones. Une fois l'import commencé un worker externe traitera les messages un par un. Le service activé est :" | 370 | enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :" |
371 | download_images_warning: "Vous avez configuré le téléchagement des images pour vos articles. Combiné à l'import classique, cette opération peut être très très longue (voire échouer). Nous vous conseillons <strong>vivement</strong> d'activer les imports asynchrones." | 371 | download_images_warning: "Vous avez configuré le téléchagement des images pour vos articles. Combiné à l'import classique, cette opération peut être très très longue (voire échouer). Nous vous conseillons <strong>vivement</strong> d'activer les imports asynchrones." |
372 | firefox: | 372 | firefox: |
373 | page_title: 'Import > Firefox' | 373 | page_title: "Import > Firefox" |
374 | description: "Cet outil va vous permettre d'importer tous vos marques-pages de Firefox. Ouvrez le panneau des marques-pages (Ctrl+Maj+O), puis dans « Importation et sauvegarde », choisissez « Sauvegarde... ». Vous allez récupérer un fichier .json. </p>" | 374 | description: "Cet outil va vous permettre d’importer tous vos marques-pages de Firefox. Ouvrez le panneau des marques-pages (Ctrl+Maj+O), puis dans « Importation et sauvegarde », choisissez « Sauvegarde... ». Vous allez récupérer un fichier .json. </p>" |
375 | how_to: "Choisissez le fichier de sauvegarde de vos marques-page et cliquez sur le bouton pour l'importer. Soyez avertis que le processus peut prendre un temps assez long car tous les articles doivent être récupérés en ligne." | 375 | how_to: "Choisissez le fichier de sauvegarde de vos marques-page et cliquez sur le bouton pour l’importer. Soyez avertis que le processus peut prendre un temps assez long car tous les articles doivent être récupérés en ligne." |
376 | chrome: | 376 | chrome: |
377 | page_title: 'Import > Chrome' | 377 | page_title: "Import > Chrome" |
378 | description: "Cet outil va vous permettre d'importer tous vos marques-pages de Google Chrome/Chromium. Pour Google Chrome, la situation du fichier dépend de votre système d'exploitation : <ul><li>Sur GNU/Linux, allez dans le répertoire <code>~/.config/google-chrome/Default/</code></li><li>Sous Windows, il devrait se trouver à <code>%LOCALAPPDATA%\\Google\\Chrome\\User Data\\Default</code></li><li>Sur OS X, il devrait se trouver dans le fichier <code>~/Library/Application Support/Google/Chrome/Default/Bookmarks</code></li></ul>Une fois que vous y êtes, copiez le fichier Bookmarks à un endroit où vous le retrouverez.<em><br>Notez que si vous utilisez Chromium à la place de Chrome, vous devez corriger les chemins en conséquence.</em></p>" | 378 | description: "Cet outil va vous permettre d’importer tous vos marques-pages de Google Chrome/Chromium. Pour Google Chrome, la situation du fichier dépend de votre système d’exploitation : <ul><li>Sur GNU/Linux, allez dans le répertoire <code>~/.config/google-chrome/Default/</code></li><li>Sous Windows, il devrait se trouver à <code>%LOCALAPPDATA%\\Google\\Chrome\\User Data\\Default</code></li><li>Sur OS X, il devrait se trouver dans le fichier <code>~/Library/Application Support/Google/Chrome/Default/Bookmarks</code></li></ul>Une fois que vous y êtes, copiez le fichier Bookmarks à un endroit où vous le retrouverez.<em><br>Notez que si vous utilisez Chromium à la place de Chrome, vous devez corriger les chemins en conséquence.</em></p>" |
379 | how_to: "Choisissez le fichier de sauvegarde de vos marques-page et cliquez sur le bouton pour l'importer. Soyez avertis que le processus peut prendre un temps assez long car tous les articles doivent être récupérés en ligne." | 379 | how_to: "Choisissez le fichier de sauvegarde de vos marques-page et cliquez sur le bouton pour l’importer. Soyez avertis que le processus peut prendre un temps assez long car tous les articles doivent être récupérés en ligne." |
380 | instapaper: | 380 | instapaper: |
381 | page_title: 'Import > Instapaper' | 381 | page_title: "Import > Instapaper" |
382 | description: 'Sur la page des paramètres (https://www.instapaper.com/user), cliquez sur "Download .CSV file" dans la section "Export". Un fichier CSV se téléchargera ("instapaper-export.csv").' | 382 | description: "Sur la page des paramètres (https://www.instapaper.com/user), cliquez sur « Download .CSV file » dans la section « Export ». Un fichier CSV sera téléchargé (« instapaper-export.csv »)." |
383 | how_to: "Choisissez le fichier de votre export Instapaper et cliquez sur le bouton ci-dessous pour l'importer." | 383 | how_to: "Choisissez le fichier de votre export Instapaper et cliquez sur le bouton ci-dessous pour l’importer." |
384 | 384 | ||
385 | developer: | 385 | developer: |
386 | page_title: 'Développeur' | 386 | page_title: "Développeur" |
387 | welcome_message: "Bienvenue sur l'API de wallabag" | 387 | welcome_message: "Bienvenue sur l’API de wallabag" |
388 | documentation: 'Documentation' | 388 | documentation: "Documentation" |
389 | how_to_first_app: 'Comment créer votre première application' | 389 | how_to_first_app: "Comment créer votre première application" |
390 | full_documentation: "Voir la documentation complète de l'API" | 390 | full_documentation: "Voir la documentation complète de l’API" |
391 | list_methods: "Lister toutes les méthodes de l'API" | 391 | list_methods: "Lister toutes les méthodes de l’API" |
392 | clients: | 392 | clients: |
393 | title: 'Clients' | 393 | title: "Clients" |
394 | create_new: 'Créer un nouveau client' | 394 | create_new: "Créer un nouveau client" |
395 | existing_clients: | 395 | existing_clients: |
396 | title: 'Les clients existants' | 396 | title: "Les clients existants" |
397 | field_id: 'ID Client' | 397 | field_id: "ID Client" |
398 | field_secret: 'Clé secrète' | 398 | field_secret: "Clé secrète" |
399 | field_uris: 'URLs de redirection' | 399 | field_uris: "Adresse de redirection" |
400 | field_grant_types: 'Type de privilège accordé' | 400 | field_grant_types: "Type de privilège accordé" |
401 | no_client: 'Aucun client pour le moment' | 401 | no_client: "Aucun client pour le moment" |
402 | remove: | 402 | remove: |
403 | warn_message_1: 'Vous avez la possibilité de supprimer le client %name%. Cette action est IRRÉVERSIBLE !' | 403 | warn_message_1: "Vous avez la possibilité de supprimer le client %name%. Cette action est IRRÉVERSIBLE !" |
404 | warn_message_2: "Si vous supprimez le client %name%, toutes les applications qui l'utilisaient ne fonctionneront plus avec votre compte wallabag." | 404 | warn_message_2: "Si vous supprimez le client %name%, toutes les applications qui l’utilisaient ne fonctionneront plus avec votre compte wallabag." |
405 | action: 'Supprimer le client %name%' | 405 | action: "Supprimer le client %name%" |
406 | client: | 406 | client: |
407 | page_title: 'Développeur > Nouveau client' | 407 | page_title: "Développeur > Nouveau client" |
408 | page_description: "Vous allez créer un nouveau client. Merci de remplir l'url de redirection vers votre application." | 408 | page_description: "Vous allez créer un nouveau client. Merci de remplir l’adresse de redirection vers votre application." |
409 | form: | 409 | form: |
410 | name_label: "Nom du client" | 410 | name_label: "Nom du client" |
411 | redirect_uris_label: 'URLs de redirection (optionnel)' | 411 | redirect_uris_label: "Adresses de redirection (optionnel)" |
412 | save_label: 'Créer un nouveau client' | 412 | save_label: "Créer un nouveau client" |
413 | action_back: 'Retour' | 413 | action_back: "Retour" |
414 | client_parameter: | 414 | client_parameter: |
415 | page_title: 'Développeur > Les paramètres de votre client' | 415 | page_title: "Développeur > Les paramètres de votre client" |
416 | page_description: 'Voilà les paramètres de votre client' | 416 | page_description: "Voilà les paramètres de votre client" |
417 | field_name: 'Nom du client' | 417 | field_name: "Nom du client" |
418 | field_id: 'ID Client' | 418 | field_id: "ID client" |
419 | field_secret: 'Clé secrète' | 419 | field_secret: "Clé secrète" |
420 | back: 'Retour' | 420 | back: "Retour" |
421 | read_howto: 'Lire "comment créer ma première application"' | 421 | read_howto: "Lire « comment créer ma première application »" |
422 | howto: | 422 | howto: |
423 | page_title: 'Développeur > Comment créer votre première application' | 423 | page_title: "Développeur > Comment créer votre première application" |
424 | description: | 424 | description: |
425 | paragraph_1: "Les commandes suivantes utilisent la <a href=\"https://github.com/jkbrzt/httpie\">librarie HTTPie</a>. Assurez-vous qu'elle soit installée avant de l'utiliser." | 425 | paragraph_1: "Les commandes suivantes utilisent la <a href=\"https://github.com/jkbrzt/httpie\">librarie HTTPie</a>. Assurez-vous qu’elle soit installée avant de l’utiliser." |
426 | paragraph_2: "Vous avez besoin d'un token pour échanger entre votre application et l'API de wallabag." | 426 | paragraph_2: "Vous avez besoin d’un token pour échanger entre votre application et l’API de wallabag." |
427 | paragraph_3: 'Pour créer un token, vous devez <a href="%link%">créer un nouveau client</a>.' | 427 | paragraph_3: "Pour créer un token, vous devez <a href=\"%link%\">créer un nouveau client</a>." |
428 | paragraph_4: 'Maintenant créez votre token (remplacer client_id, client_secret, username et password avec les bonnes valeurs):' | 428 | paragraph_4: "Maintenant créez votre token (remplacer client_id, client_secret, username et password avec les bonnes valeurs):" |
429 | paragraph_5: "L'API vous retournera une réponse comme ça :" | 429 | paragraph_5: "L’API vous retournera une réponse comme ça :" |
430 | paragraph_6: "L'access_token doit être utilisé pour faire un appel à l'API. Par exemple :" | 430 | paragraph_6: "L’access_token doit être utilisé pour faire un appel à l’API. Par exemple :" |
431 | paragraph_7: "Cet appel va retourner tous les articles de l'utilisateur." | 431 | paragraph_7: "Cet appel va retourner tous les articles de l’utilisateur." |
432 | paragraph_8: "Si vous voulez toutes les méthodes de l'API, jetez un oeil <a href=\"%link%\">à la documentation de l'API</a>." | 432 | paragraph_8: "Si vous voulez toutes les méthodes de l’API, jetez un oeil <a href=\"%link%\">à la documentation de l’API</a>." |
433 | back: 'Retour' | 433 | back: "Retour" |
434 | 434 | ||
435 | user: | 435 | user: |
436 | page_title: Gestion des utilisateurs | 436 | page_title: Gestion des utilisateurs |
@@ -444,20 +444,20 @@ user: | |||
444 | no: Non | 444 | no: Non |
445 | create_new_one: Créer un nouvel utilisateur | 445 | create_new_one: Créer un nouvel utilisateur |
446 | form: | 446 | form: |
447 | username_label: "Nom d'utilisateur" | 447 | username_label: "Nom d’utilisateur" |
448 | name_label: 'Nom' | 448 | name_label: "Nom" |
449 | password_label: 'Mot de passe' | 449 | password_label: "Mot de passe" |
450 | repeat_new_password_label: 'Confirmez votre nouveau mot de passe' | 450 | repeat_new_password_label: "Confirmez votre nouveau mot de passe" |
451 | plain_password_label: 'Mot de passe en clair' | 451 | plain_password_label: "Mot de passe en clair" |
452 | email_label: 'Adresse e-mail' | 452 | email_label: "Adresse courriel" |
453 | enabled_label: 'Activé' | 453 | enabled_label: "Activé" |
454 | locked_label: 'Bloqué' | 454 | locked_label: "Bloqué" |
455 | last_login_label: 'Dernière connexion' | 455 | last_login_label: "Dernière connexion" |
456 | twofactor_label: Double authentification | 456 | twofactor_label: "Double authentification" |
457 | save: Sauvegarder | 457 | save: "Sauvegarder" |
458 | delete: Supprimer | 458 | delete: "Supprimer" |
459 | delete_confirm: Êtes-vous sûr? | 459 | delete_confirm: "Voulez-vous vraiment ?" |
460 | back_to_list: Revenir à la liste | 460 | back_to_list: "Revenir à la liste" |
461 | 461 | ||
462 | error: | 462 | error: |
463 | page_title: Une erreur est survenue | 463 | page_title: Une erreur est survenue |
@@ -465,47 +465,46 @@ error: | |||
465 | flashes: | 465 | flashes: |
466 | config: | 466 | config: |
467 | notice: | 467 | notice: |
468 | config_saved: 'Les paramètres ont bien été mis à jour. Certains seront pris en compte après déconnexion.' | 468 | config_saved: "Les paramètres ont bien été mis à jour. Certains seront pris en compte après déconnexion." |
469 | password_updated: 'Votre mot de passe a bien été mis à jour' | 469 | password_updated: "Votre mot de passe a bien été mis à jour" |
470 | password_not_updated_demo: "En démo, vous ne pouvez pas changer le mot de passe de cet utilisateur." | 470 | password_not_updated_demo: "En démo, vous ne pouvez pas changer le mot de passe de cet utilisateur." |
471 | user_updated: 'Vos informations personnelles ont bien été mises à jour' | 471 | user_updated: "Vos informations personnelles ont bien été mises à jour" |
472 | rss_updated: 'La configuration des flux RSS a bien été mise à jour' | 472 | rss_updated: "La configuration des flux RSS a bien été mise à jour" |
473 | tagging_rules_updated: 'Règles mises à jour' | 473 | tagging_rules_updated: "Règles mises à jour" |
474 | tagging_rules_deleted: 'Règle supprimée' | 474 | tagging_rules_deleted: "Règle supprimée" |
475 | user_added: 'Utilisateur "%username%" ajouté' | 475 | rss_token_updated: "Jeton RSS mis à jour" |
476 | rss_token_updated: 'Jeton RSS mis à jour' | ||
477 | annotations_reset: Annotations supprimées | 476 | annotations_reset: Annotations supprimées |
478 | tags_reset: Tags supprimés | 477 | tags_reset: Tags supprimés |
479 | entries_reset: Articles supprimés | 478 | entries_reset: Articles supprimés |
480 | entry: | 479 | entry: |
481 | notice: | 480 | notice: |
482 | entry_already_saved: 'Article déjà sauvegardé le %date%' | 481 | entry_already_saved: "Article déjà sauvergardé le %date%" |
483 | entry_saved: 'Article enregistré' | 482 | entry_saved: "Article enregistré" |
484 | entry_saved_failed: 'Article enregistré mais impossible de récupérer le contenu' | 483 | entry_saved_failed: "Article enregistré mais impossible de récupérer le contenu" |
485 | entry_updated: 'Article mis à jour' | 484 | entry_updated: "Article mis à jour" |
486 | entry_reloaded: 'Article rechargé' | 485 | entry_reloaded: "Article rechargé" |
487 | entry_reloaded_failed: "Article mis à jour mais impossible de récupérer le contenu" | 486 | entry_reloaded_failed: "Article mis à jour mais impossible de récupérer le contenu" |
488 | entry_archived: 'Article marqué comme lu' | 487 | entry_archived: "Article marqué comme lu" |
489 | entry_unarchived: 'Article marqué comme non lu' | 488 | entry_unarchived: "Article marqué comme non lu" |
490 | entry_starred: 'Article ajouté dans les favoris' | 489 | entry_starred: "Article ajouté dans les favoris" |
491 | entry_unstarred: 'Article retiré des favoris' | 490 | entry_unstarred: "Article retiré des favoris" |
492 | entry_deleted: 'Article supprimé' | 491 | entry_deleted: "Article supprimé" |
493 | tag: | 492 | tag: |
494 | notice: | 493 | notice: |
495 | tag_added: 'Tag ajouté' | 494 | tag_added: "Tag ajouté" |
496 | import: | 495 | import: |
497 | notice: | 496 | notice: |
498 | failed: "L'import a échoué, veuillez ré-essayer" | 497 | failed: "L’import a échoué, veuillez ré-essayer" |
499 | failed_on_file: "Erreur lors du traitement de l'import. Vérifier votre fichier." | 498 | failed_on_file: "Erreur lors du traitement de l’import. Vérifiez votre fichier." |
500 | summary: "Rapport d'import: %imported% importés, %skipped% déjà présents." | 499 | summary: "Rapport d’import : %imported% importés, %skipped% déjà présents." |
501 | summary_with_queue: "Rapport d'import: %queued% en cours de traitement." | 500 | summary_with_queue: "Rapport d’import: %queued% en cours de traitement." |
502 | error: | 501 | error: |
503 | redis_enabled_not_installed: Redis est activé pour les imports asynchrones mais <u>impossible de s'y connecter</u>. Vérifier la configuration de Redis. | 502 | redis_enabled_not_installed: "Redis est activé pour les imports asynchrones mais <u>impossible de s’y connecter</u>. Vérifier la configuration de Redis." |
504 | rabbit_enabled_not_installed: RabbitMQ est activé pour les imports asynchrones mais <u>impossible de s'y connecter</u>. Vérifier la configuration de RabbitMQ. | 503 | rabbit_enabled_not_installed: "RabbitMQ est activé pour les imports asynchrones mais <u>impossible de s’y connecter</u>. Vérifier la configuration de RabbitMQ." |
505 | developer: | 504 | developer: |
506 | notice: | 505 | notice: |
507 | client_created: 'Nouveau client %name% créé' | 506 | client_created: "Nouveau client %name% créé" |
508 | client_deleted: 'Client %name% supprimé' | 507 | client_deleted: "Client %name% supprimé" |
509 | user: | 508 | user: |
510 | notice: | 509 | notice: |
511 | added: 'Utilisateur "%username%" ajouté' | 510 | added: 'Utilisateur "%username%" ajouté' |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml index a448b602..7f401684 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml | |||
@@ -150,7 +150,7 @@ entry: | |||
150 | starred: 'Contenuti preferiti' | 150 | starred: 'Contenuti preferiti' |
151 | archived: 'Contenuti archiviati' | 151 | archived: 'Contenuti archiviati' |
152 | filtered: 'Contenuti filtrati' | 152 | filtered: 'Contenuti filtrati' |
153 | # filtered_tags: 'Filtered by tags' | 153 | # filtered_tags: 'Filtered by tags:' |
154 | # untagged: 'Untagged entries' | 154 | # untagged: 'Untagged entries' |
155 | list: | 155 | list: |
156 | number_on_the_page: "{0} Non ci sono contenuti.|{1} C'è un contenuto.|]1,Inf[ Ci sono %count% contenuti." | 156 | number_on_the_page: "{0} Non ci sono contenuti.|{1} C'è un contenuto.|]1,Inf[ Ci sono %count% contenuti." |
@@ -472,7 +472,6 @@ flashes: | |||
472 | rss_updated: 'Informazioni RSS aggiornate' | 472 | rss_updated: 'Informazioni RSS aggiornate' |
473 | tagging_rules_updated: 'Regole di tagging aggiornate' | 473 | tagging_rules_updated: 'Regole di tagging aggiornate' |
474 | tagging_rules_deleted: 'Regola di tagging aggiornate' | 474 | tagging_rules_deleted: 'Regola di tagging aggiornate' |
475 | user_added: 'Utente "%username%" aggiunto' | ||
476 | rss_token_updated: 'RSS token aggiornato' | 475 | rss_token_updated: 'RSS token aggiornato' |
477 | # annotations_reset: Annotations reset | 476 | # annotations_reset: Annotations reset |
478 | # tags_reset: Tags reset | 477 | # tags_reset: Tags reset |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml index a61f7cdd..c3282b0e 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml | |||
@@ -150,7 +150,7 @@ entry: | |||
150 | starred: 'Articles favorits' | 150 | starred: 'Articles favorits' |
151 | archived: 'Articles legits' | 151 | archived: 'Articles legits' |
152 | filtered: 'Articles filtrats' | 152 | filtered: 'Articles filtrats' |
153 | filtered_tags: 'Filtats per etiquetas' | 153 | filtered_tags: 'Filtats per etiquetas:' |
154 | untagged: 'Articles sens etiqueta' | 154 | untagged: 'Articles sens etiqueta' |
155 | list: | 155 | list: |
156 | number_on_the_page: "{0} I a pas cap d'article.|{1} I a un article.|]1,Inf[ I a %count% articles." | 156 | number_on_the_page: "{0} I a pas cap d'article.|{1} I a un article.|]1,Inf[ I a %count% articles." |
@@ -472,7 +472,6 @@ flashes: | |||
472 | rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn' | 472 | rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn' |
473 | tagging_rules_updated: 'Règlas misa a jorn' | 473 | tagging_rules_updated: 'Règlas misa a jorn' |
474 | tagging_rules_deleted: 'Règla suprimida' | 474 | tagging_rules_deleted: 'Règla suprimida' |
475 | user_added: 'Utilizaire "%username%" ajustat' | ||
476 | rss_token_updated: 'Geton RSS mes a jorn' | 475 | rss_token_updated: 'Geton RSS mes a jorn' |
477 | # annotations_reset: Annotations reset | 476 | # annotations_reset: Annotations reset |
478 | # tags_reset: Tags reset | 477 | # tags_reset: Tags reset |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml index a7387b79..87731faf 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml | |||
@@ -150,7 +150,7 @@ entry: | |||
150 | starred: 'Wpisy oznaczone gwiazdką' | 150 | starred: 'Wpisy oznaczone gwiazdką' |
151 | archived: 'Zarchiwizowane wpisy' | 151 | archived: 'Zarchiwizowane wpisy' |
152 | filtered: 'Odfiltrowane wpisy' | 152 | filtered: 'Odfiltrowane wpisy' |
153 | filtered_tags: 'Filtrowane po tagach' | 153 | filtered_tags: 'Filtrowane po tagach:' |
154 | untagged: 'Odtaguj wpisy' | 154 | untagged: 'Odtaguj wpisy' |
155 | list: | 155 | list: |
156 | number_on_the_page: '{0} Nie ma wpisów.|{1} Jest jeden wpis.|]1,Inf[ Są %count% wpisy.' | 156 | number_on_the_page: '{0} Nie ma wpisów.|{1} Jest jeden wpis.|]1,Inf[ Są %count% wpisy.' |
@@ -472,7 +472,6 @@ flashes: | |||
472 | rss_updated: 'Informacje RSS zaktualizowane' | 472 | rss_updated: 'Informacje RSS zaktualizowane' |
473 | tagging_rules_updated: 'Reguły tagowania zaktualizowane' | 473 | tagging_rules_updated: 'Reguły tagowania zaktualizowane' |
474 | tagging_rules_deleted: 'Reguła tagowania usunięta' | 474 | tagging_rules_deleted: 'Reguła tagowania usunięta' |
475 | user_added: 'Użytkownik "%username%" dodany' | ||
476 | rss_token_updated: 'Token kanału RSS zaktualizowany' | 475 | rss_token_updated: 'Token kanału RSS zaktualizowany' |
477 | annotations_reset: Zresetuj adnotacje | 476 | annotations_reset: Zresetuj adnotacje |
478 | tags_reset: Zresetuj tagi | 477 | tags_reset: Zresetuj tagi |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml new file mode 100644 index 00000000..c1c60430 --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml | |||
@@ -0,0 +1,491 @@ | |||
1 | security: | ||
2 | login: | ||
3 | page_title: 'Bem vindo ao wallabag!' | ||
4 | keep_logged_in: 'Mantenha-me autenticado' | ||
5 | forgot_password: 'Esqueceu a senha?' | ||
6 | submit: 'Login' | ||
7 | register: 'Registre-se' | ||
8 | username: 'Nome de usuário' | ||
9 | password: 'Senha' | ||
10 | cancel: 'Cancelar' | ||
11 | resetting: | ||
12 | description: 'Digite seu endereço de e-mail abaixo e enviaremos instruções para resetar sua senha.' | ||
13 | register: | ||
14 | page_title: 'Criar uma conta' | ||
15 | go_to_account: 'Ir para sua conta' | ||
16 | |||
17 | menu: | ||
18 | left: | ||
19 | unread: 'Não lido' | ||
20 | starred: 'Destacado' | ||
21 | archive: 'Arquivo' | ||
22 | all_articles: 'Todas as entradas' | ||
23 | config: 'Configurações' | ||
24 | tags: 'Tags' | ||
25 | internal_settings: 'Configurações Internas' | ||
26 | import: 'Importar' | ||
27 | howto: 'How to' | ||
28 | developer: 'Desenvolvedor' | ||
29 | logout: 'Sair' | ||
30 | about: 'Sobre' | ||
31 | search: 'Pesquisa' | ||
32 | save_link: 'Salvar um link' | ||
33 | back_to_unread: 'Voltar para os artigos não lidos' | ||
34 | users_management: 'Gestão de Usuários' | ||
35 | top: | ||
36 | add_new_entry: 'Adicionar uma nova entrada' | ||
37 | search: 'Pesquisa' | ||
38 | filter_entries: 'Filtrar entradas' | ||
39 | export: 'Exportar' | ||
40 | search_form: | ||
41 | input_label: 'Digite aqui sua pesquisa' | ||
42 | |||
43 | footer: | ||
44 | wallabag: | ||
45 | elsewhere: 'Leve o wallabag com você' | ||
46 | social: 'Social' | ||
47 | powered_by: 'provido por' | ||
48 | about: 'Sobre' | ||
49 | stats: 'Desde %user_creation% você leu %nb_archives% artigos. Isso é %per_day% por dia!' | ||
50 | |||
51 | config: | ||
52 | page_title: 'Config' | ||
53 | tab_menu: | ||
54 | settings: 'Configurações' | ||
55 | rss: 'RSS' | ||
56 | user_info: 'Informação do Usuário' | ||
57 | password: 'Senha' | ||
58 | rules: 'Regras de tags' | ||
59 | new_user: 'Adicionar um usuário' | ||
60 | form: | ||
61 | save: 'Salvar' | ||
62 | form_settings: | ||
63 | theme_label: 'Tema' | ||
64 | items_per_page_label: 'Itens por página' | ||
65 | language_label: 'Idioma' | ||
66 | reading_speed: | ||
67 | label: 'Velocidade de leitura' | ||
68 | help_message: 'Você pode usar ferramentas online para estimar sua velocidade de leitura:' | ||
69 | 100_word: 'Posso ler ~100 palavras por minuto' | ||
70 | 200_word: 'Posso ler ~200 palavras por minuto' | ||
71 | 300_word: 'Posso ler ~300 palavras por minuto' | ||
72 | 400_word: 'Posso ler ~400 palavras por minuto' | ||
73 | pocket_consumer_key_label: 'Chave do consumidor do Pocket para importar conteúdo' | ||
74 | form_rss: | ||
75 | description: 'Feeds RSS providos pelo wallabag permitem que você leia seus artigos salvos em seu leitor de RSS favorito. Você precisa gerar um token primeiro.' | ||
76 | token_label: 'Token RSS' | ||
77 | no_token: 'Nenhum Token' | ||
78 | token_create: 'Criar seu token' | ||
79 | token_reset: 'Gerar novamente seu token' | ||
80 | rss_links: 'Links RSS' | ||
81 | rss_link: | ||
82 | unread: 'não lido' | ||
83 | starred: 'destacado' | ||
84 | archive: 'arquivado' | ||
85 | rss_limit: 'Número de itens no feed' | ||
86 | form_user: | ||
87 | two_factor_description: 'Habilitar autenticação de dois passos significa que você receberá um e-mail com um código a cada nova conexão desconhecida.' | ||
88 | name_label: 'Nome' | ||
89 | email_label: 'E-mail' | ||
90 | twoFactorAuthentication_label: 'Autenticação de dois passos' | ||
91 | form_password: | ||
92 | old_password_label: 'Senha atual' | ||
93 | new_password_label: 'Nova senha' | ||
94 | repeat_new_password_label: 'Repita a nova senha' | ||
95 | form_rules: | ||
96 | if_label: 'if' | ||
97 | then_tag_as_label: 'então coloque a tag' | ||
98 | delete_rule_label: 'apagar' | ||
99 | edit_rule_label: 'editar' | ||
100 | rule_label: 'Regras' | ||
101 | tags_label: 'Tags' | ||
102 | faq: | ||
103 | title: 'FAQ' | ||
104 | tagging_rules_definition_title: 'O que as « regras de tags » significam?' | ||
105 | tagging_rules_definition_description: 'São regras usadas pelo Wallabag para automaticamente adicionar tags em novos artigos.<br />Cada vez que um novo artigo é adicionado, todas as regras de tags podem ser usadas para adicionar as tags que você configurou, ajudando-o com o problema de classificar manualmente seus artigos.' | ||
106 | how_to_use_them_title: 'Como eu as utilizo?' | ||
107 | how_to_use_them_description: 'Vamos dizer que você deseja adicionar a tag « <i>leitura rápida</i> » quando o tempo de leitura for menor que 3 minutos.<br />Neste caso, você deve « readingTime <= 3 » no campo <i>Regra</i> e « <i>leitura rápida</i> » no campo <i>Tags</i>.<br />Diversas tags podem ser adicionadas simultâneamente separando-as com vírgula: « <i>leitura rápida, precisa ser lido</i> »<br />Regras complexas podem ser escritas usando os seguintes operadores pré-definidos: if « <i>readingTime >= 5 AND domainName = "github.com"</i> » então adicione a tag « <i>leitura longa, github </i> »' | ||
108 | variables_available_title: 'Quais variáveis e operadores eu posso usar para escrever regras?' | ||
109 | variables_available_description: 'As seguintes variáveis e operadores podem ser usados para criar regras de tags:' | ||
110 | meaning: 'Meaning' | ||
111 | variable_description: | ||
112 | label: 'Variável' | ||
113 | title: 'Título da entrada' | ||
114 | url: 'URL da entrada' | ||
115 | isArchived: 'Se a entrada está arquivada ou não' | ||
116 | isDestacado: 'Se a entrada está destacada ou não' | ||
117 | content: 'O conteúdo da entrada' | ||
118 | language: 'O idioma da entrada' | ||
119 | mimetype: 'O mime-type da entrada' | ||
120 | readingTime: 'O tempo estimado de leitura da entrada, em minutos' | ||
121 | domainName: 'O domínio da entrada' | ||
122 | operator_description: | ||
123 | label: 'Operador' | ||
124 | less_than: 'Menor que...' | ||
125 | strictly_less_than: 'Estritamente menor que...' | ||
126 | greater_than: 'Maior que...' | ||
127 | strictly_greater_than: 'Estritamente maior que...' | ||
128 | equal_to: 'Igual a...' | ||
129 | not_equal_to: 'Diferente de...' | ||
130 | or: 'Uma regra OU outra' | ||
131 | and: 'Uma regra E outra' | ||
132 | matches: 'Testa que um <i>assunto</i> corresponde a uma <i>pesquisa</i> (maiúscula ou minúscula).<br />Exemplo: <code>título corresponde a "futebol"</code>' | ||
133 | |||
134 | entry: | ||
135 | page_titles: | ||
136 | unread: 'Entradas não lidas' | ||
137 | starred: 'Entradas destacadas' | ||
138 | archived: 'Entradas arquivadas' | ||
139 | filtered: 'Entradas filtradas' | ||
140 | filtered_tags: 'Filtrar por tags:' | ||
141 | untagged: 'Entradas sem tags' | ||
142 | list: | ||
143 | number_on_the_page: '{0} Não existem entradas.|{1} Existe uma entrada.|]1,Inf[ Existem %count% entradas.' | ||
144 | reading_time: 'tempo estimado de leitura' | ||
145 | reading_time_minutes: 'tempo estimado de leitura: %readingTime% min' | ||
146 | reading_time_less_one_minute: 'tempo estimado de leitura: <small class="inferieur"><</small> 1 min' | ||
147 | number_of_tags: '{1}e uma outra tag|]1,Inf[e %count% outras tags' | ||
148 | reading_time_minutes_short: '%readingTime% min' | ||
149 | reading_time_less_one_minute_short: '<small class="inferieur"><</small> 1 min' | ||
150 | original_article: 'original' | ||
151 | toogle_as_read: 'Marcar como lido' | ||
152 | toogle_as_star: 'Marcar como destacado' | ||
153 | delete: 'Apagar' | ||
154 | export_title: 'Exportar' | ||
155 | filters: | ||
156 | title: 'Filtros' | ||
157 | status_label: 'Status' | ||
158 | archived_label: 'Arquivado' | ||
159 | starred_label: 'Destacado' | ||
160 | unread_label: 'Não Lido' | ||
161 | preview_picture_label: 'Possui uma imagem de preview' | ||
162 | preview_picture_help: 'Imagem de preview' | ||
163 | language_label: 'Idioma' | ||
164 | reading_time: | ||
165 | label: 'Tempo de leitura em minutos' | ||
166 | from: 'de' | ||
167 | to: 'para' | ||
168 | domain_label: 'Nome do domínio' | ||
169 | created_at: | ||
170 | label: 'Data de criação' | ||
171 | from: 'de' | ||
172 | to: 'para' | ||
173 | action: | ||
174 | clear: 'Limpar' | ||
175 | filter: 'Filtro' | ||
176 | view: | ||
177 | left_menu: | ||
178 | back_to_top: 'Voltar ao topo' | ||
179 | back_to_homepage: 'Voltar' | ||
180 | set_as_read: 'Marcar como lido' | ||
181 | set_as_unread: 'Marcar como não lido' | ||
182 | set_as_starred: 'Alternar destaque' | ||
183 | view_original_article: 'Artigo original' | ||
184 | re_fetch_content: 'Recapturar o conteúdo' | ||
185 | delete: 'Apagar' | ||
186 | add_a_tag: 'Adicionar uma tag' | ||
187 | share_content: 'Compartilhar' | ||
188 | share_email_label: 'E-mail' | ||
189 | public_link: 'link público' | ||
190 | delete_public_link: 'apagar link público' | ||
191 | download: 'Download' | ||
192 | print: 'Imprimir' | ||
193 | problem: | ||
194 | label: 'Problemas?' | ||
195 | description: 'este artigo aparece errado?' | ||
196 | edit_title: 'Editar título' | ||
197 | original_article: 'original' | ||
198 | annotations_on_the_entry: '{0} Sem anotações|{1} Uma anotação|]1,Inf[ %nbAnnotations% anotações' | ||
199 | created_at: 'Data de criação' | ||
200 | new: | ||
201 | page_title: 'Salvar nova entrada' | ||
202 | placeholder: 'http://website.com' | ||
203 | form_new: | ||
204 | url_label: Url | ||
205 | edit: | ||
206 | page_title: 'Editar uma entrada' | ||
207 | title_label: 'Título' | ||
208 | url_label: 'Url' | ||
209 | is_public_label: 'Público' | ||
210 | save_label: 'Salvar' | ||
211 | public: | ||
212 | shared_by_wallabag: "Este artigo foi compartilhado pelo <a href='%wallabag_instance%'>wallabag</a>" | ||
213 | |||
214 | about: | ||
215 | page_title: 'Sobre' | ||
216 | top_menu: | ||
217 | who_behind_wallabag: 'Quem está por trás do wallabag' | ||
218 | getting_help: 'Obtendo ajuda' | ||
219 | helping: 'Ajudando o wallabag' | ||
220 | contributors: 'Contribuidores' | ||
221 | third_party: 'Bibliotecas terceiras' | ||
222 | who_behind_wallabag: | ||
223 | developped_by: 'Desenvolvido por' | ||
224 | website: 'website' | ||
225 | many_contributors: 'E muitos outros contribuidores ♥ <a href="https://github.com/wallabag/wallabag/graphs/contributors">no Github</a>' | ||
226 | project_website: 'Website do projeto' | ||
227 | license: 'Licença' | ||
228 | version: 'Versão' | ||
229 | getting_help: | ||
230 | documentation: 'Documentação' | ||
231 | bug_reports: 'Informar bugs' | ||
232 | support: '<a href="https://support.wallabag.org">Em nosso site de suporte</a> ou <a href="https://github.com/wallabag/wallabag/issues">no GitHub</a>' | ||
233 | helping: | ||
234 | description: 'wallabag é livre e software livre. Você pode nos ajudar:' | ||
235 | by_contributing: 'contribuindo com o projeto:' | ||
236 | by_contributing_2: 'uma lista de todas as nossas necessidades' | ||
237 | by_paypal: 'via Paypal' | ||
238 | contributors: | ||
239 | description: 'Obrigado por contribuir com a aplicação web wallabag' | ||
240 | third_party: | ||
241 | description: 'Aqui está a lista de bibliotecas terceiras usadas no wallabag (com suas licenças):' | ||
242 | package: 'Pacote' | ||
243 | license: 'Licença' | ||
244 | |||
245 | howto: | ||
246 | page_title: 'How to' | ||
247 | page_description: 'Existem diferentes formas de salvar um artigo:' | ||
248 | top_menu: | ||
249 | browser_addons: 'Extensões de navegadores' | ||
250 | mobile_apps: "App's móveis" | ||
251 | bookmarklet: 'Bookmarklet' | ||
252 | form: | ||
253 | description: 'Obrigado por este formulário' | ||
254 | browser_addons: | ||
255 | firefox: 'Extensão padrão do Firefox' | ||
256 | chrome: 'Extensão do Chrome' | ||
257 | mobile_apps: | ||
258 | android: | ||
259 | via_f_droid: 'via F-Droid' | ||
260 | via_google_play: 'via Google Play' | ||
261 | ios: 'na iTunes Store' | ||
262 | windows: 'na Microsoft Store' | ||
263 | bookmarklet: | ||
264 | description: 'Arraste e solve este link na sua barra de favoritos:' | ||
265 | |||
266 | quickstart: | ||
267 | page_title: 'Começo Rápido' | ||
268 | more: 'Mais...' | ||
269 | intro: | ||
270 | title: 'Bem-vindo ao wallabag!' | ||
271 | paragraph_1: 'Nós podemos acompanhar você em sua visita ao wallabag e mostrar algumas funcionalidades que podem lhe interessar.' | ||
272 | paragraph_2: 'Siga-nos!' | ||
273 | configure: | ||
274 | title: 'Configurar a aplicação' | ||
275 | description: 'Para ter uma aplicação que atende você, dê uma olhada na configuração do wallabag.' | ||
276 | language: 'Alterar idioma e design' | ||
277 | rss: 'Habilitar feeds RSS' | ||
278 | tagging_rules: 'Escrever regras para acrescentar tags automaticamente em seus artigos' | ||
279 | admin: | ||
280 | title: 'Administração' | ||
281 | description: 'Como administrador você tem privilégios no wallabag. Você pode:' | ||
282 | new_user: 'Criar um novo usuário' | ||
283 | analytics: 'Configurar o analytics' | ||
284 | sharing: 'habilitar alguns parâmetros para compartilhamento de artigos' | ||
285 | export: 'Configurar exportação' | ||
286 | import: 'Configurar importação' | ||
287 | first_steps: | ||
288 | title: 'Primeiros passos' | ||
289 | description: "Agora o wallabag está bem configurado, é hora de arquivar a web. Você pode clicar no sinal de + no topo a direita para adicionar um link." | ||
290 | new_article: 'Salvar seu primeiro artigo' | ||
291 | unread_articles: 'E classificá-lo!' | ||
292 | migrate: | ||
293 | title: 'Migrar de um serviço existente' | ||
294 | description: 'Você está usando um outro serviço? Nós podemos ajudá-lo a recuperar seus dados para o wallabag.' | ||
295 | pocket: 'Migrar do Pocket' | ||
296 | wallabag_v1: 'Migrar do wallabag v1' | ||
297 | wallabag_v2: 'Migrar do wallabag v2' | ||
298 | readability: 'Migrate from Readability' | ||
299 | instapaper: 'Migrate from Instapaper' | ||
300 | developer: | ||
301 | title: 'Desenvolvedores' | ||
302 | description: 'Nós também agradecemos os desenvolvedores: Docker, API, traduções, etc.' | ||
303 | create_application: 'Criar sua aplicação terceira' | ||
304 | use_docker: 'Usar o Docker para instalar o wallabag' | ||
305 | docs: | ||
306 | title: 'Documentação completa' | ||
307 | description: "Existem muitas funcionalidades no wallabag. Não hesite em ler o manual para conhecê-las e aprender como usá-las." | ||
308 | annotate: 'Anotar seu artigo' | ||
309 | export: 'Converter seu artigo em ePUB ou PDF' | ||
310 | search_filters: 'veja coo você pode encontrar um artigo usanndo o motor de busca e filtros' | ||
311 | fetching_errors: 'O que eu posso fazer quando um artigo encontra erros na recuperação?' | ||
312 | all_docs: 'E outros muitos artigos!' | ||
313 | support: | ||
314 | title: 'Suporte' | ||
315 | description: 'Se você precisa de ajuda, nós estamos aqui.' | ||
316 | github: 'No GitHub' | ||
317 | email: 'Por e-mail' | ||
318 | gitter: 'No Gitter' | ||
319 | |||
320 | tag: | ||
321 | page_title: 'Tags' | ||
322 | list: | ||
323 | number_on_the_page: '{0} Não existem tags.|{1} Uma tag.|]1,Inf[ Existem %count% tags.' | ||
324 | see_untagged_entries: 'Ver entradas sem tags' | ||
325 | |||
326 | import: | ||
327 | page_title: 'Importar' | ||
328 | page_description: 'Bem-vindo ao importador do wallabag. Por favo selecione o serviço do qual deseja migrar.' | ||
329 | action: | ||
330 | import_contents: 'Importar conteúdos' | ||
331 | form: | ||
332 | mark_as_read_title: 'Marcar todos como lidos?' | ||
333 | mark_as_read_label: 'Marcar todas as entradas importadas como lidas' | ||
334 | file_label: 'Arquivo' | ||
335 | save_label: 'Carregar arquivo' | ||
336 | pocket: | ||
337 | page_title: 'Importar > Pocket' | ||
338 | description: 'Com este importador você importa todos os seus dados do Pocket. O Pocket não nos permite recuperar o conteúdo de seu serviço, então o conteúdo que pode ser lido é recarregado pelo wallabag.' | ||
339 | config_missing: | ||
340 | description: 'O importador do Pocket não está configurado.' | ||
341 | admin_message: 'Você precisa definir uma %keyurls%a pocket_consumer_key%keyurle%.' | ||
342 | user_message: 'Seu administrador do servidor precisa definir uma chave de API para o Pocket.' | ||
343 | authorize_message: 'Você pode importar seus dados de sua conta do Pocket. Você somente precisa clicar no botão abaixo e autorizar a aplicação a conectar-se ao getpocket.com.' | ||
344 | connect_to_pocket: 'Conecte ao Pocket e importe os dados' | ||
345 | wallabag_v1: | ||
346 | page_title: 'Importar > Wallabag v1' | ||
347 | description: 'Com este importador você importa todos os seus artigos do wallabag v1. Na sua página de configuração, clique em "JSON export" na opção "Export your wallabag data". Você irá criar um arquivo "wallabag-export-1-xxxx-xx-xx.json".' | ||
348 | how_to: 'Por favor, selecione seu exportador wallabag e clique no botão abaixo para carregar e importar.' | ||
349 | wallabag_v2: | ||
350 | page_title: 'Importar > Wallabag v2' | ||
351 | description: 'Com este importador você importa todos os seus artigos do wallabag v2. Vá em Todos os artigos e então, na barra lateral de exportação, clique em "JSON". Você irá criar um arquivo "All articles.json".' | ||
352 | readability: | ||
353 | page_title: 'Importar > Readability' | ||
354 | description: 'Este importador pode importar todos os artigos do Readability. Nas página ferramentas (https://www.readability.com/tools/), clique em "Export your data" na seção "Data Export". Você receberá um e-mail para fazer o download de um json (que de fato não termina com .json).' | ||
355 | how_to: 'Por favor, selecione sua exportação do Readability e clique no botão abaixo para importá-la.' | ||
356 | worker: | ||
357 | enabled: "A importação é feita assíncronamente. Uma vez que a tarefa de importação é iniciada, um trabalho externo pode executar tarefas uma por vez. O serviço atual é:" | ||
358 | firefox: | ||
359 | page_title: 'Importar > Firefox' | ||
360 | description: "Com este importador você importa todos os favoritos de seu Firefox. Somente vá até seus favoritos (Ctrl+Maj+O), e em \"Importar e Backup\" e escolha \"Backup...\". Você terá então um arquivo .json." | ||
361 | how_to: "Por favor, escolha o arquivo de backup dos favoritos e clique no botão abaixo para importá-lo. Note que o processo pode demorar até que todos os artigos tenham sido copiados." | ||
362 | chrome: | ||
363 | page_title: 'Importar > Chrome' | ||
364 | description: "Com este importador você importa todos os favoritos de seu Chrome. A localização do arquivo depende de seu sistema operacional: <ul><li>Em Linux, vá para o diretório <code>~/.config/chromium/Default/</code></li><li>Em Windows, ele deve estar em <code>%LOCALAPPDATA%\\Google\\Chrome\\User Data\\Default</code></li><li>Em OS X, ele deve estar em <code>~/Library/Application Support/Google/Chrome/Default/Bookmarks</code></li></ul>Uma vez que você pegou o arquivo, copie-o para algum lugar que você o encontre.<em><br>Note que se você possui o Chromium ao invés do Chrome, você precisa corrigir os caminhos.</em></p>" | ||
365 | how_to: "Por favor, escolha o arquivo de backup dos favoritos e clique no botão abaixo para importá-lo. Note que o processo pode demorar até que todos os artigos tenham sido copiados." | ||
366 | instapaper: | ||
367 | page_title: 'Importar > Instapaper' | ||
368 | description: 'Este importador pode importar todos os artigos do seu Instapaper. Nas página de configurações (https://www.instapaper.com/user), clique em "Download .CSV file" na seção "Export". Um arquivo CSV será baixado (algo como "instapaper-export.csv").' | ||
369 | how_to: 'Por favor, selecione sua exportação do seu Instapaper e clique no botão abaixo para importá-la.' | ||
370 | |||
371 | developer: | ||
372 | page_title: 'Desenvolvedor' | ||
373 | welcome_message: 'Bem-vindo a API do wallabag' | ||
374 | documentation: 'Documentação' | ||
375 | how_to_first_app: 'Como criar minha primeira aplicação' | ||
376 | full_documentation: 'Ver a documentação completa da API' | ||
377 | list_methods: 'Lista de métodos da API' | ||
378 | clients: | ||
379 | title: 'Clientes' | ||
380 | create_new: 'Criar um novo cliente' | ||
381 | existing_clients: | ||
382 | title: 'Clientes existentes' | ||
383 | field_id: 'ID do cliente' | ||
384 | field_secret: 'Chave do cliente' | ||
385 | field_uris: 'URIs de redirecionamento' | ||
386 | field_grant_types: 'Tipo permitido' | ||
387 | no_client: 'Nenhum cliente até agora.' | ||
388 | remove: | ||
389 | warn_message_1: 'Você tem permissão pare remover este cliente. Esta ação é IRREVERSÍVEL !' | ||
390 | warn_message_2: 'Se você remover isso, todo o aplicativo configurado com este cliente não poderá se autenticar no seu wallabag.' | ||
391 | action: 'Remover este cliente' | ||
392 | client: | ||
393 | page_title: 'Desenvolvedor > Novo cliente' | ||
394 | page_description: 'Você está prestes a criar um novo cliente. Por favor preencha o campo abaixo para a URI de redirecionamento de sua aplicação.' | ||
395 | form: | ||
396 | name_label: 'Nome do cliente' | ||
397 | redirect_uris_label: 'URIs de redirecionamento' | ||
398 | save_label: 'Criar um novo cliente' | ||
399 | action_back: 'Voltar' | ||
400 | client_parameter: | ||
401 | page_title: 'Desenvolvedor > Parâmetros de clientes' | ||
402 | page_description: 'Aqui estão os parâmetros de seus clientes.' | ||
403 | field_name: 'Nome do cliente' | ||
404 | field_id: 'ID do cliente' | ||
405 | field_secret: 'Chave do cliente' | ||
406 | back: 'Voltar' | ||
407 | read_howto: 'Leia o how-to "Criar minha primeira aplicação"' | ||
408 | howto: | ||
409 | page_title: 'Desenvolvedor > Criar minha primeira aplicação' | ||
410 | description: | ||
411 | paragraph_1: 'Os seguintes comandos fazem uso da <a href="https://github.com/jkbrzt/httpie">biblioteca HTTPie</a>. Tenha certeza que ela está instalada em seu servidor antes de usá-la.' | ||
412 | paragraph_2: 'Você precisa de um token para a comunicação entre sua aplicação terceira e a API do wallabag.' | ||
413 | paragraph_3: 'Para criar este token, você precisa <a href="%link%">criar um novo cliente</a>.' | ||
414 | paragraph_4: 'Agora, crie seu token (altere client_id, client_secret, username e password com os valores corretos):' | ||
415 | paragraph_5: 'A API pode retornar uma resposta como essa:' | ||
416 | paragraph_6: 'O access_token é utilizável para fazer uma chamada para o endpoint da API. Por exemplo:' | ||
417 | paragraph_7: 'Esta chamada pode retornar todas as entradas de seu usuário.' | ||
418 | paragraph_8: 'Se você deseja ver todos os endpoints da API, dê uma olhada <a href="%link%">em nossa documentação da API</a>.' | ||
419 | back: 'Voltar' | ||
420 | |||
421 | user: | ||
422 | page_title: 'Gerenciamento de Usuários' | ||
423 | new_user: 'Criar um novo usuário' | ||
424 | edit_user: 'Editar um usuário existente' | ||
425 | description: 'Aqui você gerencia todos os usuários (cria, edita e apaga)' | ||
426 | list: | ||
427 | actions: 'Ações' | ||
428 | edit_action: 'Editar' | ||
429 | yes: 'Sim' | ||
430 | no: 'Não' | ||
431 | create_new_one: 'Criar um novo usuário' | ||
432 | form: | ||
433 | username_label: 'Nome de Usuário' | ||
434 | name_label: 'Nome' | ||
435 | password_label: 'Senha' | ||
436 | repeat_new_password_label: 'Repita a nova senha' | ||
437 | plain_password_label: '????' | ||
438 | email_label: 'E-mail' | ||
439 | enabled_label: 'Habilitado' | ||
440 | locked_label: 'Travado' | ||
441 | last_login_label: 'Último login' | ||
442 | twofactor_label: 'Autenticação de dois passos' | ||
443 | save: 'Salvar' | ||
444 | delete: 'Apagar' | ||
445 | delete_confirm: 'Tem certeza?' | ||
446 | back_to_list: 'Voltar para a lista' | ||
447 | |||
448 | flashes: | ||
449 | config: | ||
450 | notice: | ||
451 | config_saved: 'Configiração salva. Alguns parâmetros podem ser considerados depois da desconexão.' | ||
452 | password_updated: 'Senha atualizada' | ||
453 | password_not_updated_demo: 'Em modo de demonstração, você não pode alterar a senha deste usuário.' | ||
454 | rss_updated: 'Informação de RSS atualizada' | ||
455 | tagging_rules_updated: 'Regras de tags atualizadas' | ||
456 | tagging_rules_deleted: 'Regra de tag apagada' | ||
457 | rss_token_updated: 'Token RSS atualizado' | ||
458 | entry: | ||
459 | notice: | ||
460 | entry_already_saved: 'Entrada já foi salva em %date%' | ||
461 | entry_saved: 'Entrada salva' | ||
462 | entry_saved_failed: 'Failed to save entry' | ||
463 | entry_updated: 'Entrada atualizada' | ||
464 | entry_reloaded: 'Entrada recarregada' | ||
465 | entry_reloaded_failed: 'Falha em recarregar a entrada' | ||
466 | entry_archived: 'Entrada arquivada' | ||
467 | entry_unarchived: 'Entrada desarquivada' | ||
468 | entry_starred: 'Entrada destacada' | ||
469 | entry_unstarred: 'Entrada não destacada' | ||
470 | entry_deleted: 'Entrada apagada' | ||
471 | tag: | ||
472 | notice: | ||
473 | tag_added: 'Tag adicionada' | ||
474 | import: | ||
475 | notice: | ||
476 | failed: 'Importação falhou, por favor tente novamente.' | ||
477 | failed_on_file: 'Erro ao processar a importação. Por favor verifique seu arquivo de importação.' | ||
478 | summary: 'relatório de importação: %imported% importados, %skipped% já existem.' | ||
479 | summary_with_queue: 'Importar sumáario: %queued% agendados.' | ||
480 | error: | ||
481 | redis_enabled_not_installed: 'O Redis está habilitado para importação assíncrona mas parece que <u>não podemos nos conectar nele</u>. Por favor verifique as configurações do Redis.' | ||
482 | rabbit_enabled_not_installed: 'O RabbitMQ está habilitado para importação assíncrona mas parece que <u>não podemos nos conectar nele</u>. Por favor verifique as configurações do RabbitMQ.' | ||
483 | developer: | ||
484 | notice: | ||
485 | client_created: 'Novo cliente criado.' | ||
486 | client_deleted: 'Cliente removido' | ||
487 | user: | ||
488 | notice: | ||
489 | added: 'Usuário "%username%" adicionado' | ||
490 | updated: 'Usuário "%username%" atualizado' | ||
491 | deleted: 'Usuário "%username%" removido' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml index 070abe27..50f1b6a2 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml | |||
@@ -150,7 +150,7 @@ entry: | |||
150 | # starred: 'Starred entries' | 150 | # starred: 'Starred entries' |
151 | # archived: 'Archived entries' | 151 | # archived: 'Archived entries' |
152 | # filtered: 'Filtered entries' | 152 | # filtered: 'Filtered entries' |
153 | # filtered_tags: 'Filtered by tags' | 153 | # filtered_tags: 'Filtered by tags:' |
154 | # untagged: 'Untagged entries' | 154 | # untagged: 'Untagged entries' |
155 | list: | 155 | list: |
156 | # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.' | 156 | # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.' |
@@ -472,7 +472,6 @@ flashes: | |||
472 | rss_updated: 'Informație RSS actualizată' | 472 | rss_updated: 'Informație RSS actualizată' |
473 | # tagging_rules_updated: 'Tagging rules updated' | 473 | # tagging_rules_updated: 'Tagging rules updated' |
474 | # tagging_rules_deleted: 'Tagging rule deleted' | 474 | # tagging_rules_deleted: 'Tagging rule deleted' |
475 | # user_added: 'User "%username%" added' | ||
476 | # rss_token_updated: 'RSS token updated' | 475 | # rss_token_updated: 'RSS token updated' |
477 | # annotations_reset: Annotations reset | 476 | # annotations_reset: Annotations reset |
478 | # tags_reset: Tags reset | 477 | # tags_reset: Tags reset |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml index 7679b32a..07939ebc 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml | |||
@@ -150,7 +150,7 @@ entry: | |||
150 | # starred: 'Starred entries' | 150 | # starred: 'Starred entries' |
151 | # archived: 'Archived entries' | 151 | # archived: 'Archived entries' |
152 | # filtered: 'Filtered entries' | 152 | # filtered: 'Filtered entries' |
153 | # filtered_tags: 'Filtered by tags' | 153 | # filtered_tags: 'Filtered by tags:' |
154 | # untagged: 'Untagged entries' | 154 | # untagged: 'Untagged entries' |
155 | list: | 155 | list: |
156 | number_on_the_page: '{0} Herhangi bir makale yok.|{1} Burada bir adet makale var.|]1,Inf[ Burada %count% adet makale var.' | 156 | number_on_the_page: '{0} Herhangi bir makale yok.|{1} Burada bir adet makale var.|]1,Inf[ Burada %count% adet makale var.' |
@@ -472,7 +472,6 @@ flashes: | |||
472 | rss_updated: 'RSS bilgiler güncellendi' | 472 | rss_updated: 'RSS bilgiler güncellendi' |
473 | tagging_rules_updated: 'Tagging rules updated' | 473 | tagging_rules_updated: 'Tagging rules updated' |
474 | tagging_rules_deleted: 'Tagging rule deleted' | 474 | tagging_rules_deleted: 'Tagging rule deleted' |
475 | user_added: 'User "%username%" added' | ||
476 | rss_token_updated: 'RSS token updated' | 475 | rss_token_updated: 'RSS token updated' |
477 | # annotations_reset: Annotations reset | 476 | # annotations_reset: Annotations reset |
478 | # tags_reset: Tags reset | 477 | # tags_reset: Tags reset |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.fr.yml index 7ecb4acf..2d5eca29 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.fr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.fr.yml | |||
@@ -1,6 +1,6 @@ | |||
1 | validator: | 1 | validator: |
2 | password_must_match: 'Les deux mots de passe doivent être les mêmes' | 2 | password_must_match: "Les deux mots de passe doivent être les mêmes" |
3 | password_too_short: 'Le mot de passe doit contenir au moins 8 caractères' | 3 | password_too_short: "Le mot de passe doit contenir au moins 8 caractères" |
4 | password_wrong_value: 'Votre mot de passe actuel est faux' | 4 | password_wrong_value: "Votre mot de passe actuel est faux" |
5 | item_per_page_too_high: "Ca ne va pas plaire à l'application" | 5 | item_per_page_too_high: "Ça ne va pas plaire à l’application" |
6 | rss_limit_too_hight: "Ca ne va pas plaire à l'application" | 6 | rss_limit_too_hight: "Ça ne va pas plaire à l’application" |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.pt.yml new file mode 100644 index 00000000..49890830 --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.pt.yml | |||
@@ -0,0 +1,6 @@ | |||
1 | validator: | ||
2 | password_must_match: 'Os campos de senha devem coincidir.' | ||
3 | password_too_short: 'A senha deve ter pelo menos 8 caracteres' | ||
4 | password_wrong_value: 'A senha atual informada está errada' | ||
5 | item_per_page_too_high: 'Certamente isso pode matar a aplicação' | ||
6 | rss_limit_too_hight: 'Certamente isso pode matar a aplicação' | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig index 3af88b23..5d657c7e 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig | |||
@@ -1,7 +1,11 @@ | |||
1 | {% extends "WallabagCoreBundle::layout.html.twig" %} | 1 | {% extends "WallabagCoreBundle::layout.html.twig" %} |
2 | 2 | ||
3 | {% block title %} | 3 | {% block title %} |
4 | {% include "@WallabagCore/themes/common/Entry/_title.html.twig" %} | 4 | {% set currentTag = '' %} |
5 | {% if tag is defined %} | ||
6 | {% set currentTag = tag %} | ||
7 | {% endif %} | ||
8 | {% include "@WallabagCore/themes/common/Entry/_title.html.twig" with {'currentTag': currentTag} %} | ||
5 | {% endblock %} | 9 | {% endblock %} |
6 | 10 | ||
7 | {% block content %} | 11 | {% block content %} |
@@ -49,19 +53,23 @@ | |||
49 | <!-- Export --> | 53 | <!-- Export --> |
50 | <aside id="download-form"> | 54 | <aside id="download-form"> |
51 | {% set currentRoute = app.request.attributes.get('_route') %} | 55 | {% set currentRoute = app.request.attributes.get('_route') %} |
56 | {% set currentTag = '' %} | ||
57 | {% if tag is defined %} | ||
58 | {% set currentTag = tag %} | ||
59 | {% endif %} | ||
52 | {% if currentRoute == 'homepage' %} | 60 | {% if currentRoute == 'homepage' %} |
53 | {% set currentRoute = 'unread' %} | 61 | {% set currentRoute = 'unread' %} |
54 | {% endif %} | 62 | {% endif %} |
55 | <h2>{{ 'entry.list.export_title'|trans }}</h2> | 63 | <h2>{{ 'entry.list.export_title'|trans }}</h2> |
56 | <a href="javascript: void(null);" id="download-form-close" class="close-button--popup close-button">×</a> | 64 | <a href="javascript: void(null);" id="download-form-close" class="close-button--popup close-button">×</a> |
57 | <ul> | 65 | <ul> |
58 | {% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'epub' }) }}">EPUB</a></li>{% endif %} | 66 | {% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'epub', 'tag' : currentTag }) }}">EPUB</a></li>{% endif %} |
59 | {% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'mobi' }) }}">MOBI</a></li>{% endif %} | 67 | {% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'mobi', 'tag' : currentTag }) }}">MOBI</a></li>{% endif %} |
60 | {% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'pdf' }) }}">PDF</a></li>{% endif %} | 68 | {% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'pdf', 'tag' : currentTag }) }}">PDF</a></li>{% endif %} |
61 | {% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json' }) }}">JSON</a></li>{% endif %} | 69 | {% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json', 'tag' : currentTag }) }}">JSON</a></li>{% endif %} |
62 | {% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv' }) }}">CSV</a></li>{% endif %} | 70 | {% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv', 'tag' : currentTag }) }}">CSV</a></li>{% endif %} |
63 | {% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'txt' }) }}">TXT</a></li>{% endif %} | 71 | {% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'txt', 'tag' : currentTag }) }}">TXT</a></li>{% endif %} |
64 | {% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml' }) }}">XML</a></li>{% endif %} | 72 | {% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml', 'tag' : currentTag }) }}">XML</a></li>{% endif %} |
65 | </ul> | 73 | </ul> |
66 | </aside> | 74 | </aside> |
67 | 75 | ||
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig index d1c2f203..92cabdd9 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_title.html.twig | |||
@@ -7,7 +7,7 @@ | |||
7 | {% elseif currentRoute == 'all' %} | 7 | {% elseif currentRoute == 'all' %} |
8 | {{ 'entry.page_titles.filtered'|trans }} | 8 | {{ 'entry.page_titles.filtered'|trans }} |
9 | {% elseif currentRoute == 'tag_entries' %} | 9 | {% elseif currentRoute == 'tag_entries' %} |
10 | {{ 'entry.page_titles.filtered_tags'|trans }} | 10 | {{ 'entry.page_titles.filtered_tags'|trans }} {{ currentTag }} |
11 | {% elseif currentRoute == 'untagged' %} | 11 | {% elseif currentRoute == 'untagged' %} |
12 | {{ 'entry.page_titles.untagged'|trans }} | 12 | {{ 'entry.page_titles.untagged'|trans }} |
13 | {% else %} | 13 | {% else %} |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig index 919f94ec..1225e680 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig | |||
@@ -1,7 +1,11 @@ | |||
1 | {% extends "WallabagCoreBundle::layout.html.twig" %} | 1 | {% extends "WallabagCoreBundle::layout.html.twig" %} |
2 | 2 | ||
3 | {% block title %} | 3 | {% block title %} |
4 | {% include "@WallabagCore/themes/common/Entry/_title.html.twig" %} | 4 | {% set currentTag = '' %} |
5 | {% if tag is defined %} | ||
6 | {% set currentTag = tag %} | ||
7 | {% endif %} | ||
8 | {% include "@WallabagCore/themes/common/Entry/_title.html.twig" with {'currentTag': currentTag} %} | ||
5 | {% endblock %} | 9 | {% endblock %} |
6 | 10 | ||
7 | {% block content %} | 11 | {% block content %} |
@@ -95,18 +99,22 @@ | |||
95 | <!-- Export --> | 99 | <!-- Export --> |
96 | <div id="export" class="side-nav fixed right-aligned"> | 100 | <div id="export" class="side-nav fixed right-aligned"> |
97 | {% set currentRoute = app.request.attributes.get('_route') %} | 101 | {% set currentRoute = app.request.attributes.get('_route') %} |
102 | {% set currentTag = '' %} | ||
103 | {% if tag is defined %} | ||
104 | {% set currentTag = tag %} | ||
105 | {% endif %} | ||
98 | {% if currentRoute == 'homepage' %} | 106 | {% if currentRoute == 'homepage' %} |
99 | {% set currentRoute = 'unread' %} | 107 | {% set currentRoute = 'unread' %} |
100 | {% endif %} | 108 | {% endif %} |
101 | <h4 class="center">{{ 'entry.list.export_title'|trans }}</h4> | 109 | <h4 class="center">{{ 'entry.list.export_title'|trans }}</h4> |
102 | <ul> | 110 | <ul> |
103 | {% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'epub' }) }}">EPUB</a></li>{% endif %} | 111 | {% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'epub', 'tag' : currentTag }) }}">EPUB</a></li>{% endif %} |
104 | {% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'mobi' }) }}">MOBI</a></li>{% endif %} | 112 | {% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'mobi', 'tag' : currentTag }) }}">MOBI</a></li>{% endif %} |
105 | {% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'pdf' }) }}">PDF</a></li>{% endif %} | 113 | {% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'pdf', 'tag' : currentTag }) }}">PDF</a></li>{% endif %} |
106 | {% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json' }) }}">JSON</a></li>{% endif %} | 114 | {% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json', 'tag' : currentTag }) }}">JSON</a></li>{% endif %} |
107 | {% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv' }) }}">CSV</a></li>{% endif %} | 115 | {% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv', 'tag' : currentTag }) }}">CSV</a></li>{% endif %} |
108 | {% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'txt' }) }}">TXT</a></li>{% endif %} | 116 | {% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'txt', 'tag' : currentTag }) }}">TXT</a></li>{% endif %} |
109 | {% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml' }) }}">XML</a></li>{% endif %} | 117 | {% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml', 'tag' : currentTag }) }}">XML</a></li>{% endif %} |
110 | </ul> | 118 | </ul> |
111 | </div> | 119 | </div> |
112 | 120 | ||
diff --git a/src/Wallabag/ImportBundle/Command/ImportCommand.php b/src/Wallabag/ImportBundle/Command/ImportCommand.php index 2f7a906e..13f3dcb9 100644 --- a/src/Wallabag/ImportBundle/Command/ImportCommand.php +++ b/src/Wallabag/ImportBundle/Command/ImportCommand.php | |||
@@ -56,6 +56,9 @@ class ImportCommand extends ContainerAwareCommand | |||
56 | case 'instapaper': | 56 | case 'instapaper': |
57 | $import = $this->getContainer()->get('wallabag_import.instapaper.import'); | 57 | $import = $this->getContainer()->get('wallabag_import.instapaper.import'); |
58 | break; | 58 | break; |
59 | case 'instapaper': | ||
60 | $wallabag = $this->getContainer()->get('wallabag_import.instapaper.import'); | ||
61 | break; | ||
59 | case 'v1': | 62 | case 'v1': |
60 | default: | 63 | default: |
61 | $import = $this->getContainer()->get('wallabag_import.wallabag_v1.import'); | 64 | $import = $this->getContainer()->get('wallabag_import.wallabag_v1.import'); |
diff --git a/src/Wallabag/UserBundle/Mailer/AuthCodeMailer.php b/src/Wallabag/UserBundle/Mailer/AuthCodeMailer.php index ca9d18f1..961208f2 100644 --- a/src/Wallabag/UserBundle/Mailer/AuthCodeMailer.php +++ b/src/Wallabag/UserBundle/Mailer/AuthCodeMailer.php | |||
@@ -4,7 +4,6 @@ namespace Wallabag\UserBundle\Mailer; | |||
4 | 4 | ||
5 | use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface; | 5 | use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface; |
6 | use Scheb\TwoFactorBundle\Mailer\AuthCodeMailerInterface; | 6 | use Scheb\TwoFactorBundle\Mailer\AuthCodeMailerInterface; |
7 | use Craue\ConfigBundle\Util\Config; | ||
8 | 7 | ||
9 | /** | 8 | /** |
10 | * Custom mailer for TwoFactorBundle email. | 9 | * Custom mailer for TwoFactorBundle email. |
@@ -61,16 +60,17 @@ class AuthCodeMailer implements AuthCodeMailerInterface | |||
61 | * @param \Twig_Environment $twig | 60 | * @param \Twig_Environment $twig |
62 | * @param string $senderEmail | 61 | * @param string $senderEmail |
63 | * @param string $senderName | 62 | * @param string $senderName |
64 | * @param Config $craueConfig Craue\Config instance to get wallabag support url from database | 63 | * @param string $supportUrl wallabag support url |
64 | * @param string $wallabagUrl wallabag instance url | ||
65 | */ | 65 | */ |
66 | public function __construct(\Swift_Mailer $mailer, \Twig_Environment $twig, $senderEmail, $senderName, Config $craueConfig) | 66 | public function __construct(\Swift_Mailer $mailer, \Twig_Environment $twig, $senderEmail, $senderName, $supportUrl, $wallabagUrl) |
67 | { | 67 | { |
68 | $this->mailer = $mailer; | 68 | $this->mailer = $mailer; |
69 | $this->twig = $twig; | 69 | $this->twig = $twig; |
70 | $this->senderEmail = $senderEmail; | 70 | $this->senderEmail = $senderEmail; |
71 | $this->senderName = $senderName; | 71 | $this->senderName = $senderName; |
72 | $this->supportUrl = $craueConfig->get('wallabag_support_url'); | 72 | $this->supportUrl = $supportUrl; |
73 | $this->wallabagUrl = $craueConfig->get('wallabag_url'); | 73 | $this->wallabagUrl = $wallabagUrl; |
74 | } | 74 | } |
75 | 75 | ||
76 | /** | 76 | /** |
diff --git a/src/Wallabag/UserBundle/Resources/config/services.yml b/src/Wallabag/UserBundle/Resources/config/services.yml index 8062e53f..164a25ec 100644 --- a/src/Wallabag/UserBundle/Resources/config/services.yml +++ b/src/Wallabag/UserBundle/Resources/config/services.yml | |||
@@ -6,7 +6,8 @@ services: | |||
6 | - "@twig" | 6 | - "@twig" |
7 | - "%scheb_two_factor.email.sender_email%" | 7 | - "%scheb_two_factor.email.sender_email%" |
8 | - "%scheb_two_factor.email.sender_name%" | 8 | - "%scheb_two_factor.email.sender_name%" |
9 | - "@craue_config" | 9 | - '@=service(''craue_config'').get(''wallabag_support_url'')' |
10 | - '@=service(''craue_config'').get(''wallabag_url'')' | ||
10 | 11 | ||
11 | wallabag_user.password_resetting: | 12 | wallabag_user.password_resetting: |
12 | class: Wallabag\UserBundle\EventListener\PasswordResettingListener | 13 | class: Wallabag\UserBundle\EventListener\PasswordResettingListener |
diff --git a/src/Wallabag/UserBundle/Resources/translations/wallabag_user.fr.yml b/src/Wallabag/UserBundle/Resources/translations/wallabag_user.fr.yml index 30ab5dd9..fbc95a05 100644 --- a/src/Wallabag/UserBundle/Resources/translations/wallabag_user.fr.yml +++ b/src/Wallabag/UserBundle/Resources/translations/wallabag_user.fr.yml | |||
@@ -1,11 +1,11 @@ | |||
1 | # Two factor mail | 1 | # Two factor mail |
2 | auth_code: | 2 | auth_code: |
3 | on: 'sur' | 3 | on: "sur" |
4 | mailer: | 4 | mailer: |
5 | subject: "Code d'authentification wallabag" | 5 | subject: "Code d’authentification wallabag" |
6 | body: | 6 | body: |
7 | hello: "Bonjour %user%," | 7 | hello: "Bonjour %user%," |
8 | first_para: "Comme vous avez activé la double authentification sur votre compte wallabag et que vous venez de vous connecter depuis un nouvel appareil (ordinateur, téléphone, etc.), nous vous envoyons un code pour valider votre connexion." | 8 | first_para: "Comme vous avez activé la double authentification sur votre compte wallabag et que vous venez de vous connecter depuis un nouvel appareil (ordinateur, téléphone, etc.), nous vous envoyons un code pour valider votre connexion." |
9 | second_para: "Voici le code à renseigner :" | 9 | second_para: "Voici le code à renseigner :" |
10 | support: "Si vous avez un problème de connexion, n'hésitez pas à contacter le support :" | 10 | support: "Si vous avez un problème de connexion, n’hésitez pas à contacter le support :" |
11 | signature: "L'équipe wallabag" | 11 | signature: "L’équipe wallabag" |
diff --git a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php new file mode 100644 index 00000000..566e9493 --- /dev/null +++ b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php | |||
@@ -0,0 +1,681 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Tests\Wallabag\ApiBundle\Controller; | ||
4 | |||
5 | use Tests\Wallabag\ApiBundle\WallabagApiTestCase; | ||
6 | use Wallabag\CoreBundle\Entity\Tag; | ||
7 | |||
8 | class EntryRestControllerTest extends WallabagApiTestCase | ||
9 | { | ||
10 | public function testGetOneEntry() | ||
11 | { | ||
12 | $entry = $this->client->getContainer() | ||
13 | ->get('doctrine.orm.entity_manager') | ||
14 | ->getRepository('WallabagCoreBundle:Entry') | ||
15 | ->findOneBy(['user' => 1, 'isArchived' => false]); | ||
16 | |||
17 | if (!$entry) { | ||
18 | $this->markTestSkipped('No content found in db.'); | ||
19 | } | ||
20 | |||
21 | $this->client->request('GET', '/api/entries/'.$entry->getId().'.json'); | ||
22 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
23 | |||
24 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
25 | |||
26 | $this->assertEquals($entry->getTitle(), $content['title']); | ||
27 | $this->assertEquals($entry->getUrl(), $content['url']); | ||
28 | $this->assertCount(count($entry->getTags()), $content['tags']); | ||
29 | $this->assertEquals($entry->getUserName(), $content['user_name']); | ||
30 | $this->assertEquals($entry->getUserEmail(), $content['user_email']); | ||
31 | $this->assertEquals($entry->getUserId(), $content['user_id']); | ||
32 | |||
33 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
34 | } | ||
35 | |||
36 | public function testExportEntry() | ||
37 | { | ||
38 | $entry = $this->client->getContainer() | ||
39 | ->get('doctrine.orm.entity_manager') | ||
40 | ->getRepository('WallabagCoreBundle:Entry') | ||
41 | ->findOneBy(['user' => 1, 'isArchived' => false]); | ||
42 | |||
43 | if (!$entry) { | ||
44 | $this->markTestSkipped('No content found in db.'); | ||
45 | } | ||
46 | |||
47 | $this->client->request('GET', '/api/entries/'.$entry->getId().'/export.epub'); | ||
48 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
49 | |||
50 | // epub format got the content type in the content | ||
51 | $this->assertContains('application/epub', $this->client->getResponse()->getContent()); | ||
52 | $this->assertEquals('application/epub+zip', $this->client->getResponse()->headers->get('Content-Type')); | ||
53 | |||
54 | // re-auth client for mobi | ||
55 | $client = $this->createAuthorizedClient(); | ||
56 | $client->request('GET', '/api/entries/'.$entry->getId().'/export.mobi'); | ||
57 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
58 | |||
59 | $this->assertEquals('application/x-mobipocket-ebook', $client->getResponse()->headers->get('Content-Type')); | ||
60 | |||
61 | // re-auth client for pdf | ||
62 | $client = $this->createAuthorizedClient(); | ||
63 | $client->request('GET', '/api/entries/'.$entry->getId().'/export.pdf'); | ||
64 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
65 | |||
66 | $this->assertContains('PDF-', $client->getResponse()->getContent()); | ||
67 | $this->assertEquals('application/pdf', $client->getResponse()->headers->get('Content-Type')); | ||
68 | |||
69 | // re-auth client for pdf | ||
70 | $client = $this->createAuthorizedClient(); | ||
71 | $client->request('GET', '/api/entries/'.$entry->getId().'/export.txt'); | ||
72 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
73 | |||
74 | $this->assertContains('text/plain', $client->getResponse()->headers->get('Content-Type')); | ||
75 | |||
76 | // re-auth client for pdf | ||
77 | $client = $this->createAuthorizedClient(); | ||
78 | $client->request('GET', '/api/entries/'.$entry->getId().'/export.csv'); | ||
79 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
80 | |||
81 | $this->assertContains('application/csv', $client->getResponse()->headers->get('Content-Type')); | ||
82 | } | ||
83 | |||
84 | public function testGetOneEntryWrongUser() | ||
85 | { | ||
86 | $entry = $this->client->getContainer() | ||
87 | ->get('doctrine.orm.entity_manager') | ||
88 | ->getRepository('WallabagCoreBundle:Entry') | ||
89 | ->findOneBy(['user' => 2, 'isArchived' => false]); | ||
90 | |||
91 | if (!$entry) { | ||
92 | $this->markTestSkipped('No content found in db.'); | ||
93 | } | ||
94 | |||
95 | $this->client->request('GET', '/api/entries/'.$entry->getId().'.json'); | ||
96 | |||
97 | $this->assertEquals(403, $this->client->getResponse()->getStatusCode()); | ||
98 | } | ||
99 | |||
100 | public function testGetEntries() | ||
101 | { | ||
102 | $this->client->request('GET', '/api/entries'); | ||
103 | |||
104 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
105 | |||
106 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
107 | |||
108 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
109 | $this->assertNotEmpty($content['_embedded']['items']); | ||
110 | $this->assertGreaterThanOrEqual(1, $content['total']); | ||
111 | $this->assertEquals(1, $content['page']); | ||
112 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
113 | |||
114 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
115 | } | ||
116 | |||
117 | public function testGetEntriesWithFullOptions() | ||
118 | { | ||
119 | $this->client->request('GET', '/api/entries', [ | ||
120 | 'archive' => 1, | ||
121 | 'starred' => 1, | ||
122 | 'sort' => 'updated', | ||
123 | 'order' => 'asc', | ||
124 | 'page' => 1, | ||
125 | 'perPage' => 2, | ||
126 | 'tags' => 'foo', | ||
127 | 'since' => 1443274283, | ||
128 | ]); | ||
129 | |||
130 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
131 | |||
132 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
133 | |||
134 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
135 | $this->assertArrayHasKey('items', $content['_embedded']); | ||
136 | $this->assertGreaterThanOrEqual(0, $content['total']); | ||
137 | $this->assertEquals(1, $content['page']); | ||
138 | $this->assertEquals(2, $content['limit']); | ||
139 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
140 | |||
141 | $this->assertArrayHasKey('_links', $content); | ||
142 | $this->assertArrayHasKey('self', $content['_links']); | ||
143 | $this->assertArrayHasKey('first', $content['_links']); | ||
144 | $this->assertArrayHasKey('last', $content['_links']); | ||
145 | |||
146 | foreach (['self', 'first', 'last'] as $link) { | ||
147 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
148 | $this->assertContains('archive=1', $content['_links'][$link]['href']); | ||
149 | $this->assertContains('starred=1', $content['_links'][$link]['href']); | ||
150 | $this->assertContains('sort=updated', $content['_links'][$link]['href']); | ||
151 | $this->assertContains('order=asc', $content['_links'][$link]['href']); | ||
152 | $this->assertContains('tags=foo', $content['_links'][$link]['href']); | ||
153 | $this->assertContains('since=1443274283', $content['_links'][$link]['href']); | ||
154 | } | ||
155 | |||
156 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
157 | } | ||
158 | |||
159 | public function testGetStarredEntries() | ||
160 | { | ||
161 | $this->client->request('GET', '/api/entries', ['starred' => 1, 'sort' => 'updated']); | ||
162 | |||
163 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
164 | |||
165 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
166 | |||
167 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
168 | $this->assertNotEmpty($content['_embedded']['items']); | ||
169 | $this->assertGreaterThanOrEqual(1, $content['total']); | ||
170 | $this->assertEquals(1, $content['page']); | ||
171 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
172 | |||
173 | $this->assertArrayHasKey('_links', $content); | ||
174 | $this->assertArrayHasKey('self', $content['_links']); | ||
175 | $this->assertArrayHasKey('first', $content['_links']); | ||
176 | $this->assertArrayHasKey('last', $content['_links']); | ||
177 | |||
178 | foreach (['self', 'first', 'last'] as $link) { | ||
179 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
180 | $this->assertContains('starred=1', $content['_links'][$link]['href']); | ||
181 | $this->assertContains('sort=updated', $content['_links'][$link]['href']); | ||
182 | } | ||
183 | |||
184 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
185 | } | ||
186 | |||
187 | public function testGetArchiveEntries() | ||
188 | { | ||
189 | $this->client->request('GET', '/api/entries', ['archive' => 1]); | ||
190 | |||
191 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
192 | |||
193 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
194 | |||
195 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
196 | $this->assertNotEmpty($content['_embedded']['items']); | ||
197 | $this->assertGreaterThanOrEqual(1, $content['total']); | ||
198 | $this->assertEquals(1, $content['page']); | ||
199 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
200 | |||
201 | $this->assertArrayHasKey('_links', $content); | ||
202 | $this->assertArrayHasKey('self', $content['_links']); | ||
203 | $this->assertArrayHasKey('first', $content['_links']); | ||
204 | $this->assertArrayHasKey('last', $content['_links']); | ||
205 | |||
206 | foreach (['self', 'first', 'last'] as $link) { | ||
207 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
208 | $this->assertContains('archive=1', $content['_links'][$link]['href']); | ||
209 | } | ||
210 | |||
211 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
212 | } | ||
213 | |||
214 | public function testGetTaggedEntries() | ||
215 | { | ||
216 | $this->client->request('GET', '/api/entries', ['tags' => 'foo,bar']); | ||
217 | |||
218 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
219 | |||
220 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
221 | |||
222 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
223 | $this->assertNotEmpty($content['_embedded']['items']); | ||
224 | $this->assertGreaterThanOrEqual(1, $content['total']); | ||
225 | $this->assertEquals(1, $content['page']); | ||
226 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
227 | |||
228 | $this->assertArrayHasKey('_links', $content); | ||
229 | $this->assertArrayHasKey('self', $content['_links']); | ||
230 | $this->assertArrayHasKey('first', $content['_links']); | ||
231 | $this->assertArrayHasKey('last', $content['_links']); | ||
232 | |||
233 | foreach (['self', 'first', 'last'] as $link) { | ||
234 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
235 | $this->assertContains('tags='.urlencode('foo,bar'), $content['_links'][$link]['href']); | ||
236 | } | ||
237 | |||
238 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
239 | } | ||
240 | |||
241 | public function testGetDatedEntries() | ||
242 | { | ||
243 | $this->client->request('GET', '/api/entries', ['since' => 1443274283]); | ||
244 | |||
245 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
246 | |||
247 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
248 | |||
249 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
250 | $this->assertNotEmpty($content['_embedded']['items']); | ||
251 | $this->assertGreaterThanOrEqual(1, $content['total']); | ||
252 | $this->assertEquals(1, $content['page']); | ||
253 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
254 | |||
255 | $this->assertArrayHasKey('_links', $content); | ||
256 | $this->assertArrayHasKey('self', $content['_links']); | ||
257 | $this->assertArrayHasKey('first', $content['_links']); | ||
258 | $this->assertArrayHasKey('last', $content['_links']); | ||
259 | |||
260 | foreach (['self', 'first', 'last'] as $link) { | ||
261 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
262 | $this->assertContains('since=1443274283', $content['_links'][$link]['href']); | ||
263 | } | ||
264 | |||
265 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
266 | } | ||
267 | |||
268 | public function testGetDatedSupEntries() | ||
269 | { | ||
270 | $future = new \DateTime(date('Y-m-d H:i:s')); | ||
271 | $this->client->request('GET', '/api/entries', ['since' => $future->getTimestamp() + 1000]); | ||
272 | |||
273 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
274 | |||
275 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
276 | |||
277 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
278 | $this->assertEmpty($content['_embedded']['items']); | ||
279 | $this->assertEquals(0, $content['total']); | ||
280 | $this->assertEquals(1, $content['page']); | ||
281 | $this->assertEquals(1, $content['pages']); | ||
282 | |||
283 | $this->assertArrayHasKey('_links', $content); | ||
284 | $this->assertArrayHasKey('self', $content['_links']); | ||
285 | $this->assertArrayHasKey('first', $content['_links']); | ||
286 | $this->assertArrayHasKey('last', $content['_links']); | ||
287 | |||
288 | foreach (['self', 'first', 'last'] as $link) { | ||
289 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
290 | $this->assertContains('since='.($future->getTimestamp() + 1000), $content['_links'][$link]['href']); | ||
291 | } | ||
292 | |||
293 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
294 | } | ||
295 | |||
296 | public function testDeleteEntry() | ||
297 | { | ||
298 | $entry = $this->client->getContainer() | ||
299 | ->get('doctrine.orm.entity_manager') | ||
300 | ->getRepository('WallabagCoreBundle:Entry') | ||
301 | ->findOneByUser(1); | ||
302 | |||
303 | if (!$entry) { | ||
304 | $this->markTestSkipped('No content found in db.'); | ||
305 | } | ||
306 | |||
307 | $this->client->request('DELETE', '/api/entries/'.$entry->getId().'.json'); | ||
308 | |||
309 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
310 | |||
311 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
312 | |||
313 | $this->assertEquals($entry->getTitle(), $content['title']); | ||
314 | $this->assertEquals($entry->getUrl(), $content['url']); | ||
315 | |||
316 | // We'll try to delete this entry again | ||
317 | $this->client->request('DELETE', '/api/entries/'.$entry->getId().'.json'); | ||
318 | |||
319 | $this->assertEquals(404, $this->client->getResponse()->getStatusCode()); | ||
320 | } | ||
321 | |||
322 | public function testPostEntry() | ||
323 | { | ||
324 | $this->client->request('POST', '/api/entries.json', [ | ||
325 | 'url' => 'http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', | ||
326 | 'tags' => 'google', | ||
327 | 'title' => 'New title for my article', | ||
328 | ]); | ||
329 | |||
330 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
331 | |||
332 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
333 | |||
334 | $this->assertGreaterThan(0, $content['id']); | ||
335 | $this->assertEquals('http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', $content['url']); | ||
336 | $this->assertEquals(false, $content['is_archived']); | ||
337 | $this->assertEquals(false, $content['is_starred']); | ||
338 | $this->assertEquals('New title for my article', $content['title']); | ||
339 | $this->assertEquals(1, $content['user_id']); | ||
340 | $this->assertCount(1, $content['tags']); | ||
341 | } | ||
342 | |||
343 | public function testPostSameEntry() | ||
344 | { | ||
345 | $this->client->request('POST', '/api/entries.json', [ | ||
346 | 'url' => 'http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', | ||
347 | 'archive' => '1', | ||
348 | 'tags' => 'google, apple', | ||
349 | ]); | ||
350 | |||
351 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
352 | |||
353 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
354 | |||
355 | $this->assertGreaterThan(0, $content['id']); | ||
356 | $this->assertEquals('http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', $content['url']); | ||
357 | $this->assertEquals(true, $content['is_archived']); | ||
358 | $this->assertEquals(false, $content['is_starred']); | ||
359 | $this->assertCount(2, $content['tags']); | ||
360 | } | ||
361 | |||
362 | public function testPostArchivedAndStarredEntry() | ||
363 | { | ||
364 | $this->client->request('POST', '/api/entries.json', [ | ||
365 | 'url' => 'http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', | ||
366 | 'archive' => '1', | ||
367 | 'starred' => '1', | ||
368 | ]); | ||
369 | |||
370 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
371 | |||
372 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
373 | |||
374 | $this->assertGreaterThan(0, $content['id']); | ||
375 | $this->assertEquals('http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', $content['url']); | ||
376 | $this->assertEquals(true, $content['is_archived']); | ||
377 | $this->assertEquals(true, $content['is_starred']); | ||
378 | $this->assertEquals(1, $content['user_id']); | ||
379 | } | ||
380 | |||
381 | public function testPostArchivedAndStarredEntryWithoutQuotes() | ||
382 | { | ||
383 | $this->client->request('POST', '/api/entries.json', [ | ||
384 | 'url' => 'http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', | ||
385 | 'archive' => 0, | ||
386 | 'starred' => 1, | ||
387 | ]); | ||
388 | |||
389 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
390 | |||
391 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
392 | |||
393 | $this->assertGreaterThan(0, $content['id']); | ||
394 | $this->assertEquals('http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', $content['url']); | ||
395 | $this->assertEquals(false, $content['is_archived']); | ||
396 | $this->assertEquals(true, $content['is_starred']); | ||
397 | } | ||
398 | |||
399 | public function testPatchEntry() | ||
400 | { | ||
401 | $entry = $this->client->getContainer() | ||
402 | ->get('doctrine.orm.entity_manager') | ||
403 | ->getRepository('WallabagCoreBundle:Entry') | ||
404 | ->findOneByUser(1); | ||
405 | |||
406 | if (!$entry) { | ||
407 | $this->markTestSkipped('No content found in db.'); | ||
408 | } | ||
409 | |||
410 | // hydrate the tags relations | ||
411 | $nbTags = count($entry->getTags()); | ||
412 | |||
413 | $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [ | ||
414 | 'title' => 'New awesome title', | ||
415 | 'tags' => 'new tag '.uniqid(), | ||
416 | 'starred' => '1', | ||
417 | 'archive' => '0', | ||
418 | ]); | ||
419 | |||
420 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
421 | |||
422 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
423 | |||
424 | $this->assertEquals($entry->getId(), $content['id']); | ||
425 | $this->assertEquals($entry->getUrl(), $content['url']); | ||
426 | $this->assertEquals('New awesome title', $content['title']); | ||
427 | $this->assertGreaterThan($nbTags, count($content['tags'])); | ||
428 | $this->assertEquals(1, $content['user_id']); | ||
429 | } | ||
430 | |||
431 | public function testPatchEntryWithoutQuotes() | ||
432 | { | ||
433 | $entry = $this->client->getContainer() | ||
434 | ->get('doctrine.orm.entity_manager') | ||
435 | ->getRepository('WallabagCoreBundle:Entry') | ||
436 | ->findOneByUser(1); | ||
437 | |||
438 | if (!$entry) { | ||
439 | $this->markTestSkipped('No content found in db.'); | ||
440 | } | ||
441 | |||
442 | // hydrate the tags relations | ||
443 | $nbTags = count($entry->getTags()); | ||
444 | |||
445 | $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [ | ||
446 | 'title' => 'New awesome title', | ||
447 | 'tags' => 'new tag '.uniqid(), | ||
448 | 'starred' => 1, | ||
449 | 'archive' => 0, | ||
450 | ]); | ||
451 | |||
452 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
453 | |||
454 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
455 | |||
456 | $this->assertEquals($entry->getId(), $content['id']); | ||
457 | $this->assertEquals($entry->getUrl(), $content['url']); | ||
458 | $this->assertEquals('New awesome title', $content['title']); | ||
459 | $this->assertGreaterThan($nbTags, count($content['tags'])); | ||
460 | } | ||
461 | |||
462 | public function testGetTagsEntry() | ||
463 | { | ||
464 | $entry = $this->client->getContainer() | ||
465 | ->get('doctrine.orm.entity_manager') | ||
466 | ->getRepository('WallabagCoreBundle:Entry') | ||
467 | ->findOneWithTags($this->user->getId()); | ||
468 | |||
469 | $entry = $entry[0]; | ||
470 | |||
471 | if (!$entry) { | ||
472 | $this->markTestSkipped('No content found in db.'); | ||
473 | } | ||
474 | |||
475 | $tags = []; | ||
476 | foreach ($entry->getTags() as $tag) { | ||
477 | $tags[] = ['id' => $tag->getId(), 'label' => $tag->getLabel(), 'slug' => $tag->getSlug()]; | ||
478 | } | ||
479 | |||
480 | $this->client->request('GET', '/api/entries/'.$entry->getId().'/tags'); | ||
481 | |||
482 | $this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $this->client->getResponse()->getContent()); | ||
483 | } | ||
484 | |||
485 | public function testPostTagsOnEntry() | ||
486 | { | ||
487 | $entry = $this->client->getContainer() | ||
488 | ->get('doctrine.orm.entity_manager') | ||
489 | ->getRepository('WallabagCoreBundle:Entry') | ||
490 | ->findOneByUser(1); | ||
491 | |||
492 | if (!$entry) { | ||
493 | $this->markTestSkipped('No content found in db.'); | ||
494 | } | ||
495 | |||
496 | $nbTags = count($entry->getTags()); | ||
497 | |||
498 | $newTags = 'tag1,tag2,tag3'; | ||
499 | |||
500 | $this->client->request('POST', '/api/entries/'.$entry->getId().'/tags', ['tags' => $newTags]); | ||
501 | |||
502 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
503 | |||
504 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
505 | |||
506 | $this->assertArrayHasKey('tags', $content); | ||
507 | $this->assertEquals($nbTags + 3, count($content['tags'])); | ||
508 | |||
509 | $entryDB = $this->client->getContainer() | ||
510 | ->get('doctrine.orm.entity_manager') | ||
511 | ->getRepository('WallabagCoreBundle:Entry') | ||
512 | ->find($entry->getId()); | ||
513 | |||
514 | $tagsInDB = []; | ||
515 | foreach ($entryDB->getTags()->toArray() as $tag) { | ||
516 | $tagsInDB[$tag->getId()] = $tag->getLabel(); | ||
517 | } | ||
518 | |||
519 | foreach (explode(',', $newTags) as $tag) { | ||
520 | $this->assertContains($tag, $tagsInDB); | ||
521 | } | ||
522 | } | ||
523 | |||
524 | public function testDeleteOneTagEntry() | ||
525 | { | ||
526 | $entry = $this->client->getContainer() | ||
527 | ->get('doctrine.orm.entity_manager') | ||
528 | ->getRepository('WallabagCoreBundle:Entry') | ||
529 | ->findOneWithTags($this->user->getId()); | ||
530 | $entry = $entry[0]; | ||
531 | |||
532 | if (!$entry) { | ||
533 | $this->markTestSkipped('No content found in db.'); | ||
534 | } | ||
535 | |||
536 | // hydrate the tags relations | ||
537 | $nbTags = count($entry->getTags()); | ||
538 | $tag = $entry->getTags()[0]; | ||
539 | |||
540 | $this->client->request('DELETE', '/api/entries/'.$entry->getId().'/tags/'.$tag->getId().'.json'); | ||
541 | |||
542 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
543 | |||
544 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
545 | |||
546 | $this->assertArrayHasKey('tags', $content); | ||
547 | $this->assertEquals($nbTags - 1, count($content['tags'])); | ||
548 | } | ||
549 | |||
550 | public function testSaveIsArchivedAfterPost() | ||
551 | { | ||
552 | $entry = $this->client->getContainer() | ||
553 | ->get('doctrine.orm.entity_manager') | ||
554 | ->getRepository('WallabagCoreBundle:Entry') | ||
555 | ->findOneBy(['user' => 1, 'isArchived' => true]); | ||
556 | |||
557 | if (!$entry) { | ||
558 | $this->markTestSkipped('No content found in db.'); | ||
559 | } | ||
560 | |||
561 | $this->client->request('POST', '/api/entries.json', [ | ||
562 | 'url' => $entry->getUrl(), | ||
563 | ]); | ||
564 | |||
565 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
566 | |||
567 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
568 | |||
569 | $this->assertEquals(true, $content['is_archived']); | ||
570 | } | ||
571 | |||
572 | public function testSaveIsStarredAfterPost() | ||
573 | { | ||
574 | $entry = $this->client->getContainer() | ||
575 | ->get('doctrine.orm.entity_manager') | ||
576 | ->getRepository('WallabagCoreBundle:Entry') | ||
577 | ->findOneBy(['user' => 1, 'isStarred' => true]); | ||
578 | |||
579 | if (!$entry) { | ||
580 | $this->markTestSkipped('No content found in db.'); | ||
581 | } | ||
582 | |||
583 | $this->client->request('POST', '/api/entries.json', [ | ||
584 | 'url' => $entry->getUrl(), | ||
585 | ]); | ||
586 | |||
587 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
588 | |||
589 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
590 | |||
591 | $this->assertEquals(true, $content['is_starred']); | ||
592 | } | ||
593 | |||
594 | public function testSaveIsArchivedAfterPatch() | ||
595 | { | ||
596 | $entry = $this->client->getContainer() | ||
597 | ->get('doctrine.orm.entity_manager') | ||
598 | ->getRepository('WallabagCoreBundle:Entry') | ||
599 | ->findOneBy(['user' => 1, 'isArchived' => true]); | ||
600 | |||
601 | if (!$entry) { | ||
602 | $this->markTestSkipped('No content found in db.'); | ||
603 | } | ||
604 | |||
605 | $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [ | ||
606 | 'title' => $entry->getTitle().'++', | ||
607 | ]); | ||
608 | |||
609 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
610 | |||
611 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
612 | |||
613 | $this->assertEquals(true, $content['is_archived']); | ||
614 | } | ||
615 | |||
616 | public function testSaveIsStarredAfterPatch() | ||
617 | { | ||
618 | $entry = $this->client->getContainer() | ||
619 | ->get('doctrine.orm.entity_manager') | ||
620 | ->getRepository('WallabagCoreBundle:Entry') | ||
621 | ->findOneBy(['user' => 1, 'isStarred' => true]); | ||
622 | |||
623 | if (!$entry) { | ||
624 | $this->markTestSkipped('No content found in db.'); | ||
625 | } | ||
626 | $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [ | ||
627 | 'title' => $entry->getTitle().'++', | ||
628 | ]); | ||
629 | |||
630 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
631 | |||
632 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
633 | |||
634 | $this->assertEquals(true, $content['is_starred']); | ||
635 | } | ||
636 | |||
637 | public function testGetEntriesExists() | ||
638 | { | ||
639 | $this->client->request('GET', '/api/entries/exists?url=http://0.0.0.0/entry2'); | ||
640 | |||
641 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
642 | |||
643 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
644 | |||
645 | $this->assertEquals(true, $content['exists']); | ||
646 | } | ||
647 | |||
648 | public function testGetEntriesExistsWithManyUrls() | ||
649 | { | ||
650 | $url1 = 'http://0.0.0.0/entry2'; | ||
651 | $url2 = 'http://0.0.0.0/entry10'; | ||
652 | $this->client->request('GET', '/api/entries/exists?urls[]='.$url1.'&urls[]='.$url2); | ||
653 | |||
654 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
655 | |||
656 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
657 | |||
658 | $this->assertArrayHasKey($url1, $content); | ||
659 | $this->assertArrayHasKey($url2, $content); | ||
660 | $this->assertEquals(true, $content[$url1]); | ||
661 | $this->assertEquals(false, $content[$url2]); | ||
662 | } | ||
663 | |||
664 | public function testGetEntriesExistsWhichDoesNotExists() | ||
665 | { | ||
666 | $this->client->request('GET', '/api/entries/exists?url=http://google.com/entry2'); | ||
667 | |||
668 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
669 | |||
670 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
671 | |||
672 | $this->assertEquals(false, $content['exists']); | ||
673 | } | ||
674 | |||
675 | public function testGetEntriesExistsWithNoUrl() | ||
676 | { | ||
677 | $this->client->request('GET', '/api/entries/exists?url='); | ||
678 | |||
679 | $this->assertEquals(403, $this->client->getResponse()->getStatusCode()); | ||
680 | } | ||
681 | } | ||
diff --git a/tests/Wallabag/ApiBundle/Controller/TagRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/TagRestControllerTest.php new file mode 100644 index 00000000..bde5251f --- /dev/null +++ b/tests/Wallabag/ApiBundle/Controller/TagRestControllerTest.php | |||
@@ -0,0 +1,162 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Tests\Wallabag\ApiBundle\Controller; | ||
4 | |||
5 | use Tests\Wallabag\ApiBundle\WallabagApiTestCase; | ||
6 | use Wallabag\CoreBundle\Entity\Tag; | ||
7 | |||
8 | class TagRestControllerTest extends WallabagApiTestCase | ||
9 | { | ||
10 | public function testGetUserTags() | ||
11 | { | ||
12 | $this->client->request('GET', '/api/tags.json'); | ||
13 | |||
14 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
15 | |||
16 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
17 | |||
18 | $this->assertGreaterThan(0, $content); | ||
19 | $this->assertArrayHasKey('id', $content[0]); | ||
20 | $this->assertArrayHasKey('label', $content[0]); | ||
21 | |||
22 | return end($content); | ||
23 | } | ||
24 | |||
25 | /** | ||
26 | * @depends testGetUserTags | ||
27 | */ | ||
28 | public function testDeleteUserTag($tag) | ||
29 | { | ||
30 | $tagName = $tag['label']; | ||
31 | |||
32 | $this->client->request('DELETE', '/api/tags/'.$tag['id'].'.json'); | ||
33 | |||
34 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
35 | |||
36 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
37 | |||
38 | $this->assertArrayHasKey('label', $content); | ||
39 | $this->assertEquals($tag['label'], $content['label']); | ||
40 | $this->assertEquals($tag['slug'], $content['slug']); | ||
41 | |||
42 | $entries = $this->client->getContainer() | ||
43 | ->get('doctrine.orm.entity_manager') | ||
44 | ->getRepository('WallabagCoreBundle:Entry') | ||
45 | ->findAllByTagId($this->user->getId(), $tag['id']); | ||
46 | |||
47 | $this->assertCount(0, $entries); | ||
48 | |||
49 | $tag = $this->client->getContainer() | ||
50 | ->get('doctrine.orm.entity_manager') | ||
51 | ->getRepository('WallabagCoreBundle:Tag') | ||
52 | ->findOneByLabel($tagName); | ||
53 | |||
54 | $this->assertNull($tag, $tagName.' was removed because it begun an orphan tag'); | ||
55 | } | ||
56 | |||
57 | public function testDeleteTagByLabel() | ||
58 | { | ||
59 | $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); | ||
60 | $entry = $this->client->getContainer() | ||
61 | ->get('doctrine.orm.entity_manager') | ||
62 | ->getRepository('WallabagCoreBundle:Entry') | ||
63 | ->findOneWithTags($this->user->getId()); | ||
64 | |||
65 | $entry = $entry[0]; | ||
66 | |||
67 | $tag = new Tag(); | ||
68 | $tag->setLabel('Awesome tag for test'); | ||
69 | $em->persist($tag); | ||
70 | |||
71 | $entry->addTag($tag); | ||
72 | |||
73 | $em->persist($entry); | ||
74 | $em->flush(); | ||
75 | |||
76 | $this->client->request('DELETE', '/api/tag/label.json', ['tag' => $tag->getLabel()]); | ||
77 | |||
78 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
79 | |||
80 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
81 | |||
82 | $this->assertArrayHasKey('label', $content); | ||
83 | $this->assertEquals($tag->getLabel(), $content['label']); | ||
84 | $this->assertEquals($tag->getSlug(), $content['slug']); | ||
85 | |||
86 | $entries = $this->client->getContainer() | ||
87 | ->get('doctrine.orm.entity_manager') | ||
88 | ->getRepository('WallabagCoreBundle:Entry') | ||
89 | ->findAllByTagId($this->user->getId(), $tag->getId()); | ||
90 | |||
91 | $this->assertCount(0, $entries); | ||
92 | } | ||
93 | |||
94 | public function testDeleteTagByLabelNotFound() | ||
95 | { | ||
96 | $this->client->request('DELETE', '/api/tag/label.json', ['tag' => 'does not exist']); | ||
97 | |||
98 | $this->assertEquals(404, $this->client->getResponse()->getStatusCode()); | ||
99 | } | ||
100 | |||
101 | public function testDeleteTagsByLabel() | ||
102 | { | ||
103 | $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); | ||
104 | $entry = $this->client->getContainer() | ||
105 | ->get('doctrine.orm.entity_manager') | ||
106 | ->getRepository('WallabagCoreBundle:Entry') | ||
107 | ->findOneWithTags($this->user->getId()); | ||
108 | |||
109 | $entry = $entry[0]; | ||
110 | |||
111 | $tag = new Tag(); | ||
112 | $tag->setLabel('Awesome tag for tagsLabel'); | ||
113 | $em->persist($tag); | ||
114 | |||
115 | $tag2 = new Tag(); | ||
116 | $tag2->setLabel('Awesome tag for tagsLabel 2'); | ||
117 | $em->persist($tag2); | ||
118 | |||
119 | $entry->addTag($tag); | ||
120 | $entry->addTag($tag2); | ||
121 | |||
122 | $em->persist($entry); | ||
123 | $em->flush(); | ||
124 | |||
125 | $this->client->request('DELETE', '/api/tags/label.json', ['tags' => $tag->getLabel().','.$tag2->getLabel()]); | ||
126 | |||
127 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
128 | |||
129 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
130 | |||
131 | $this->assertCount(2, $content); | ||
132 | |||
133 | $this->assertArrayHasKey('label', $content[0]); | ||
134 | $this->assertEquals($tag->getLabel(), $content[0]['label']); | ||
135 | $this->assertEquals($tag->getSlug(), $content[0]['slug']); | ||
136 | |||
137 | $this->assertArrayHasKey('label', $content[1]); | ||
138 | $this->assertEquals($tag2->getLabel(), $content[1]['label']); | ||
139 | $this->assertEquals($tag2->getSlug(), $content[1]['slug']); | ||
140 | |||
141 | $entries = $this->client->getContainer() | ||
142 | ->get('doctrine.orm.entity_manager') | ||
143 | ->getRepository('WallabagCoreBundle:Entry') | ||
144 | ->findAllByTagId($this->user->getId(), $tag->getId()); | ||
145 | |||
146 | $this->assertCount(0, $entries); | ||
147 | |||
148 | $entries = $this->client->getContainer() | ||
149 | ->get('doctrine.orm.entity_manager') | ||
150 | ->getRepository('WallabagCoreBundle:Entry') | ||
151 | ->findAllByTagId($this->user->getId(), $tag2->getId()); | ||
152 | |||
153 | $this->assertCount(0, $entries); | ||
154 | } | ||
155 | |||
156 | public function testDeleteTagsByLabelNotFound() | ||
157 | { | ||
158 | $this->client->request('DELETE', '/api/tags/label.json', ['tags' => 'does not exist']); | ||
159 | |||
160 | $this->assertEquals(404, $this->client->getResponse()->getStatusCode()); | ||
161 | } | ||
162 | } | ||
diff --git a/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php index 6bca3c8b..c87e58de 100644 --- a/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php +++ b/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php | |||
@@ -3,705 +3,9 @@ | |||
3 | namespace Tests\Wallabag\ApiBundle\Controller; | 3 | namespace Tests\Wallabag\ApiBundle\Controller; |
4 | 4 | ||
5 | use Tests\Wallabag\ApiBundle\WallabagApiTestCase; | 5 | use Tests\Wallabag\ApiBundle\WallabagApiTestCase; |
6 | use Wallabag\CoreBundle\Entity\Tag; | ||
7 | 6 | ||
8 | class WallabagRestControllerTest extends WallabagApiTestCase | 7 | class WallabagRestControllerTest extends WallabagApiTestCase |
9 | { | 8 | { |
10 | protected static $salt; | ||
11 | |||
12 | public function testGetOneEntry() | ||
13 | { | ||
14 | $entry = $this->client->getContainer() | ||
15 | ->get('doctrine.orm.entity_manager') | ||
16 | ->getRepository('WallabagCoreBundle:Entry') | ||
17 | ->findOneBy(['user' => 1, 'isArchived' => false]); | ||
18 | |||
19 | if (!$entry) { | ||
20 | $this->markTestSkipped('No content found in db.'); | ||
21 | } | ||
22 | |||
23 | $this->client->request('GET', '/api/entries/'.$entry->getId().'.json'); | ||
24 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
25 | |||
26 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
27 | |||
28 | $this->assertEquals($entry->getTitle(), $content['title']); | ||
29 | $this->assertEquals($entry->getUrl(), $content['url']); | ||
30 | $this->assertCount(count($entry->getTags()), $content['tags']); | ||
31 | $this->assertEquals($entry->getUserName(), $content['user_name']); | ||
32 | $this->assertEquals($entry->getUserEmail(), $content['user_email']); | ||
33 | $this->assertEquals($entry->getUserId(), $content['user_id']); | ||
34 | |||
35 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
36 | } | ||
37 | |||
38 | public function testExportEntry() | ||
39 | { | ||
40 | $entry = $this->client->getContainer() | ||
41 | ->get('doctrine.orm.entity_manager') | ||
42 | ->getRepository('WallabagCoreBundle:Entry') | ||
43 | ->findOneBy(['user' => 1, 'isArchived' => false]); | ||
44 | |||
45 | if (!$entry) { | ||
46 | $this->markTestSkipped('No content found in db.'); | ||
47 | } | ||
48 | |||
49 | $this->client->request('GET', '/api/entries/'.$entry->getId().'/export.epub'); | ||
50 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
51 | |||
52 | // epub format got the content type in the content | ||
53 | $this->assertContains('application/epub', $this->client->getResponse()->getContent()); | ||
54 | $this->assertEquals('application/epub+zip', $this->client->getResponse()->headers->get('Content-Type')); | ||
55 | |||
56 | // re-auth client for mobi | ||
57 | $client = $this->createAuthorizedClient(); | ||
58 | $client->request('GET', '/api/entries/'.$entry->getId().'/export.mobi'); | ||
59 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
60 | |||
61 | $this->assertEquals('application/x-mobipocket-ebook', $client->getResponse()->headers->get('Content-Type')); | ||
62 | |||
63 | // re-auth client for pdf | ||
64 | $client = $this->createAuthorizedClient(); | ||
65 | $client->request('GET', '/api/entries/'.$entry->getId().'/export.pdf'); | ||
66 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
67 | |||
68 | $this->assertContains('PDF-', $client->getResponse()->getContent()); | ||
69 | $this->assertEquals('application/pdf', $client->getResponse()->headers->get('Content-Type')); | ||
70 | |||
71 | // re-auth client for pdf | ||
72 | $client = $this->createAuthorizedClient(); | ||
73 | $client->request('GET', '/api/entries/'.$entry->getId().'/export.txt'); | ||
74 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
75 | |||
76 | $this->assertContains('text/plain', $client->getResponse()->headers->get('Content-Type')); | ||
77 | |||
78 | // re-auth client for pdf | ||
79 | $client = $this->createAuthorizedClient(); | ||
80 | $client->request('GET', '/api/entries/'.$entry->getId().'/export.csv'); | ||
81 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
82 | |||
83 | $this->assertContains('application/csv', $client->getResponse()->headers->get('Content-Type')); | ||
84 | } | ||
85 | |||
86 | public function testGetOneEntryWrongUser() | ||
87 | { | ||
88 | $entry = $this->client->getContainer() | ||
89 | ->get('doctrine.orm.entity_manager') | ||
90 | ->getRepository('WallabagCoreBundle:Entry') | ||
91 | ->findOneBy(['user' => 2, 'isArchived' => false]); | ||
92 | |||
93 | if (!$entry) { | ||
94 | $this->markTestSkipped('No content found in db.'); | ||
95 | } | ||
96 | |||
97 | $this->client->request('GET', '/api/entries/'.$entry->getId().'.json'); | ||
98 | |||
99 | $this->assertEquals(403, $this->client->getResponse()->getStatusCode()); | ||
100 | } | ||
101 | |||
102 | public function testGetEntries() | ||
103 | { | ||
104 | $this->client->request('GET', '/api/entries'); | ||
105 | |||
106 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
107 | |||
108 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
109 | |||
110 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
111 | $this->assertNotEmpty($content['_embedded']['items']); | ||
112 | $this->assertGreaterThanOrEqual(1, $content['total']); | ||
113 | $this->assertEquals(1, $content['page']); | ||
114 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
115 | |||
116 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
117 | } | ||
118 | |||
119 | public function testGetEntriesWithFullOptions() | ||
120 | { | ||
121 | $this->client->request('GET', '/api/entries', [ | ||
122 | 'archive' => 1, | ||
123 | 'starred' => 1, | ||
124 | 'sort' => 'updated', | ||
125 | 'order' => 'asc', | ||
126 | 'page' => 1, | ||
127 | 'perPage' => 2, | ||
128 | 'tags' => 'foo', | ||
129 | 'since' => 1443274283, | ||
130 | ]); | ||
131 | |||
132 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
133 | |||
134 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
135 | |||
136 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
137 | $this->assertArrayHasKey('items', $content['_embedded']); | ||
138 | $this->assertGreaterThanOrEqual(0, $content['total']); | ||
139 | $this->assertEquals(1, $content['page']); | ||
140 | $this->assertEquals(2, $content['limit']); | ||
141 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
142 | |||
143 | $this->assertArrayHasKey('_links', $content); | ||
144 | $this->assertArrayHasKey('self', $content['_links']); | ||
145 | $this->assertArrayHasKey('first', $content['_links']); | ||
146 | $this->assertArrayHasKey('last', $content['_links']); | ||
147 | |||
148 | foreach (['self', 'first', 'last'] as $link) { | ||
149 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
150 | $this->assertContains('archive=1', $content['_links'][$link]['href']); | ||
151 | $this->assertContains('starred=1', $content['_links'][$link]['href']); | ||
152 | $this->assertContains('sort=updated', $content['_links'][$link]['href']); | ||
153 | $this->assertContains('order=asc', $content['_links'][$link]['href']); | ||
154 | $this->assertContains('tags=foo', $content['_links'][$link]['href']); | ||
155 | $this->assertContains('since=1443274283', $content['_links'][$link]['href']); | ||
156 | } | ||
157 | |||
158 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
159 | } | ||
160 | |||
161 | public function testGetStarredEntries() | ||
162 | { | ||
163 | $this->client->request('GET', '/api/entries', ['starred' => 1, 'sort' => 'updated']); | ||
164 | |||
165 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
166 | |||
167 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
168 | |||
169 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
170 | $this->assertNotEmpty($content['_embedded']['items']); | ||
171 | $this->assertGreaterThanOrEqual(1, $content['total']); | ||
172 | $this->assertEquals(1, $content['page']); | ||
173 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
174 | |||
175 | $this->assertArrayHasKey('_links', $content); | ||
176 | $this->assertArrayHasKey('self', $content['_links']); | ||
177 | $this->assertArrayHasKey('first', $content['_links']); | ||
178 | $this->assertArrayHasKey('last', $content['_links']); | ||
179 | |||
180 | foreach (['self', 'first', 'last'] as $link) { | ||
181 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
182 | $this->assertContains('starred=1', $content['_links'][$link]['href']); | ||
183 | $this->assertContains('sort=updated', $content['_links'][$link]['href']); | ||
184 | } | ||
185 | |||
186 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
187 | } | ||
188 | |||
189 | public function testGetArchiveEntries() | ||
190 | { | ||
191 | $this->client->request('GET', '/api/entries', ['archive' => 1]); | ||
192 | |||
193 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
194 | |||
195 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
196 | |||
197 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
198 | $this->assertNotEmpty($content['_embedded']['items']); | ||
199 | $this->assertGreaterThanOrEqual(1, $content['total']); | ||
200 | $this->assertEquals(1, $content['page']); | ||
201 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
202 | |||
203 | $this->assertArrayHasKey('_links', $content); | ||
204 | $this->assertArrayHasKey('self', $content['_links']); | ||
205 | $this->assertArrayHasKey('first', $content['_links']); | ||
206 | $this->assertArrayHasKey('last', $content['_links']); | ||
207 | |||
208 | foreach (['self', 'first', 'last'] as $link) { | ||
209 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
210 | $this->assertContains('archive=1', $content['_links'][$link]['href']); | ||
211 | } | ||
212 | |||
213 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
214 | } | ||
215 | |||
216 | public function testGetTaggedEntries() | ||
217 | { | ||
218 | $this->client->request('GET', '/api/entries', ['tags' => 'foo,bar']); | ||
219 | |||
220 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
221 | |||
222 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
223 | |||
224 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
225 | $this->assertNotEmpty($content['_embedded']['items']); | ||
226 | $this->assertGreaterThanOrEqual(1, $content['total']); | ||
227 | $this->assertEquals(1, $content['page']); | ||
228 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
229 | |||
230 | $this->assertArrayHasKey('_links', $content); | ||
231 | $this->assertArrayHasKey('self', $content['_links']); | ||
232 | $this->assertArrayHasKey('first', $content['_links']); | ||
233 | $this->assertArrayHasKey('last', $content['_links']); | ||
234 | |||
235 | foreach (['self', 'first', 'last'] as $link) { | ||
236 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
237 | $this->assertContains('tags='.urlencode('foo,bar'), $content['_links'][$link]['href']); | ||
238 | } | ||
239 | |||
240 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
241 | } | ||
242 | |||
243 | public function testGetDatedEntries() | ||
244 | { | ||
245 | $this->client->request('GET', '/api/entries', ['since' => 1443274283]); | ||
246 | |||
247 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
248 | |||
249 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
250 | |||
251 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
252 | $this->assertNotEmpty($content['_embedded']['items']); | ||
253 | $this->assertGreaterThanOrEqual(1, $content['total']); | ||
254 | $this->assertEquals(1, $content['page']); | ||
255 | $this->assertGreaterThanOrEqual(1, $content['pages']); | ||
256 | |||
257 | $this->assertArrayHasKey('_links', $content); | ||
258 | $this->assertArrayHasKey('self', $content['_links']); | ||
259 | $this->assertArrayHasKey('first', $content['_links']); | ||
260 | $this->assertArrayHasKey('last', $content['_links']); | ||
261 | |||
262 | foreach (['self', 'first', 'last'] as $link) { | ||
263 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
264 | $this->assertContains('since=1443274283', $content['_links'][$link]['href']); | ||
265 | } | ||
266 | |||
267 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
268 | } | ||
269 | |||
270 | public function testGetDatedSupEntries() | ||
271 | { | ||
272 | $future = new \DateTime(date('Y-m-d H:i:s')); | ||
273 | $this->client->request('GET', '/api/entries', ['since' => $future->getTimestamp() + 1000]); | ||
274 | |||
275 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
276 | |||
277 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
278 | |||
279 | $this->assertGreaterThanOrEqual(1, count($content)); | ||
280 | $this->assertEmpty($content['_embedded']['items']); | ||
281 | $this->assertEquals(0, $content['total']); | ||
282 | $this->assertEquals(1, $content['page']); | ||
283 | $this->assertEquals(1, $content['pages']); | ||
284 | |||
285 | $this->assertArrayHasKey('_links', $content); | ||
286 | $this->assertArrayHasKey('self', $content['_links']); | ||
287 | $this->assertArrayHasKey('first', $content['_links']); | ||
288 | $this->assertArrayHasKey('last', $content['_links']); | ||
289 | |||
290 | foreach (['self', 'first', 'last'] as $link) { | ||
291 | $this->assertArrayHasKey('href', $content['_links'][$link]); | ||
292 | $this->assertContains('since='.($future->getTimestamp() + 1000), $content['_links'][$link]['href']); | ||
293 | } | ||
294 | |||
295 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); | ||
296 | } | ||
297 | |||
298 | public function testDeleteEntry() | ||
299 | { | ||
300 | $entry = $this->client->getContainer() | ||
301 | ->get('doctrine.orm.entity_manager') | ||
302 | ->getRepository('WallabagCoreBundle:Entry') | ||
303 | ->findOneByUser(1); | ||
304 | |||
305 | if (!$entry) { | ||
306 | $this->markTestSkipped('No content found in db.'); | ||
307 | } | ||
308 | |||
309 | $this->client->request('DELETE', '/api/entries/'.$entry->getId().'.json'); | ||
310 | |||
311 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
312 | |||
313 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
314 | |||
315 | $this->assertEquals($entry->getTitle(), $content['title']); | ||
316 | $this->assertEquals($entry->getUrl(), $content['url']); | ||
317 | |||
318 | // We'll try to delete this entry again | ||
319 | $this->client->request('DELETE', '/api/entries/'.$entry->getId().'.json'); | ||
320 | |||
321 | $this->assertEquals(404, $this->client->getResponse()->getStatusCode()); | ||
322 | } | ||
323 | |||
324 | public function testPostEntry() | ||
325 | { | ||
326 | $this->client->request('POST', '/api/entries.json', [ | ||
327 | 'url' => 'http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', | ||
328 | 'tags' => 'google', | ||
329 | 'title' => 'New title for my article', | ||
330 | ]); | ||
331 | |||
332 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
333 | |||
334 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
335 | |||
336 | $this->assertGreaterThan(0, $content['id']); | ||
337 | $this->assertEquals('http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', $content['url']); | ||
338 | $this->assertEquals(false, $content['is_archived']); | ||
339 | $this->assertEquals(false, $content['is_starred']); | ||
340 | $this->assertEquals('New title for my article', $content['title']); | ||
341 | $this->assertEquals(1, $content['user_id']); | ||
342 | $this->assertCount(1, $content['tags']); | ||
343 | } | ||
344 | |||
345 | public function testPostSameEntry() | ||
346 | { | ||
347 | $this->client->request('POST', '/api/entries.json', [ | ||
348 | 'url' => 'http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', | ||
349 | 'archive' => '1', | ||
350 | 'tags' => 'google, apple', | ||
351 | ]); | ||
352 | |||
353 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
354 | |||
355 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
356 | |||
357 | $this->assertGreaterThan(0, $content['id']); | ||
358 | $this->assertEquals('http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', $content['url']); | ||
359 | $this->assertEquals(true, $content['is_archived']); | ||
360 | $this->assertEquals(false, $content['is_starred']); | ||
361 | $this->assertCount(2, $content['tags']); | ||
362 | } | ||
363 | |||
364 | public function testPostArchivedAndStarredEntry() | ||
365 | { | ||
366 | $this->client->request('POST', '/api/entries.json', [ | ||
367 | 'url' => 'http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', | ||
368 | 'archive' => '1', | ||
369 | 'starred' => '1', | ||
370 | ]); | ||
371 | |||
372 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
373 | |||
374 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
375 | |||
376 | $this->assertGreaterThan(0, $content['id']); | ||
377 | $this->assertEquals('http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', $content['url']); | ||
378 | $this->assertEquals(true, $content['is_archived']); | ||
379 | $this->assertEquals(true, $content['is_starred']); | ||
380 | $this->assertEquals(1, $content['user_id']); | ||
381 | } | ||
382 | |||
383 | public function testPostArchivedAndStarredEntryWithoutQuotes() | ||
384 | { | ||
385 | $this->client->request('POST', '/api/entries.json', [ | ||
386 | 'url' => 'http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', | ||
387 | 'archive' => 0, | ||
388 | 'starred' => 1, | ||
389 | ]); | ||
390 | |||
391 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
392 | |||
393 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
394 | |||
395 | $this->assertGreaterThan(0, $content['id']); | ||
396 | $this->assertEquals('http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html', $content['url']); | ||
397 | $this->assertEquals(false, $content['is_archived']); | ||
398 | $this->assertEquals(true, $content['is_starred']); | ||
399 | } | ||
400 | |||
401 | public function testPatchEntry() | ||
402 | { | ||
403 | $entry = $this->client->getContainer() | ||
404 | ->get('doctrine.orm.entity_manager') | ||
405 | ->getRepository('WallabagCoreBundle:Entry') | ||
406 | ->findOneByUser(1); | ||
407 | |||
408 | if (!$entry) { | ||
409 | $this->markTestSkipped('No content found in db.'); | ||
410 | } | ||
411 | |||
412 | // hydrate the tags relations | ||
413 | $nbTags = count($entry->getTags()); | ||
414 | |||
415 | $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [ | ||
416 | 'title' => 'New awesome title', | ||
417 | 'tags' => 'new tag '.uniqid(), | ||
418 | 'starred' => '1', | ||
419 | 'archive' => '0', | ||
420 | ]); | ||
421 | |||
422 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
423 | |||
424 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
425 | |||
426 | $this->assertEquals($entry->getId(), $content['id']); | ||
427 | $this->assertEquals($entry->getUrl(), $content['url']); | ||
428 | $this->assertEquals('New awesome title', $content['title']); | ||
429 | $this->assertGreaterThan($nbTags, count($content['tags'])); | ||
430 | $this->assertEquals(1, $content['user_id']); | ||
431 | } | ||
432 | |||
433 | public function testPatchEntryWithoutQuotes() | ||
434 | { | ||
435 | $entry = $this->client->getContainer() | ||
436 | ->get('doctrine.orm.entity_manager') | ||
437 | ->getRepository('WallabagCoreBundle:Entry') | ||
438 | ->findOneByUser(1); | ||
439 | |||
440 | if (!$entry) { | ||
441 | $this->markTestSkipped('No content found in db.'); | ||
442 | } | ||
443 | |||
444 | // hydrate the tags relations | ||
445 | $nbTags = count($entry->getTags()); | ||
446 | |||
447 | $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [ | ||
448 | 'title' => 'New awesome title', | ||
449 | 'tags' => 'new tag '.uniqid(), | ||
450 | 'starred' => 1, | ||
451 | 'archive' => 0, | ||
452 | ]); | ||
453 | |||
454 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
455 | |||
456 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
457 | |||
458 | $this->assertEquals($entry->getId(), $content['id']); | ||
459 | $this->assertEquals($entry->getUrl(), $content['url']); | ||
460 | $this->assertEquals('New awesome title', $content['title']); | ||
461 | $this->assertGreaterThan($nbTags, count($content['tags'])); | ||
462 | } | ||
463 | |||
464 | public function testGetTagsEntry() | ||
465 | { | ||
466 | $entry = $this->client->getContainer() | ||
467 | ->get('doctrine.orm.entity_manager') | ||
468 | ->getRepository('WallabagCoreBundle:Entry') | ||
469 | ->findOneWithTags($this->user->getId()); | ||
470 | |||
471 | $entry = $entry[0]; | ||
472 | |||
473 | if (!$entry) { | ||
474 | $this->markTestSkipped('No content found in db.'); | ||
475 | } | ||
476 | |||
477 | $tags = []; | ||
478 | foreach ($entry->getTags() as $tag) { | ||
479 | $tags[] = ['id' => $tag->getId(), 'label' => $tag->getLabel(), 'slug' => $tag->getSlug()]; | ||
480 | } | ||
481 | |||
482 | $this->client->request('GET', '/api/entries/'.$entry->getId().'/tags'); | ||
483 | |||
484 | $this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $this->client->getResponse()->getContent()); | ||
485 | } | ||
486 | |||
487 | public function testPostTagsOnEntry() | ||
488 | { | ||
489 | $entry = $this->client->getContainer() | ||
490 | ->get('doctrine.orm.entity_manager') | ||
491 | ->getRepository('WallabagCoreBundle:Entry') | ||
492 | ->findOneByUser(1); | ||
493 | |||
494 | if (!$entry) { | ||
495 | $this->markTestSkipped('No content found in db.'); | ||
496 | } | ||
497 | |||
498 | $nbTags = count($entry->getTags()); | ||
499 | |||
500 | $newTags = 'tag1,tag2,tag3'; | ||
501 | |||
502 | $this->client->request('POST', '/api/entries/'.$entry->getId().'/tags', ['tags' => $newTags]); | ||
503 | |||
504 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
505 | |||
506 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
507 | |||
508 | $this->assertArrayHasKey('tags', $content); | ||
509 | $this->assertEquals($nbTags + 3, count($content['tags'])); | ||
510 | |||
511 | $entryDB = $this->client->getContainer() | ||
512 | ->get('doctrine.orm.entity_manager') | ||
513 | ->getRepository('WallabagCoreBundle:Entry') | ||
514 | ->find($entry->getId()); | ||
515 | |||
516 | $tagsInDB = []; | ||
517 | foreach ($entryDB->getTags()->toArray() as $tag) { | ||
518 | $tagsInDB[$tag->getId()] = $tag->getLabel(); | ||
519 | } | ||
520 | |||
521 | foreach (explode(',', $newTags) as $tag) { | ||
522 | $this->assertContains($tag, $tagsInDB); | ||
523 | } | ||
524 | } | ||
525 | |||
526 | public function testDeleteOneTagEntry() | ||
527 | { | ||
528 | $entry = $this->client->getContainer() | ||
529 | ->get('doctrine.orm.entity_manager') | ||
530 | ->getRepository('WallabagCoreBundle:Entry') | ||
531 | ->findOneWithTags($this->user->getId()); | ||
532 | $entry = $entry[0]; | ||
533 | |||
534 | if (!$entry) { | ||
535 | $this->markTestSkipped('No content found in db.'); | ||
536 | } | ||
537 | |||
538 | // hydrate the tags relations | ||
539 | $nbTags = count($entry->getTags()); | ||
540 | $tag = $entry->getTags()[0]; | ||
541 | |||
542 | $this->client->request('DELETE', '/api/entries/'.$entry->getId().'/tags/'.$tag->getId().'.json'); | ||
543 | |||
544 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
545 | |||
546 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
547 | |||
548 | $this->assertArrayHasKey('tags', $content); | ||
549 | $this->assertEquals($nbTags - 1, count($content['tags'])); | ||
550 | } | ||
551 | |||
552 | public function testGetUserTags() | ||
553 | { | ||
554 | $this->client->request('GET', '/api/tags.json'); | ||
555 | |||
556 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
557 | |||
558 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
559 | |||
560 | $this->assertGreaterThan(0, $content); | ||
561 | $this->assertArrayHasKey('id', $content[0]); | ||
562 | $this->assertArrayHasKey('label', $content[0]); | ||
563 | |||
564 | return end($content); | ||
565 | } | ||
566 | |||
567 | /** | ||
568 | * @depends testGetUserTags | ||
569 | */ | ||
570 | public function testDeleteUserTag($tag) | ||
571 | { | ||
572 | $tagName = $tag['label']; | ||
573 | |||
574 | $this->client->request('DELETE', '/api/tags/'.$tag['id'].'.json'); | ||
575 | |||
576 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
577 | |||
578 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
579 | |||
580 | $this->assertArrayHasKey('label', $content); | ||
581 | $this->assertEquals($tag['label'], $content['label']); | ||
582 | $this->assertEquals($tag['slug'], $content['slug']); | ||
583 | |||
584 | $entries = $this->client->getContainer() | ||
585 | ->get('doctrine.orm.entity_manager') | ||
586 | ->getRepository('WallabagCoreBundle:Entry') | ||
587 | ->findAllByTagId($this->user->getId(), $tag['id']); | ||
588 | |||
589 | $this->assertCount(0, $entries); | ||
590 | |||
591 | $tag = $this->client->getContainer() | ||
592 | ->get('doctrine.orm.entity_manager') | ||
593 | ->getRepository('WallabagCoreBundle:Tag') | ||
594 | ->findOneByLabel($tagName); | ||
595 | |||
596 | $this->assertNull($tag, $tagName.' was removed because it begun an orphan tag'); | ||
597 | } | ||
598 | |||
599 | public function testDeleteTagByLabel() | ||
600 | { | ||
601 | $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); | ||
602 | $entry = $this->client->getContainer() | ||
603 | ->get('doctrine.orm.entity_manager') | ||
604 | ->getRepository('WallabagCoreBundle:Entry') | ||
605 | ->findOneWithTags($this->user->getId()); | ||
606 | |||
607 | $entry = $entry[0]; | ||
608 | |||
609 | $tag = new Tag(); | ||
610 | $tag->setLabel('Awesome tag for test'); | ||
611 | $em->persist($tag); | ||
612 | |||
613 | $entry->addTag($tag); | ||
614 | |||
615 | $em->persist($entry); | ||
616 | $em->flush(); | ||
617 | |||
618 | $this->client->request('DELETE', '/api/tag/label.json', ['tag' => $tag->getLabel()]); | ||
619 | |||
620 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
621 | |||
622 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
623 | |||
624 | $this->assertArrayHasKey('label', $content); | ||
625 | $this->assertEquals($tag->getLabel(), $content['label']); | ||
626 | $this->assertEquals($tag->getSlug(), $content['slug']); | ||
627 | |||
628 | $entries = $this->client->getContainer() | ||
629 | ->get('doctrine.orm.entity_manager') | ||
630 | ->getRepository('WallabagCoreBundle:Entry') | ||
631 | ->findAllByTagId($this->user->getId(), $tag->getId()); | ||
632 | |||
633 | $this->assertCount(0, $entries); | ||
634 | } | ||
635 | |||
636 | public function testDeleteTagByLabelNotFound() | ||
637 | { | ||
638 | $this->client->request('DELETE', '/api/tag/label.json', ['tag' => 'does not exist']); | ||
639 | |||
640 | $this->assertEquals(404, $this->client->getResponse()->getStatusCode()); | ||
641 | } | ||
642 | |||
643 | public function testDeleteTagsByLabel() | ||
644 | { | ||
645 | $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); | ||
646 | $entry = $this->client->getContainer() | ||
647 | ->get('doctrine.orm.entity_manager') | ||
648 | ->getRepository('WallabagCoreBundle:Entry') | ||
649 | ->findOneWithTags($this->user->getId()); | ||
650 | |||
651 | $entry = $entry[0]; | ||
652 | |||
653 | $tag = new Tag(); | ||
654 | $tag->setLabel('Awesome tag for tagsLabel'); | ||
655 | $em->persist($tag); | ||
656 | |||
657 | $tag2 = new Tag(); | ||
658 | $tag2->setLabel('Awesome tag for tagsLabel 2'); | ||
659 | $em->persist($tag2); | ||
660 | |||
661 | $entry->addTag($tag); | ||
662 | $entry->addTag($tag2); | ||
663 | |||
664 | $em->persist($entry); | ||
665 | $em->flush(); | ||
666 | |||
667 | $this->client->request('DELETE', '/api/tags/label.json', ['tags' => $tag->getLabel().','.$tag2->getLabel()]); | ||
668 | |||
669 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
670 | |||
671 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
672 | |||
673 | $this->assertCount(2, $content); | ||
674 | |||
675 | $this->assertArrayHasKey('label', $content[0]); | ||
676 | $this->assertEquals($tag->getLabel(), $content[0]['label']); | ||
677 | $this->assertEquals($tag->getSlug(), $content[0]['slug']); | ||
678 | |||
679 | $this->assertArrayHasKey('label', $content[1]); | ||
680 | $this->assertEquals($tag2->getLabel(), $content[1]['label']); | ||
681 | $this->assertEquals($tag2->getSlug(), $content[1]['slug']); | ||
682 | |||
683 | $entries = $this->client->getContainer() | ||
684 | ->get('doctrine.orm.entity_manager') | ||
685 | ->getRepository('WallabagCoreBundle:Entry') | ||
686 | ->findAllByTagId($this->user->getId(), $tag->getId()); | ||
687 | |||
688 | $this->assertCount(0, $entries); | ||
689 | |||
690 | $entries = $this->client->getContainer() | ||
691 | ->get('doctrine.orm.entity_manager') | ||
692 | ->getRepository('WallabagCoreBundle:Entry') | ||
693 | ->findAllByTagId($this->user->getId(), $tag2->getId()); | ||
694 | |||
695 | $this->assertCount(0, $entries); | ||
696 | } | ||
697 | |||
698 | public function testDeleteTagsByLabelNotFound() | ||
699 | { | ||
700 | $this->client->request('DELETE', '/api/tags/label.json', ['tags' => 'does not exist']); | ||
701 | |||
702 | $this->assertEquals(404, $this->client->getResponse()->getStatusCode()); | ||
703 | } | ||
704 | |||
705 | public function testGetVersion() | 9 | public function testGetVersion() |
706 | { | 10 | { |
707 | $this->client->request('GET', '/api/version'); | 11 | $this->client->request('GET', '/api/version'); |
@@ -712,136 +16,4 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
712 | 16 | ||
713 | $this->assertEquals($this->client->getContainer()->getParameter('wallabag_core.version'), $content); | 17 | $this->assertEquals($this->client->getContainer()->getParameter('wallabag_core.version'), $content); |
714 | } | 18 | } |
715 | |||
716 | public function testSaveIsArchivedAfterPost() | ||
717 | { | ||
718 | $entry = $this->client->getContainer() | ||
719 | ->get('doctrine.orm.entity_manager') | ||
720 | ->getRepository('WallabagCoreBundle:Entry') | ||
721 | ->findOneBy(['user' => 1, 'isArchived' => true]); | ||
722 | |||
723 | if (!$entry) { | ||
724 | $this->markTestSkipped('No content found in db.'); | ||
725 | } | ||
726 | |||
727 | $this->client->request('POST', '/api/entries.json', [ | ||
728 | 'url' => $entry->getUrl(), | ||
729 | ]); | ||
730 | |||
731 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
732 | |||
733 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
734 | |||
735 | $this->assertEquals(true, $content['is_archived']); | ||
736 | } | ||
737 | |||
738 | public function testSaveIsStarredAfterPost() | ||
739 | { | ||
740 | $entry = $this->client->getContainer() | ||
741 | ->get('doctrine.orm.entity_manager') | ||
742 | ->getRepository('WallabagCoreBundle:Entry') | ||
743 | ->findOneBy(['user' => 1, 'isStarred' => true]); | ||
744 | |||
745 | if (!$entry) { | ||
746 | $this->markTestSkipped('No content found in db.'); | ||
747 | } | ||
748 | |||
749 | $this->client->request('POST', '/api/entries.json', [ | ||
750 | 'url' => $entry->getUrl(), | ||
751 | ]); | ||
752 | |||
753 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
754 | |||
755 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
756 | |||
757 | $this->assertEquals(true, $content['is_starred']); | ||
758 | } | ||
759 | |||
760 | public function testSaveIsArchivedAfterPatch() | ||
761 | { | ||
762 | $entry = $this->client->getContainer() | ||
763 | ->get('doctrine.orm.entity_manager') | ||
764 | ->getRepository('WallabagCoreBundle:Entry') | ||
765 | ->findOneBy(['user' => 1, 'isArchived' => true]); | ||
766 | |||
767 | if (!$entry) { | ||
768 | $this->markTestSkipped('No content found in db.'); | ||
769 | } | ||
770 | |||
771 | $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [ | ||
772 | 'title' => $entry->getTitle().'++', | ||
773 | ]); | ||
774 | |||
775 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
776 | |||
777 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
778 | |||
779 | $this->assertEquals(true, $content['is_archived']); | ||
780 | } | ||
781 | |||
782 | public function testSaveIsStarredAfterPatch() | ||
783 | { | ||
784 | $entry = $this->client->getContainer() | ||
785 | ->get('doctrine.orm.entity_manager') | ||
786 | ->getRepository('WallabagCoreBundle:Entry') | ||
787 | ->findOneBy(['user' => 1, 'isStarred' => true]); | ||
788 | |||
789 | if (!$entry) { | ||
790 | $this->markTestSkipped('No content found in db.'); | ||
791 | } | ||
792 | $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [ | ||
793 | 'title' => $entry->getTitle().'++', | ||
794 | ]); | ||
795 | |||
796 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
797 | |||
798 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
799 | |||
800 | $this->assertEquals(true, $content['is_starred']); | ||
801 | } | ||
802 | |||
803 | public function testGetEntriesExists() | ||
804 | { | ||
805 | $this->client->request('GET', '/api/entries/exists?url=http://0.0.0.0/entry2'); | ||
806 | |||
807 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
808 | |||
809 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
810 | |||
811 | $this->assertEquals(true, $content['exists']); | ||
812 | } | ||
813 | |||
814 | public function testGetEntriesExistsWithManyUrls() | ||
815 | { | ||
816 | $url1 = 'http://0.0.0.0/entry2'; | ||
817 | $url2 = 'http://0.0.0.0/entry10'; | ||
818 | $this->client->request('GET', '/api/entries/exists?urls[]='.$url1.'&urls[]='.$url2); | ||
819 | |||
820 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
821 | |||
822 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
823 | |||
824 | $this->assertArrayHasKey($url1, $content); | ||
825 | $this->assertArrayHasKey($url2, $content); | ||
826 | $this->assertEquals(true, $content[$url1]); | ||
827 | $this->assertEquals(false, $content[$url2]); | ||
828 | } | ||
829 | |||
830 | public function testGetEntriesExistsWhichDoesNotExists() | ||
831 | { | ||
832 | $this->client->request('GET', '/api/entries/exists?url=http://google.com/entry2'); | ||
833 | |||
834 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
835 | |||
836 | $content = json_decode($this->client->getResponse()->getContent(), true); | ||
837 | |||
838 | $this->assertEquals(false, $content['exists']); | ||
839 | } | ||
840 | |||
841 | public function testGetEntriesExistsWithNoUrl() | ||
842 | { | ||
843 | $this->client->request('GET', '/api/entries/exists?url='); | ||
844 | |||
845 | $this->assertEquals(403, $this->client->getResponse()->getStatusCode()); | ||
846 | } | ||
847 | } | 19 | } |
diff --git a/tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php b/tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php index 9ecd8bc4..5ca886bd 100644 --- a/tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php +++ b/tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php | |||
@@ -117,6 +117,17 @@ class ExportControllerTest extends WallabagCoreTestCase | |||
117 | $this->assertEquals('application/pdf', $headers->get('content-type')); | 117 | $this->assertEquals('application/pdf', $headers->get('content-type')); |
118 | $this->assertEquals('attachment; filename="All articles.pdf"', $headers->get('content-disposition')); | 118 | $this->assertEquals('attachment; filename="All articles.pdf"', $headers->get('content-disposition')); |
119 | $this->assertEquals('binary', $headers->get('content-transfer-encoding')); | 119 | $this->assertEquals('binary', $headers->get('content-transfer-encoding')); |
120 | |||
121 | ob_start(); | ||
122 | $crawler = $client->request('GET', '/export/tag_entries.pdf?tag=foo'); | ||
123 | ob_end_clean(); | ||
124 | |||
125 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
126 | |||
127 | $headers = $client->getResponse()->headers; | ||
128 | $this->assertEquals('application/pdf', $headers->get('content-type')); | ||
129 | $this->assertEquals('attachment; filename="Tag_entries articles.pdf"', $headers->get('content-disposition')); | ||
130 | $this->assertEquals('binary', $headers->get('content-transfer-encoding')); | ||
120 | } | 131 | } |
121 | 132 | ||
122 | public function testTxtExport() | 133 | public function testTxtExport() |
diff --git a/tests/Wallabag/UserBundle/Mailer/AuthCodeMailerTest.php b/tests/Wallabag/UserBundle/Mailer/AuthCodeMailerTest.php index 441d6519..21412da6 100644 --- a/tests/Wallabag/UserBundle/Mailer/AuthCodeMailerTest.php +++ b/tests/Wallabag/UserBundle/Mailer/AuthCodeMailerTest.php | |||
@@ -26,7 +26,6 @@ class AuthCodeMailerTest extends \PHPUnit_Framework_TestCase | |||
26 | protected $mailer; | 26 | protected $mailer; |
27 | protected $spool; | 27 | protected $spool; |
28 | protected $twig; | 28 | protected $twig; |
29 | protected $config; | ||
30 | 29 | ||
31 | protected function setUp() | 30 | protected function setUp() |
32 | { | 31 | { |
@@ -44,14 +43,6 @@ class AuthCodeMailerTest extends \PHPUnit_Framework_TestCase | |||
44 | TWIG; | 43 | TWIG; |
45 | 44 | ||
46 | $this->twig = new \Twig_Environment(new \Twig_Loader_Array(['WallabagUserBundle:TwoFactor:email_auth_code.html.twig' => $twigTemplate])); | 45 | $this->twig = new \Twig_Environment(new \Twig_Loader_Array(['WallabagUserBundle:TwoFactor:email_auth_code.html.twig' => $twigTemplate])); |
47 | |||
48 | $this->config = $this->getMockBuilder('Craue\ConfigBundle\Util\Config') | ||
49 | ->disableOriginalConstructor() | ||
50 | ->getMock(); | ||
51 | |||
52 | $this->config->expects($this->any()) | ||
53 | ->method('get') | ||
54 | ->willReturn('http://0.0.0.0/support'); | ||
55 | } | 46 | } |
56 | 47 | ||
57 | public function testSendEmail() | 48 | public function testSendEmail() |
@@ -67,7 +58,8 @@ TWIG; | |||
67 | $this->twig, | 58 | $this->twig, |
68 | 'nobody@test.io', | 59 | 'nobody@test.io', |
69 | 'wallabag test', | 60 | 'wallabag test', |
70 | $this->config | 61 | 'http://0.0.0.0/support', |
62 | 'http://0.0.0.0/' | ||
71 | ); | 63 | ); |
72 | 64 | ||
73 | $authCodeMailer->sendAuthCode($user); | 65 | $authCodeMailer->sendAuthCode($user); |