aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorThomas Citharel <tcit@tcit.fr>2017-05-04 14:35:14 +0200
committerGitHub <noreply@github.com>2017-05-04 14:35:14 +0200
commit6b76ae3d1f6a061237f983622b2d3708beded368 (patch)
treec0085b25c6ff8eec25f2b785cf985a8e95f25b09
parent3b4502e0e663866e7bac00164fd935fdc92309d6 (diff)
parentb5b6877976bc32f23e51c2fb0f3f973f0d571b10 (diff)
downloadwallabag-6b76ae3d1f6a061237f983622b2d3708beded368.tar.gz
wallabag-6b76ae3d1f6a061237f983622b2d3708beded368.tar.zst
wallabag-6b76ae3d1f6a061237f983622b2d3708beded368.zip
Merge pull request #3060 from wallabag/search-users
Search & paginate users
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.da.yml2
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.de.yml2
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.en.yml2
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.es.yml2
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml2
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml140
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.it.yml2
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml2
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml2
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml2
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml2
-rw-r--r--src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml2
-rw-r--r--src/Wallabag/UserBundle/Controller/ManageController.php67
-rw-r--r--src/Wallabag/UserBundle/Form/SearchUserType.php29
-rw-r--r--src/Wallabag/UserBundle/Repository/UserRepository.php13
-rw-r--r--src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig77
-rw-r--r--tests/Wallabag/UserBundle/Controller/ManageControllerTest.php26
17 files changed, 256 insertions, 118 deletions
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
index e5211b57..57319af7 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
@@ -512,6 +512,8 @@ user:
512 # delete: Delete 512 # delete: Delete
513 # delete_confirm: Are you sure? 513 # delete_confirm: Are you sure?
514 # back_to_list: Back to list 514 # back_to_list: Back to list
515 search:
516 # placeholder: Filter by username or email
515 517
516error: 518error:
517 # page_title: An error occurred 519 # page_title: An error occurred
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
index 893a4564..a7bcecc6 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
@@ -513,6 +513,8 @@ user:
513 delete: Löschen 513 delete: Löschen
514 delete_confirm: Bist du sicher? 514 delete_confirm: Bist du sicher?
515 back_to_list: Zurück zur Liste 515 back_to_list: Zurück zur Liste
516 search:
517 # placeholder: Filter by username or email
516 518
517error: 519error:
518 page_title: Ein Fehler ist aufgetreten 520 page_title: Ein Fehler ist aufgetreten
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
index 4b745683..1ef2874d 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
@@ -513,6 +513,8 @@ user:
513 delete: Delete 513 delete: Delete
514 delete_confirm: Are you sure? 514 delete_confirm: Are you sure?
515 back_to_list: Back to list 515 back_to_list: Back to list
516 search:
517 placeholder: Filter by username or email
516 518
517error: 519error:
518 page_title: An error occurred 520 page_title: An error occurred
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
index 99d25859..6cd079b0 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
@@ -513,6 +513,8 @@ user:
513 delete: Eliminar 513 delete: Eliminar
514 delete_confirm: ¿Estás seguro? 514 delete_confirm: ¿Estás seguro?
515 back_to_list: Volver a la lista 515 back_to_list: Volver a la lista
516 search:
517 # placeholder: Filter by username or email
516 518
517error: 519error:
518 page_title: Ha ocurrido un error 520 page_title: Ha ocurrido un error
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
index ccd9d555..fb6e315e 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
@@ -513,6 +513,8 @@ user:
513 # delete: Delete 513 # delete: Delete
514 # delete_confirm: Are you sure? 514 # delete_confirm: Are you sure?
515 # back_to_list: Back to list 515 # back_to_list: Back to list
516 search:
517 # placeholder: Filter by username or email
516 518
517error: 519error:
518 # page_title: An error occurred 520 # page_title: An error occurred
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
index a0f100f7..ad886363 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
@@ -46,7 +46,7 @@ footer:
46 social: "Social" 46 social: "Social"
47 powered_by: "propulsé par" 47 powered_by: "propulsé par"
48 about: "À propos" 48 about: "À propos"
49 stats: Depuis le %user_creation%, vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour ! 49 stats: "Depuis le %user_creation%, vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour !"
50 50
51config: 51config:
52 page_title: "Configuration" 52 page_title: "Configuration"
@@ -71,16 +71,16 @@ config:
71 300_word: "Je lis environ 300 mots par minute" 71 300_word: "Je lis environ 300 mots par minute"
72 400_word: "Je lis environ 400 mots par minute" 72 400_word: "Je lis environ 400 mots par minute"
73 action_mark_as_read: 73 action_mark_as_read:
74 label: 'Où souhaitez-vous être redirigé après avoir marqué un article comme lu ?' 74 label: "Où souhaitez-vous être redirigé après avoir marqué un article comme lu ?"
75 redirect_homepage: "À la page d'accueil" 75 redirect_homepage: "À la page daccueil"
76 redirect_current_page: 'À la page courante' 76 redirect_current_page: "À la page courante"
77 pocket_consumer_key_label: Clé d’authentification Pocket pour importer les données 77 pocket_consumer_key_label: "Clé d’authentification Pocket pour importer les données"
78 android_configuration: Configurez votre application Android 78 android_configuration: "Configurez votre application Android"
79 help_theme: "L'affichage de wallabag est personnalisable. C'est ici que vous choisissez le thème que vous préférez." 79 help_theme: "L’affichage de wallabag est personnalisable. C’est ici que vous choisissez le thème que vous préférez."
80 help_items_per_page: "Vous pouvez définir le nombre d'articles affichés sur chaque page." 80 help_items_per_page: "Vous pouvez définir le nombre darticles affichés sur chaque page."
81 help_reading_speed: "wallabag calcule une durée de lecture pour chaque article. Vous pouvez définir ici, grâce à cette liste déroulante, si vous lisez plus ou moins vite. wallabag recalculera la durée de lecture de chaque article." 81 help_reading_speed: "wallabag calcule une durée de lecture pour chaque article. Vous pouvez définir ici, grâce à cette liste déroulante, si vous lisez plus ou moins vite. wallabag recalculera la durée de lecture de chaque article."
82 help_language: "Vous pouvez définir la langue de l'interface de wallabag." 82 help_language: "Vous pouvez définir la langue de linterface de wallabag."
83 help_pocket_consumer_key: "Nécessaire pour l'import depuis Pocket. Vous pouvez le créer depuis votre compte Pocket." 83 help_pocket_consumer_key: "Nécessaire pour l’import depuis Pocket. Vous pouvez le créer depuis votre compte Pocket."
84 form_rss: 84 form_rss:
85 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." 85 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."
86 token_label: "Jeton RSS" 86 token_label: "Jeton RSS"
@@ -100,18 +100,18 @@ config:
100 twoFactorAuthentication_label: "Double authentification" 100 twoFactorAuthentication_label: "Double authentification"
101 help_twoFactorAuthentication: "Si vous activez 2FA, à chaque tentative de connexion à wallabag, vous recevrez un code par email." 101 help_twoFactorAuthentication: "Si vous activez 2FA, à chaque tentative de connexion à wallabag, vous recevrez un code par email."
102 delete: 102 delete:
103 title: Supprimer mon compte (attention danger !) 103 title: "Supprimer mon compte (attention danger !)"
104 description: Si vous confirmez la suppression de votre compte, TOUS les articles, TOUS les tags, TOUTES les annotations et votre compte seront DÉFINITIVEMENT supprimé (c'est IRRÉVERSIBLE). Vous serez ensuite déconnecté. 104 description: "Si vous confirmez la suppression de votre compte, TOUS les articles, TOUS les tags, TOUTES les annotations et votre compte seront DÉFINITIVEMENT supprimé (c’est IRRÉVERSIBLE). Vous serez ensuite déconnecté."
105 confirm: Vous êtes vraiment sûr ? (C'EST IRRÉVERSIBLE) 105 confirm: "Vous êtes vraiment sûr ? (CEST IRRÉVERSIBLE)"
106 button: 'Supprimer mon compte' 106 button: "Supprimer mon compte"
107 reset: 107 reset:
108 title: Réinitialisation (attention danger !) 108 title: "Réinitialisation (attention danger !)"
109 description: En cliquant sur les boutons ci-dessous vous avez la possibilité de supprimer certaines informations de votre compte. Attention, ces actions sont IRRÉVERSIBLES ! 109 description: "En cliquant sur les boutons ci-dessous vous avez la possibilité de supprimer certaines informations de votre compte. Attention, ces actions sont IRRÉVERSIBLES !"
110 annotations: Supprimer TOUTES les annotations 110 annotations: "Supprimer TOUTES les annotations"
111 tags: Supprimer TOUS les tags 111 tags: "Supprimer TOUS les tags"
112 entries: Supprimer TOUS les articles 112 entries: "Supprimer TOUS les articles"
113 archived: Supprimer TOUS les articles archivés 113 archived: "Supprimer TOUS les articles archivés"
114 confirm: Êtes-vous vraiment vraiment sûr ? (C'EST IRRÉVERSIBLE) 114 confirm: "Êtes-vous vraiment vraiment sûr ? (CEST IRRÉVERSIBLE)"
115 form_password: 115 form_password:
116 description: "Vous pouvez changer ici votre mot de passe. Le mot de passe doit contenir au moins 8 caractères." 116 description: "Vous pouvez changer ici votre mot de passe. Le mot de passe doit contenir au moins 8 caractères."
117 old_password_label: "Mot de passe actuel" 117 old_password_label: "Mot de passe actuel"
@@ -164,7 +164,7 @@ entry:
164 archived: "Articles lus" 164 archived: "Articles lus"
165 filtered: "Articles filtrés" 165 filtered: "Articles filtrés"
166 filtered_tags: "Articles filtrés par tags :" 166 filtered_tags: "Articles filtrés par tags :"
167 filtered_search: 'Articles filtrés par recherche :' 167 filtered_search: "Articles filtrés par recherche :"
168 untagged: "Article sans tag" 168 untagged: "Article sans tag"
169 list: 169 list:
170 number_on_the_page: "{0} Il n’y a pas d’article.|{1} Il y a un article.|]1,Inf[ Il y a %count% articles." 170 number_on_the_page: "{0} Il n’y a pas d’article.|{1} Il y a un article.|]1,Inf[ Il y a %count% articles."
@@ -188,7 +188,7 @@ entry:
188 preview_picture_label: "A une photo" 188 preview_picture_label: "A une photo"
189 preview_picture_help: "Photo" 189 preview_picture_help: "Photo"
190 language_label: "Langue" 190 language_label: "Langue"
191 http_status_label: 'Statut HTTP' 191 http_status_label: "Statut HTTP"
192 reading_time: 192 reading_time:
193 label: "Durée de lecture en minutes" 193 label: "Durée de lecture en minutes"
194 from: "de" 194 from: "de"
@@ -298,32 +298,32 @@ howto:
298 bookmarklet: 298 bookmarklet:
299 description: "Glissez et déposez ce lien dans votre barre de favoris :" 299 description: "Glissez et déposez ce lien dans votre barre de favoris :"
300 shortcuts: 300 shortcuts:
301 page_description: Voici les raccourcis disponibles dans wallabag. 301 page_description: "Voici les raccourcis disponibles dans wallabag."
302 shortcut: Raccourci 302 shortcut: "Raccourci"
303 action: Action 303 action: "Action"
304 all_pages_title: Raccourcis disponibles dans toutes les pages 304 all_pages_title: "Raccourcis disponibles dans toutes les pages"
305 go_unread: Afficher les articles non lus 305 go_unread: "Afficher les articles non lus"
306 go_starred: Afficher les articles favoris 306 go_starred: "Afficher les articles favoris"
307 go_archive: Afficher les articles lus 307 go_archive: "Afficher les articles lus"
308 go_all: Afficher tous les articles 308 go_all: "Afficher tous les articles"
309 go_tags: Afficher les tags 309 go_tags: "Afficher les tags"
310 go_config: Aller à la configuration 310 go_config: "Aller à la configuration"
311 go_import: Aller aux imports 311 go_import: "Aller aux imports"
312 go_developers: Aller à la section Développeurs 312 go_developers: "Aller à la section Développeurs"
313 go_howto: Afficher l'aide (cette page !) 313 go_howto: "Afficher laide (cette page !)"
314 go_logout: Se déconnecter 314 go_logout: "Se déconnecter"
315 list_title: Raccourcis disponibles dans les pages de liste 315 list_title: "Raccourcis disponibles dans les pages de liste"
316 search: Afficher le formulaire de recherche 316 search: "Afficher le formulaire de recherche"
317 article_title: Raccourcis disponibles quand on affiche un article 317 article_title: "Raccourcis disponibles quand on affiche un article"
318 open_original: Ouvrir l'URL originale de l'article 318 open_original: "Ouvrir lURL originale de larticle"
319 toggle_favorite: Changer le statut Favori de l'article 319 toggle_favorite: "Changer le statut Favori de larticle"
320 toggle_archive: Changer le status Lu de l'article 320 toggle_archive: "Changer le status Lu de larticle"
321 delete: Supprimer l'article 321 delete: "Supprimer larticle"
322 material_title: Raccourcis disponibles avec le thème Material uniquement 322 material_title: "Raccourcis disponibles avec le thème Material uniquement"
323 add_link: Ajouter un nouvel article 323 add_link: "Ajouter un nouvel article"
324 hide_form: Masquer le formulaire courant (recherche ou nouvel article) 324 hide_form: "Masquer le formulaire courant (recherche ou nouvel article)"
325 arrows_navigation: Naviguer à travers les articles 325 arrows_navigation: "Naviguer à travers les articles"
326 open_article: Afficher l'article sélectionné 326 open_article: "Afficher larticle sélectionné"
327 327
328quickstart: 328quickstart:
329 page_title: "Pour bien débuter" 329 page_title: "Pour bien débuter"
@@ -385,8 +385,8 @@ tag:
385 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." 385 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."
386 see_untagged_entries: "Voir les articles sans tag" 386 see_untagged_entries: "Voir les articles sans tag"
387 new: 387 new:
388 add: 'Ajouter' 388 add: "Ajouter"
389 placeholder: 'Vous pouvez ajouter plusieurs tags, séparés par une virgule.' 389 placeholder: "Vous pouvez ajouter plusieurs tags, séparés par une virgule."
390 390
391import: 391import:
392 page_title: "Importer" 392 page_title: "Importer"
@@ -420,7 +420,7 @@ import:
420 how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." 420 how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer."
421 worker: 421 worker:
422 enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :" 422 enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :"
423 download_images_warning: "Vous avez configuré le téléchagement des images pour vos articles. Combiné à l'import classique, cette opération peut être très très longue (voire échouer). Nous vous conseillons <strong>vivement</strong> d'activer les imports asynchrones." 423 download_images_warning: "Vous avez configuré le téléchagement des images pour vos articles. Combiné à l’import classique, cette opération peut être très très longue (voire échouer). Nous vous conseillons <strong>vivement</strong> d’activer les imports asynchrones."
424 firefox: 424 firefox:
425 page_title: "Import > Firefox" 425 page_title: "Import > Firefox"
426 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>" 426 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>"
@@ -489,16 +489,16 @@ developer:
489 back: "Retour" 489 back: "Retour"
490 490
491user: 491user:
492 page_title: Gestion des utilisateurs 492 page_title: "Gestion des utilisateurs"
493 new_user: Créer un nouvel utilisateur 493 new_user: "Créer un nouvel utilisateur"
494 edit_user: Éditer un utilisateur existant 494 edit_user: "Éditer un utilisateur existant"
495 description: Ici vous pouvez gérer vos utilisateurs (création, mise à jour et suppression) 495 description: "Ici vous pouvez gérer vos utilisateurs (création, mise à jour et suppression)"
496 list: 496 list:
497 actions: Actions 497 actions: "Actions"
498 edit_action: Éditer 498 edit_action: "Éditer"
499 yes: Oui 499 yes: "Oui"
500 no: Non 500 no: "Non"
501 create_new_one: Créer un nouvel utilisateur 501 create_new_one: "Créer un nouvel utilisateur"
502 form: 502 form:
503 username_label: "Nom d’utilisateur" 503 username_label: "Nom d’utilisateur"
504 name_label: "Nom" 504 name_label: "Nom"
@@ -513,9 +513,11 @@ user:
513 delete: "Supprimer" 513 delete: "Supprimer"
514 delete_confirm: "Voulez-vous vraiment ?" 514 delete_confirm: "Voulez-vous vraiment ?"
515 back_to_list: "Revenir à la liste" 515 back_to_list: "Revenir à la liste"
516 search:
517 placeholder: "Filtrer par nom d’utilisateur ou email"
516 518
517error: 519error:
518 page_title: Une erreur est survenue 520 page_title: "Une erreur est survenue"
519 521
520flashes: 522flashes:
521 config: 523 config:
@@ -528,10 +530,10 @@ flashes:
528 tagging_rules_updated: "Règles mises à jour" 530 tagging_rules_updated: "Règles mises à jour"
529 tagging_rules_deleted: "Règle supprimée" 531 tagging_rules_deleted: "Règle supprimée"
530 rss_token_updated: "Jeton RSS mis à jour" 532 rss_token_updated: "Jeton RSS mis à jour"
531 annotations_reset: Annotations supprimées 533 annotations_reset: "Annotations supprimées"
532 tags_reset: Tags supprimés 534 tags_reset: "Tags supprimés"
533 entries_reset: Articles supprimés 535 entries_reset: "Articles supprimés"
534 archived_reset: Articles archivés supprimés 536 archived_reset: "Articles archivés supprimés"
535 entry: 537 entry:
536 notice: 538 notice:
537 entry_already_saved: "Article déjà sauvegardé le %date%" 539 entry_already_saved: "Article déjà sauvegardé le %date%"
@@ -563,6 +565,6 @@ flashes:
563 client_deleted: "Client %name% supprimé" 565 client_deleted: "Client %name% supprimé"
564 user: 566 user:
565 notice: 567 notice:
566 added: 'Utilisateur "%username%" ajouté' 568 added: "Utilisateur \"%username%\" ajouté"
567 updated: 'Utilisateur "%username%" mis à jour' 569 updated: "Utilisateur \"%username%\" mis à jour"
568 deleted: 'Utilisateur "%username%" supprimé' 570 deleted: "Utilisateur \"%username%\" supprimé"
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
index 374071ce..5a9605ff 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
@@ -513,6 +513,8 @@ user:
513 # delete: Delete 513 # delete: Delete
514 # delete_confirm: Are you sure? 514 # delete_confirm: Are you sure?
515 # back_to_list: Back to list 515 # back_to_list: Back to list
516 search:
517 # placeholder: Filter by username or email
516 518
517error: 519error:
518 # page_title: An error occurred 520 # page_title: An error occurred
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
index b01c611b..942bc257 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
@@ -513,6 +513,8 @@ user:
513 delete: 'Suprimir' 513 delete: 'Suprimir'
514 delete_confirm: 'Sètz segur ?' 514 delete_confirm: 'Sètz segur ?'
515 back_to_list: 'Tornar a la lista' 515 back_to_list: 'Tornar a la lista'
516 search:
517 # placeholder: Filter by username or email
516 518
517error: 519error:
518 page_title: Una error s'es produsida 520 page_title: Una error s'es produsida
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
index d76ac328..fea90440 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
@@ -513,6 +513,8 @@ user:
513 delete: Usuń 513 delete: Usuń
514 delete_confirm: Jesteś pewien? 514 delete_confirm: Jesteś pewien?
515 back_to_list: Powrót do listy 515 back_to_list: Powrót do listy
516 search:
517 # placeholder: Filter by username or email
516 518
517error: 519error:
518 page_title: Wystąpił błąd 520 page_title: Wystąpił błąd
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
index 98dfcd25..c59991f8 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml
@@ -513,6 +513,8 @@ user:
513 delete: 'Apagar' 513 delete: 'Apagar'
514 delete_confirm: 'Tem certeza?' 514 delete_confirm: 'Tem certeza?'
515 back_to_list: 'Voltar para a lista' 515 back_to_list: 'Voltar para a lista'
516 search:
517 # placeholder: Filter by username or email
516 518
517error: 519error:
518 # page_title: An error occurred 520 # page_title: An error occurred
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
index 8c07c13f..5846b7cc 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
@@ -513,6 +513,8 @@ user:
513 # delete: Delete 513 # delete: Delete
514 # delete_confirm: Are you sure? 514 # delete_confirm: Are you sure?
515 # back_to_list: Back to list 515 # back_to_list: Back to list
516 search:
517 # placeholder: Filter by username or email
516 518
517error: 519error:
518 # page_title: An error occurred 520 # page_title: An error occurred
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
index bd21cb67..430fb96b 100644
--- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
+++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
@@ -513,6 +513,8 @@ user:
513 # delete: Delete 513 # delete: Delete
514 # delete_confirm: Are you sure? 514 # delete_confirm: Are you sure?
515 # back_to_list: Back to list 515 # back_to_list: Back to list
516 search:
517 # placeholder: Filter by username or email
516 518
517error: 519error:
518 # page_title: An error occurred 520 # page_title: An error occurred
diff --git a/src/Wallabag/UserBundle/Controller/ManageController.php b/src/Wallabag/UserBundle/Controller/ManageController.php
index 92ee2b41..1c5c86d4 100644
--- a/src/Wallabag/UserBundle/Controller/ManageController.php
+++ b/src/Wallabag/UserBundle/Controller/ManageController.php
@@ -4,12 +4,15 @@ namespace Wallabag\UserBundle\Controller;
4 4
5use FOS\UserBundle\Event\UserEvent; 5use FOS\UserBundle\Event\UserEvent;
6use FOS\UserBundle\FOSUserEvents; 6use FOS\UserBundle\FOSUserEvents;
7use Pagerfanta\Adapter\DoctrineORMAdapter;
8use Pagerfanta\Exception\OutOfRangeCurrentPageException;
9use Pagerfanta\Pagerfanta;
7use Symfony\Component\HttpFoundation\Request; 10use Symfony\Component\HttpFoundation\Request;
8use Symfony\Bundle\FrameworkBundle\Controller\Controller; 11use Symfony\Bundle\FrameworkBundle\Controller\Controller;
9use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; 12use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
10use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; 13use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
11use Wallabag\UserBundle\Entity\User; 14use Wallabag\UserBundle\Entity\User;
12use Wallabag\CoreBundle\Entity\Config; 15use Wallabag\UserBundle\Form\SearchUserType;
13 16
14/** 17/**
15 * User controller. 18 * User controller.
@@ -17,23 +20,6 @@ use Wallabag\CoreBundle\Entity\Config;
17class ManageController extends Controller 20class ManageController extends Controller
18{ 21{
19 /** 22 /**
20 * Lists all User entities.
21 *
22 * @Route("/", name="user_index")
23 * @Method("GET")
24 */
25 public function indexAction()
26 {
27 $em = $this->getDoctrine()->getManager();
28
29 $users = $em->getRepository('WallabagUserBundle:User')->findAll();
30
31 return $this->render('WallabagUserBundle:Manage:index.html.twig', array(
32 'users' => $users,
33 ));
34 }
35
36 /**
37 * Creates a new User entity. 23 * Creates a new User entity.
38 * 24 *
39 * @Route("/new", name="user_new") 25 * @Route("/new", name="user_new")
@@ -146,4 +132,49 @@ class ManageController extends Controller
146 ->getForm() 132 ->getForm()
147 ; 133 ;
148 } 134 }
135
136 /**
137 * @param Request $request
138 * @param int $page
139 *
140 * @Route("/list/{page}", name="user_index", defaults={"page" = 1})
141 *
142 * Default parameter for page is hardcoded (in duplication of the defaults from the Route)
143 * because this controller is also called inside the layout template without any page as argument
144 *
145 * @return \Symfony\Component\HttpFoundation\Response
146 */
147 public function searchFormAction(Request $request, $page = 1)
148 {
149 $em = $this->getDoctrine()->getManager();
150 $qb = $em->getRepository('WallabagUserBundle:User')->createQueryBuilder('u');
151
152 $form = $this->createForm(SearchUserType::class);
153 $form->handleRequest($request);
154
155 if ($form->isSubmitted() && $form->isValid()) {
156 $this->get('logger')->info('searching users');
157
158 $searchTerm = (isset($request->get('search_user')['term']) ? $request->get('search_user')['term'] : '');
159
160 $qb = $em->getRepository('WallabagUserBundle:User')->getQueryBuilderForSearch($searchTerm);
161 }
162
163 $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false);
164 $pagerFanta = new Pagerfanta($pagerAdapter);
165 $pagerFanta->setMaxPerPage(50);
166
167 try {
168 $pagerFanta->setCurrentPage($page);
169 } catch (OutOfRangeCurrentPageException $e) {
170 if ($page > 1) {
171 return $this->redirect($this->generateUrl('user_index', ['page' => $pagerFanta->getNbPages()]), 302);
172 }
173 }
174
175 return $this->render('WallabagUserBundle:Manage:index.html.twig', [
176 'searchForm' => $form->createView(),
177 'users' => $pagerFanta,
178 ]);
179 }
149} 180}
diff --git a/src/Wallabag/UserBundle/Form/SearchUserType.php b/src/Wallabag/UserBundle/Form/SearchUserType.php
new file mode 100644
index 00000000..9ce46ee1
--- /dev/null
+++ b/src/Wallabag/UserBundle/Form/SearchUserType.php
@@ -0,0 +1,29 @@
1<?php
2
3namespace Wallabag\UserBundle\Form;
4
5use Symfony\Component\Form\AbstractType;
6use Symfony\Component\Form\Extension\Core\Type\TextType;
7use Symfony\Component\Form\FormBuilderInterface;
8use Symfony\Component\OptionsResolver\OptionsResolver;
9
10class SearchUserType extends AbstractType
11{
12 public function buildForm(FormBuilderInterface $builder, array $options)
13 {
14 $builder
15 ->setMethod('GET')
16 ->add('term', TextType::class, [
17 'required' => true,
18 'label' => 'user.new.form_search.term_label',
19 ])
20 ;
21 }
22
23 public function configureOptions(OptionsResolver $resolver)
24 {
25 $resolver->setDefaults([
26 'csrf_protection' => false,
27 ]);
28 }
29}
diff --git a/src/Wallabag/UserBundle/Repository/UserRepository.php b/src/Wallabag/UserBundle/Repository/UserRepository.php
index f913f52d..6adbe329 100644
--- a/src/Wallabag/UserBundle/Repository/UserRepository.php
+++ b/src/Wallabag/UserBundle/Repository/UserRepository.php
@@ -52,4 +52,17 @@ class UserRepository extends EntityRepository
52 ->getQuery() 52 ->getQuery()
53 ->getSingleScalarResult(); 53 ->getSingleScalarResult();
54 } 54 }
55
56 /**
57 * Retrieves users filtered with a search term.
58 *
59 * @param string $term
60 *
61 * @return QueryBuilder
62 */
63 public function getQueryBuilderForSearch($term)
64 {
65 return $this->createQueryBuilder('u')
66 ->andWhere('lower(u.username) LIKE lower(:term) OR lower(u.email) LIKE lower(:term) OR lower(u.name) LIKE lower(:term)')->setParameter('term', '%'.$term.'%');
67 }
55} 68}
diff --git a/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig b/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig
index daba29e4..15002632 100644
--- a/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig
+++ b/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig
@@ -7,37 +7,60 @@
7 <div class="row"> 7 <div class="row">
8 <div class="col s12"> 8 <div class="col s12">
9 <div class="card-panel"> 9 <div class="card-panel">
10 {% if users.getNbPages > 1 %}
11 {{ pagerfanta(users, 'twitter_bootstrap_translated', {'proximity': 1}) }}
12 {% endif %}
10 <div class="row"> 13 <div class="row">
11 <div class="input-field col s12"> 14 <div class="col s6">
12 <p class="help">{{ 'user.description'|trans|raw }}</p> 15 <p class="help">{{ 'user.description'|trans|raw }}</p>
16 </div>
17 <div class="col s6">
18 <div class="input-field">
19 <form name="search_users" method="GET" action="{{ path('user_index')}}">
20 {% if form_errors(searchForm) %}
21 <span class="black-text">{{ form_errors(searchForm) }}</span>
22 {% endif %}
23
24 {% if form_errors(searchForm.term) %}
25 <span class="black-text">{{ form_errors(searchForm.term) }}</span>
26 {% endif %}
13 27
14 <table class="bordered"> 28 {{ form_widget(searchForm.term, { 'attr': {'autocomplete': 'off', 'placeholder': 'user.search.placeholder'} }) }}
15 <thead> 29
16 <tr> 30 {{ form_rest(searchForm) }}
17 <th>{{ 'user.form.username_label'|trans }}</th> 31 </form>
18 <th>{{ 'user.form.email_label'|trans }}</th> 32 </div>
19 <th>{{ 'user.form.last_login_label'|trans }}</th>
20 <th>{{ 'user.list.actions'|trans }}</th>
21 </tr>
22 </thead>
23 <tbody>
24 {% for user in users %}
25 <tr>
26 <td>{{ user.username }}</td>
27 <td>{{ user.email }}</td>
28 <td>{% if user.lastLogin %}{{ user.lastLogin|date('Y-m-d H:i:s') }}{% endif %}</td>
29 <td>
30 <a href="{{ path('user_edit', { 'id': user.id }) }}">{{ 'user.list.edit_action'|trans }}</a>
31 </td>
32 </tr>
33 {% endfor %}
34 </tbody>
35 </table>
36 <br />
37 <p>
38 <a href="{{ path('user_new') }}" class="waves-effect waves-light btn">{{ 'user.list.create_new_one'|trans }}</a>
39 </p>
40 </div> 33 </div>
34
35 <table class="bordered">
36 <thead>
37 <tr>
38 <th>{{ 'user.form.username_label'|trans }}</th>
39 <th>{{ 'user.form.email_label'|trans }}</th>
40 <th>{{ 'user.form.last_login_label'|trans }}</th>
41 <th>{{ 'user.list.actions'|trans }}</th>
42 </tr>
43 </thead>
44 <tbody>
45 {% for user in users %}
46 <tr>
47 <td>{{ user.username }}</td>
48 <td>{{ user.email }}</td>
49 <td>{% if user.lastLogin %}{{ user.lastLogin|date('Y-m-d H:i:s') }}{% endif %}</td>
50 <td>
51 <a href="{{ path('user_edit', { 'id': user.id }) }}">{{ 'user.list.edit_action'|trans }}</a>
52 </td>
53 </tr>
54 {% endfor %}
55 </tbody>
56 </table>
57 <br />
58 <p>
59 <a href="{{ path('user_new') }}" class="waves-effect waves-light btn">{{ 'user.list.create_new_one'|trans }}</a>
60 </p>
61 {% if users.getNbPages > 1 %}
62 {{ pagerfanta(users, 'twitter_bootstrap_translated', {'proximity': 1}) }}
63 {% endif %}
41 </div> 64 </div>
42 </div> 65 </div>
43 </div> 66 </div>
diff --git a/tests/Wallabag/UserBundle/Controller/ManageControllerTest.php b/tests/Wallabag/UserBundle/Controller/ManageControllerTest.php
index 4faddfc4..44b9a030 100644
--- a/tests/Wallabag/UserBundle/Controller/ManageControllerTest.php
+++ b/tests/Wallabag/UserBundle/Controller/ManageControllerTest.php
@@ -10,7 +10,7 @@ class ManageControllerTest extends WallabagCoreTestCase
10 { 10 {
11 $client = $this->getClient(); 11 $client = $this->getClient();
12 12
13 $client->request('GET', '/users/'); 13 $client->request('GET', '/users/list');
14 14
15 $this->assertEquals(302, $client->getResponse()->getStatusCode()); 15 $this->assertEquals(302, $client->getResponse()->getStatusCode());
16 $this->assertContains('login', $client->getResponse()->headers->get('location')); 16 $this->assertContains('login', $client->getResponse()->headers->get('location'));
@@ -22,7 +22,7 @@ class ManageControllerTest extends WallabagCoreTestCase
22 $client = $this->getClient(); 22 $client = $this->getClient();
23 23
24 // Create a new user in the database 24 // Create a new user in the database
25 $crawler = $client->request('GET', '/users/'); 25 $crawler = $client->request('GET', '/users/list');
26 $this->assertEquals(200, $client->getResponse()->getStatusCode(), 'Unexpected HTTP status code for GET /users/'); 26 $this->assertEquals(200, $client->getResponse()->getStatusCode(), 'Unexpected HTTP status code for GET /users/');
27 $crawler = $client->click($crawler->selectLink('user.list.create_new_one')->link()); 27 $crawler = $client->click($crawler->selectLink('user.list.create_new_one')->link());
28 28
@@ -36,7 +36,7 @@ class ManageControllerTest extends WallabagCoreTestCase
36 36
37 $client->submit($form); 37 $client->submit($form);
38 $client->followRedirect(); 38 $client->followRedirect();
39 $crawler = $client->request('GET', '/users/'); 39 $crawler = $client->request('GET', '/users/list');
40 40
41 // Check data in the show view 41 // Check data in the show view
42 $this->assertGreaterThan(0, $crawler->filter('td:contains("test_user")')->count(), 'Missing element td:contains("test_user")'); 42 $this->assertGreaterThan(0, $crawler->filter('td:contains("test_user")')->count(), 'Missing element td:contains("test_user")');
@@ -57,7 +57,7 @@ class ManageControllerTest extends WallabagCoreTestCase
57 // Check the element contains an attribute with value equals "Foo User" 57 // Check the element contains an attribute with value equals "Foo User"
58 $this->assertGreaterThan(0, $crawler->filter('[value="Foo User"]')->count(), 'Missing element [value="Foo User"]'); 58 $this->assertGreaterThan(0, $crawler->filter('[value="Foo User"]')->count(), 'Missing element [value="Foo User"]');
59 59
60 $crawler = $client->request('GET', '/users/'); 60 $crawler = $client->request('GET', '/users/list');
61 $crawler = $client->click($crawler->selectLink('user.list.edit_action')->last()->link()); 61 $crawler = $client->click($crawler->selectLink('user.list.edit_action')->last()->link());
62 62
63 // Delete the user 63 // Delete the user
@@ -78,4 +78,22 @@ class ManageControllerTest extends WallabagCoreTestCase
78 78
79 $this->assertEquals('disabled', $disabled[0]); 79 $this->assertEquals('disabled', $disabled[0]);
80 } 80 }
81
82 public function testUserSearch()
83 {
84 $this->logInAs('admin');
85 $client = $this->getClient();
86
87 // Search on unread list
88 $crawler = $client->request('GET', '/users/list');
89
90 $form = $crawler->filter('form[name=search_users]')->form();
91 $data = [
92 'search_user[term]' => 'admin',
93 ];
94
95 $crawler = $client->submit($form, $data);
96
97 $this->assertCount(2, $crawler->filter('tr')); // 1 result + table header
98 }
81} 99}