Compare commits

...

96 Commits
2.1.2 ... 2.1.3

Author SHA1 Message Date
b6a3e571b2 Forgot .travis.yml file 2016-11-04 13:59:40 +01:00
fbffcdad7c Release wallabag 2.1.3 2016-11-04 13:57:56 +01:00
4ec9f5c334 Merge pull request #2536 from wallabag/prepare-213
Prepare wallabag 2.1.3 🚀
2016-11-04 13:32:47 +01:00
505e8acd46 Update CHANGELOG
Usage of github-changes from @lalitkapoor
2016-11-04 13:32:30 +01:00
a3f7d8a870 Prepare wallabag 2.1.3 🚀 2016-11-04 09:10:15 +01:00
2db9142bfc Merge pull request #2460 from wallabag/ui-changes
UI Changes
2016-11-04 08:49:52 +01:00
f53f542fa5 Merge pull request #2534 from wallabag/default-sort
Use created_at as default sort
2016-11-04 08:48:42 +01:00
ce11952447 Use created_at as default sort
Imported content use the real creation date. If we sort by id, it'll
display contents in reverse order.
2016-11-04 07:34:38 +01:00
d56d416d9b Deal with more resolution issues
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2016-11-03 12:49:57 +01:00
3221b2e413 Fix resolution issues and 'title' issues
Also, modify editorconfig for css files.

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2016-11-03 11:28:40 +01:00
84795d015b Merge pull request #2530 from wallabag/add-doc-failed-load-external-entity
Added documentation about failed to load external entity error
2016-11-02 20:07:10 +01:00
a49159e9eb Update faq.rst 2016-11-02 18:08:25 +01:00
b1057c6a5e update German faq.rst 2016-11-02 17:16:57 +01:00
cce77ebc2d update German faq.rst 2016-11-02 17:15:32 +01:00
4b5e95c0e5 Added more info about Doctrine / PHP bug 2016-11-02 17:02:48 +01:00
585fd2a4ad Added documentation about failed to load external entity error
Fix #2529
2016-11-02 16:58:20 +01:00
3be554c78f Merge pull request #2519 from Jibec/master
Translation update - French
2016-11-02 07:48:10 +01:00
9c15470691 wallabag is full lowercase 2016-11-02 07:15:24 +01:00
be4f6bb4f3 Merge pull request #2528 from Kdecherf/slashdot-cookie
Force user-agent for .slashdot.org
2016-11-01 22:29:43 +01:00
22ef6f96e2 Force user-agent for .slashdot.org
Slashdot replaces actual content with a form regarding cookies when
requests originate from France. By forcing the user-agent, the warning
seems to go away.

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2016-11-01 19:51:45 +01:00
db1d962f7b Merge pull request #2524 from lologhi/instapaper-cli-import
Add Instapaper to CLI import
2016-11-01 14:26:57 +01:00
995d909d0f Add Instapaper to CLI import 2016-11-01 18:33:32 +08:00
303b3c61a7 Translation - French - tiny typo 2016-10-31 20:16:36 +01:00
31dd328df4 Translation - French - tiny escape correction 2016-10-31 20:12:29 +01:00
337b47c216 Translation - French - tiny escape correction 2016-10-31 20:01:07 +01:00
e52ee565f7 Merge pull request #2521 from foxmask/master
fix path for  the install scripts
2016-10-31 09:56:07 +01:00
7d9abd1ab5 Merge pull request #2520 from wallabag/config
Inject parameter instead of service
2016-10-31 09:39:53 +01:00
99692e8c33 fix path for in install scripts 2016-10-30 22:46:09 +01:00
038469e73f fix path for in install scripts 2016-10-30 22:38:38 +01:00
e1632cea0e Fix tests 2016-10-30 20:43:37 +01:00
4b3c983ab8 Inject parameter instead of service
We are injecting CraueConfig service when we only need to retrieve one or two values from it.
Instead I discovered we can directly inject a value from a service in the service definition!
2016-10-30 20:27:41 +01:00
f74061f1e1 Merge pull request #2518 from wallabag/docs-dev-3rd-party-by-strubbl
docs: fix link to wallabag-stats project
2016-10-30 12:27:25 +01:00
2087e499b4 Translation update - French 2016-10-30 12:11:24 +01:00
f6aff9a7a8 Translation update - French 2016-10-30 12:10:59 +01:00
431d038aa7 Translation update - French 2016-10-30 12:10:14 +01:00
a0eddc598e Translation update - French 2016-10-30 12:09:08 +01:00
90ec78f05a docs: fix link to wallabag-stats project 2016-10-30 12:01:24 +01:00
2a2ceceaa4 Merge pull request #2514 from wallabag/docs-dev-3rd-party-by-strubbl
docs: update 3rd party projects by Strubbl
2016-10-30 07:04:11 +01:00
a40b2b7ee4 docs: update 3rd party projects by Strubbl 2016-10-29 19:59:05 +02:00
ca9858537e Merge pull request #2513 from wallabag/update-capistrano-config
Updated Capistrano configuration
2016-10-29 15:36:58 +02:00
c4b3933bae Updated Capistrano configuration 2016-10-29 14:39:07 +02:00
67b270d996 Merge pull request #2506 from wallabag/fix-export-with-tags
Fixed entries export filtered with a tag
2016-10-29 14:27:46 +02:00
794ac861cb Added test for export by filtering with tag 2016-10-29 14:03:55 +02:00
920d88599a Fixed entries export filtered with a tag
Fix #2505
2016-10-29 13:43:11 +02:00
267087d969 Merge pull request #2509 from wallabag/explode-api-controller
Exploded WallabagRestController into many controllers
2016-10-29 13:20:01 +02:00
bc4564a709 Removed useless route 2016-10-28 15:24:58 +02:00
900c844861 Exploded WallabagRestController into many controllers
Fix #2503
2016-10-28 14:46:30 +02:00
eca4d030bf Merge pull request #2508 from wallabag/fix-redis-rabbit-doc
Added the whole path to parameters.yml file
2016-10-27 16:53:57 +02:00
37da178696 Added the whole path to parameters.yml file 2016-10-27 14:46:21 +02:00
069d39dfaf Merge pull request #2507 from wallabag/add-check-makefile
Added require.sh to check if composer is installed
2016-10-27 13:55:17 +02:00
c4d9a8d6d0 Usage of composer.phar if it's found 2016-10-27 10:09:43 +02:00
9998c72533 Added require.sh to check if composer is installed 2016-10-27 09:53:09 +02:00
f3b637fbfe Merge pull request #2500 from wallabag/add-check-composer
Added a check in Makefile to see if composer is installed
2016-10-27 07:30:50 +02:00
a5d6a7d8b5 Merge pull request #2504 from wallabag/change-page-title-filter-tags
Added tag label in the page title
2016-10-26 16:09:37 +02:00
73e8df9e44 Renamed variable for page title 2016-10-26 15:49:44 +02:00
7a5043f187 Added tag label in the page title
Fix #2472
2016-10-26 13:53:23 +02:00
c68720d40e Merge pull request #2473 from pmichelazzo/master
Portuguese (Brazilian) translation
2016-10-25 11:41:24 +02:00
e8eec07ae6 Translated missed line 147 2016-10-25 07:25:08 -02:00
c5ad143e75 Added a check in Makefile to see if composer is installed
Fix #2498
2016-10-24 22:28:44 +02:00
e1d4ed2d81 Merge pull request #2493 from bmillemathias/add_link_content_fetch_problem
Add relevant links to fetch content error page
2016-10-24 21:44:22 +02:00
d36e5e20a5 Removed the email part 2016-10-24 21:01:13 +02:00
d1b075509d Add relevant links to fetch content error pages.
We link to the troubleshooting guide and give the mail link.
2016-10-23 15:34:52 +02:00
c64b4941d5 Merge pull request #2486 from wallabag/translate-backup-doc
Added 🇫🇷 documentation for wallabag backup
2016-10-22 22:23:15 +02:00
2ca1cc0c18 Translate backup.rst to German 2016-10-22 21:11:20 +02:00
67aa755fb5 Added 🇫🇷 documentation for wallabag backup 2016-10-22 14:16:17 +02:00
f69636c4db Merge pull request #2484 from bmillemathias/fix_2479
Document what to backup in Wallabag
2016-10-22 13:55:46 +02:00
f7f6bfb7e9 Merge pull request #2485 from bmillemathias/fix_android_documentation
Fix missing words in Android application documentation
2016-10-22 13:31:14 +02:00
89abc0b61b Add missing words in the documentation 2016-10-22 13:21:53 +02:00
817724a7b8 Re-add bottom pagination
Also pager.html.twig wasn’t necessary, there weren't enough duplication to put them in a dedicated template
2016-10-22 13:09:16 +02:00
ab61dbc073 remove padding on small widths
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2016-10-22 13:08:12 +02:00
34aa06a9b9 WIP
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2016-10-22 13:08:04 +02:00
fc75009de7 Document what to backup in Wallabag
Fixes issue #2479
2016-10-22 09:14:07 +02:00
f99ddbc6f8 Merge pull request #2481 from wallabag/some-fixes
Some fixes
2016-10-21 15:15:41 +02:00
49dee2d2de Merge pull request #2482 from wallabag/avoid-bad-refresh
If reload content failed, don’t update it
2016-10-21 15:12:29 +02:00
b64d8f2c9f Update Twitter cards description 2016-10-21 10:51:20 +02:00
2cbf0d05d4 Update translation for piwik_host 2016-10-21 10:45:39 +02:00
645dc7594b Fix missing translations 2016-10-21 07:52:55 +02:00
818cbe1fee Fix Portuguese translation 2016-10-21 07:18:41 +02:00
8e53bf3aa1 Update translation 2016-10-21 00:12:53 +02:00
f6798f69c3 This test doesn’t require an internet connection 2016-10-20 23:26:16 +02:00
9095497191 Fix test 2016-10-20 23:03:37 +02:00
2297d60f10 If reload content failed, don’t update it
In case user wants a fresh version of the current one and the website isn’t available, don’t erase it with a boring message saying wallabag wasn’t able to refresh the content.
2016-10-20 22:49:46 +02:00
5453500458 Requeue depending on producer
Browser import can requeue message from `parseEntry` but we should take care of the way import are handled (depending on the producer)
2016-10-20 21:17:45 +02:00
1e3d74a9cf Avoid RabbitMQ consumer to loop
When the `parseEntry` returns null it means the entry already exists in the database. Sending `false` as return, will requeue the message which will then loop forever.
2016-10-20 21:17:03 +02:00
576d285ddf Translate date
I use a kind of hacky way to convert the user locale (defined with 2 letters, like `fr`) into a local with 5 letters (like `fr_FR`). I guess it should work on most of the case..
2016-10-20 21:16:01 +02:00
166e8cc6a9 Fix french translation 2016-10-20 21:14:46 +02:00
1cad9416c9 Update Portuguese translations 2016-10-20 21:11:10 +02:00
b669224762 Validate ALL translations files 2016-10-20 21:11:09 +02:00
a42cc6ff89 Update validators.pt.yml file
Some missed strings translated.
2016-10-20 08:57:24 +02:00
5d6cffedb1 Update Portuguese (Brazilian) translation
Adding the config.yml on the package.
2016-10-19 19:25:48 +02:00
99731f0bb1 Merge remote-tracking branch 'wallabag/master' 2016-10-18 22:48:23 +02:00
3a3c6b866b Portuguese (Brazilian) translation
This the Portuguese (Brazilian) for the Wallaby v2.
2016-10-18 20:51:31 +02:00
7180aaed45 Merge pull request #2467 from wallabag/version-213dev
Update wallabag version for master branch
2016-10-17 11:57:52 +02:00
f31361a6d9 Update wallabag version for master branch 2016-10-17 11:56:29 +02:00
fd461c25aa Merge pull request #2466 from wallabag/fix-update-doc
Removed MD5 hash in documentation
2016-10-17 11:40:03 +02:00
39c3dd30d2 Removed MD5 hash in documentation 2016-10-17 11:38:11 +02:00
108 changed files with 11106 additions and 2829 deletions

View File

@ -8,3 +8,7 @@ indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
[*.css]
indent_style = space
indent_size = 2

View File

@ -70,9 +70,11 @@ before_install:
- if [[ $TRAVIS_REPO_SLUG = wallabag/wallabag ]]; then cp .composer-auth.json ~/.composer/auth.json; fi;
script:
- travis_wait bash composer install -o --no-interaction --no-progress --prefer-dist
- travis_wait bash composer update --no-interaction --no-progress
- ant prepare-$DB
- if [[ $VALIDATE_TRANSLATION_FILE = '' ]]; then phpunit -v ; fi;
- if [[ $CS_FIXER = run ]]; then php bin/php-cs-fixer fix src/ --verbose --dry-run ; fi;
- if [[ $VALIDATE_TRANSLATION_FILE = run ]]; then php bin/console lint:yaml src/Wallabag/CoreBundle/Resources/translations -v ; fi;
- if [[ $VALIDATE_TRANSLATION_FILE = run ]]; then php bin/console lint:yaml app/Resources/CraueConfigBundle/translations -v ; fi;
- if [[ $VALIDATE_TRANSLATION_FILE = run ]]; then php bin/console lint:yaml app/Resources/FOSUserBundle/translations -v ; fi;
- if [[ $ASSETS = build ]]; then ./node_modules/grunt-cli/bin/grunt tests; fi;

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,8 @@ require 'capistrano/setup'
# Include default deployment tasks
require 'capistrano/deploy'
require 'capistrano/composer'
require 'capistrano/file-permissions'
require 'capistrano/symfony'
# Load custom tasks from `lib/capistrano/tasks` if you have any defined

View File

@ -1,5 +1,6 @@
source "https://rubygems.org"
gem 'capistrano', '~> 3.4'
gem 'capistrano-composer'
gem 'capistrano-symfony', '~> 1.0.0.rc1'
gem 'capistrano-composer', '~> 0.0.3'
gem 'capistrano-file-permissions'

View File

@ -29,8 +29,9 @@ PLATFORMS
DEPENDENCIES
capistrano (~> 3.4)
capistrano-composer (~> 0.0.3)
capistrano-composer
capistrano-file-permissions
capistrano-symfony (~> 1.0.0.rc1)
BUNDLED WITH
1.11.2
1.13.5

View File

@ -41,6 +41,6 @@ travis: ## Make some stuff for Travis-CI
deploy: ## Deploy wallabag
@bundle exec cap staging deploy
.PHONY: help clean install update build test release travis deploy
.PHONY: help clean install update build test release travis deploy run dev
.DEFAULT_GOAL := install

View File

@ -9,7 +9,7 @@ During this documentation, we assume the release is `$LAST_WALLABAG_RELEASE`.
#### Files to edit
- `app/config/config.yml` (`wallabag_core.version`)
- `CHANGELOG.md` (by using this command `github_changelog_generator --no-compare-link --header-label="# Changelog" --no-issues --no-pr-wo-labels --since-tag="1.9.2"`. [github-changelog-generator is available here](https://github.com/skywinder/github-changelog-generator))
- `CHANGELOG.md` (by using this command `github-changes -o wallabag -r wallabag -k YOURGITHUBTOKEN --only-pulls --use-commit-body --title Changelog --date-format YYYY/MM/DD --between-tags 2.0.0-alpha.0...master -n 2.1.3`. [github-changes is available here](https://github.com/lalitkapoor/github-changes))
#### Create release on GitHub

View File

@ -23,7 +23,7 @@ export: "eksport"
import: "import"
misc: "misc"
modify_settings: "Gem ændring"
piwik_host: Hosting af din side hos Piwik
piwik_host: Hosting af din side hos Piwik (uden http:// eller https://)
piwik_site_id: ID for din side hos Piwik
piwik_enabled: Aktiver Piwik
demo_mode_enabled: "Aktiver demo-indstilling? (anvendes kun til wallabags offentlige demo)"

View File

@ -23,7 +23,7 @@ export: "Export"
import: "Import"
misc: "Verschiedenes"
modify_settings: "Übernehmen"
piwik_host: Host deiner Webseite in Piwik
piwik_host: Host deiner Webseite in Piwik (ohne http:// oder https://)
piwik_site_id: ID deiner Webseite in Piwik
piwik_enabled: Piwik aktivieren
demo_mode_enabled: "Test-Modus aktivieren? (nur für die öffentliche wallabag-Demo genutzt)"

View File

@ -23,7 +23,7 @@ export: "export"
import: "import"
misc: "misc"
modify_settings: "apply"
piwik_host: Host of your website in Piwik
piwik_host: Host of your website in Piwik (without http:// ou https://)
piwik_site_id: ID of your website in Piwik
piwik_enabled: Enable Piwik
demo_mode_enabled: "Enable demo mode ? (only used for the wallabag public demo)"

View File

@ -23,7 +23,7 @@ export: "exportar"
import: "importar"
misc: "misc"
modify_settings: "modificar configuración"
piwik_host: Host de tu website de Piwik
piwik_host: Host de tu website de Piwik (sin http:// o https://)
piwik_site_id: ID de tu website de Piwik
piwik_enabled: Activar Piwik
demo_mode_enabled: "Activar modo demo (sólo usado para la demo de wallabag)"

View File

@ -23,7 +23,7 @@ export: "برون‌سپاری"
import: "درون‌ریزی"
misc: "غیره"
modify_settings: "اعمال"
# piwik_host: Host of your website in Piwik
# piwik_host: Host of your website in Piwik (without http:// or https://)
# piwik_site_id: ID of your website in Piwik
# piwik_enabled: Enable Piwik
# demo_mode_enabled: "Enable demo mode ? (only used for the wallabag public demo)"

View File

@ -23,7 +23,7 @@ export: "export"
import: "import"
misc: "divers"
modify_settings: "appliquer"
piwik_host: URL de votre site dans Piwik
piwik_host: URL de votre site dans Piwik (sans http:// ou https://)
piwik_site_id: ID de votre site dans Piwik
piwik_enabled: Activer Piwik
demo_mode_enabled: "Activer le mode démo ? (utiliser uniquement pour la démo publique de wallabag)"

View File

@ -23,7 +23,7 @@ export: "esporta"
import: "importa"
misc: "misc"
modify_settings: "applica"
piwik_host: Host del tuo sito in Piwik
piwik_host: Host del tuo sito in Piwik (senza http:// o https://)
piwik_site_id: ID del tuo sito in Piwik
piwik_enabled: Abilita Piwik
demo_mode_enabled: "Abilita modalità demo ? (usato solo per la demo pubblica di wallabag)"

View File

@ -23,7 +23,7 @@ export: "expòrt"
import: "impòrt"
misc: "divèrs"
modify_settings: "aplicar"
piwik_host: URL de vòstre site dins Piwik
piwik_host: URL de vòstre site dins Piwik (sense http:// o https://)
piwik_site_id: ID de vòstre site dins Piwik
piwik_enabled: Activar Piwik
demo_mode_enabled: "Activar lo mode demostracion ? (utilizar solament per la demostracion publica de wallabag)"

View File

@ -23,7 +23,7 @@ export: "eksport"
import: "import"
misc: "różne"
modify_settings: "zatwierdz"
piwik_host: Host twojej strony Piwik
piwik_host: Host twojej strony Piwik (bez http:// lub https://)
piwik_site_id: ID twojej strony Piwik
piwik_enabled: Włacz Piwik
demo_mode_enabled: "Włacz tryb demo? (używany wyłącznie dla publicznej demonstracji Wallabag)"

View File

@ -0,0 +1,29 @@
download_pictures: Download imagens no seu servidor
carrot: Habilitar compartilhamento para o Carrot
diaspora_url: URL Diaspora, se o serviço está habilitado
export_epub: Habilita exportação para ePub
export_mobi: Habilita exportação para .mobi
export_pdf: Habilita exportação para PDF
export_csv: Habilita exportação para CSV
export_json: Habilita exportação para JSON
export_txt: Habilita exportação para TXT
export_xml: Habilita exportação para XML
pocket_consumer_key: Chave de consumidor do Pocket para importar conteúdo (https://getpocket.com/developer/docs/authentication)
shaarli_url: URL Shaarli, se o serviço está habilitado
share_diaspora: Habilitar compartilhamento para o Diaspora
share_mail: Habilitar compartilhamento por e-mail
share_shaarli: Habilitar compartilhamento para o Shaarli
share_twitter: Habilitar compartilhamento para o Twitter
show_printlink: Mostrar um link para imprimir o conteúdo
wallabag_support_url: URL de Suporte do wallabag
wallabag_url: URL de *sua* instância do wallabag
entry: "artigo"
export: "exportar"
import: "importar"
misc: "misc"
modify_settings: "aplicar"
piwik_host: Host de seu website Piwik
piwik_site_id: ID de seu website Piwik
piwik_enabled: Habilitar Piwik
demo_mode_enabled: "Habilitar modo demo? (somente usado para o demo público do wallabag)"
demo_mode_username: "Usuário demo"

View File

@ -23,7 +23,7 @@ export: "exportă"
import: "importă"
misc: "diverse"
modify_settings: "aplică"
# piwik_host: Host of your website in Piwik
# piwik_host: Host of your website in Piwik (without http:// or https://)
# piwik_site_id: ID of your website in Piwik
# piwik_enabled: Enable Piwik
# demo_mode_enabled: "Enable demo mode ? (only used for the wallabag public demo)"

View File

@ -23,7 +23,7 @@
# import: "import"
# misc: "misc"
# modify_settings: "apply"
# piwik_host: Host of your website in Piwik
# piwik_host: Host of your website in Piwik (without http:// or https://)
# piwik_site_id: ID of your website in Piwik
# piwik_enabled: Enable Piwik
# demo_mode_enabled: "Enable demo mode ? (only used for the wallabag public demo)"

View File

@ -1,2 +1,2 @@
Login: "Se connecter"
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."
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."

View File

@ -0,0 +1,2 @@
Login: "Login"
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."

View File

@ -581,7 +581,6 @@ img.preview {
div.pagination ul {
text-align: right;
margin-bottom: 50px;
}
.nb-results {

View File

@ -17,12 +17,11 @@
0 = Common
========================================================================== */
@font-face {
font-family: icomoon;
src: url("../fonts/IcoMoon-Free.ttf");
font-weight: normal;
font-style: normal;
}
/**
*
* Material icons
*
*/
@font-face {
font-family: 'Material Icons';
@ -62,6 +61,33 @@
font-feature-settings: 'liga';
}
/* Rules for sizing the icon. */
.material-icons.md-18 { font-size: 18px; }
.material-icons.md-24 { font-size: 24px; }
.material-icons.md-36 { font-size: 36px; }
.material-icons.md-48 { font-size: 48px; }
/* Rules for using icons as black on a light background. */
.material-icons.md-dark { color: rgba(0, 0, 0, 0.54); }
.material-icons.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); }
/* Rules for using icons as white on a dark background. */
.material-icons.md-light { color: rgba(255, 255, 255, 1); }
.material-icons.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); }
/**
*
* Icomoon icons
*
*/
@font-face {
font-family: icomoon;
src: url("../fonts/IcoMoon-Free.ttf");
font-weight: normal;
font-style: normal;
}
[class^="icon-"]::before,
[class*=" icon-"]::before {
font-family: icomoon;
@ -207,8 +233,17 @@ div.pagination ul .next.disabled {
color: #fff;
}
.page-footer .footer-copyright {
min-width: 50px;
height: auto !important;
line-height: 1em !important;
}
.page-footer .footer-copyright p {
display: inline;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
display: block;
}
.hidden {
@ -397,13 +432,23 @@ main ul.row {
}
.data .card .card-body {
height: 22em;
height: 19em;
overflow: hidden;
}
.card .card-content .card-title {
line-height: 32px;
max-height: 64px;
.card .card-content .card-title,
.card .card-reveal .card-title {
line-height: 22.8px;
max-height: 80px;
font-size: 19px;
font-family: roberto, "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #313131;
}
.card .card-content .activator,
.card .card-reveal .activator {
cursor: pointer;
font-family: "Material Icons";
}
.card .card-content i.right,
@ -411,6 +456,18 @@ main ul.row {
margin-left: 0;
}
.card .card-content .original {
line-height: 24px;
font-size: 15px;
}
a.original {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
display: block;
}
.card .card-entry-labels {
position: absolute;
top: 10px;
@ -433,7 +490,7 @@ main ul.row {
}
.card .card-entry-labels-hidden {
margin-top: 5px;
margin: 2.5px auto;
}
.card .card-entry-labels-hidden li {
@ -464,8 +521,8 @@ main ul.row {
margin-bottom: 10px;
}
.card .card-action .original {
line-height: 24px;
.card .card-action {
padding: 10px 5px 10px 15px;
}
.card .card-action ul.links {
@ -483,6 +540,11 @@ main ul.row {
color: #fff;
}
.card .card-action .reading-time {
display: inline-flex;
vertical-align: middle;
}
.quickstart .card .card-action a,
.quickstart .card .card-action a:hover {
color: #fff !important;
@ -499,7 +561,7 @@ main ul.row {
}
.card .card-image {
height: 14em;
height: 10em;
}
.card .card-image .preview {
@ -526,7 +588,8 @@ main ul.row {
}
#article > header > h1 {
font-size: 1.6em;
font-size: 2em;
margin: 2.1rem 0 0.68rem;
}
.reader-mode {
@ -565,9 +628,21 @@ main ul.row {
z-index: 9999;
}
#article aside .link {
color: #000;
#article aside .tools {
font-size: 0.8em;
display: flex;
flex-flow: row wrap;
margin: 0 auto;
}
#article aside .tools li {
display: inline-flex;
vertical-align: middle;
margin: auto 10px;
}
#article aside .tools a {
color: #000;
text-decoration: none;
}
@ -578,10 +653,11 @@ main ul.row {
#article aside .chip {
background-color: rgba(0, 151, 167, 0.85);
color: #fff;
padding: 0 15px 0 10px;
margin: auto 2px;
}
#article aside .chip a,
#article aside .chip i {
color: #fff;
}
@ -608,7 +684,6 @@ main ul.row {
}
#article {
padding: 15px;
max-width: 35em;
margin-left: auto;
margin-right: auto;
@ -652,7 +727,14 @@ main ul.row {
}
}
@media only screen and (min-width: 993px) and (max-width: 1180px) {
@media only screen and (min-width: 1200px) and (max-width: 1650px) {
.row .col.l3 {
width: 33.33333%;
margin-left: 0;
}
}
@media only screen and (min-width: 993px) and (max-width: 1200px) {
.row .col.l1 {
width: 25%;
margin-left: 0;
@ -708,6 +790,14 @@ main ul.row {
.nb-results {
display: none;
}
main ul.row {
padding: 0;
}
.row .col {
padding: 0;
}
}
/* ==========================================================================

View File

@ -1,9 +1,4 @@
# config valid only for current version of Capistrano
lock '3.4.0'
set :log_path, "var/logs"
set :cache_path, "var/cache"
set :symfony_console_path, 'bin/console'
set :application, 'wallabag'
set :repo_url, 'git@github.com:wallabag/wallabag.git'
@ -11,8 +6,6 @@ set :repo_url, 'git@github.com:wallabag/wallabag.git'
set :ssh_user, 'framasoft_bag'
server '78.46.248.87', user: fetch(:ssh_user), roles: %w{web app db}
set :scm, :git
set :format, :pretty
set :log_level, :info
# set :log_level, :debug
@ -23,4 +16,4 @@ set :linked_files, %w{app/config/parameters.yml}
set :linked_dirs, [fetch(:log_path), "var/sessions", "web/uploads", "data"]
set :keep_releases, 3
after 'deploy:finishing', 'deploy:cleanup'
after 'deploy:updated', 'symfony:cache:clear'

View File

@ -30,7 +30,7 @@ framework:
assets: ~
wallabag_core:
version: 2.1.2
version: 2.1.3
paypal_url: "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9UBA65LG3FX9Y&lc=gb"
languages:
en: 'English'
@ -44,12 +44,15 @@ wallabag_core:
es: 'Español'
oc: 'Occitan'
it: 'Italiano'
pt: 'Português'
items_on_page: 12
theme: material
language: '%locale%'
rss_limit: 50
reading_speed: 1
cache_lifetime: 10
fetching_error_message: |
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>.
wallabag_user:
registration_enabled: "%fosuser_registration%"

7769
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -264,7 +264,8 @@ Drittanbieter Ressourcen
Einige Applikationen oder Bibliotheken nutzen unsere API. Hier ist eine nicht abschließende Aufzählung von ihnen:
- `Java wrapper for the wallabag API <https://github.com/Strubbl/wallabag-java>`_ von Strubbl.
- `Java wrapper for the wallabag API <https://github.com/Strubbl/jWallabag>`_ von Strubbl.
- `.NET library for the wallabag v2 API <https://github.com/jlnostr/wallabag-api>`_ von Julian Oster.
- `Python API for wallabag <https://github.com/foxmask/wallabag_api>`_ von FoxMaSk, für sein Projekt `Trigger Happy <https://blog.trigger-happy.eu/>`_.
- `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.
- `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>`_.

View File

@ -37,7 +37,7 @@ RabbitMQ stoppen
Konfigure RabbitMQ in wallabag
------------------------------
Bearbeite die Datei ``parameters.yml``, um die RabbitMQ Konfiguration einzurichten. Die Standardkonfiguration sollte ok sein:
Bearbeite die Datei ``app/config/parameters.yml``, um die RabbitMQ Konfiguration einzurichten. Die Standardkonfiguration sollte ok sein:
.. code:: yaml

View File

@ -28,7 +28,7 @@ Der Redis Service läuft eventuell schon direkt nach der Installation. Falls nic
Konfigure Redis in wallabag
---------------------------
Bearbeite die Datei ``parameters.yml``, um die RabbitMQ Konfiguration einzurichten. Die Standardkonfiguration sollte ok sein:
Bearbeite die Datei ``app/config/parameters.yml``, um die RabbitMQ Konfiguration einzurichten. Die Standardkonfiguration sollte ok sein:
.. code:: yaml

View File

@ -45,6 +45,7 @@ Die Dokumentation ist in anderen Sprachen verfügbar :
user/tags
user/android
user/parameters
user/backup
.. _dev-docs:

25
docs/de/user/backup.rst Normal file
View File

@ -0,0 +1,25 @@
wallabag sichern
================
Da es manchmal vorkommen kann, dass dir ein Fehler mit deiner wallabag unterläuft und du Daten verlierst oder deine wallabag auf einen anderen Server verschieben willst, ist eine Sicherung der Daten sicher ratsam.
Dieser Artikel beschreibt, was du für die Sicherung benötigst.
Grundlegende Einstellungen
--------------------------
wallabag speichert grundlegende Parameter (etwa der SMTP-Server oder das Datenbank-Backend) in der Datei `app/config/parameters.yml`.
Datenbank
---------
Da wallabag verschiedene Datenbank-Typen unterstützt, hängt der Weg der Sicherung von dem verwendeten Typ ab. Daher verweisen wir an dieser Stelle auf die entsprechenden Dokumentationen:
Hier sind einige Beispiele:
- MySQL: http://dev.mysql.com/doc/refman/5.7/en/backup-methods.html
- PostgreSQL: https://www.postgresql.org/docs/current/static/backup.html
SQLite
~~~~~~
Um die SQLite-Datenbank zu sichern, ist es lediglich notwendig, das Verzeichnis `data/db` aus dem wallabag-Installations-Ordner zu kopieren.
Bilder
------
Die Bilder, die von wallabag empfangen worden, sind unter `data/assets/images` gespeichert (der Bilder-Speicher wird in wallabag 2.2 implementiert).

View File

@ -43,3 +43,10 @@ Ich habe mein Passwort vergessen
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
und du wirst eine E-Mail zum Passwort zurücksetzen erhalten.
Ich erhalte den Fehler ``failed to load external entity``, wenn ich wallabag installiere
----------------------------------------------------------------------------------------
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.
Dies ist ein Doctrine / PHP Fehler - nichts, woran wir etwas ändern können.

View File

@ -4,14 +4,14 @@ Wallabag von 2.1.x auf 2.1.y updaten
Update auf einem dedizierten Webserver
--------------------------------------
Das neueste Release ist auf https://www.wallabag.org/pages/download-wallabag.html veröffentlicht. Um deine wallabag Installation auf die neueste Version upzudaten, führe die folgenden Kommandos in deinem wallabag Ordner aus (ersetze ``2.1.2`` mit der neuesten Releasenummer):
Das neueste Release ist auf https://www.wallabag.org/pages/download-wallabag.html veröffentlicht. Um deine wallabag Installation auf die neueste Version upzudaten, führe die folgenden Kommandos in deinem wallabag Ordner aus (ersetze ``2.1.3`` mit der neuesten Releasenummer):
::
rm -rf var/cache/*
git fetch origin
git fetch --tags
git checkout 2.1.2 --force
git checkout 2.1.3 --force
SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist
php bin/console cache:clear --env=prod
@ -26,7 +26,7 @@ Lade das neueste Release von wallabag herunter:
wget http://wllbg.org/latest-v2-package && tar xvf latest-v2-package
(md5 Hashsumme von 2.1.1: ``9584a3b60a2b2a4de87f536548caac93``)
Du findest die `md5 Hashsumme des neuesten Pakets auf unserer Website <https://www.wallabag.org/pages/download-wallabag.html>`_.
Entpacke das Archiv in deinen wallabag Ordner und ersetze ``app/config/parameters.yml`` mit deiner Datei.

View File

@ -263,7 +263,8 @@ Third party resources
Some applications or libraries use our API. Here is a non-exhaustive list of them:
- `Java wrapper for the wallabag API <https://github.com/Strubbl/wallabag-java>`_ by Strubbl.
- `Java wrapper for the wallabag API <https://github.com/Strubbl/jWallabag>`_ by Strubbl.
- `.NET library for the wallabag v2 API <https://github.com/jlnostr/wallabag-api>`_ by Julian Oster.
- `Python API for wallabag <https://github.com/foxmask/wallabag_api>`_ by FoxMaSk, for his project `Trigger Happy <https://blog.trigger-happy.eu/>`_.
- `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.
- `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>`_.

View File

@ -37,7 +37,7 @@ Stop RabbitMQ
Configure RabbitMQ in wallabag
------------------------------
Edit your ``parameters.yml`` file to edit RabbitMQ configuration. The default one should be ok:
Edit your ``app/config/parameters.yml`` file to edit RabbitMQ configuration. The default one should be ok:
.. code:: yaml

View File

@ -28,7 +28,7 @@ The server might be already running after installing, if not you can launch it u
Configure Redis in wallabag
---------------------------
Edit your ``parameters.yml`` file to edit Redis configuration. The default one should be ok:
Edit your ``app/config/parameters.yml`` file to edit Redis configuration. The default one should be ok:
.. code:: yaml

View File

@ -46,6 +46,7 @@ The documentation is available in other languages:
user/tags
user/android
user/parameters
user/backup
.. _dev-docs:

View File

@ -79,7 +79,7 @@ Known limitations
2FA
~~~
Currently the does not support two-factor authentication. You should disable that to get the app working.
Currently the Android application does not support two-factor authentication. You should disable that to get the application working.
Limited amount of articles with wallabag v2

25
docs/en/user/backup.rst Normal file
View File

@ -0,0 +1,25 @@
Backup wallabag
===============
Because sometimes you may do a mistake with your wallabag and lose data or in case you need to move your wallabag to another server you want to backup your data.
This articles describes what you need to backup.
Basic settings
--------------
wallabag stores some basic parameters (like SMTP server or database backend) in the file `app/config/parameters.yml`.
Database
--------
As wallabag supports different kinds of database, the way to perform the backup depends on the database you use, so you need to refer to the vendor documentation.
Here's some examples:
- MySQL: http://dev.mysql.com/doc/refman/5.7/en/backup-methods.html
- PostgreSQL: https://www.postgresql.org/docs/current/static/backup.html
SQLite
~~~~~~
To backup the SQLite database, you just need to copy the directory `data/db` from the wallabag application directory.
Images
------
The images retrieved by wallabag are stored under `data/assets/images` (the images storage will be implemented in wallabag 2.2).

View File

@ -46,3 +46,10 @@ I forgot my password
You can reset your password by clicking on ``Forgot your password?`` link,
on the login page. Then, fill the form with your email address or your username,
you'll receive an email to reset your password.
I've got the ``failed to load external entity`` error when I try to install wallabag
------------------------------------------------------------------------------------
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.
This is a Doctrine / PHP bug, nothing we can do about it.

View File

@ -4,14 +4,14 @@ Upgrading from 2.1.x to 2.1.y
Upgrade on a dedicated web server
---------------------------------
The last release is published on https://www.wallabag.org/pages/download-wallabag.html. In order to upgrade your wallabag installation and get the last version, run the following commands in you wallabag folder (replace ``2.1.2`` by the last release number):
The last release is published on https://www.wallabag.org/pages/download-wallabag.html. In order to upgrade your wallabag installation and get the last version, run the following commands in you wallabag folder (replace ``2.1.3`` by the last release number):
::
rm -rf var/cache/*
git fetch origin
git fetch --tags
git checkout 2.1.2 --force
git checkout 2.1.3 --force
SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist
php bin/console cache:clear --env=prod
@ -26,7 +26,7 @@ Download the last release of wallabag:
wget http://wllbg.org/latest-v2-package && tar xvf latest-v2-package
(md5 hash of the 2.1.1 package: ``9584a3b60a2b2a4de87f536548caac93``)
You will find the `md5 hash of the latest package on our website <https://www.wallabag.org/pages/download-wallabag.html>`_.
Extract the archive in your wallabag folder and replace ``app/config/parameters.yml`` with yours.

View File

@ -263,7 +263,8 @@ Ressources tierces
Certaines applications ou bibliothèques utilisent notre API. En voici une liste non exhaustive :
- `Java wrapper for the wallabag API <https://github.com/Strubbl/wallabag-java>`_ par Strubbl.
- `Java wrapper for the wallabag API <https://github.com/Strubbl/jWallabag>`_ par Strubbl.
- `.NET library for the wallabag v2 API <https://github.com/jlnostr/wallabag-api>`_ par Julian Oster.
- `Python API for wallabag <https://github.com/foxmask/wallabag_api>`_ par FoxMaSk, pour son projet `Trigger Happy <https://blog.trigger-happy.eu/>`_.
- `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.
- `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>`_.

View File

@ -37,7 +37,7 @@ Arrêter RabbitMQ
Configurer RabbitMQ dans wallabag
---------------------------------
Modifiez votre fichier ``parameters.yml`` pour éditer la configuration RabbitMQ. Celle par défaut devrait convenir :
Modifiez votre fichier ``app/config/parameters.yml`` pour éditer la configuration RabbitMQ. Celle par défaut devrait convenir :
.. code:: yaml

View File

@ -28,7 +28,7 @@ Le serveur devrait déjà être démarré après l'installation. Si ce n'est pas
Configurer Redis dans wallabag
-------------------------------
Modifiez votre fichier ``parameters.yml`` pour éditer la configuration Redis. Celle par défaut devrait convenir :
Modifiez votre fichier ``app/config/parameters.yml`` pour éditer la configuration Redis. Celle par défaut devrait convenir :
.. code:: yaml

View File

@ -46,6 +46,7 @@ La documentation est disponible dans d'autres langues :
user/filters
user/tags
user/parameters
user/backup
.. _dev-docs:

26
docs/fr/user/backup.rst Normal file
View File

@ -0,0 +1,26 @@
Sauvegarde de wallabag
======================
Parce que des fois vous faites des erreurs avec votre installation de wallabag et vous perdez des données ou parce que vous souhaitez migrer votre installation sur un autre serveur, vous souhaitez faire une sauvegarde de vos données.
Cette documentation décrit ce que vous devez sauvegarder.
Configuration
-------------
wallabag stocke quelques paramètres (comme la configuration SMTP ou les infos de bases de données) dans le fichier `app/config/parameters.yml`.
Base de données
---------------
Comme wallabag supporte différentes sortes de bases de données, la manière de sauvegarder dépend du type de base de données que vous utilisez, donc vous devez vous en référez à la documentation correspondante.
Quelques exemples :
- MySQL: http://dev.mysql.com/doc/refman/5.7/en/backup-methods.html
- PostgreSQL: https://www.postgresql.org/docs/current/static/backup.html
SQLite
~~~~~~
Pour sauvegarder une base SQLite, vous devez juste copier le répertoire `data/db` de votre installation wallabag.
Images
------
Les images sauvegardées par wallabag sont stockées dans `data/assets/images` (le stockage des images sera implémenté dans wallabag 2.2).

View File

@ -33,3 +33,10 @@ J'ai oublié mon mot de passe
Vous pouvez réinitialiser votre mot de passe en cliquant sur ``Mot de passe oublié ?``,
sur la page de connexion. Ensuite, renseignez votre adresse email ou votre nom d'utilisateur,
un email vous sera envoyé.
J'ai l'erreur ``failed to load external entity`` quand j'essaie d'installer wallabag
------------------------------------------------------------------------------------
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.
C'est un bug lié à PHP et Doctrine, nous ne pouvons rien faire de notre côté.

View File

@ -4,14 +4,14 @@ Mettre à jour de la 2.1.x à la 2.1.y
Mise à jour sur un serveur dédié
--------------------------------
La dernière version de wallabag est publiée à cette adresse : https://www.wallabag.org/pages/download-wallabag.html. Pour mettre à jour votre installation de wallabag, exécutez les commandes suivantes dans votre répertoire d'installation (remplacez ``2.1.2`` par le numéro de la dernière version) :
La dernière version de wallabag est publiée à cette adresse : https://www.wallabag.org/pages/download-wallabag.html. Pour mettre à jour votre installation de wallabag, exécutez les commandes suivantes dans votre répertoire d'installation (remplacez ``2.1.3`` par le numéro de la dernière version) :
::
rm -rf var/cache/*
git fetch origin
git fetch --tags
git checkout 2.1.2 --force
git checkout 2.1.3 --force
SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist
php bin/console cache:clear --env=prod
@ -26,7 +26,7 @@ Téléchargez la dernière version de wallabag :
wget http://wllbg.org/latest-v2-package && tar xvf latest-v2-package
(hash md5 de l'archive 2.1.1 : ``9584a3b60a2b2a4de87f536548caac93``)
Vous trouverez `le hash md5 du dernier package sur notre site <https://www.wallabag.org/pages/download-wallabag.html>`_.
Décompressez l'archive dans votre répertoire d'installation et remplacez le fichier ``app/config/parameters.yml`` avec le votre.

View File

@ -1,7 +1,13 @@
#! /usr/bin/env bash
# You can execute this file to install wallabag dev environmnet
# eg: `sh install.sh prod`
#!/usr/bin/env bash
# You can execute this file to install wallabag dev environment
# eg: `sh dev.sh`
composer install
COMPOSER_COMMAND='composer'
DIR="${BASH_SOURCE}"
if [ ! -d "$DIR" ]; then DIR="$PWD/scripts"; fi
. "$DIR/require.sh"
$COMPOSER_COMMAND install
php bin/console wallabag:install
php bin/console server:run

View File

@ -1,10 +1,16 @@
#! /usr/bin/env bash
#!/usr/bin/env bash
# You can execute this file to install wallabag
# eg: `sh install.sh prod`
COMPOSER_COMMAND='composer'
DIR="${BASH_SOURCE}"
if [ ! -d "$DIR" ]; then DIR="$PWD/scripts"; fi
. "$DIR/require.sh"
ENV=$1
TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
git checkout $TAG
SYMFONY_ENV=$ENV composer install --no-dev -o --prefer-dist
SYMFONY_ENV=$ENV $COMPOSER_COMMAND install --no-dev -o --prefer-dist
php bin/console wallabag:install --env=$ENV

9
scripts/require.sh Normal file
View File

@ -0,0 +1,9 @@
#! /usr/bin/env bash
# File used to check dependencies
if [ ! -f composer.phar ]; then
echo "composer.phar not found, we'll see if composer is installed globally."
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; }
else
COMPOSER_COMMAND='composer.phar'
fi

View File

@ -1,7 +1,13 @@
#! /usr/bin/env bash
#!/usr/bin/env bash
# You can execute this file to update wallabag
# eg: `sh update.sh prod`
COMPOSER_COMMAND='composer'
DIR="${BASH_SOURCE}"
if [ ! -d "$DIR" ]; then DIR="$PWD/scripts"; fi
. "$DIR/require.sh"
ENV=$1
TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
@ -9,5 +15,5 @@ rm -rf var/cache/*
git fetch origin
git fetch --tags
git checkout $TAG --force
SYMFONY_ENV=$ENV composer install --no-dev -o --prefer-dist
SYMFONY_ENV=$ENV $COMPOSER_COMMAND install --no-dev -o --prefer-dist
php bin/console cache:clear --env=$ENV

View File

@ -0,0 +1,367 @@
<?php
namespace Wallabag\ApiBundle\Controller;
use Hateoas\Configuration\Route;
use Hateoas\Representation\Factory\PagerfantaFactory;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Entity\Tag;
class EntryRestController extends WallabagRestController
{
/**
* Check if an entry exist by url.
*
* @ApiDoc(
* parameters={
* {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"},
* {"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"}
* }
* )
*
* @return JsonResponse
*/
public function getEntriesExistsAction(Request $request)
{
$this->validateAuthentication();
$urls = $request->query->get('urls', []);
// handle multiple urls first
if (!empty($urls)) {
$results = [];
foreach ($urls as $url) {
$res = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->findByUrlAndUserId($url, $this->getUser()->getId());
$results[$url] = false === $res ? false : true;
}
$json = $this->get('serializer')->serialize($results, 'json');
return (new JsonResponse())->setJson($json);
}
// let's see if it is a simple url?
$url = $request->query->get('url', '');
if (empty($url)) {
throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId());
}
$res = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->findByUrlAndUserId($url, $this->getUser()->getId());
$exists = false === $res ? false : true;
$json = $this->get('serializer')->serialize(['exists' => $exists], 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Retrieve all entries. It could be filtered by many options.
*
* @ApiDoc(
* parameters={
* {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by archived status."},
* {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by starred status."},
* {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."},
* {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."},
* {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."},
* {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."},
* {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."},
* {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."},
* }
* )
*
* @return JsonResponse
*/
public function getEntriesAction(Request $request)
{
$this->validateAuthentication();
$isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive');
$isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred');
$sort = $request->query->get('sort', 'created');
$order = $request->query->get('order', 'desc');
$page = (int) $request->query->get('page', 1);
$perPage = (int) $request->query->get('perPage', 30);
$tags = $request->query->get('tags', '');
$since = $request->query->get('since', 0);
$pager = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags);
$pager->setCurrentPage($page);
$pager->setMaxPerPage($perPage);
$pagerfantaFactory = new PagerfantaFactory('page', 'perPage');
$paginatedCollection = $pagerfantaFactory->createRepresentation(
$pager,
new Route(
'api_get_entries',
[
'archive' => $isArchived,
'starred' => $isStarred,
'sort' => $sort,
'order' => $order,
'page' => $page,
'perPage' => $perPage,
'tags' => $tags,
'since' => $since,
],
UrlGeneratorInterface::ABSOLUTE_URL
)
);
$json = $this->get('serializer')->serialize($paginatedCollection, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Retrieve a single entry.
*
* @ApiDoc(
* requirements={
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* }
* )
*
* @return JsonResponse
*/
public function getEntryAction(Entry $entry)
{
$this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$json = $this->get('serializer')->serialize($entry, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Create an entry.
*
* @ApiDoc(
* parameters={
* {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."},
* {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."},
* {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
* {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"},
* {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"},
* }
* )
*
* @return JsonResponse
*/
public function postEntriesAction(Request $request)
{
$this->validateAuthentication();
$url = $request->request->get('url');
$title = $request->request->get('title');
$isArchived = $request->request->get('archive');
$isStarred = $request->request->get('starred');
$entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId());
if (false === $entry) {
$entry = $this->get('wallabag_core.content_proxy')->updateEntry(
new Entry($this->getUser()),
$url
);
}
if (!is_null($title)) {
$entry->setTitle($title);
}
$tags = $request->request->get('tags', '');
if (!empty($tags)) {
$this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
}
if (!is_null($isStarred)) {
$entry->setStarred((bool) $isStarred);
}
if (!is_null($isArchived)) {
$entry->setArchived((bool) $isArchived);
}
$em = $this->getDoctrine()->getManager();
$em->persist($entry);
$em->flush();
$json = $this->get('serializer')->serialize($entry, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Change several properties of an entry.
*
* @ApiDoc(
* requirements={
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* },
* parameters={
* {"name"="title", "dataType"="string", "required"=false},
* {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
* {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."},
* {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."},
* }
* )
*
* @return JsonResponse
*/
public function patchEntriesAction(Entry $entry, Request $request)
{
$this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$title = $request->request->get('title');
$isArchived = $request->request->get('archive');
$isStarred = $request->request->get('starred');
if (!is_null($title)) {
$entry->setTitle($title);
}
if (!is_null($isArchived)) {
$entry->setArchived((bool) $isArchived);
}
if (!is_null($isStarred)) {
$entry->setStarred((bool) $isStarred);
}
$tags = $request->request->get('tags', '');
if (!empty($tags)) {
$this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
}
$em = $this->getDoctrine()->getManager();
$em->flush();
$json = $this->get('serializer')->serialize($entry, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Delete **permanently** an entry.
*
* @ApiDoc(
* requirements={
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* }
* )
*
* @return JsonResponse
*/
public function deleteEntriesAction(Entry $entry)
{
$this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$em = $this->getDoctrine()->getManager();
$em->remove($entry);
$em->flush();
$json = $this->get('serializer')->serialize($entry, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Retrieve all tags for an entry.
*
* @ApiDoc(
* requirements={
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* }
* )
*
* @return JsonResponse
*/
public function getEntriesTagsAction(Entry $entry)
{
$this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$json = $this->get('serializer')->serialize($entry->getTags(), 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Add one or more tags to an entry.
*
* @ApiDoc(
* requirements={
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* },
* parameters={
* {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
* }
* )
*
* @return JsonResponse
*/
public function postEntriesTagsAction(Request $request, Entry $entry)
{
$this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$tags = $request->request->get('tags', '');
if (!empty($tags)) {
$this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
}
$em = $this->getDoctrine()->getManager();
$em->persist($entry);
$em->flush();
$json = $this->get('serializer')->serialize($entry, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Permanently remove one tag for an entry.
*
* @ApiDoc(
* requirements={
* {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"},
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* }
* )
*
* @return JsonResponse
*/
public function deleteEntriesTagsAction(Entry $entry, Tag $tag)
{
$this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$entry->removeTag($tag);
$em = $this->getDoctrine()->getManager();
$em->persist($entry);
$em->flush();
$json = $this->get('serializer')->serialize($entry, 'json');
return (new JsonResponse())->setJson($json);
}
}

View File

@ -0,0 +1,171 @@
<?php
namespace Wallabag\ApiBundle\Controller;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Entity\Tag;
class TagRestController extends WallabagRestController
{
/**
* Retrieve all tags.
*
* @ApiDoc()
*
* @return JsonResponse
*/
public function getTagsAction()
{
$this->validateAuthentication();
$tags = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Tag')
->findAllTags($this->getUser()->getId());
$json = $this->get('serializer')->serialize($tags, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Permanently remove one tag from **every** entry.
*
* @ApiDoc(
* requirements={
* {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"}
* }
* )
*
* @return JsonResponse
*/
public function deleteTagLabelAction(Request $request)
{
$this->validateAuthentication();
$label = $request->request->get('tag', '');
$tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label);
if (empty($tag)) {
throw $this->createNotFoundException('Tag not found');
}
$this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->removeTag($this->getUser()->getId(), $tag);
$this->cleanOrphanTag($tag);
$json = $this->get('serializer')->serialize($tag, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Permanently remove some tags from **every** entry.
*
* @ApiDoc(
* requirements={
* {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"}
* }
* )
*
* @return JsonResponse
*/
public function deleteTagsLabelAction(Request $request)
{
$this->validateAuthentication();
$tagsLabels = $request->request->get('tags', '');
$tags = [];
foreach (explode(',', $tagsLabels) as $tagLabel) {
$tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel);
if (!empty($tagEntity)) {
$tags[] = $tagEntity;
}
}
if (empty($tags)) {
throw $this->createNotFoundException('Tags not found');
}
$this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->removeTags($this->getUser()->getId(), $tags);
$this->cleanOrphanTag($tags);
$json = $this->get('serializer')->serialize($tags, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Permanently remove one tag from **every** entry.
*
* @ApiDoc(
* requirements={
* {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"}
* }
* )
*
* @return JsonResponse
*/
public function deleteTagAction(Tag $tag)
{
$this->validateAuthentication();
$this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->removeTag($this->getUser()->getId(), $tag);
$this->cleanOrphanTag($tag);
$json = $this->get('serializer')->serialize($tag, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Retrieve version number.
*
* @ApiDoc()
*
* @return JsonResponse
*/
public function getVersionAction()
{
$version = $this->container->getParameter('wallabag_core.version');
$json = $this->get('serializer')->serialize($version, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Remove orphan tag in case no entries are associated to it.
*
* @param Tag|array $tags
*/
private function cleanOrphanTag($tags)
{
if (!is_array($tags)) {
$tags = [$tags];
}
$em = $this->getDoctrine()->getManager();
foreach ($tags as $tag) {
if (count($tag->getEntries()) === 0) {
$em->remove($tag);
}
}
$em->flush();
}
}

View File

@ -3,543 +3,25 @@
namespace Wallabag\ApiBundle\Controller;
use FOS\RestBundle\Controller\FOSRestController;
use Hateoas\Configuration\Route;
use Hateoas\Representation\Factory\PagerfantaFactory;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Entity\Tag;
class WallabagRestController extends FOSRestController
{
private function validateAuthentication()
protected function validateAuthentication()
{
if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
throw new AccessDeniedException();
}
}
/**
* Check if an entry exist by url.
*
* @ApiDoc(
* parameters={
* {"name"="url", "dataType"="string", "required"=true, "format"="An url", "description"="Url to check if it exists"},
* {"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"}
* }
* )
*
* @return JsonResponse
*/
public function getEntriesExistsAction(Request $request)
{
$this->validateAuthentication();
$urls = $request->query->get('urls', []);
// handle multiple urls first
if (!empty($urls)) {
$results = [];
foreach ($urls as $url) {
$res = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->findByUrlAndUserId($url, $this->getUser()->getId());
$results[$url] = false === $res ? false : true;
}
$json = $this->get('serializer')->serialize($results, 'json');
return (new JsonResponse())->setJson($json);
}
// let's see if it is a simple url?
$url = $request->query->get('url', '');
if (empty($url)) {
throw $this->createAccessDeniedException('URL is empty?, logged user id: '.$this->getUser()->getId());
}
$res = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->findByUrlAndUserId($url, $this->getUser()->getId());
$exists = false === $res ? false : true;
$json = $this->get('serializer')->serialize(['exists' => $exists], 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Retrieve all entries. It could be filtered by many options.
*
* @ApiDoc(
* parameters={
* {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by archived status."},
* {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by starred status."},
* {"name"="sort", "dataType"="string", "required"=false, "format"="'created' or 'updated', default 'created'", "description"="sort entries by date."},
* {"name"="order", "dataType"="string", "required"=false, "format"="'asc' or 'desc', default 'desc'", "description"="order of sort."},
* {"name"="page", "dataType"="integer", "required"=false, "format"="default '1'", "description"="what page you want."},
* {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."},
* {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."},
* {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."},
* }
* )
*
* @return JsonResponse
*/
public function getEntriesAction(Request $request)
{
$this->validateAuthentication();
$isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive');
$isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred');
$sort = $request->query->get('sort', 'created');
$order = $request->query->get('order', 'desc');
$page = (int) $request->query->get('page', 1);
$perPage = (int) $request->query->get('perPage', 30);
$tags = $request->query->get('tags', '');
$since = $request->query->get('since', 0);
$pager = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags);
$pager->setCurrentPage($page);
$pager->setMaxPerPage($perPage);
$pagerfantaFactory = new PagerfantaFactory('page', 'perPage');
$paginatedCollection = $pagerfantaFactory->createRepresentation(
$pager,
new Route(
'api_get_entries',
[
'archive' => $isArchived,
'starred' => $isStarred,
'sort' => $sort,
'order' => $order,
'page' => $page,
'perPage' => $perPage,
'tags' => $tags,
'since' => $since,
],
UrlGeneratorInterface::ABSOLUTE_URL
)
);
$json = $this->get('serializer')->serialize($paginatedCollection, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Retrieve a single entry.
*
* @ApiDoc(
* requirements={
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* }
* )
*
* @return JsonResponse
*/
public function getEntryAction(Entry $entry)
{
$this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$json = $this->get('serializer')->serialize($entry, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Create an entry.
*
* @ApiDoc(
* parameters={
* {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."},
* {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."},
* {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
* {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"},
* {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"},
* }
* )
*
* @return JsonResponse
*/
public function postEntriesAction(Request $request)
{
$this->validateAuthentication();
$url = $request->request->get('url');
$title = $request->request->get('title');
$isArchived = $request->request->get('archive');
$isStarred = $request->request->get('starred');
$entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId());
if (false === $entry) {
$entry = $this->get('wallabag_core.content_proxy')->updateEntry(
new Entry($this->getUser()),
$url
);
}
if (!is_null($title)) {
$entry->setTitle($title);
}
$tags = $request->request->get('tags', '');
if (!empty($tags)) {
$this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
}
if (!is_null($isStarred)) {
$entry->setStarred((bool) $isStarred);
}
if (!is_null($isArchived)) {
$entry->setArchived((bool) $isArchived);
}
$em = $this->getDoctrine()->getManager();
$em->persist($entry);
$em->flush();
$json = $this->get('serializer')->serialize($entry, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Change several properties of an entry.
*
* @ApiDoc(
* requirements={
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* },
* parameters={
* {"name"="title", "dataType"="string", "required"=false},
* {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
* {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."},
* {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."},
* }
* )
*
* @return JsonResponse
*/
public function patchEntriesAction(Entry $entry, Request $request)
{
$this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$title = $request->request->get('title');
$isArchived = $request->request->get('archive');
$isStarred = $request->request->get('starred');
if (!is_null($title)) {
$entry->setTitle($title);
}
if (!is_null($isArchived)) {
$entry->setArchived((bool) $isArchived);
}
if (!is_null($isStarred)) {
$entry->setStarred((bool) $isStarred);
}
$tags = $request->request->get('tags', '');
if (!empty($tags)) {
$this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
}
$em = $this->getDoctrine()->getManager();
$em->flush();
$json = $this->get('serializer')->serialize($entry, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Delete **permanently** an entry.
*
* @ApiDoc(
* requirements={
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* }
* )
*
* @return JsonResponse
*/
public function deleteEntriesAction(Entry $entry)
{
$this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$em = $this->getDoctrine()->getManager();
$em->remove($entry);
$em->flush();
$json = $this->get('serializer')->serialize($entry, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Retrieve all tags for an entry.
*
* @ApiDoc(
* requirements={
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* }
* )
*
* @return JsonResponse
*/
public function getEntriesTagsAction(Entry $entry)
{
$this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$json = $this->get('serializer')->serialize($entry->getTags(), 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Add one or more tags to an entry.
*
* @ApiDoc(
* requirements={
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* },
* parameters={
* {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
* }
* )
*
* @return JsonResponse
*/
public function postEntriesTagsAction(Request $request, Entry $entry)
{
$this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$tags = $request->request->get('tags', '');
if (!empty($tags)) {
$this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags);
}
$em = $this->getDoctrine()->getManager();
$em->persist($entry);
$em->flush();
$json = $this->get('serializer')->serialize($entry, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Permanently remove one tag for an entry.
*
* @ApiDoc(
* requirements={
* {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag ID"},
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* }
* )
*
* @return JsonResponse
*/
public function deleteEntriesTagsAction(Entry $entry, Tag $tag)
{
$this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$entry->removeTag($tag);
$em = $this->getDoctrine()->getManager();
$em->persist($entry);
$em->flush();
$json = $this->get('serializer')->serialize($entry, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Retrieve all tags.
*
* @ApiDoc()
*
* @return JsonResponse
*/
public function getTagsAction()
{
$this->validateAuthentication();
$tags = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Tag')
->findAllTags($this->getUser()->getId());
$json = $this->get('serializer')->serialize($tags, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Permanently remove one tag from **every** entry.
*
* @ApiDoc(
* requirements={
* {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"}
* }
* )
*
* @return JsonResponse
*/
public function deleteTagLabelAction(Request $request)
{
$this->validateAuthentication();
$label = $request->request->get('tag', '');
$tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label);
if (empty($tag)) {
throw $this->createNotFoundException('Tag not found');
}
$this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->removeTag($this->getUser()->getId(), $tag);
$this->cleanOrphanTag($tag);
$json = $this->get('serializer')->serialize($tag, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Permanently remove some tags from **every** entry.
*
* @ApiDoc(
* requirements={
* {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"}
* }
* )
*
* @return JsonResponse
*/
public function deleteTagsLabelAction(Request $request)
{
$this->validateAuthentication();
$tagsLabels = $request->request->get('tags', '');
$tags = [];
foreach (explode(',', $tagsLabels) as $tagLabel) {
$tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel);
if (!empty($tagEntity)) {
$tags[] = $tagEntity;
}
}
if (empty($tags)) {
throw $this->createNotFoundException('Tags not found');
}
$this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->removeTags($this->getUser()->getId(), $tags);
$this->cleanOrphanTag($tags);
$json = $this->get('serializer')->serialize($tags, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Permanently remove one tag from **every** entry.
*
* @ApiDoc(
* requirements={
* {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"}
* }
* )
*
* @return JsonResponse
*/
public function deleteTagAction(Tag $tag)
{
$this->validateAuthentication();
$this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->removeTag($this->getUser()->getId(), $tag);
$this->cleanOrphanTag($tag);
$json = $this->get('serializer')->serialize($tag, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Retrieve version number.
*
* @ApiDoc()
*
* @return JsonResponse
*/
public function getVersionAction()
{
$version = $this->container->getParameter('wallabag_core.version');
$json = $this->get('serializer')->serialize($version, 'json');
return (new JsonResponse())->setJson($json);
}
/**
* Remove orphan tag in case no entries are associated to it.
*
* @param Tag|array $tags
*/
private function cleanOrphanTag($tags)
{
if (!is_array($tags)) {
$tags = [$tags];
}
$em = $this->getDoctrine()->getManager();
foreach ($tags as $tag) {
if (count($tag->getEntries()) === 0) {
$em->remove($tag);
}
}
$em->flush();
}
/**
* Validate that the first id is equal to the second one.
* If not, throw exception. It means a user try to access information from an other user.
*
* @param int $requestUserId User id from the requested source
*/
private function validateUserAccess($requestUserId)
protected function validateUserAccess($requestUserId)
{
$user = $this->get('security.token_storage')->getToken()->getUser();
if ($requestUserId != $user->getId()) {

View File

@ -1,4 +1,9 @@
entries:
type: rest
resource: "WallabagApiBundle:WallabagRest"
resource: "WallabagApiBundle:EntryRest"
name_prefix: api_
tags:
type: rest
resource: "WallabagApiBundle:TagRest"
name_prefix: api_

View File

@ -364,7 +364,7 @@ class InstallCommand extends ContainerAwareCommand
],
[
'name' => 'piwik_host',
'value' => 'http://v2.wallabag.org',
'value' => 'v2.wallabag.org',
'section' => 'analytics',
],
[

View File

@ -330,6 +330,15 @@ class EntryController extends Controller
$this->updateEntry($entry, 'entry_reloaded');
// if refreshing entry failed, don't save it
if ($this->getParameter('wallabag_core.fetching_error_message') === $entry->getContent()) {
$bag = $this->get('session')->getFlashBag();
$bag->clear();
$bag->add('notice', 'flashes.entry.notice.entry_reloaded_failed');
return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()]));
}
$em = $this->getDoctrine()->getManager();
$em->persist($entry);
$em->flush();

View File

@ -4,8 +4,10 @@ namespace Wallabag\CoreBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Entity\Tag;
/**
* The try/catch can be removed once all formats will be implemented.
@ -51,15 +53,24 @@ class ExportController extends Controller
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function downloadEntriesAction($format, $category)
public function downloadEntriesAction(Request $request, $format, $category)
{
$method = ucfirst($category);
$methodBuilder = 'getBuilderFor'.$method.'ByUser';
$entries = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->$methodBuilder($this->getUser()->getId())
->getQuery()
->getResult();
if ($category == 'tag_entries') {
$tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneBySlug($request->query->get('tag'));
$entries = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->findAllByTagId($this->getUser()->getId(), $tag->getId());
} else {
$entries = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->$methodBuilder($this->getUser()->getId())
->getQuery()
->getResult();
}
try {
return $this->get('wallabag_core.helper.entries_export')

View File

@ -143,6 +143,7 @@ class TagController extends Controller
'form' => null,
'entries' => $entries,
'currentPage' => $page,
'tag' => $tag->getLabel(),
]);
}
}

View File

@ -122,7 +122,7 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface
],
[
'name' => 'piwik_host',
'value' => 'http://v2.wallabag.org',
'value' => 'v2.wallabag.org',
'section' => 'analytics',
],
[

View File

@ -39,6 +39,8 @@ class Configuration implements ConfigurationInterface
->integerNode('cache_lifetime')
->defaultValue(10)
->end()
->scalarNode('fetching_error_message')
->end()
->end()
;

View File

@ -23,6 +23,7 @@ class WallabagCoreExtension extends Extension
$container->setParameter('wallabag_core.version', $config['version']);
$container->setParameter('wallabag_core.paypal_url', $config['paypal_url']);
$container->setParameter('wallabag_core.cache_lifetime', $config['cache_lifetime']);
$container->setParameter('wallabag_core.fetching_error_message', $config['fetching_error_message']);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');

View File

@ -8,7 +8,6 @@ use JMS\Serializer\SerializerBuilder;
use PHPePub\Core\EPub;
use PHPePub\Core\Structure\OPF\DublinCore;
use Symfony\Component\HttpFoundation\Response;
use Craue\ConfigBundle\Util\Config;
/**
* This class doesn't have unit test BUT it's fully covered by a functional test with ExportControllerTest.
@ -27,12 +26,12 @@ class EntriesExport
</div>';
/**
* @param Config $craueConfig CraueConfig instance to get wallabag instance url from database
* @param string $wallabagUrl Wallabag instance url
* @param string $logoPath Path to the logo FROM THE BUNDLE SCOPE
*/
public function __construct(Config $craueConfig, $logoPath)
public function __construct($wallabagUrl, $logoPath)
{
$this->wallabagUrl = $craueConfig->get('wallabag_url');
$this->wallabagUrl = $wallabagUrl;
$this->logoPath = $logoPath;
}

View File

@ -22,7 +22,7 @@ class EntryRepository extends EntityRepository
return $this->createQueryBuilder('e')
->leftJoin('e.user', 'u')
->andWhere('u.id = :userId')->setParameter('userId', $userId)
->orderBy('e.id', 'desc')
->orderBy('e.createdAt', 'desc')
;
}

View File

@ -40,7 +40,7 @@ services:
class: Graby\Graby
arguments:
-
error_message: "wallabag can't retrieve contents for this article. Please report this issue to us."
error_message: '%wallabag_core.fetching_error_message%'
http_client:
user_agents:
'lifehacker.com': 'PHP/5.2'
@ -55,6 +55,7 @@ services:
'.fok.nl': 'Googlebot/2.1'
'getpocket.com': 'PHP/5.2'
'iansommerville.com': 'PHP/5.2'
'.slashdot.org': 'PHP/5.2'
calls:
- [ setLogger, [ "@logger" ] ]
tags:
@ -91,7 +92,7 @@ services:
wallabag_core.helper.entries_export:
class: Wallabag\CoreBundle\Helper\EntriesExport
arguments:
- "@craue_config"
- '@=service(''craue_config'').get(''wallabag_url'')'
- src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-152.png
wallabag.operator.array.matches:

View File

@ -137,7 +137,7 @@ entry:
# starred: 'Starred entries'
# archived: 'Archived entries'
# filtered: 'Filtered entries'
# filtered_tags: 'Filtered by tags'
# filtered_tags: 'Filtered by tags:'
# untagged: 'Untagged entries'
list:
# number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
@ -445,6 +445,9 @@ user:
# delete_confirm: Are you sure?
# back_to_list: Back to list
error:
# page_title: An error occurred
flashes:
config:
notice:

View File

@ -137,7 +137,7 @@ entry:
starred: 'Favorisierte Einträge'
archived: 'Archivierte Einträge'
filtered: 'Gefilterte Einträge'
filtered_tags: 'Gefiltert nach Tags'
filtered_tags: 'Gefiltert nach Tags:'
untagged: 'Nicht getaggte Einträge'
list:
number_on_the_page: '{0} Es gibt keine Einträge.|{1} Es gibt einen Eintrag.|]1,Inf[ Es gibt %count% Einträge.'
@ -445,6 +445,9 @@ user:
delete_confirm: Bist du sicher?
back_to_list: Zurück zur Liste
error:
# page_title: An error occurred
flashes:
config:
notice:

View File

@ -137,7 +137,7 @@ entry:
starred: 'Starred entries'
archived: 'Archived entries'
filtered: 'Filtered entries'
filtered_tags: 'Filtered by tags'
filtered_tags: 'Filtered by tags:'
untagged: 'Untagged entries'
list:
number_on_the_page: '{0} There are no entries.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
@ -445,6 +445,9 @@ user:
delete_confirm: Are you sure?
back_to_list: Back to list
error:
page_title: An error occurred
flashes:
config:
notice:

View File

@ -137,7 +137,7 @@ entry:
starred: 'Artículos favoritos'
archived: 'Artículos archivados'
filtered: 'Artículos filtrados'
# filtered_tags: 'Filtered by tags'
# filtered_tags: 'Filtered by tags:'
# untagged: 'Untagged entries'
list:
number_on_the_page: '{0} No hay artículos.|{1} Hay un artículo.|]1,Inf[ Hay %count% artículos.'
@ -445,6 +445,9 @@ user:
# delete_confirm: Are you sure?
# back_to_list: Back to list
error:
# page_title: An error occurred
flashes:
config:
notice:

View File

@ -137,7 +137,7 @@ entry:
starred: 'مقاله‌های برگزیده'
archived: 'مقاله‌های بایگانی‌شده'
filtered: 'مقاله‌های فیلترشده'
# filtered_tags: 'Filtered by tags'
# filtered_tags: 'Filtered by tags:'
# untagged: 'Untagged entries'
list:
number_on_the_page: '{0} هیج مقاله‌ای نیست.|{1} یک مقاله هست.|]1,Inf[ %count% مقاله هست.'
@ -444,6 +444,9 @@ user:
# delete_confirm: Are you sure?
# back_to_list: Back to list
error:
# page_title: An error occurred
flashes:
config:
notice:

View File

@ -1,422 +1,422 @@
security:
login:
page_title: 'Bienvenue sur wallabag !'
keep_logged_in: 'Rester connecté'
forgot_password: 'Mot de passe oublié ?'
submit: 'Se connecter'
register: 'Créer un compte'
username: "Nom d'utilisateur"
password: 'Mot de passe'
cancel: 'Annuler'
page_title: "Bienvenue sur wallabag !"
keep_logged_in: "Rester connecté"
forgot_password: "Mot de passe oublié ?"
submit: "Se connecter"
register: "Créer un compte"
username: "Nom dutilisateur"
password: "Mot de passe"
cancel: "Annuler"
resetting:
description: "Saisissez votre adresse e-mail ci-dessous, nous vous enverrons les instructions pour réinitialiser votre mot de passe."
description: "Saisissez votre adresse courriel ci-dessous, nous vous enverrons les instructions pour réinitialiser votre mot de passe."
register:
page_title: 'Se créer un compte'
go_to_account: 'Aller sur votre compte'
page_title: "Se créer un compte"
go_to_account: "Aller sur votre compte"
menu:
left:
unread: 'Non lus'
starred: 'Favoris'
archive: 'Lus'
all_articles: 'Tous les articles'
config: 'Configuration'
tags: 'Tags'
internal_settings: 'Configuration interne'
import: 'Importer'
howto: 'Aide'
developer: 'Développeur'
logout: 'Déconnexion'
about: 'À propos'
search: 'Recherche'
save_link: 'Sauvegarder un nouvel article'
back_to_unread: 'Retour aux articles non lus'
users_management: 'Gestion des utilisateurs'
unread: "Non lus"
starred: "Favoris"
archive: "Lus"
all_articles: "Tous les articles"
config: "Configuration"
tags: "Tags"
internal_settings: "Configuration interne"
import: "Importer"
howto: "Aide"
developer: "Développeur"
logout: "Déconnexion"
about: "À propos"
search: "Recherche"
save_link: "Sauvegarder un nouvel article"
back_to_unread: "Retour aux articles non lus"
users_management: "Gestion des utilisateurs"
top:
add_new_entry: 'Sauvegarder un nouvel article'
search: 'Rechercher'
filter_entries: 'Filtrer les articles'
export: 'Exporter'
add_new_entry: "Sauvegarder un nouvel article"
search: "Rechercher"
filter_entries: "Filtrer les articles"
export: "Exporter"
search_form:
input_label: 'Saisissez votre terme de recherche'
input_label: "Saisissez votre terme de recherche"
footer:
wallabag:
elsewhere: 'Emportez wallabag avec vous'
social: 'Social'
powered_by: 'propulsé par'
about: 'À propos'
elsewhere: "Emportez wallabag avec vous"
social: "Social"
powered_by: "propulsé par"
about: "À propos"
stats: Depuis le %user_creation% vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour !
config:
page_title: 'Configuration'
page_title: "Configuration"
tab_menu:
settings: 'Paramètres'
rss: 'RSS'
user_info: 'Mon compte'
password: 'Mot de passe'
rules: 'Règles de tag automatiques'
new_user: 'Créer un compte'
settings: "Paramètres"
rss: "RSS"
user_info: "Mon compte"
password: "Mot de passe"
rules: "Règles de tag automatiques"
new_user: "Créer un compte"
form:
save: 'Enregistrer'
save: "Enregistrer"
form_settings:
theme_label: 'Thème'
items_per_page_label: "Nombre d'articles par page"
language_label: 'Langue'
theme_label: "Thème"
items_per_page_label: "Nombre darticles par page"
language_label: "Langue"
reading_speed:
label: 'Vitesse de lecture'
help_message: 'Vous pouvez utiliser un outil en ligne pour estimer votre vitesse de lecture :'
100_word: 'Je lis environ 100 mots par minute'
200_word: 'Je lis environ 200 mots par minute'
300_word: 'Je lis environ 300 mots par minute'
400_word: 'Je lis environ 400 mots par minute'
pocket_consumer_key_label: Clé d'authentification Pocket pour importer les données
label: "Vitesse de lecture"
help_message: "Vous pouvez utiliser un outil en ligne pour estimer votre vitesse de lecture :"
100_word: "Je lis environ 100 mots par minute"
200_word: "Je lis environ 200 mots par minute"
300_word: "Je lis environ 300 mots par minute"
400_word: "Je lis environ 400 mots par minute"
pocket_consumer_key_label: Clé dauthentification Pocket pour importer les données
form_rss:
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."
token_label: 'Jeton RSS'
no_token: 'Aucun jeton généré'
token_create: 'Créez votre jeton'
token_reset: 'Réinitialisez votre jeton'
rss_links: 'URL de vos flux RSS'
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 dabord créer un jeton."
token_label: "Jeton RSS"
no_token: "Aucun jeton généré"
token_create: "Créez votre jeton"
token_reset: "Réinitialisez votre jeton"
rss_links: "Adresse de vos flux RSS"
rss_link:
unread: 'non lus'
starred: 'favoris'
archive: 'lus'
rss_limit: "Nombre d'articles dans le flux"
unread: "non lus"
starred: "favoris"
archive: "lus"
rss_limit: "Nombre darticles dans le flux"
form_user:
two_factor_description: "Activer l'authentification double-facteur veut dire que vous allez recevoir un code par email à chaque nouvelle connexion non approuvée."
name_label: 'Nom'
email_label: 'Adresse e-mail'
twoFactorAuthentication_label: 'Double authentification'
two_factor_description: "Activer lauthentification double-facteur veut dire que vous allez recevoir un code par courriel à chaque nouvelle connexion non approuvée."
name_label: "Nom"
email_label: "Adresse courriel"
twoFactorAuthentication_label: "Double authentification"
form_password:
old_password_label: 'Mot de passe actuel'
new_password_label: 'Nouveau mot de passe'
repeat_new_password_label: 'Confirmez votre nouveau mot de passe'
old_password_label: "Mot de passe actuel"
new_password_label: "Nouveau mot de passe"
repeat_new_password_label: "Confirmez votre nouveau mot de passe"
form_rules:
if_label: 'si'
then_tag_as_label: 'alors attribuer les tags'
delete_rule_label: 'supprimer'
edit_rule_label: 'éditer'
rule_label: 'Règle'
tags_label: 'Tags'
if_label: "si"
then_tag_as_label: "alors attribuer les tags"
delete_rule_label: "supprimer"
edit_rule_label: "éditer"
rule_label: "Règle"
tags_label: "Tags"
faq:
title: 'FAQ'
tagging_rules_definition_title: 'Que signifient les règles de tag automatiques ?'
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."
how_to_use_them_title: 'Comment les utiliser ?'
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 &lt;= 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 &gt;= 5 AND domainName = \"github.com\"</i> » alors attribuer les tags « <i>lecture longue, github </i> »'
variables_available_title: 'Quelles variables et opérateurs puis-je utiliser pour écrire des règles ?'
variables_available_description: 'Les variables et opérateurs suivants peuvent être utilisés pour écrire des règles de tag automatiques :'
meaning: 'Signification'
title: "FAQ"
tagging_rules_definition_title: "Que signifient les règles de tag automatiques ?"
tagging_rules_definition_description: "Ce sont des règles utilisées par wallabag pour classer automatiquement vos nouveaux articles.<br />À chaque fois quun nouvel article est ajouté, toutes les règles de tag automatiques seront utilisées afin dajouter les tags que vous avez configurés, vous épargnant ainsi leffort de classifier vos articles manuellement."
how_to_use_them_title: "Comment les utiliser ?"
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 &lt;= 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 &gt;= 5 AND domainName = \"github.com\"</i> » alors attribuer les tags « <i>lecture longue, github</i> »"
variables_available_title: "Quelles variables et opérateurs puis-je utiliser pour écrire des règles ?"
variables_available_description: "Les variables et opérateurs suivants peuvent être utilisés pour écrire des règles de tag automatiques :"
meaning: "Signification"
variable_description:
label: 'Variable'
title: "Titre de l'article"
url: "URL de l'article"
isArchived: "Si l'article est archivé ou non"
isStarred: "Si l'article est favori ou non"
content: "Le contenu de l'article"
language: "La langue de l'article"
mimetype: "Le type MIME de l'article"
readingTime: "Le temps de lecture estimé de l'article, en minutes"
domainName: "Le nom de domaine de l'article"
label: "Variable"
title: "Titre de larticle"
url: "Adresse de larticle"
isArchived: "Si larticle est archivé ou non"
isStarred: "Si larticle est favori ou non"
content: "Le contenu de larticle"
language: "La langue de larticle"
mimetype: "Le type MIME de larticle"
readingTime: "Le temps de lecture estimé de larticle, en minutes"
domainName: "Le nom de domaine de larticle"
operator_description:
label: 'Opérateur'
less_than: 'Moins que…...'
strictly_less_than: 'Strictement moins que…'
greater_than: 'Plus que…'
strictly_greater_than: 'Strictement plus que…'
equal_to: 'Égal à…'
not_equal_to: 'Différent de…'
or: "Une règle OU l'autre"
and: "Une règle ET l'autre"
matches: 'Teste si un <i>sujet</i> correspond à une <i>recherche</i> (non sensible à la casse).<br />Exemple : <code>title matches "football"</code>'
label: "Opérateur"
less_than: "Moins que…..."
strictly_less_than: "Strictement moins que…"
greater_than: "Plus que…"
strictly_greater_than: "Strictement plus que…"
equal_to: "Égal à…"
not_equal_to: "Différent de…"
or: "Une règle OU lautre"
and: "Une règle ET lautre"
matches: "Teste si un <i>sujet</i> correspond à une <i>recherche</i> (non sensible à la casse).<br />Exemple : <code>title matches \"football\"</code>"
entry:
page_titles:
unread: 'Articles non lus'
starred: 'Articles favoris'
archived: 'Articles lus'
filtered: 'Articles filtrés'
filtered_tags: 'Articles filtrés par tags'
untagged: 'Article sans tag'
unread: "Articles non lus"
starred: "Articles favoris"
archived: "Articles lus"
filtered: "Articles filtrés"
filtered_tags: "Articles filtrés par tags :"
untagged: "Article sans tag"
list:
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."
reading_time: 'durée de lecture'
reading_time_minutes: 'durée de lecture: %readingTime% min'
reading_time_less_one_minute: 'durée de lecture: <small class="inferieur">&lt;</small> 1 min'
number_of_tags: '{1}et un autre tag|]1,Inf[et %count% autres tags'
reading_time_minutes_short: '%readingTime% min'
reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min'
original_article: 'original'
toogle_as_read: 'Marquer comme lu/non lu'
toogle_as_star: 'Marquer comme favori'
delete: 'Supprimer'
export_title: 'Exporter'
number_on_the_page: "{0} Il ny a pas darticles.|{1} Il y a un article.|]1,Inf[ Il y a %count% articles."
reading_time: "durée de lecture"
reading_time_minutes: "durée de lecture: %readingTime% min"
reading_time_less_one_minute: "durée de lecture: <small class=\"inferieur\">&lt;</small> 1 min"
number_of_tags: "{1}et un autre tag|]1,Inf[et %count% autres tags"
reading_time_minutes_short: "%readingTime% min"
reading_time_less_one_minute_short: "<small class=\"inferieur\">&lt;</small> 1 min"
original_article: "original"
toogle_as_read: "Marquer comme lu/non lu"
toogle_as_star: "Marquer comme favori"
delete: "Supprimer"
export_title: "Exporter"
filters:
title: 'Filtres'
status_label: 'Status'
archived_label: 'Lus'
starred_label: 'Favoris'
unread_label: 'Non lus'
preview_picture_label: 'A une photo'
preview_picture_help: 'Photo'
language_label: 'Langue'
title: "Filtres"
status_label: "Status"
archived_label: "Lus"
starred_label: "Favoris"
unread_label: "Non lus"
preview_picture_label: "A une photo"
preview_picture_help: "Photo"
language_label: "Langue"
reading_time:
label: 'Durée de lecture en minutes'
from: 'de'
to: 'à'
domain_label: 'Nom de domaine'
label: "Durée de lecture en minutes"
from: "de"
to: "à"
domain_label: "Nom de domaine"
created_at:
label: 'Date de création'
from: 'de'
to: 'à'
label: "Date de création"
from: "de"
to: "à"
action:
clear: 'Effacer'
filter: 'Filtrer'
clear: "Effacer"
filter: "Filtrer"
view:
left_menu:
back_to_top: 'Revenir en haut'
back_to_homepage: 'Retour'
set_as_read: 'Marquer comme lu'
set_as_unread: 'Marquer comme non lu'
set_as_starred: 'Mettre en favori'
view_original_article: 'Article original'
re_fetch_content: 'Recharger le contenu'
delete: 'Supprimer'
add_a_tag: 'Ajouter un tag'
share_content: 'Partager'
share_email_label: 'Email'
public_link: 'Lien public'
delete_public_link: 'Supprimer lien public'
download: 'Télécharger'
print: 'Imprimer'
back_to_top: "Revenir en haut"
back_to_homepage: "Retour"
set_as_read: "Marquer comme lu"
set_as_unread: "Marquer comme non lu"
set_as_starred: "Mettre en favori"
view_original_article: "Article original"
re_fetch_content: "Recharger le contenu"
delete: "Supprimer"
add_a_tag: "Ajouter un tag"
share_content: "Partager"
share_email_label: "Courriel"
public_link: "Lien public"
delete_public_link: "Supprimer le lien public"
download: "Télécharger"
print: "Imprimer"
problem:
label: 'Un problème ?'
description: "Est-ce que cet article s'affiche mal ?"
edit_title: 'Modifier le titre'
original_article: 'original'
annotations_on_the_entry: '{0} Aucune annotation|{1} Une annotation|]1,Inf[ %count% annotations'
created_at: 'Date de création'
label: "Un problème ?"
description: "Est-ce que cet article saffiche mal ?"
edit_title: "Modifier le titre"
original_article: "original"
annotations_on_the_entry: "{0} Aucune annotation|{1} Une annotation|]1,Inf[ %count% annotations"
created_at: "Date de création"
new:
page_title: 'Sauvegarder un nouvel article'
placeholder: 'http://website.com'
page_title: "Sauvegarder un nouvel article"
placeholder: "http://website.com"
form_new:
url_label: Url
url_label: "Adresse"
edit:
page_title: 'Éditer un article'
title_label: 'Titre'
url_label: 'Url'
is_public_label: 'Public'
save_label: 'Enregistrer'
page_title: "Éditer un article"
title_label: "Titre"
url_label: "Adresse"
is_public_label: "Public"
save_label: "Enregistrer"
public:
shared_by_wallabag: "Cet article a été partagé par <a href='%wallabag_instance%'>wallabag</a>"
shared_by_wallabag: "Cet article a été partagé par <a href=\"%wallabag_instance%\">wallabag</a>"
about:
page_title: 'À propos'
page_title: "À propos"
top_menu:
who_behind_wallabag: "L'équipe derrière wallabag"
getting_help: "Besoin d'aide"
helping: 'Aider wallabag'
contributors: 'Contributeurs'
third_party: 'Librairies tierces'
who_behind_wallabag: "Léquipe derrière wallabag"
getting_help: "Besoin daide"
helping: "Aider wallabag"
contributors: "Contributeurs"
third_party: "Librairies tierces"
who_behind_wallabag:
developped_by: 'Développé par'
website: 'Site web'
many_contributors: 'Et plein de contributeurs ♥ <a href="https://github.com/wallabag/wallabag/graphs/contributors">sur Github</a>'
project_website: 'Site web du projet'
license: 'Licence'
version: 'Version'
developped_by: "Développé par"
website: "Site web"
many_contributors: "Et plein de contributeurs ♥ <a href=\"https://github.com/wallabag/wallabag/graphs/contributors\">sur Github</a>"
project_website: "Site web du projet"
license: "Licence"
version: "Version"
getting_help:
documentation: 'Documentation'
bug_reports: 'Rapport de bugs'
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>'
documentation: "Documentation"
bug_reports: "Rapport de bogue"
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>"
helping:
description: 'wallabag est gratuit et opensource. Vous pouvez nous aider :'
by_contributing: 'en contribuant au projet :'
by_contributing_2: 'un ticket recense tous nos besoins'
by_paypal: 'via Paypal'
description: "wallabag est gratuit et opensource. Vous pouvez nous aider :"
by_contributing: "en contribuant au projet :"
by_contributing_2: "un ticket recense tous nos besoins"
by_paypal: "via Paypal"
contributors:
description: "Merci aux contributeurs de l'application web de wallabag"
description: "Merci aux contributeurs de lapplication web de wallabag"
third_party:
description: 'Voici la liste des dépendances utilisées dans wallabag (et leur license) :'
package: 'Dépendance'
license: 'Licence'
description: "Voici la liste des dépendances utilisées dans wallabag (et leur license) :"
package: "Dépendance"
license: "Licence"
howto:
page_title: 'Aide'
page_description: "Il y a plusieurs façon d'enregistrer un article :"
page_title: "Aide"
page_description: "Il y a plusieurs façon denregistrer un article :"
top_menu:
browser_addons: 'Extensions de navigateur'
mobile_apps: 'Applications smartphone'
bookmarklet: 'Bookmarklet'
browser_addons: "Extensions de navigateur"
mobile_apps: "Applications smartphone"
bookmarklet: "Bookmarklet"
form:
description: 'Grâce à ce formulaire'
description: "Grâce à ce formulaire"
browser_addons:
firefox: 'Extension Firefox'
chrome: 'Extension Chrome'
firefox: "Extension Firefox"
chrome: "Extension Chrome"
mobile_apps:
android:
via_f_droid: 'via F-Droid'
via_google_play: 'via Google Play'
ios: 'sur iTunes Store'
windows: 'sur Microsoft Store'
via_f_droid: "via F-Droid"
via_google_play: "via Google Play"
ios: "sur iTunes Store"
windows: "sur Microsoft Store"
bookmarklet:
description: 'Glissez et déposez ce lien dans votre barre de favoris :'
description: "Glissez et déposez ce lien dans votre barre de favoris :"
quickstart:
page_title: 'Pour bien débuter'
more: 'Et plus encore…'
page_title: "Pour bien débuter"
more: "Et plus encore…"
intro:
title: 'Bienvenue sur wallabag !'
title: "Bienvenue sur wallabag !"
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."
paragraph_2: 'Suivez-nous !'
paragraph_2: "Suivez-nous !"
configure:
title: "Configurez l'application"
description: 'Pour voir une application qui vous correspond, allez voir du côté de la configuration de wallabag.'
language: "Changez la langue et le design de l'application"
rss: 'Activez les flux RSS'
tagging_rules: 'Écrivez des règles pour classer automatiquement vos articles'
title: "Configurez lapplication"
description: "Pour voir une application qui vous correspond, allez voir du côté de la configuration de wallabag."
language: "Changez la langue et le design de lapplication"
rss: "Activez les flux RSS"
tagging_rules: "Écrivez des règles pour classer automatiquement vos articles"
admin:
title: 'Administration'
description: "En tant qu'administrateur sur wallabag, vous avez des privilèges qui vous permettent de :"
new_user: 'Créer un nouvel utilisateur'
analytics: 'Configurer les statistiques'
sharing: 'Activer des paramètres de partages'
export: "Configurer les formats d'export"
import: "Configurer l'import"
title: "Administration"
description: "En tant quadministrateur sur wallabag, vous avez des privilèges qui vous permettent de :"
new_user: "Créer un nouvel utilisateur"
analytics: "Configurer les statistiques"
sharing: "Activer des paramètres de partages"
export: "Configurer les formats dexport"
import: "Configurer limport"
first_steps:
title: 'Premiers pas'
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."
new_article: 'Ajoutez votre premier article'
unread_articles: 'Et rangez-le !'
title: "Premiers pas"
description: "Maintenant que wallabag est bien configuré, il est temps darchiver le web. Vous pouvez cliquer sur le signe + dans le coin en haut à droite."
new_article: "Ajoutez votre premier article"
unread_articles: "Et rangez-le !"
migrate:
title: 'Migrer depuis un service existant'
description: "Vous êtes un ancien utilisateur d'un service existant ? Nous allons vous aider à récupérer vos données sur wallabag."
pocket: 'Migrer depuis Pocket'
wallabag_v1: 'Migrer depuis wallabag v1'
wallabag_v2: 'Migrer depuis wallabag v2'
readability: 'Migrer depuis Readability'
instapaper: 'Migrer depuis Instapaper'
title: "Migrer depuis un service existant"
description: "Vous êtes un ancien utilisateur dun service existant ? Nous allons vous aider à récupérer vos données sur wallabag."
pocket: "Migrer depuis Pocket"
wallabag_v1: "Migrer depuis wallabag v1"
wallabag_v2: "Migrer depuis wallabag v2"
readability: "Migrer depuis Readability"
instapaper: "Migrer depuis Instapaper"
developer:
title: 'Pour les développeurs'
description: 'Nous avons aussi pensé aux développeurs : Docker, API, traductions, etc.'
create_application: 'Créer votre application tierce'
use_docker: 'Utiliser Docker pour installer wallabag'
title: "Pour les développeurs"
description: "Nous avons aussi pensé aux développeurs : Docker, API, traductions, etc."
create_application: "Créer votre application tierce"
use_docker: "Utiliser Docker pour installer wallabag"
docs:
title: 'Documentation complète'
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."
annotate: 'Annoter votre article'
export: 'Convertissez vos articles en ePub ou en PDF'
search_filters: "Apprenez à utiliser le moteur de recherche et les filtres pour retrouver l'article qui vous intéresse"
fetching_errors: "Que faire si mon article n'est pas correctement récupéré ?"
all_docs: "Et encore plein d'autres choses !"
title: "Documentation complète"
description: "Il y a tellement de fonctionnalités dans wallabag. Nhésitez pas à lire le manuel pour les connaitre et apprendre comment les utiliser."
annotate: "Annoter votre article"
export: "Convertissez vos articles en ePub ou en PDF"
search_filters: "Apprenez à utiliser le moteur de recherche et les filtres pour retrouver larticle qui vous intéresse"
fetching_errors: "Que faire si mon article nest pas correctement récupéré ?"
all_docs: "Et encore plein dautres choses !"
support:
title: 'Support'
description: 'Parce que vous avez peut-être besoin de nous poser une question, nous sommes disponibles pour vous.'
github: 'Sur GitHub'
email: 'Par e-mail'
gitter: 'Sur Gitter'
title: "Support"
description: "Parce que vous avez peut-être besoin de nous poser une question, nous sommes disponibles pour vous."
github: "Sur GitHub"
email: "Par courriel"
gitter: "Sur Gitter"
tag:
page_title: 'Tags'
page_title: "Tags"
list:
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."
see_untagged_entries: 'Voir les articles sans tag'
number_on_the_page: "{0} Il ny a pas de tag.|{1} Il y a un tag.|]1,Inf[ Il y a %count% tags."
see_untagged_entries: "Voir les articles sans tag"
import:
page_title: 'Importer'
page_description: "Bienvenue dans l'outil de migration de wallabag. Choisissez ci-dessous le service depuis lequel vous souhaitez migrer."
page_title: "Importer"
page_description: "Bienvenue dans loutil de migration de wallabag. Choisissez ci-dessous le service depuis lequel vous souhaitez migrer."
action:
import_contents: 'Importer les contenus'
import_contents: "Importer les contenus"
form:
mark_as_read_title: 'Marquer tout comme lu ?'
mark_as_read_label: 'Marquer tous les contenus importés comme lus'
file_label: 'Fichier'
save_label: 'Importer le fichier'
mark_as_read_title: "Marquer tout comme lu ?"
mark_as_read_label: "Marquer tous les contenus importés comme lus"
file_label: "Fichier"
save_label: "Importer le fichier"
pocket:
page_title: 'Importer > Pocket'
page_title: "Importer > Pocket"
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."
config_missing:
description: "L'import à partir de Pocket n'est pas configuré."
admin_message: "Vous devez définir %keyurls%une clé pour l'API Pocket%keyurle%."
user_message: "L'administrateur de votre serveur doit définir une clé pour l'API Pocket."
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."
connect_to_pocket: 'Se connecter à Pocket et importer les données'
description: "Limport à partir de Pocket nest pas configuré."
admin_message: "Vous devez définir %keyurls%une clé pour lAPI Pocket%keyurle%."
user_message: "Ladministrateur de votre serveur doit définir une clé pour lAPI Pocket."
authorize_message: "Vous pouvez importer vos données depuis votre compte Pocket. Vous navez quà cliquer sur le bouton ci-dessous et à autoriser wallabag à se connecter à getpocket.com."
connect_to_pocket: "Se connecter à Pocket et importer les données"
wallabag_v1:
page_title: 'Importer > Wallabag v1'
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".'
how_to: "Choisissez le fichier de votre export wallabag v1 et cliquez sur le bouton ci-dessous pour l'importer."
page_title: "Importer > wallabag v1"
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 »."
how_to: "Choisissez le fichier de votre export wallabag v1 et cliquez sur le bouton ci-dessous pour limporter."
wallabag_v2:
page_title: 'Importer > Wallabag v2'
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\""
page_title: "Importer > wallabag v2"
description: "Cet outil va importer tous vos articles dune 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 »"
readability:
page_title: 'Importer > Readability'
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.'
how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l'importer."
page_title: "Importer > Readability"
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."
how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour limporter."
worker:
enabled: "Les imports sont asynchrones. Une fois l'import commencé un worker externe traitera les messages un par un. Le service activé est :"
enabled: "Les imports sont asynchrones. Une fois limport commencé un worker externe traitera les messages un par un. Le service activé est :"
firefox:
page_title: 'Import > Firefox'
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>"
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."
page_title: "Import > Firefox"
description: "Cet outil va vous permettre dimporter 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>"
how_to: "Choisissez le fichier de sauvegarde de vos marques-page et cliquez sur le bouton pour limporter. Soyez avertis que le processus peut prendre un temps assez long car tous les articles doivent être récupérés en ligne."
chrome:
page_title: 'Import > Chrome'
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>"
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."
page_title: "Import > Chrome"
description: "Cet outil va vous permettre dimporter tous vos marques-pages de Google Chrome/Chromium. Pour Google Chrome, la situation du fichier dépend de votre système dexploitation : <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>"
how_to: "Choisissez le fichier de sauvegarde de vos marques-page et cliquez sur le bouton pour limporter. Soyez avertis que le processus peut prendre un temps assez long car tous les articles doivent être récupérés en ligne."
instapaper:
page_title: 'Import > Instapaper'
description: 'Sur la page des paramètres (`https://www.instapaper.com/user<https://www.instapaper.com/user>`_), cliquez sur "Download .CSV file" dans la section "Export". Un fichier CSV se téléchargera ("instapaper-export.csv").'
how_to: "Choisissez le fichier de votre export Instapaper et cliquez sur le bouton ci-dessous pour l'importer."
page_title: "Import > Instapaper"
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 »)."
how_to: "Choisissez le fichier de votre export Instapaper et cliquez sur le bouton ci-dessous pour limporter."
developer:
page_title: 'Développeur'
welcome_message: "Bienvenue sur l'API de wallabag"
documentation: 'Documentation'
how_to_first_app: 'Comment créer votre première application'
full_documentation: "Voir la documentation complète de l'API"
list_methods: "Lister toutes les méthodes de l'API"
page_title: "Développeur"
welcome_message: "Bienvenue sur lAPI de wallabag"
documentation: "Documentation"
how_to_first_app: "Comment créer votre première application"
full_documentation: "Voir la documentation complète de lAPI"
list_methods: "Lister toutes les méthodes de lAPI"
clients:
title: 'Clients'
create_new: 'Créer un nouveau client'
title: "Clients"
create_new: "Créer un nouveau client"
existing_clients:
title: 'Les clients existants'
field_id: 'ID Client'
field_secret: 'Clé secrète'
field_uris: 'URLs de redirection'
field_grant_types: 'Type de privilège accordé'
no_client: 'Aucun client pour le moment'
title: "Les clients existants"
field_id: "ID Client"
field_secret: "Clé secrète"
field_uris: "Adresse de redirection"
field_grant_types: "Type de privilège accordé"
no_client: "Aucun client pour le moment"
remove:
warn_message_1: 'Vous avez la possibilité de supprimer le client %name%. Cette action est IRREVERSIBLE !'
warn_message_2: "Si vous supprimez le client %name%, toutes les applications qui l'utilisaient ne fonctionneront plus avec votre compte wallabag."
action: 'Supprimer le client %name%'
warn_message_1: "Vous avez la possibilité de supprimer le client %name%. Cette action est IRRÉVERSIBLE !"
warn_message_2: "Si vous supprimez le client %name%, toutes les applications qui lutilisaient ne fonctionneront plus avec votre compte wallabag."
action: "Supprimer le client %name%"
client:
page_title: 'Développeur > Nouveau client'
page_description: "Vous allez créer un nouveau client. Merci de remplir l'url de redirection vers votre application."
page_title: "Développeur > Nouveau client"
page_description: "Vous allez créer un nouveau client. Merci de remplir ladresse de redirection vers votre application."
form:
name_label: "Nom du client"
redirect_uris_label: 'URLs de redirection (optionnel)'
save_label: 'Créer un nouveau client'
action_back: 'Retour'
redirect_uris_label: "Adresses de redirection (optionnel)"
save_label: "Créer un nouveau client"
action_back: "Retour"
client_parameter:
page_title: 'Développeur > Les paramètres de votre client'
page_description: 'Voilà les paramètres de votre client'
field_name: 'Nom du client'
field_id: 'ID Client'
field_secret: 'Clé secrète'
back: 'Retour'
read_howto: 'Lire "comment créer ma première application"'
page_title: "Développeur > Les paramètres de votre client"
page_description: "Voilà les paramètres de votre client"
field_name: "Nom du client"
field_id: "ID client"
field_secret: "Clé secrète"
back: "Retour"
read_howto: "Lire « comment créer ma première application »"
howto:
page_title: 'Développeur > Comment créer votre première application'
page_title: "Développeur > Comment créer votre première application"
description:
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."
paragraph_2: "Vous avez besoin d'un token pour échanger entre votre application et l'API de wallabag."
paragraph_3: 'Pour créer un token, vous devez <a href="%link%">créer un nouveau client</a>.'
paragraph_4: 'Maintenant créez votre token (remplacer client_id, client_secret, username et password avec les bonnes valeurs):'
paragraph_5: "L'API vous retournera une réponse comme ça :"
paragraph_6: "L'access_token doit être utilisé pour faire un appel à l'API. Par exemple :"
paragraph_7: "Cet appel va retourner tous les articles de l'utilisateur."
paragraph_8: "Si vous voulez toutes les méthodes de l'API, jetez un oeil <a href=\"%link%\">à la documentation de l'API</a>."
back: 'Retour'
paragraph_1: "Les commandes suivantes utilisent la <a href=\"https://github.com/jkbrzt/httpie\">librarie HTTPie</a>. Assurez-vous quelle soit installée avant de lutiliser."
paragraph_2: "Vous avez besoin dun token pour échanger entre votre application et lAPI de wallabag."
paragraph_3: "Pour créer un token, vous devez <a href=\"%link%\">créer un nouveau client</a>."
paragraph_4: "Maintenant créez votre token (remplacer client_id, client_secret, username et password avec les bonnes valeurs):"
paragraph_5: "LAPI vous retournera une réponse comme ça :"
paragraph_6: "Laccess_token doit être utilisé pour faire un appel à lAPI. Par exemple :"
paragraph_7: "Cet appel va retourner tous les articles de lutilisateur."
paragraph_8: "Si vous voulez toutes les méthodes de lAPI, jetez un oeil <a href=\"%link%\">à la documentation de lAPI</a>."
back: "Retour"
user:
page_title: Gestion des utilisateurs
@ -430,59 +430,62 @@ user:
no: Non
create_new_one: Créer un nouvel utilisateur
form:
username_label: "Nom d'utilisateur"
name_label: 'Nom'
password_label: 'Mot de passe'
repeat_new_password_label: 'Confirmez votre nouveau mot de passe'
plain_password_label: 'Mot de passe en clair'
email_label: 'Adresse e-mail'
enabled_label: 'Activé'
locked_label: 'Bloqué'
last_login_label: 'Dernière connexion'
twofactor_label: Double authentification
save: Sauvegarder
delete: Supprimer
delete_confirm: Êtes-vous sûr?
back_to_list: Revenir à la liste
username_label: "Nom dutilisateur"
name_label: "Nom"
password_label: "Mot de passe"
repeat_new_password_label: "Confirmez votre nouveau mot de passe"
plain_password_label: "Mot de passe en clair"
email_label: "Adresse courriel"
enabled_label: "Activé"
locked_label: "Bloqué"
last_login_label: "Dernière connexion"
twofactor_label: "Double authentification"
save: "Sauvegarder"
delete: "Supprimer"
delete_confirm: "Voulez-vous vraiment ?"
back_to_list: "Revenir à la liste"
error:
page_title: Une erreur est survenue
flashes:
config:
notice:
config_saved: 'Les paramètres ont bien été mis à jour. Certains seront pris en compte après déconnexion.'
password_updated: 'Votre mot de passe a bien été mis à jour'
config_saved: "Les paramètres ont bien été mis à jour. Certains seront pris en compte après déconnexion."
password_updated: "Votre mot de passe a bien été mis à jour"
password_not_updated_demo: "En démo, vous ne pouvez pas changer le mot de passe de cet utilisateur."
user_updated: 'Vos informations personnelles ont bien été mises à jour'
rss_updated: 'La configuration des flux RSS a bien été mise à jour'
tagging_rules_updated: 'Règles mises à jour'
tagging_rules_deleted: 'Règle supprimée'
user_added: 'Utilisateur "%username%" ajouté'
rss_token_updated: 'Jeton RSS mis à jour'
user_updated: "Vos informations personnelles ont bien été mises à jour"
rss_updated: "La configuration des flux RSS a bien été mise à jour"
tagging_rules_updated: "Règles mises à jour"
tagging_rules_deleted: "Règle supprimée"
user_added: "Utilisateur \"%username%\" ajouté"
rss_token_updated: "Jeton RSS mis à jour"
entry:
notice:
entry_already_saved: 'Article déjà sauvergardé le %date%'
entry_saved: 'Article enregistré'
entry_saved_failed: 'Article enregistré mais impossible de récupérer le contenu'
entry_updated: 'Article mis à jour'
entry_reloaded: 'Article rechargé'
entry_already_saved: "Article déjà sauvergardé le %date%"
entry_saved: "Article enregistré"
entry_saved_failed: "Article enregistré mais impossible de récupérer le contenu"
entry_updated: "Article mis à jour"
entry_reloaded: "Article rechargé"
entry_reloaded_failed: "Article mis à jour mais impossible de récupérer le contenu"
entry_archived: 'Article marqué comme lu'
entry_unarchived: 'Article marqué comme non lu'
entry_starred: 'Article ajouté dans les favoris'
entry_unstarred: 'Article retiré des favoris'
entry_deleted: 'Article supprimé'
entry_archived: "Article marqué comme lu"
entry_unarchived: "Article marqué comme non lu"
entry_starred: "Article ajouté dans les favoris"
entry_unstarred: "Article retiré des favoris"
entry_deleted: "Article supprimé"
tag:
notice:
tag_added: 'Tag ajouté'
tag_added: "Tag ajouté"
import:
notice:
failed: "L'import a échoué, veuillez ré-essayer"
failed_on_file: "Erreur lors du traitement de l'import. Vérifier votre fichier."
summary: "Rapport d'import: %imported% importés, %skipped% déjà présents."
summary_with_queue: "Rapport d'import: %queued% en cours de traitement."
failed: "Limport a échoué, veuillez ré-essayer"
failed_on_file: "Erreur lors du traitement de limport. Vérifiez votre fichier."
summary: "Rapport dimport : %imported% importés, %skipped% déjà présents."
summary_with_queue: "Rapport dimport: %queued% en cours de traitement."
error:
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.
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.
redis_enabled_not_installed: "Redis est activé pour les imports asynchrones mais <u>impossible de sy connecter</u>. Vérifier la configuration de Redis."
rabbit_enabled_not_installed: "RabbitMQ est activé pour les imports asynchrones mais <u>impossible de sy connecter</u>. Vérifier la configuration de RabbitMQ."
developer:
notice:
client_created: 'Nouveau client %name% créé'
client_deleted: 'Client %name% supprimé'
client_created: "Nouveau client %name% créé"
client_deleted: "Client %name% supprimé"

View File

@ -137,7 +137,7 @@ entry:
starred: 'Contenuti preferiti'
archived: 'Contenuti archiviati'
filtered: 'Contenuti filtrati'
# filtered_tags: 'Filtered by tags'
# filtered_tags: 'Filtered by tags:'
# untagged: 'Untagged entries'
list:
number_on_the_page: "{0} Non ci sono contenuti.|{1} C'è un contenuto.|]1,Inf[ Ci sono %count% contenuti."
@ -445,6 +445,9 @@ user:
# delete_confirm: Are you sure?
# back_to_list: Back to list
error:
# page_title: An error occurred
flashes:
config:
notice:

View File

@ -137,7 +137,7 @@ entry:
starred: 'Articles favorits'
archived: 'Articles legits'
filtered: 'Articles filtrats'
filtered_tags: 'Filtats per etiquetas'
filtered_tags: 'Filtats per etiquetas:'
untagged: 'Articles sens etiqueta'
list:
number_on_the_page: "{0} I a pas cap d'article.|{1} I a un article.|]1,Inf[ I a %count% articles."
@ -445,6 +445,9 @@ user:
# delete_confirm: Are you sure?
# back_to_list: Back to list
error:
# page_title: An error occurred
flashes:
config:
notice:

View File

@ -137,7 +137,7 @@ entry:
starred: 'Wpisy oznaczone gwiazdką'
archived: 'Zarchiwizowane wpisy'
filtered: 'Odfiltrowane wpisy'
filtered_tags: 'Filtrowane po tagach'
filtered_tags: 'Filtrowane po tagach:'
untagged: 'Odtaguj wpisy'
list:
number_on_the_page: '{0} Nie ma wpisów.|{1} Jest jeden wpis.|]1,Inf[ Są %count% wpisy.'
@ -445,6 +445,9 @@ user:
delete_confirm: Jesteś pewien?
back_to_list: Powrót do listy
error:
# page_title: An error occurred
flashes:
config:
notice:

View File

@ -0,0 +1,492 @@
security:
login:
page_title: 'Bem vindo ao wallabag!'
keep_logged_in: 'Mantenha-me autenticado'
forgot_password: 'Esqueceu a senha?'
submit: 'Login'
register: 'Registre-se'
username: 'Nome de usuário'
password: 'Senha'
cancel: 'Cancelar'
resetting:
description: 'Digite seu endereço de e-mail abaixo e enviaremos instruções para resetar sua senha.'
register:
page_title: 'Criar uma conta'
go_to_account: 'Ir para sua conta'
menu:
left:
unread: 'Não lido'
starred: 'Destacado'
archive: 'Arquivo'
all_articles: 'Todas as entradas'
config: 'Configurações'
tags: 'Tags'
internal_settings: 'Configurações Internas'
import: 'Importar'
howto: 'How to'
developer: 'Desenvolvedor'
logout: 'Sair'
about: 'Sobre'
search: 'Pesquisa'
save_link: 'Salvar um link'
back_to_unread: 'Voltar para os artigos não lidos'
users_management: 'Gestão de Usuários'
top:
add_new_entry: 'Adicionar uma nova entrada'
search: 'Pesquisa'
filter_entries: 'Filtrar entradas'
export: 'Exportar'
search_form:
input_label: 'Digite aqui sua pesquisa'
footer:
wallabag:
elsewhere: 'Leve o wallabag com você'
social: 'Social'
powered_by: 'provido por'
about: 'Sobre'
stats: 'Desde %user_creation% você leu %nb_archives% artigos. Isso é %per_day% por dia!'
config:
page_title: 'Config'
tab_menu:
settings: 'Configurações'
rss: 'RSS'
user_info: 'Informação do Usuário'
password: 'Senha'
rules: 'Regras de tags'
new_user: 'Adicionar um usuário'
form:
save: 'Salvar'
form_settings:
theme_label: 'Tema'
items_per_page_label: 'Itens por página'
language_label: 'Idioma'
reading_speed:
label: 'Velocidade de leitura'
help_message: 'Você pode usar ferramentas online para estimar sua velocidade de leitura:'
100_word: 'Posso ler ~100 palavras por minuto'
200_word: 'Posso ler ~200 palavras por minuto'
300_word: 'Posso ler ~300 palavras por minuto'
400_word: 'Posso ler ~400 palavras por minuto'
pocket_consumer_key_label: 'Chave do consumidor do Pocket para importar conteúdo'
form_rss:
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.'
token_label: 'Token RSS'
no_token: 'Nenhum Token'
token_create: 'Criar seu token'
token_reset: 'Gerar novamente seu token'
rss_links: 'Links RSS'
rss_link:
unread: 'não lido'
starred: 'destacado'
archive: 'arquivado'
rss_limit: 'Número de itens no feed'
form_user:
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.'
name_label: 'Nome'
email_label: 'E-mail'
twoFactorAuthentication_label: 'Autenticação de dois passos'
form_password:
old_password_label: 'Senha atual'
new_password_label: 'Nova senha'
repeat_new_password_label: 'Repita a nova senha'
form_rules:
if_label: 'if'
then_tag_as_label: 'então coloque a tag'
delete_rule_label: 'apagar'
edit_rule_label: 'editar'
rule_label: 'Regras'
tags_label: 'Tags'
faq:
title: 'FAQ'
tagging_rules_definition_title: 'O que as « regras de tags » significam?'
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.'
how_to_use_them_title: 'Como eu as utilizo?'
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 &lt;= 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 &gt;= 5 AND domainName = "github.com"</i> » então adicione a tag « <i>leitura longa, github </i> »'
variables_available_title: 'Quais variáveis e operadores eu posso usar para escrever regras?'
variables_available_description: 'As seguintes variáveis e operadores podem ser usados para criar regras de tags:'
meaning: 'Meaning'
variable_description:
label: 'Variável'
title: 'Título da entrada'
url: 'URL da entrada'
isArchived: 'Se a entrada está arquivada ou não'
isDestacado: 'Se a entrada está destacada ou não'
content: 'O conteúdo da entrada'
language: 'O idioma da entrada'
mimetype: 'O mime-type da entrada'
readingTime: 'O tempo estimado de leitura da entrada, em minutos'
domainName: 'O domínio da entrada'
operator_description:
label: 'Operador'
less_than: 'Menor que...'
strictly_less_than: 'Estritamente menor que...'
greater_than: 'Maior que...'
strictly_greater_than: 'Estritamente maior que...'
equal_to: 'Igual a...'
not_equal_to: 'Diferente de...'
or: 'Uma regra OU outra'
and: 'Uma regra E outra'
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>'
entry:
page_titles:
unread: 'Entradas não lidas'
starred: 'Entradas destacadas'
archived: 'Entradas arquivadas'
filtered: 'Entradas filtradas'
filtered_tags: 'Filtrar por tags:'
untagged: 'Entradas sem tags'
list:
number_on_the_page: '{0} Não existem entradas.|{1} Existe uma entrada.|]1,Inf[ Existem %count% entradas.'
reading_time: 'tempo estimado de leitura'
reading_time_minutes: 'tempo estimado de leitura: %readingTime% min'
reading_time_less_one_minute: 'tempo estimado de leitura: <small class="inferieur">&lt;</small> 1 min'
number_of_tags: '{1}e uma outra tag|]1,Inf[e %count% outras tags'
reading_time_minutes_short: '%readingTime% min'
reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min'
original_article: 'original'
toogle_as_read: 'Marcar como lido'
toogle_as_star: 'Marcar como destacado'
delete: 'Apagar'
export_title: 'Exportar'
filters:
title: 'Filtros'
status_label: 'Status'
archived_label: 'Arquivado'
starred_label: 'Destacado'
unread_label: 'Não Lido'
preview_picture_label: 'Possui uma imagem de preview'
preview_picture_help: 'Imagem de preview'
language_label: 'Idioma'
reading_time:
label: 'Tempo de leitura em minutos'
from: 'de'
to: 'para'
domain_label: 'Nome do domínio'
created_at:
label: 'Data de criação'
from: 'de'
to: 'para'
action:
clear: 'Limpar'
filter: 'Filtro'
view:
left_menu:
back_to_top: 'Voltar ao topo'
back_to_homepage: 'Voltar'
set_as_read: 'Marcar como lido'
set_as_unread: 'Marcar como não lido'
set_as_starred: 'Alternar destaque'
view_original_article: 'Artigo original'
re_fetch_content: 'Recapturar o conteúdo'
delete: 'Apagar'
add_a_tag: 'Adicionar uma tag'
share_content: 'Compartilhar'
share_email_label: 'E-mail'
public_link: 'link público'
delete_public_link: 'apagar link público'
download: 'Download'
print: 'Imprimir'
problem:
label: 'Problemas?'
description: 'este artigo aparece errado?'
edit_title: 'Editar título'
original_article: 'original'
annotations_on_the_entry: '{0} Sem anotações|{1} Uma anotação|]1,Inf[ %nbAnnotations% anotações'
created_at: 'Data de criação'
new:
page_title: 'Salvar nova entrada'
placeholder: 'http://website.com'
form_new:
url_label: Url
edit:
page_title: 'Editar uma entrada'
title_label: 'Título'
url_label: 'Url'
is_public_label: 'Público'
save_label: 'Salvar'
public:
shared_by_wallabag: "Este artigo foi compartilhado pelo <a href='%wallabag_instance%'>wallabag</a>"
about:
page_title: 'Sobre'
top_menu:
who_behind_wallabag: 'Quem está por trás do wallabag'
getting_help: 'Obtendo ajuda'
helping: 'Ajudando o wallabag'
contributors: 'Contribuidores'
third_party: 'Bibliotecas terceiras'
who_behind_wallabag:
developped_by: 'Desenvolvido por'
website: 'website'
many_contributors: 'E muitos outros contribuidores ♥ <a href="https://github.com/wallabag/wallabag/graphs/contributors">no Github</a>'
project_website: 'Website do projeto'
license: 'Licença'
version: 'Versão'
getting_help:
documentation: 'Documentação'
bug_reports: 'Informar bugs'
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>'
helping:
description: 'wallabag é livre e software livre. Você pode nos ajudar:'
by_contributing: 'contribuindo com o projeto:'
by_contributing_2: 'uma lista de todas as nossas necessidades'
by_paypal: 'via Paypal'
contributors:
description: 'Obrigado por contribuir com a aplicação web wallabag'
third_party:
description: 'Aqui está a lista de bibliotecas terceiras usadas no wallabag (com suas licenças):'
package: 'Pacote'
license: 'Licença'
howto:
page_title: 'How to'
page_description: 'Existem diferentes formas de salvar um artigo:'
top_menu:
browser_addons: 'Extensões de navegadores'
mobile_apps: "App's móveis"
bookmarklet: 'Bookmarklet'
form:
description: 'Obrigado por este formulário'
browser_addons:
firefox: 'Extensão padrão do Firefox'
chrome: 'Extensão do Chrome'
mobile_apps:
android:
via_f_droid: 'via F-Droid'
via_google_play: 'via Google Play'
ios: 'na iTunes Store'
windows: 'na Microsoft Store'
bookmarklet:
description: 'Arraste e solve este link na sua barra de favoritos:'
quickstart:
page_title: 'Começo Rápido'
more: 'Mais...'
intro:
title: 'Bem-vindo ao wallabag!'
paragraph_1: 'Nós podemos acompanhar você em sua visita ao wallabag e mostrar algumas funcionalidades que podem lhe interessar.'
paragraph_2: 'Siga-nos!'
configure:
title: 'Configurar a aplicação'
description: 'Para ter uma aplicação que atende você, dê uma olhada na configuração do wallabag.'
language: 'Alterar idioma e design'
rss: 'Habilitar feeds RSS'
tagging_rules: 'Escrever regras para acrescentar tags automaticamente em seus artigos'
admin:
title: 'Administração'
description: 'Como administrador você tem privilégios no wallabag. Você pode:'
new_user: 'Criar um novo usuário'
analytics: 'Configurar o analytics'
sharing: 'habilitar alguns parâmetros para compartilhamento de artigos'
export: 'Configurar exportação'
import: 'Configurar importação'
first_steps:
title: 'Primeiros passos'
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."
new_article: 'Salvar seu primeiro artigo'
unread_articles: 'E classificá-lo!'
migrate:
title: 'Migrar de um serviço existente'
description: 'Você está usando um outro serviço? Nós podemos ajudá-lo a recuperar seus dados para o wallabag.'
pocket: 'Migrar do Pocket'
wallabag_v1: 'Migrar do wallabag v1'
wallabag_v2: 'Migrar do wallabag v2'
readability: 'Migrate from Readability'
instapaper: 'Migrate from Instapaper'
developer:
title: 'Desenvolvedores'
description: 'Nós também agradecemos os desenvolvedores: Docker, API, traduções, etc.'
create_application: 'Criar sua aplicação terceira'
use_docker: 'Usar o Docker para instalar o wallabag'
docs:
title: 'Documentação completa'
description: "Existem muitas funcionalidades no wallabag. Não hesite em ler o manual para conhecê-las e aprender como usá-las."
annotate: 'Anotar seu artigo'
export: 'Converter seu artigo em ePUB ou PDF'
search_filters: 'veja coo você pode encontrar um artigo usanndo o motor de busca e filtros'
fetching_errors: 'O que eu posso fazer quando um artigo encontra erros na recuperação?'
all_docs: 'E outros muitos artigos!'
support:
title: 'Suporte'
description: 'Se você precisa de ajuda, nós estamos aqui.'
github: 'No GitHub'
email: 'Por e-mail'
gitter: 'No Gitter'
tag:
page_title: 'Tags'
list:
number_on_the_page: '{0} Não existem tags.|{1} Uma tag.|]1,Inf[ Existem %count% tags.'
see_untagged_entries: 'Ver entradas sem tags'
import:
page_title: 'Importar'
page_description: 'Bem-vindo ao importador do wallabag. Por favo selecione o serviço do qual deseja migrar.'
action:
import_contents: 'Importar conteúdos'
form:
mark_as_read_title: 'Marcar todos como lidos?'
mark_as_read_label: 'Marcar todas as entradas importadas como lidas'
file_label: 'Arquivo'
save_label: 'Carregar arquivo'
pocket:
page_title: 'Importar > Pocket'
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.'
config_missing:
description: 'O importador do Pocket não está configurado.'
admin_message: 'Você precisa definir uma %keyurls%a pocket_consumer_key%keyurle%.'
user_message: 'Seu administrador do servidor precisa definir uma chave de API para o Pocket.'
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.'
connect_to_pocket: 'Conecte ao Pocket e importe os dados'
wallabag_v1:
page_title: 'Importar > Wallabag v1'
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".'
how_to: 'Por favor, selecione seu exportador wallabag e clique no botão abaixo para carregar e importar.'
wallabag_v2:
page_title: 'Importar > Wallabag v2'
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".'
readability:
page_title: 'Importar > Readability'
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).'
how_to: 'Por favor, selecione sua exportação do Readability e clique no botão abaixo para importá-la.'
worker:
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 é:"
firefox:
page_title: 'Importar > Firefox'
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."
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."
chrome:
page_title: 'Importar > Chrome'
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>"
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."
instapaper:
page_title: 'Importar > Instapaper'
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").'
how_to: 'Por favor, selecione sua exportação do seu Instapaper e clique no botão abaixo para importá-la.'
developer:
page_title: 'Desenvolvedor'
welcome_message: 'Bem-vindo a API do wallabag'
documentation: 'Documentação'
how_to_first_app: 'Como criar minha primeira aplicação'
full_documentation: 'Ver a documentação completa da API'
list_methods: 'Lista de métodos da API'
clients:
title: 'Clientes'
create_new: 'Criar um novo cliente'
existing_clients:
title: 'Clientes existentes'
field_id: 'ID do cliente'
field_secret: 'Chave do cliente'
field_uris: 'URIs de redirecionamento'
field_grant_types: 'Tipo permitido'
no_client: 'Nenhum cliente até agora.'
remove:
warn_message_1: 'Você tem permissão pare remover este cliente. Esta ação é IRREVERSÍVEL !'
warn_message_2: 'Se você remover isso, todo o aplicativo configurado com este cliente não poderá se autenticar no seu wallabag.'
action: 'Remover este cliente'
client:
page_title: 'Desenvolvedor > Novo cliente'
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.'
form:
name_label: 'Nome do cliente'
redirect_uris_label: 'URIs de redirecionamento'
save_label: 'Criar um novo cliente'
action_back: 'Voltar'
client_parameter:
page_title: 'Desenvolvedor > Parâmetros de clientes'
page_description: 'Aqui estão os parâmetros de seus clientes.'
field_name: 'Nome do cliente'
field_id: 'ID do cliente'
field_secret: 'Chave do cliente'
back: 'Voltar'
read_howto: 'Leia o how-to "Criar minha primeira aplicação"'
howto:
page_title: 'Desenvolvedor > Criar minha primeira aplicação'
description:
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.'
paragraph_2: 'Você precisa de um token para a comunicação entre sua aplicação terceira e a API do wallabag.'
paragraph_3: 'Para criar este token, você precisa <a href="%link%">criar um novo cliente</a>.'
paragraph_4: 'Agora, crie seu token (altere client_id, client_secret, username e password com os valores corretos):'
paragraph_5: 'A API pode retornar uma resposta como essa:'
paragraph_6: 'O access_token é utilizável para fazer uma chamada para o endpoint da API. Por exemplo:'
paragraph_7: 'Esta chamada pode retornar todas as entradas de seu usuário.'
paragraph_8: 'Se você deseja ver todos os endpoints da API, dê uma olhada <a href="%link%">em nossa documentação da API</a>.'
back: 'Voltar'
user:
page_title: 'Gerenciamento de Usuários'
new_user: 'Criar um novo usuário'
edit_user: 'Editar um usuário existente'
description: 'Aqui você gerencia todos os usuários (cria, edita e apaga)'
list:
actions: 'Ações'
edit_action: 'Editar'
yes: 'Sim'
no: 'Não'
create_new_one: 'Criar um novo usuário'
form:
username_label: 'Nome de Usuário'
name_label: 'Nome'
password_label: 'Senha'
repeat_new_password_label: 'Repita a nova senha'
plain_password_label: '????'
email_label: 'E-mail'
enabled_label: 'Habilitado'
locked_label: 'Travado'
last_login_label: 'Último login'
twofactor_label: 'Autenticação de dois passos'
save: 'Salvar'
delete: 'Apagar'
delete_confirm: 'Tem certeza?'
back_to_list: 'Voltar para a lista'
flashes:
config:
notice:
config_saved: 'Configiração salva. Alguns parâmetros podem ser considerados depois da desconexão.'
password_updated: 'Senha atualizada'
password_not_updated_demo: 'Em modo de demonstração, você não pode alterar a senha deste usuário.'
user_updated: 'Informação atualizada'
rss_updated: 'Informação de RSS atualizada'
tagging_rules_updated: 'Regras de tags atualizadas'
tagging_rules_deleted: 'Regra de tag apagada'
rss_token_updated: 'Token RSS atualizado'
entry:
notice:
entry_already_saved: 'Entrada já foi salva em %date%'
entry_saved: 'Entrada salva'
entry_saved_failed: 'Failed to save entry'
entry_updated: 'Entrada atualizada'
entry_reloaded: 'Entrada recarregada'
entry_reloaded_failed: 'Falha em recarregar a entrada'
entry_archived: 'Entrada arquivada'
entry_unarchived: 'Entrada desarquivada'
entry_starred: 'Entrada destacada'
entry_unstarred: 'Entrada não destacada'
entry_deleted: 'Entrada apagada'
tag:
notice:
tag_added: 'Tag adicionada'
import:
notice:
failed: 'Importação falhou, por favor tente novamente.'
failed_on_file: 'Erro ao processar a importação. Por favor verifique seu arquivo de importação.'
summary: 'relatório de importação: %imported% importados, %skipped% já existem.'
summary_with_queue: 'Importar sumáario: %queued% agendados.'
error:
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.'
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.'
developer:
notice:
client_created: 'Novo cliente criado.'
client_deleted: 'Cliente removido'
user:
notice:
added: 'Usuário "%username%" adicionado'
updated: 'Usuário "%username%" atualizado'
deleted: 'Usuário "%username%" removido'

View File

@ -137,7 +137,7 @@ entry:
# starred: 'Starred entries'
# archived: 'Archived entries'
# filtered: 'Filtered entries'
# filtered_tags: 'Filtered by tags'
# filtered_tags: 'Filtered by tags:'
# untagged: 'Untagged entries'
list:
# number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
@ -445,6 +445,9 @@ user:
# delete_confirm: Are you sure?
# back_to_list: Back to list
error:
# page_title: An error occurred
flashes:
config:
notice:

View File

@ -136,7 +136,7 @@ entry:
# starred: 'Starred entries'
# archived: 'Archived entries'
# filtered: 'Filtered entries'
# filtered_tags: 'Filtered by tags'
# filtered_tags: 'Filtered by tags:'
# untagged: 'Untagged entries'
list:
number_on_the_page: '{0} Herhangi bir makale yok.|{1} Burada bir adet makale var.|]1,Inf[ Burada %count% adet makale var.'
@ -444,6 +444,9 @@ user:
# delete_confirm: Are you sure?
# back_to_list: Back to list
error:
# page_title: An error occurred
flashes:
config:
notice:

View File

@ -1,6 +1,6 @@
validator:
password_must_match: 'Les deux mots de passe doivent être les mêmes'
password_too_short: 'Le mot de passe doit contenir au moins 8 caractères'
password_wrong_value: 'Votre mot de passe actuel est faux'
item_per_page_too_high: "Ca ne va pas plaire à l'application"
rss_limit_too_hight: "Ca ne va pas plaire à l'application"
password_must_match: "Les deux mots de passe doivent être les mêmes"
password_too_short: "Le mot de passe doit contenir au moins 8 caractères"
password_wrong_value: "Votre mot de passe actuel est faux"
item_per_page_too_high: "Ça ne va pas plaire à lapplication"
rss_limit_too_hight: "Ça ne va pas plaire à lapplication"

View File

@ -0,0 +1,6 @@
validator:
password_must_match: 'Os campos de senha devem coincidir.'
password_too_short: 'A senha deve ter pelo menos 8 caracteres'
password_wrong_value: 'A senha atual informada está errada'
item_per_page_too_high: 'Certamente isso pode matar a aplicação'
rss_limit_too_hight: 'Certamente isso pode matar a aplicação'

View File

@ -1,11 +1,25 @@
{% extends "WallabagCoreBundle::layout.html.twig" %}
{% block title %}
{% include "@WallabagCore/themes/common/Entry/_title.html.twig" %}
{% set currentTag = '' %}
{% if tag is defined %}
{% set currentTag = tag %}
{% endif %}
{% include "@WallabagCore/themes/common/Entry/_title.html.twig" with {'currentTag': currentTag} %}
{% endblock %}
{% block content %}
{% include "WallabagCoreBundle:Entry:pager.html.twig" with {'entries': entries} %}
<div class="results">
<div class="nb-results">{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}</div>
<div class="pagination">
<i class="btn-clickable download-btn material-icons md-36">file_download</i>
<i class="btn-clickable filter-btn material-icons md-36">filter_list</i>
{% if entries.getNbPages > 1 %}
{{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }}
{% endif %}
</div>
</div>
{% for entry in entries %}
<div id="entry-{{ entry.id|e }}" class="entry">
@ -46,28 +60,36 @@
</div>
{% endfor %}
{% if entries.getNbPages > 1 %}
{{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }}
{% endif %}
<!-- Export -->
<aside id="download-form">
{% set currentRoute = app.request.attributes.get('_route') %}
{% set currentTag = '' %}
{% if tag is defined %}
{% set currentTag = tag %}
{% endif %}
{% if currentRoute == 'homepage' %}
{% set currentRoute = 'unread' %}
{% endif %}
<h2>{{ 'entry.list.export_title'|trans }}</h2>
<a href="javascript: void(null);" id="download-form-close" class="close-button--popup close-button">&times;</a>
<ul>
{% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'epub' }) }}">EPUB</a></li>{% endif %}
{% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'mobi' }) }}">MOBI</a></li>{% endif %}
{% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'pdf' }) }}">PDF</a></li>{% endif %}
{% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json' }) }}">JSON</a></li>{% endif %}
{% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv' }) }}">CSV</a></li>{% endif %}
{% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'txt' }) }}">TXT</a></li>{% endif %}
{% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml' }) }}">XML</a></li>{% endif %}
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
</ul>
</aside>
<!-- Filter -->
{% if form is not null %}
<div id="filters" class="">
<div id="filters">
<form method="get" action="{{ path('all') }}">
<h2>{{ 'entry.filters.title'|trans }}</h2>
<a href="javascript: void(null);" id="filter-form-close" class="close-button--popup close-button">&times;</a>

View File

@ -1,12 +0,0 @@
{% block pager %}
<div class="results">
<div class="nb-results">{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}</div>
<div class="pagination">
<i class="btn-clickable download-btn material-icons md-36">file_download</i>
<i class="btn-clickable filter-btn material-icons md-36">filter_list</i>
{% if entries.getNbPages > 1 %}
{{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }}
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -7,7 +7,7 @@
{% elseif currentRoute == 'all' %}
{{ 'entry.page_titles.filtered'|trans }}
{% elseif currentRoute == 'tag_entries' %}
{{ 'entry.page_titles.filtered_tags'|trans }}
{{ 'entry.page_titles.filtered_tags'|trans }} {{ currentTag }}
{% elseif currentRoute == 'untagged' %}
{{ 'entry.page_titles.untagged'|trans }}
{% else %}

View File

@ -39,7 +39,7 @@
<meta name="twitter:image" content="{{ picturePath }}" />
<meta name="twitter:site" content="@wallabagapp" />
<meta name="twitter:title" content="{{ entry.title | raw }}" />
<meta name="twitter:description" content="{{ entry.title | raw }}" />
<meta name="twitter:description" content="{{ entry.content|striptags|slice(0, 300)|raw }}&hellip;" />
</head>
<body>
<header>

View File

@ -1,15 +1,27 @@
{% extends "WallabagCoreBundle::layout.html.twig" %}
{% block title %}
{% include "@WallabagCore/themes/common/Entry/_title.html.twig" %}
{% set currentTag = '' %}
{% if tag is defined %}
{% set currentTag = tag %}
{% endif %}
{% include "@WallabagCore/themes/common/Entry/_title.html.twig" with {'currentTag': currentTag} %}
{% endblock %}
{% block content %}
{% include "WallabagCoreBundle:Entry:pager.html.twig" with {'entries': entries} %}
<div class="results clearfix">
<div class="nb-results left">
{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}
</div>
{% if entries.getNbPages > 1 %}
{{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }}
{% endif %}
</div>
<br />
<ul class="row data">
{% for entry in entries %}
<li id="entry-{{ entry.id|e }}" class="col l4 m6 s12">
<li id="entry-{{ entry.id|e }}" class="col l3 m6 s12">
<div class="card">
<div class="card-body">
@ -26,24 +38,19 @@
<div class="card-content">
{% if not entry.previewPicture is null %}
<i class="card-title grey-text text-darken-4 activator material-icons right">more_vert</i>
<i class="grey-text text-darken-4 activator material-icons right">more_vert</i>
{% endif %}
<span class="card-title dot-ellipsis dot-resize-update"><a href="{{ path('view', { 'id': entry.id }) }}" title="{{ entry.title|raw }}">{{ entry.title|striptags|raw }}</a></span>
<span class="card-title dot-ellipsis dot-resize-update"><a href="{{ path('view', { 'id': entry.id }) }}" title="{{ entry.title | raw | striptags }}">{{ entry.title| striptags | truncate(80, true, '…') | raw }}</a></span>
<div class="estimatedTime grey-text">
<span class="tool reading-time">
{% set readingTime = entry.readingTime / app.user.config.readingSpeed %}
{% if readingTime > 0 %}
{{ 'entry.list.reading_time_minutes'|trans({'%readingTime%': readingTime|round}) }}
{% else %}
{{ 'entry.list.reading_time_less_one_minute'|trans|raw }}
{% endif %}
</span>
<div class="original grey-text">
<a href="{{ entry.url|e }}" target="_blank" title="{{ entry.domainName|removeWww }}" class="tool original grey-text">
<span>{{ entry.domainName|removeWww }}</span>
</a>
</div>
{% if entry.previewPicture is null %}
<p>{{ entry.content|striptags|slice(0, 300)|raw }}&hellip;</p>
<p>{{ entry.content|striptags|slice(0, 250)|raw }}&hellip;</p>
<ul class="card-entry-labels-hidden">
{% for tag in entry.tags | slice(0, 2) %}
<li><a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a></li>
@ -55,16 +62,10 @@
{% if not entry.previewPicture is null %}
<div class="card-reveal">
<i class="card-title grey-text text-darken-4 material-icons right">clear</i>
<span class="card-title"><a href="{{ path('view', { 'id': entry.id }) }}">{{ entry.title|raw }}</a></span>
<i class="card-title activator grey-text text-darken-4 material-icons right">clear</i>
<span class="card-title" title="{{ entry.title | raw | striptags }}"><a href="{{ path('view', { 'id': entry.id }) }}">{{ entry.title | raw | striptags | truncate(90, true, '…') }}</a></span>
<div class="estimatedTime grey-text">
<span class="tool reading-time">
{% if readingTime > 0 %}{{ 'entry.list.reading_time_minutes'|trans({'%readingTime%': readingTime|round}) }}{% else %}{{ 'entry.list.reading_time_less_one_minute'|trans|raw }}{% endif %}
</span>
</div>
<p>{{ entry.content|striptags|slice(0, 300)|raw }}&hellip;</p>
<p>{{ entry.content|striptags|slice(0, 250)|raw }}&hellip;</p>
<ul class="card-entry-labels-hidden">
{% for tag in entry.tags %}
@ -75,8 +76,9 @@
{% endif %}
<div class="card-action">
<span class="bold">
<a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.list.original_article'|trans }}: {{ entry.title|e }} - {{ entry.domainName|removeWww }}" class="tool original grey-text"><span>{{ entry.domainName|removeWww|truncate(18) }}</span></a>
<span class="reading-time grey-text">
<i class="material-icons" title="{{ 'entry.list.reading_time'|trans }}">timer</i>
{{ entry.readingTime / app.user.config.readingSpeed }} min
</span>
<ul class="tools right">
@ -92,21 +94,29 @@
{% endfor %}
</ul>
{% if entries.getNbPages > 1 %}
{{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }}
{% endif %}
<!-- Export -->
<div id="export" class="side-nav fixed right-aligned">
{% set currentRoute = app.request.attributes.get('_route') %}
{% set currentTag = '' %}
{% if tag is defined %}
{% set currentTag = tag %}
{% endif %}
{% if currentRoute == 'homepage' %}
{% set currentRoute = 'unread' %}
{% endif %}
<h4 class="center">{{ 'entry.list.export_title'|trans }}</h4>
<ul>
{% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'epub' }) }}">EPUB</a></li>{% endif %}
{% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'mobi' }) }}">MOBI</a></li>{% endif %}
{% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'pdf' }) }}">PDF</a></li>{% endif %}
{% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json' }) }}">JSON</a></li>{% endif %}
{% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv' }) }}">CSV</a></li>{% endif %}
{% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'txt' }) }}">TXT</a></li>{% endif %}
{% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml' }) }}">XML</a></li>{% endif %}
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
{% 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 %}
</ul>
</div>

View File

@ -212,27 +212,38 @@
<h1>{{ entry.title|raw }} <a href="{{ path('edit', { 'id': entry.id }) }}" title="{{ 'entry.view.edit_title'|trans }}">✎</a></h1>
</header>
<aside>
{% set readingTime = entry.readingTime / app.user.config.readingSpeed %}
<i class="material-icons">timer</i>
<span class="link">
{% if readingTime > 0 %}
{{ 'entry.list.reading_time_minutes_short'|trans({'%readingTime%': readingTime|round}) }}
{% else %}
{{ 'entry.list.reading_time_less_one_minute_short'|trans|raw }}
{% endif %}
</span>
<i class="material-icons" title="{{ 'entry.view.created_at'|trans }}">today</i><span class="link">{{ entry.createdAt|date('Y-m-d') }}</span>
<a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e }}" class="tool">
<i class="material-icons link">link</i> <span class="link">{{ entry.domainName|removeWww }}</span>
</a>
<span class="tool"><i class="material-icons link">comment</i></span> <span class="link">{{ 'entry.view.annotations_on_the_entry'|transchoice(entry.annotations | length) }}</span>
<div id="list">
{% for tag in entry.tags %}
<div class="chip">
<a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a> <a href="{{ path('remove_tag', { 'entry': entry.id, 'tag': tag.id }) }}"><i class="material-icons">delete</i></a>
</div>
{% endfor %}
</div>
<ul class="tools">
<li>
{% set readingTime = entry.readingTime / app.user.config.readingSpeed %}
<i class="material-icons">timer</i>
{% if readingTime > 0 %}
{{ 'entry.list.reading_time_minutes_short'|trans({'%readingTime%': readingTime|round}) }}
{% else %}
{{ 'entry.list.reading_time_less_one_minute_short'|trans|raw }}
{% endif %}
</li>
<li>
<i class="material-icons" title="{{ 'entry.view.created_at'|trans }}">today</i>
{{ entry.createdAt|date('Y-m-d') }}
</li>
<li>
<i class="material-icons link">link</i>
<a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e }}" class="tool">
{{ entry.domainName|removeWww }}
</a>
</li>
<li>
<i class="material-icons link">comment</i>
{{ 'entry.view.annotations_on_the_entry'|transchoice(entry.annotations | length) }}
</li>
<li id="list">
{% for tag in entry.tags %}
<div class="chip">
<a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a> <a href="{{ path('remove_tag', { 'entry': entry.id, 'tag': tag.id }) }}"><i class="material-icons">delete</i></a>
</div>
{% endfor %}
</li>
</ul>
<div class="input-field nav-panel-add-tag" style="display: none">
{{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }}

View File

@ -1,10 +0,0 @@
{% block pager %}
<div class="results clearfix">
<div class="nb-results left">
{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}
</div>
{% if entries.getNbPages > 1 %}
{{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }}
{% endif %}
</div>
{% endblock %}

View File

@ -127,12 +127,12 @@
<div class="footer-copyright">
<div class="container">
<div class="row">
<div class="col s8">
<p>
<div class="col m12 l8 hide-on-small-only">
<p title="{{ display_stats() | raw | striptags }}">
{{ display_stats() }}
</p>
</div>
<div class="col s4">
<div class="col s12 l4">
<p>
{{ 'footer.wallabag.powered_by'|trans }} <a target="_blank" href="https://wallabag.org" class="grey-text text-lighten-4">wallabag</a>
<a class="grey-text text-lighten-4" href="{{ path('about') }}">{{ 'footer.wallabag.about'|trans|lower }}</a>

View File

@ -138,8 +138,11 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa
$interval = $user->getCreatedAt()->diff(new \DateTime('now'));
$nbDays = (int) $interval->format('%a') ?: 1;
// force setlocale for date translation
setlocale(LC_TIME, strtolower($user->getConfig()->getLanguage()).'_'.strtoupper(strtolower($user->getConfig()->getLanguage())));
return $this->translator->trans('footer.stats', [
'%user_creation%' => $user->getCreatedAt()->format('F jS, Y'),
'%user_creation%' => strftime('%e %B %Y', $user->getCreatedAt()->getTimestamp()),
'%nb_archives%' => $nbArchives,
'%per_day%' => round($nbArchives / $nbDays, 2),
]);

View File

@ -50,6 +50,9 @@ class ImportCommand extends ContainerAwareCommand
case 'chrome':
$wallabag = $this->getContainer()->get('wallabag_import.chrome.import');
break;
case 'instapaper':
$wallabag = $this->getContainer()->get('wallabag_import.instapaper.import');
break;
case 'v1':
default:
$wallabag = $this->getContainer()->get('wallabag_import.wallabag_v1.import');

View File

@ -50,9 +50,10 @@ abstract class AbstractConsumer
$entry = $this->import->parseEntry($storedEntry);
if (null === $entry) {
$this->logger->warning('Unable to parse entry', ['entry' => $storedEntry]);
$this->logger->warning('Entry already exists', ['entry' => $storedEntry]);
return false;
// return true to skip message
return true;
}
try {

View File

@ -139,12 +139,24 @@ abstract class BrowserImport extends AbstractImport
public function parseEntry(array $importedEntry)
{
if ((!array_key_exists('guid', $importedEntry) || (!array_key_exists('id', $importedEntry))) && is_array(reset($importedEntry))) {
if ($this->producer) {
$this->parseEntriesForProducer($importedEntry);
return;
}
$this->parseEntries($importedEntry);
return;
}
if (array_key_exists('children', $importedEntry)) {
if ($this->producer) {
$this->parseEntriesForProducer($importedEntry['children']);
return;
}
$this->parseEntries($importedEntry['children']);
return;

View File

@ -20,7 +20,6 @@ services:
arguments:
- "@doctrine.orm.entity_manager"
- "@wallabag_core.content_proxy"
- "@craue_config"
calls:
- [ setClient, [ "@wallabag_import.pocket.client" ] ]
- [ setLogger, [ "@logger" ]]

View File

@ -4,7 +4,6 @@ namespace Wallabag\UserBundle\Mailer;
use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface;
use Scheb\TwoFactorBundle\Mailer\AuthCodeMailerInterface;
use Craue\ConfigBundle\Util\Config;
/**
* Custom mailer for TwoFactorBundle email.
@ -61,16 +60,17 @@ class AuthCodeMailer implements AuthCodeMailerInterface
* @param \Twig_Environment $twig
* @param string $senderEmail
* @param string $senderName
* @param Config $craueConfig Craue\Config instance to get wallabag support url from database
* @param string $supportUrl wallabag support url
* @param string $wallabagUrl wallabag instance url
*/
public function __construct(\Swift_Mailer $mailer, \Twig_Environment $twig, $senderEmail, $senderName, Config $craueConfig)
public function __construct(\Swift_Mailer $mailer, \Twig_Environment $twig, $senderEmail, $senderName, $supportUrl, $wallabagUrl)
{
$this->mailer = $mailer;
$this->twig = $twig;
$this->senderEmail = $senderEmail;
$this->senderName = $senderName;
$this->supportUrl = $craueConfig->get('wallabag_support_url');
$this->wallabagUrl = $craueConfig->get('wallabag_url');
$this->supportUrl = $supportUrl;
$this->wallabagUrl = $wallabagUrl;
}
/**

View File

@ -6,7 +6,8 @@ services:
- "@twig"
- "%scheb_two_factor.email.sender_email%"
- "%scheb_two_factor.email.sender_name%"
- "@craue_config"
- '@=service(''craue_config'').get(''wallabag_support_url'')'
- '@=service(''craue_config'').get(''wallabag_url'')'
wallabag_user.password_resetting:
class: Wallabag\UserBundle\EventListener\PasswordResettingListener

View File

@ -1,11 +1,11 @@
# Two factor mail
auth_code:
on: 'sur'
on: "sur"
mailer:
subject: "Code d'authentification wallabag"
subject: "Code dauthentification wallabag"
body:
hello: "Bonjour %user%,"
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."
second_para: "Voici le code à renseigner :"
support: "Si vous avez un problème de connexion, n'hésitez pas à contacter le support :"
signature: "L'équipe wallabag"
support: "Si vous avez un problème de connexion, nhésitez pas à contacter le support :"
signature: "Léquipe wallabag"

View File

@ -0,0 +1,673 @@
<?php
namespace Tests\Wallabag\ApiBundle\Controller;
use Tests\Wallabag\ApiBundle\WallabagApiTestCase;
use Wallabag\CoreBundle\Entity\Tag;
class EntryRestControllerTest extends WallabagApiTestCase
{
public function testGetOneEntry()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(['user' => 1, 'isArchived' => false]);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('GET', '/api/entries/'.$entry->getId().'.json');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getTitle(), $content['title']);
$this->assertEquals($entry->getUrl(), $content['url']);
$this->assertCount(count($entry->getTags()), $content['tags']);
$this->assertEquals($entry->getUserName(), $content['user_name']);
$this->assertEquals($entry->getUserEmail(), $content['user_email']);
$this->assertEquals($entry->getUserId(), $content['user_id']);
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetOneEntryWrongUser()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(['user' => 2, 'isArchived' => false]);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('GET', '/api/entries/'.$entry->getId().'.json');
$this->assertEquals(403, $this->client->getResponse()->getStatusCode());
}
public function testGetEntries()
{
$this->client->request('GET', '/api/entries');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetEntriesWithFullOptions()
{
$this->client->request('GET', '/api/entries', [
'archive' => 1,
'starred' => 1,
'sort' => 'updated',
'order' => 'asc',
'page' => 1,
'perPage' => 2,
'tags' => 'foo',
'since' => 1443274283,
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertArrayHasKey('items', $content['_embedded']);
$this->assertGreaterThanOrEqual(0, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertEquals(2, $content['limit']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertArrayHasKey('_links', $content);
$this->assertArrayHasKey('self', $content['_links']);
$this->assertArrayHasKey('first', $content['_links']);
$this->assertArrayHasKey('last', $content['_links']);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertContains('archive=1', $content['_links'][$link]['href']);
$this->assertContains('starred=1', $content['_links'][$link]['href']);
$this->assertContains('sort=updated', $content['_links'][$link]['href']);
$this->assertContains('order=asc', $content['_links'][$link]['href']);
$this->assertContains('tags=foo', $content['_links'][$link]['href']);
$this->assertContains('since=1443274283', $content['_links'][$link]['href']);
}
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetStarredEntries()
{
$this->client->request('GET', '/api/entries', ['starred' => 1, 'sort' => 'updated']);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertArrayHasKey('_links', $content);
$this->assertArrayHasKey('self', $content['_links']);
$this->assertArrayHasKey('first', $content['_links']);
$this->assertArrayHasKey('last', $content['_links']);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertContains('starred=1', $content['_links'][$link]['href']);
$this->assertContains('sort=updated', $content['_links'][$link]['href']);
}
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetArchiveEntries()
{
$this->client->request('GET', '/api/entries', ['archive' => 1]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertArrayHasKey('_links', $content);
$this->assertArrayHasKey('self', $content['_links']);
$this->assertArrayHasKey('first', $content['_links']);
$this->assertArrayHasKey('last', $content['_links']);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertContains('archive=1', $content['_links'][$link]['href']);
}
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetTaggedEntries()
{
$this->client->request('GET', '/api/entries', ['tags' => 'foo,bar']);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertArrayHasKey('_links', $content);
$this->assertArrayHasKey('self', $content['_links']);
$this->assertArrayHasKey('first', $content['_links']);
$this->assertArrayHasKey('last', $content['_links']);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertContains('tags='.urlencode('foo,bar'), $content['_links'][$link]['href']);
}
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetDatedEntries()
{
$this->client->request('GET', '/api/entries', ['since' => 1443274283]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertArrayHasKey('_links', $content);
$this->assertArrayHasKey('self', $content['_links']);
$this->assertArrayHasKey('first', $content['_links']);
$this->assertArrayHasKey('last', $content['_links']);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertContains('since=1443274283', $content['_links'][$link]['href']);
}
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetDatedSupEntries()
{
$future = new \DateTime(date('Y-m-d H:i:s'));
$this->client->request('GET', '/api/entries', ['since' => $future->getTimestamp() + 1000]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertEmpty($content['_embedded']['items']);
$this->assertEquals(0, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertEquals(1, $content['pages']);
$this->assertArrayHasKey('_links', $content);
$this->assertArrayHasKey('self', $content['_links']);
$this->assertArrayHasKey('first', $content['_links']);
$this->assertArrayHasKey('last', $content['_links']);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertContains('since='.($future->getTimestamp() + 1000), $content['_links'][$link]['href']);
}
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testDeleteEntry()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('DELETE', '/api/entries/'.$entry->getId().'.json');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getTitle(), $content['title']);
$this->assertEquals($entry->getUrl(), $content['url']);
// We'll try to delete this entry again
$this->client->request('DELETE', '/api/entries/'.$entry->getId().'.json');
$this->assertEquals(404, $this->client->getResponse()->getStatusCode());
}
public function testPostEntry()
{
$this->client->request('POST', '/api/entries.json', [
'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',
'tags' => 'google',
'title' => 'New title for my article',
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content['id']);
$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']);
$this->assertEquals(false, $content['is_archived']);
$this->assertEquals(false, $content['is_starred']);
$this->assertEquals('New title for my article', $content['title']);
$this->assertEquals(1, $content['user_id']);
$this->assertCount(1, $content['tags']);
}
public function testPostSameEntry()
{
$this->client->request('POST', '/api/entries.json', [
'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',
'archive' => '1',
'tags' => 'google, apple',
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content['id']);
$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']);
$this->assertEquals(true, $content['is_archived']);
$this->assertEquals(false, $content['is_starred']);
$this->assertCount(2, $content['tags']);
}
public function testPostArchivedAndStarredEntry()
{
$this->client->request('POST', '/api/entries.json', [
'url' => 'http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html',
'archive' => '1',
'starred' => '1',
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content['id']);
$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']);
$this->assertEquals(true, $content['is_archived']);
$this->assertEquals(true, $content['is_starred']);
$this->assertEquals(1, $content['user_id']);
}
public function testPostArchivedAndStarredEntryWithoutQuotes()
{
$this->client->request('POST', '/api/entries.json', [
'url' => 'http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html',
'archive' => 0,
'starred' => 1,
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content['id']);
$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']);
$this->assertEquals(false, $content['is_archived']);
$this->assertEquals(true, $content['is_starred']);
}
public function testPatchEntry()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
// hydrate the tags relations
$nbTags = count($entry->getTags());
$this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [
'title' => 'New awesome title',
'tags' => 'new tag '.uniqid(),
'starred' => '1',
'archive' => '0',
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getId(), $content['id']);
$this->assertEquals($entry->getUrl(), $content['url']);
$this->assertEquals('New awesome title', $content['title']);
$this->assertGreaterThan($nbTags, count($content['tags']));
$this->assertEquals(1, $content['user_id']);
}
public function testPatchEntryWithoutQuotes()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
// hydrate the tags relations
$nbTags = count($entry->getTags());
$this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [
'title' => 'New awesome title',
'tags' => 'new tag '.uniqid(),
'starred' => 1,
'archive' => 0,
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getId(), $content['id']);
$this->assertEquals($entry->getUrl(), $content['url']);
$this->assertEquals('New awesome title', $content['title']);
$this->assertGreaterThan($nbTags, count($content['tags']));
}
public function testGetTagsEntry()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneWithTags($this->user->getId());
$entry = $entry[0];
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$tags = [];
foreach ($entry->getTags() as $tag) {
$tags[] = ['id' => $tag->getId(), 'label' => $tag->getLabel(), 'slug' => $tag->getSlug()];
}
$this->client->request('GET', '/api/entries/'.$entry->getId().'/tags');
$this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $this->client->getResponse()->getContent());
}
public function testPostTagsOnEntry()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$nbTags = count($entry->getTags());
$newTags = 'tag1,tag2,tag3';
$this->client->request('POST', '/api/entries/'.$entry->getId().'/tags', ['tags' => $newTags]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('tags', $content);
$this->assertEquals($nbTags + 3, count($content['tags']));
$entryDB = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->find($entry->getId());
$tagsInDB = [];
foreach ($entryDB->getTags()->toArray() as $tag) {
$tagsInDB[$tag->getId()] = $tag->getLabel();
}
foreach (explode(',', $newTags) as $tag) {
$this->assertContains($tag, $tagsInDB);
}
}
public function testDeleteOneTagEntry()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneWithTags($this->user->getId());
$entry = $entry[0];
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
// hydrate the tags relations
$nbTags = count($entry->getTags());
$tag = $entry->getTags()[0];
$this->client->request('DELETE', '/api/entries/'.$entry->getId().'/tags/'.$tag->getId().'.json');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('tags', $content);
$this->assertEquals($nbTags - 1, count($content['tags']));
}
public function testSaveIsArchivedAfterPost()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(['user' => 1, 'isArchived' => true]);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('POST', '/api/entries.json', [
'url' => $entry->getUrl(),
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(true, $content['is_archived']);
}
public function testSaveIsStarredAfterPost()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(['user' => 1, 'isStarred' => true]);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('POST', '/api/entries.json', [
'url' => $entry->getUrl(),
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(true, $content['is_starred']);
}
public function testSaveIsArchivedAfterPatch()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(['user' => 1, 'isArchived' => true]);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [
'title' => $entry->getTitle().'++',
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(true, $content['is_archived']);
}
public function testSaveIsStarredAfterPatch()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(['user' => 1, 'isStarred' => true]);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [
'title' => $entry->getTitle().'++',
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(true, $content['is_starred']);
}
public function testGetEntriesExists()
{
$this->client->request('GET', '/api/entries/exists?url=http://0.0.0.0/entry2');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(true, $content['exists']);
}
public function testGetEntriesExistsWithManyUrls()
{
$url1 = 'http://0.0.0.0/entry2';
$url2 = 'http://0.0.0.0/entry10';
$this->client->request('GET', '/api/entries/exists?urls[]='.$url1.'&urls[]='.$url2);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey($url1, $content);
$this->assertArrayHasKey($url2, $content);
$this->assertEquals(true, $content[$url1]);
$this->assertEquals(false, $content[$url2]);
}
public function testGetEntriesExistsWhichDoesNotExists()
{
$this->client->request('GET', '/api/entries/exists?url=http://google.com/entry2');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(false, $content['exists']);
}
public function testGetEntriesExistsWithNoUrl()
{
$this->client->request('GET', '/api/entries/exists?url=');
$this->assertEquals(403, $this->client->getResponse()->getStatusCode());
}
}

View File

@ -0,0 +1,162 @@
<?php
namespace Tests\Wallabag\ApiBundle\Controller;
use Tests\Wallabag\ApiBundle\WallabagApiTestCase;
use Wallabag\CoreBundle\Entity\Tag;
class TagRestControllerTest extends WallabagApiTestCase
{
public function testGetUserTags()
{
$this->client->request('GET', '/api/tags.json');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content);
$this->assertArrayHasKey('id', $content[0]);
$this->assertArrayHasKey('label', $content[0]);
return end($content);
}
/**
* @depends testGetUserTags
*/
public function testDeleteUserTag($tag)
{
$tagName = $tag['label'];
$this->client->request('DELETE', '/api/tags/'.$tag['id'].'.json');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('label', $content);
$this->assertEquals($tag['label'], $content['label']);
$this->assertEquals($tag['slug'], $content['slug']);
$entries = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findAllByTagId($this->user->getId(), $tag['id']);
$this->assertCount(0, $entries);
$tag = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Tag')
->findOneByLabel($tagName);
$this->assertNull($tag, $tagName.' was removed because it begun an orphan tag');
}
public function testDeleteTagByLabel()
{
$em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneWithTags($this->user->getId());
$entry = $entry[0];
$tag = new Tag();
$tag->setLabel('Awesome tag for test');
$em->persist($tag);
$entry->addTag($tag);
$em->persist($entry);
$em->flush();
$this->client->request('DELETE', '/api/tag/label.json', ['tag' => $tag->getLabel()]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('label', $content);
$this->assertEquals($tag->getLabel(), $content['label']);
$this->assertEquals($tag->getSlug(), $content['slug']);
$entries = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findAllByTagId($this->user->getId(), $tag->getId());
$this->assertCount(0, $entries);
}
public function testDeleteTagByLabelNotFound()
{
$this->client->request('DELETE', '/api/tag/label.json', ['tag' => 'does not exist']);
$this->assertEquals(404, $this->client->getResponse()->getStatusCode());
}
public function testDeleteTagsByLabel()
{
$em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneWithTags($this->user->getId());
$entry = $entry[0];
$tag = new Tag();
$tag->setLabel('Awesome tag for tagsLabel');
$em->persist($tag);
$tag2 = new Tag();
$tag2->setLabel('Awesome tag for tagsLabel 2');
$em->persist($tag2);
$entry->addTag($tag);
$entry->addTag($tag2);
$em->persist($entry);
$em->flush();
$this->client->request('DELETE', '/api/tags/label.json', ['tags' => $tag->getLabel().','.$tag2->getLabel()]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertCount(2, $content);
$this->assertArrayHasKey('label', $content[0]);
$this->assertEquals($tag->getLabel(), $content[0]['label']);
$this->assertEquals($tag->getSlug(), $content[0]['slug']);
$this->assertArrayHasKey('label', $content[1]);
$this->assertEquals($tag2->getLabel(), $content[1]['label']);
$this->assertEquals($tag2->getSlug(), $content[1]['slug']);
$entries = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findAllByTagId($this->user->getId(), $tag->getId());
$this->assertCount(0, $entries);
$entries = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findAllByTagId($this->user->getId(), $tag2->getId());
$this->assertCount(0, $entries);
}
public function testDeleteTagsByLabelNotFound()
{
$this->client->request('DELETE', '/api/tags/label.json', ['tags' => 'does not exist']);
$this->assertEquals(404, $this->client->getResponse()->getStatusCode());
}
}

View File

@ -3,697 +3,9 @@
namespace Tests\Wallabag\ApiBundle\Controller;
use Tests\Wallabag\ApiBundle\WallabagApiTestCase;
use Wallabag\CoreBundle\Entity\Tag;
class WallabagRestControllerTest extends WallabagApiTestCase
{
protected static $salt;
public function testGetOneEntry()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(['user' => 1, 'isArchived' => false]);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('GET', '/api/entries/'.$entry->getId().'.json');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getTitle(), $content['title']);
$this->assertEquals($entry->getUrl(), $content['url']);
$this->assertCount(count($entry->getTags()), $content['tags']);
$this->assertEquals($entry->getUserName(), $content['user_name']);
$this->assertEquals($entry->getUserEmail(), $content['user_email']);
$this->assertEquals($entry->getUserId(), $content['user_id']);
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetOneEntryWrongUser()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(['user' => 2, 'isArchived' => false]);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('GET', '/api/entries/'.$entry->getId().'.json');
$this->assertEquals(403, $this->client->getResponse()->getStatusCode());
}
public function testGetEntries()
{
$this->client->request('GET', '/api/entries');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetEntriesWithFullOptions()
{
$this->client->request('GET', '/api/entries', [
'archive' => 1,
'starred' => 1,
'sort' => 'updated',
'order' => 'asc',
'page' => 1,
'perPage' => 2,
'tags' => 'foo',
'since' => 1443274283,
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertArrayHasKey('items', $content['_embedded']);
$this->assertGreaterThanOrEqual(0, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertEquals(2, $content['limit']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertArrayHasKey('_links', $content);
$this->assertArrayHasKey('self', $content['_links']);
$this->assertArrayHasKey('first', $content['_links']);
$this->assertArrayHasKey('last', $content['_links']);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertContains('archive=1', $content['_links'][$link]['href']);
$this->assertContains('starred=1', $content['_links'][$link]['href']);
$this->assertContains('sort=updated', $content['_links'][$link]['href']);
$this->assertContains('order=asc', $content['_links'][$link]['href']);
$this->assertContains('tags=foo', $content['_links'][$link]['href']);
$this->assertContains('since=1443274283', $content['_links'][$link]['href']);
}
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetStarredEntries()
{
$this->client->request('GET', '/api/entries', ['starred' => 1, 'sort' => 'updated']);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertArrayHasKey('_links', $content);
$this->assertArrayHasKey('self', $content['_links']);
$this->assertArrayHasKey('first', $content['_links']);
$this->assertArrayHasKey('last', $content['_links']);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertContains('starred=1', $content['_links'][$link]['href']);
$this->assertContains('sort=updated', $content['_links'][$link]['href']);
}
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetArchiveEntries()
{
$this->client->request('GET', '/api/entries', ['archive' => 1]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertArrayHasKey('_links', $content);
$this->assertArrayHasKey('self', $content['_links']);
$this->assertArrayHasKey('first', $content['_links']);
$this->assertArrayHasKey('last', $content['_links']);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertContains('archive=1', $content['_links'][$link]['href']);
}
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetTaggedEntries()
{
$this->client->request('GET', '/api/entries', ['tags' => 'foo,bar']);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertArrayHasKey('_links', $content);
$this->assertArrayHasKey('self', $content['_links']);
$this->assertArrayHasKey('first', $content['_links']);
$this->assertArrayHasKey('last', $content['_links']);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertContains('tags='.urlencode('foo,bar'), $content['_links'][$link]['href']);
}
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetDatedEntries()
{
$this->client->request('GET', '/api/entries', ['since' => 1443274283]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertArrayHasKey('_links', $content);
$this->assertArrayHasKey('self', $content['_links']);
$this->assertArrayHasKey('first', $content['_links']);
$this->assertArrayHasKey('last', $content['_links']);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertContains('since=1443274283', $content['_links'][$link]['href']);
}
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testGetDatedSupEntries()
{
$future = new \DateTime(date('Y-m-d H:i:s'));
$this->client->request('GET', '/api/entries', ['since' => $future->getTimestamp() + 1000]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertEmpty($content['_embedded']['items']);
$this->assertEquals(0, $content['total']);
$this->assertEquals(1, $content['page']);
$this->assertEquals(1, $content['pages']);
$this->assertArrayHasKey('_links', $content);
$this->assertArrayHasKey('self', $content['_links']);
$this->assertArrayHasKey('first', $content['_links']);
$this->assertArrayHasKey('last', $content['_links']);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertContains('since='.($future->getTimestamp() + 1000), $content['_links'][$link]['href']);
}
$this->assertTrue(
$this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
);
}
public function testDeleteEntry()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('DELETE', '/api/entries/'.$entry->getId().'.json');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getTitle(), $content['title']);
$this->assertEquals($entry->getUrl(), $content['url']);
// We'll try to delete this entry again
$this->client->request('DELETE', '/api/entries/'.$entry->getId().'.json');
$this->assertEquals(404, $this->client->getResponse()->getStatusCode());
}
public function testPostEntry()
{
$this->client->request('POST', '/api/entries.json', [
'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',
'tags' => 'google',
'title' => 'New title for my article',
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content['id']);
$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']);
$this->assertEquals(false, $content['is_archived']);
$this->assertEquals(false, $content['is_starred']);
$this->assertEquals('New title for my article', $content['title']);
$this->assertEquals(1, $content['user_id']);
$this->assertCount(1, $content['tags']);
}
public function testPostSameEntry()
{
$this->client->request('POST', '/api/entries.json', [
'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',
'archive' => '1',
'tags' => 'google, apple',
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content['id']);
$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']);
$this->assertEquals(true, $content['is_archived']);
$this->assertEquals(false, $content['is_starred']);
$this->assertCount(2, $content['tags']);
}
public function testPostArchivedAndStarredEntry()
{
$this->client->request('POST', '/api/entries.json', [
'url' => 'http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html',
'archive' => '1',
'starred' => '1',
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content['id']);
$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']);
$this->assertEquals(true, $content['is_archived']);
$this->assertEquals(true, $content['is_starred']);
$this->assertEquals(1, $content['user_id']);
}
public function testPostArchivedAndStarredEntryWithoutQuotes()
{
$this->client->request('POST', '/api/entries.json', [
'url' => 'http://www.lemonde.fr/idees/article/2016/02/08/preserver-la-liberte-d-expression-sur-les-reseaux-sociaux_4861503_3232.html',
'archive' => 0,
'starred' => 1,
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content['id']);
$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']);
$this->assertEquals(false, $content['is_archived']);
$this->assertEquals(true, $content['is_starred']);
}
public function testPatchEntry()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
// hydrate the tags relations
$nbTags = count($entry->getTags());
$this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [
'title' => 'New awesome title',
'tags' => 'new tag '.uniqid(),
'starred' => '1',
'archive' => '0',
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getId(), $content['id']);
$this->assertEquals($entry->getUrl(), $content['url']);
$this->assertEquals('New awesome title', $content['title']);
$this->assertGreaterThan($nbTags, count($content['tags']));
$this->assertEquals(1, $content['user_id']);
}
public function testPatchEntryWithoutQuotes()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
// hydrate the tags relations
$nbTags = count($entry->getTags());
$this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [
'title' => 'New awesome title',
'tags' => 'new tag '.uniqid(),
'starred' => 1,
'archive' => 0,
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getId(), $content['id']);
$this->assertEquals($entry->getUrl(), $content['url']);
$this->assertEquals('New awesome title', $content['title']);
$this->assertGreaterThan($nbTags, count($content['tags']));
}
public function testGetTagsEntry()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneWithTags($this->user->getId());
$entry = $entry[0];
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$tags = [];
foreach ($entry->getTags() as $tag) {
$tags[] = ['id' => $tag->getId(), 'label' => $tag->getLabel(), 'slug' => $tag->getSlug()];
}
$this->client->request('GET', '/api/entries/'.$entry->getId().'/tags');
$this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $this->client->getResponse()->getContent());
}
public function testPostTagsOnEntry()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$nbTags = count($entry->getTags());
$newTags = 'tag1,tag2,tag3';
$this->client->request('POST', '/api/entries/'.$entry->getId().'/tags', ['tags' => $newTags]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('tags', $content);
$this->assertEquals($nbTags + 3, count($content['tags']));
$entryDB = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->find($entry->getId());
$tagsInDB = [];
foreach ($entryDB->getTags()->toArray() as $tag) {
$tagsInDB[$tag->getId()] = $tag->getLabel();
}
foreach (explode(',', $newTags) as $tag) {
$this->assertContains($tag, $tagsInDB);
}
}
public function testDeleteOneTagEntry()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneWithTags($this->user->getId());
$entry = $entry[0];
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
// hydrate the tags relations
$nbTags = count($entry->getTags());
$tag = $entry->getTags()[0];
$this->client->request('DELETE', '/api/entries/'.$entry->getId().'/tags/'.$tag->getId().'.json');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('tags', $content);
$this->assertEquals($nbTags - 1, count($content['tags']));
}
public function testGetUserTags()
{
$this->client->request('GET', '/api/tags.json');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content);
$this->assertArrayHasKey('id', $content[0]);
$this->assertArrayHasKey('label', $content[0]);
return end($content);
}
/**
* @depends testGetUserTags
*/
public function testDeleteUserTag($tag)
{
$tagName = $tag['label'];
$this->client->request('DELETE', '/api/tags/'.$tag['id'].'.json');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('label', $content);
$this->assertEquals($tag['label'], $content['label']);
$this->assertEquals($tag['slug'], $content['slug']);
$entries = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findAllByTagId($this->user->getId(), $tag['id']);
$this->assertCount(0, $entries);
$tag = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Tag')
->findOneByLabel($tagName);
$this->assertNull($tag, $tagName.' was removed because it begun an orphan tag');
}
public function testDeleteTagByLabel()
{
$em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneWithTags($this->user->getId());
$entry = $entry[0];
$tag = new Tag();
$tag->setLabel('Awesome tag for test');
$em->persist($tag);
$entry->addTag($tag);
$em->persist($entry);
$em->flush();
$this->client->request('DELETE', '/api/tag/label.json', ['tag' => $tag->getLabel()]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('label', $content);
$this->assertEquals($tag->getLabel(), $content['label']);
$this->assertEquals($tag->getSlug(), $content['slug']);
$entries = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findAllByTagId($this->user->getId(), $tag->getId());
$this->assertCount(0, $entries);
}
public function testDeleteTagByLabelNotFound()
{
$this->client->request('DELETE', '/api/tag/label.json', ['tag' => 'does not exist']);
$this->assertEquals(404, $this->client->getResponse()->getStatusCode());
}
public function testDeleteTagsByLabel()
{
$em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneWithTags($this->user->getId());
$entry = $entry[0];
$tag = new Tag();
$tag->setLabel('Awesome tag for tagsLabel');
$em->persist($tag);
$tag2 = new Tag();
$tag2->setLabel('Awesome tag for tagsLabel 2');
$em->persist($tag2);
$entry->addTag($tag);
$entry->addTag($tag2);
$em->persist($entry);
$em->flush();
$this->client->request('DELETE', '/api/tags/label.json', ['tags' => $tag->getLabel().','.$tag2->getLabel()]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertCount(2, $content);
$this->assertArrayHasKey('label', $content[0]);
$this->assertEquals($tag->getLabel(), $content[0]['label']);
$this->assertEquals($tag->getSlug(), $content[0]['slug']);
$this->assertArrayHasKey('label', $content[1]);
$this->assertEquals($tag2->getLabel(), $content[1]['label']);
$this->assertEquals($tag2->getSlug(), $content[1]['slug']);
$entries = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findAllByTagId($this->user->getId(), $tag->getId());
$this->assertCount(0, $entries);
$entries = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findAllByTagId($this->user->getId(), $tag2->getId());
$this->assertCount(0, $entries);
}
public function testDeleteTagsByLabelNotFound()
{
$this->client->request('DELETE', '/api/tags/label.json', ['tags' => 'does not exist']);
$this->assertEquals(404, $this->client->getResponse()->getStatusCode());
}
public function testGetVersion()
{
$this->client->request('GET', '/api/version');
@ -704,136 +16,4 @@ class WallabagRestControllerTest extends WallabagApiTestCase
$this->assertEquals($this->client->getContainer()->getParameter('wallabag_core.version'), $content);
}
public function testSaveIsArchivedAfterPost()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(['user' => 1, 'isArchived' => true]);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('POST', '/api/entries.json', [
'url' => $entry->getUrl(),
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(true, $content['is_archived']);
}
public function testSaveIsStarredAfterPost()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(['user' => 1, 'isStarred' => true]);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('POST', '/api/entries.json', [
'url' => $entry->getUrl(),
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(true, $content['is_starred']);
}
public function testSaveIsArchivedAfterPatch()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(['user' => 1, 'isArchived' => true]);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [
'title' => $entry->getTitle().'++',
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(true, $content['is_archived']);
}
public function testSaveIsStarredAfterPatch()
{
$entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(['user' => 1, 'isStarred' => true]);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [
'title' => $entry->getTitle().'++',
]);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(true, $content['is_starred']);
}
public function testGetEntriesExists()
{
$this->client->request('GET', '/api/entries/exists?url=http://0.0.0.0/entry2');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(true, $content['exists']);
}
public function testGetEntriesExistsWithManyUrls()
{
$url1 = 'http://0.0.0.0/entry2';
$url2 = 'http://0.0.0.0/entry10';
$this->client->request('GET', '/api/entries/exists?urls[]='.$url1.'&urls[]='.$url2);
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey($url1, $content);
$this->assertArrayHasKey($url2, $content);
$this->assertEquals(true, $content[$url1]);
$this->assertEquals(false, $content[$url2]);
}
public function testGetEntriesExistsWhichDoesNotExists()
{
$this->client->request('GET', '/api/entries/exists?url=http://google.com/entry2');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(false, $content['exists']);
}
public function testGetEntriesExistsWithNoUrl()
{
$this->client->request('GET', '/api/entries/exists?url=');
$this->assertEquals(403, $this->client->getResponse()->getStatusCode());
}
}

Some files were not shown because too many files have changed in this diff Show More