Add filter to users management page

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2017-04-27 15:58:32 +02:00 committed by Nicolas Lœuillet
parent e1d64050ad
commit c37515f880
17 changed files with 170 additions and 28 deletions

View File

@ -512,6 +512,9 @@ user:
# delete: Delete
# delete_confirm: Are you sure?
# back_to_list: Back to list
search:
# label: Filter
# placeholder: Filter by username or email
error:
# page_title: An error occurred

View File

@ -512,6 +512,9 @@ user:
delete: Löschen
delete_confirm: Bist du sicher?
back_to_list: Zurück zur Liste
search:
# label: Filter
# placeholder: Filter by username or email
error:
page_title: Ein Fehler ist aufgetreten

View File

@ -512,6 +512,9 @@ user:
delete: Delete
delete_confirm: Are you sure?
back_to_list: Back to list
search:
label: Filter
placeholder: Filter by username or email
error:
page_title: An error occurred

View File

@ -512,6 +512,9 @@ user:
delete: Eliminar
delete_confirm: ¿Estás seguro?
back_to_list: Volver a la lista
search:
# label: Filter
# placeholder: Filter by username or email
error:
page_title: Ha ocurrido un error

View File

@ -512,6 +512,9 @@ user:
# delete: Delete
# delete_confirm: Are you sure?
# back_to_list: Back to list
search:
# label: Filter
# placeholder: Filter by username or email
error:
# page_title: An error occurred

View File

@ -512,6 +512,9 @@ user:
delete: "Supprimer"
delete_confirm: "Voulez-vous vraiment ?"
back_to_list: "Revenir à la liste"
search:
label: Filtrer
placeholder: Filtrer par nom d'utilisateur ou email
error:
page_title: Une erreur est survenue

View File

@ -512,6 +512,9 @@ user:
# delete: Delete
# delete_confirm: Are you sure?
# back_to_list: Back to list
search:
# label: Filter
# placeholder: Filter by username or email
error:
# page_title: An error occurred

View File

@ -512,6 +512,9 @@ user:
delete: 'Suprimir'
delete_confirm: 'Sètz segur ?'
back_to_list: 'Tornar a la lista'
search:
# label: Filter
# placeholder: Filter by username or email
error:
page_title: Una error s'es produsida

View File

@ -512,6 +512,9 @@ user:
delete: Usuń
delete_confirm: Jesteś pewien?
back_to_list: Powrót do listy
search:
# label: Filter
# placeholder: Filter by username or email
error:
page_title: Wystąpił błąd

View File

@ -512,6 +512,9 @@ user:
delete: 'Apagar'
delete_confirm: 'Tem certeza?'
back_to_list: 'Voltar para a lista'
search:
# label: Filter
# placeholder: Filter by username or email
error:
# page_title: An error occurred

View File

@ -512,6 +512,9 @@ user:
# delete: Delete
# delete_confirm: Are you sure?
# back_to_list: Back to list
search:
# label: Filter
# placeholder: Filter by username or email
error:
# page_title: An error occurred

View File

@ -512,6 +512,9 @@ user:
# delete: Delete
# delete_confirm: Are you sure?
# back_to_list: Back to list
search:
# label: Filter
# placeholder: Filter by username or email
error:
# page_title: An error occurred

View File

@ -10,6 +10,7 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Wallabag\UserBundle\Entity\User;
use Wallabag\CoreBundle\Entity\Config;
use Wallabag\UserBundle\Form\SearchUserType;
/**
* User controller.
@ -146,4 +147,45 @@ class ManageController extends Controller
->getForm()
;
}
/**
* @param Request $request
* @param int $page
*
* @Route("/search/{page}", name="user-search", defaults={"page" = 1})
*
* Default parameter for page is hardcoded (in duplication of the defaults from the Route)
* because this controller is also called inside the layout template without any page as argument
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function searchFormAction(Request $request, $page = 1, $currentRoute = null)
{
// fallback to retrieve currentRoute from query parameter instead of injected one (when using inside a template)
if (null === $currentRoute && $request->query->has('currentRoute')) {
$currentRoute = $request->query->get('currentRoute');
}
$form = $this->createForm(SearchUserType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->get('logger')->info('searching users');
$em = $this->getDoctrine()->getManager();
$searchTerm = (isset($request->get('search_user')['term']) ? $request->get('search_user')['term'] : '');
$users = $em->getRepository('WallabagUserBundle:User')->getUsersForSearch($searchTerm);
return $this->render('WallabagUserBundle:Manage:index.html.twig', array(
'users' => $users,
));
}
return $this->render('WallabagUserBundle:Manage:search_form.html.twig', [
'form' => $form->createView(),
'currentRoute' => $currentRoute,
]);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace Wallabag\UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class SearchUserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setMethod('GET')
->add('term', TextType::class, [
'required' => true,
'label' => 'user.new.form_search.term_label',
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'csrf_protection' => false,
]);
}
}

View File

@ -52,4 +52,19 @@ class UserRepository extends EntityRepository
->getQuery()
->getSingleScalarResult();
}
/**
* Retrieves users filtered with a search term.
*
* @param string $term
*
* @return QueryBuilder
*/
public function getUsersForSearch($term)
{
return $this->createQueryBuilder('u')
->andWhere('lower(u.username) LIKE lower(:term) OR lower(u.email) LIKE lower(:term) OR lower(u.name) LIKE lower(:term)')->setParameter('term', '%'.$term.'%')
->getQuery()
->getResult();
}
}

View File

@ -8,36 +8,41 @@
<div class="col s12">
<div class="card-panel">
<div class="row">
<div class="input-field col s12">
<div class="col s6">
<p class="help">{{ 'user.description'|trans|raw }}</p>
<table class="bordered">
<thead>
<tr>
<th>{{ 'user.form.username_label'|trans }}</th>
<th>{{ 'user.form.email_label'|trans }}</th>
<th>{{ 'user.form.last_login_label'|trans }}</th>
<th>{{ 'user.list.actions'|trans }}</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.username }}</td>
<td>{{ user.email }}</td>
<td>{% if user.lastLogin %}{{ user.lastLogin|date('Y-m-d H:i:s') }}{% endif %}</td>
<td>
<a href="{{ path('user_edit', { 'id': user.id }) }}">{{ 'user.list.edit_action'|trans }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<br />
<p>
<a href="{{ path('user_new') }}" class="waves-effect waves-light btn">{{ 'user.list.create_new_one'|trans }}</a>
</p>
</div>
<div class="col s6">
<div class="input-field">
{{ render(controller("WallabagUserBundle:Manage:searchForm", {'currentRoute': app.request.attributes.get('_route')})) }}
</div>
</div>
<table class="bordered">
<thead>
<tr>
<th>{{ 'user.form.username_label'|trans }}</th>
<th>{{ 'user.form.email_label'|trans }}</th>
<th>{{ 'user.form2017-03-10 16:51:07.last_login_label'|trans }}</th>
<th>{{ 'user.list.actions'|trans }}</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.username }}</td>
<td>{{ user.email }}</td>
<td>{% if user.lastLogin %}{{ user.lastLogin|date('Y-m-d H:i:s') }}{% endif %}</td>
<td>
<a href="{{ path('user_edit', { 'id': user.id }) }}">{{ 'user.list.edit_action'|trans }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<br />
<p>
<a href="{{ path('user_new') }}" class="waves-effect waves-light btn">{{ 'user.list.create_new_one'|trans }}</a>
</p>
</div>
</div>
</div>

View File

@ -0,0 +1,15 @@
<form name="search" method="GET" action="{{ path('user-search')}}">
{% if form_errors(form) %}
<span class="black-text">{{ form_errors(form) }}</span>
{% endif %}
{% if form_errors(form.term) %}
<span class="black-text">{{ form_errors(form.term) }}</span>
{% endif %}
<i class="material-icons prefix">search</i>
{{ form_widget(form.term, { 'attr': {'autocomplete': 'off', 'placeholder': 'user.search.placeholder'} }) }}
<label for="search_user_term" class="active">{{ 'user.search.label' | trans }}</label>
{{ form_rest(form) }}
</form>