Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the possibility to sort and filter users in the backend #3066

Merged
merged 3 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/bolt/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,6 @@ reset_password_settings:
mail_name: "Bolt CMS"
mail_subject: "Your password reset request"
mail_template: "reset_password/email.html.twig"

# Adds sorting and filtering users in backend
user_show_sort&filter: true
1 change: 1 addition & 0 deletions src/Configuration/Parser/GeneralParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ protected function getDefaultConfig(): array
'extensions_allowed' => ['png', 'jpeg', 'jpg', 'gif'],
'default_avatar' => '',
],
'user_show_sort&filter' => false
];
}
}
14 changes: 13 additions & 1 deletion src/Controller/Backend/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,17 @@ public function __construct(UserRepository $users)
*/
public function users(Query $query): Response
{
$users = new ArrayAdapter($this->users->findBy([], ['username' => 'ASC'], 1000));
$order = 'username';
if ($this->request->get('sortBy')) {
$order = $this->getFromRequest('sortBy');
}

$like = '';
if ($this->request->get('filter')) {
$like = '%' . $this->getFromRequest('filter') . '%';
}

$users = new ArrayAdapter($this->users->findUsers($like, $order));
$currentPage = (int) $this->getFromRequest('page', '1');
$users = new Pagerfanta($users);
$users->setMaxPerPage(self::PAGESIZE)
Expand All @@ -45,6 +55,8 @@ public function users(Query $query): Response
'title' => 'controller.user.title',
'subtitle' => 'controller.user.subtitle',
'users' => $users,
'sortBy' => $this->getFromRequest('sortBy'),
'filterValue' => $this->getFromRequest('filter'),
];

return $this->render('@bolt/users/listing.html.twig', $twigVars);
Expand Down
35 changes: 35 additions & 0 deletions src/Repository/UserRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@

use Bolt\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\Query\Expr;
use Doctrine\Persistence\ManagerRegistry;

class UserRepository extends ServiceEntityRepository
{
/** @var string[] */
private $userColumns = ['id', 'displayName', 'username', 'roles', 'email', 'lastIp'];

public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
Expand Down Expand Up @@ -46,6 +50,25 @@ public function findOneByCredentials(string $username): ?User
return $user instanceof User ? $user : null;
}

public function findUsers(string $like, string $orderBy = null)
{
$alias = 'user';
$qb = $this->createQueryBuilder($alias);

if ($like) {
foreach ($this->userColumns as $col) {
$qb
->orWhere(
$qb->expr()->like(sprintf('%s.%s', $alias, $col), sprintf(':%s', $col))
)
->setParameter($col, $like);
}
}
$qb->orderBy($this->createSortBy($orderBy, $alias));

return $qb->getQuery()->getResult();
}

public function getFirstAdminUser(): ?User
{
$qb = $this->createQueryBuilder('user');
Expand All @@ -71,4 +94,16 @@ public static function factory(string $displayName = '', string $username = '',

return $user;
}

private function createSortBy($order, $alias): Expr\OrderBy
{
if (mb_strpos($order, '-') === 0) {
$direction = 'DESC';
$order = sprintf('%s.%s', $alias, mb_substr($order, 1));
} else {
$direction = 'ASC';
$order = sprintf('%s.%s', $alias, $order);
}
return new Expr\OrderBy($order, $direction);
}
}
72 changes: 72 additions & 0 deletions templates/users/listing.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,76 @@
{# The 'aside' section is the right sidebar of the page. If omitted, 'main' will take up the full width. #}
{% block aside %}
{{ widgets('users_aside_top') }}

{% if config.get('general/user_show_sort&filter') %}
{% set filterOptions = {
'id': "id",
'displayName': "displayName",
'username': "username",
'roles': "roles",
'lastseenAt': "lastseenAt",
'lastIp': "lastIp"
} %}

<div class="card mb-3">
<div class="card-header">
{{ macro.icon('star') }} {{ title|trans }}
</div>
<div class="card-body">
<p>
{% if is_granted('user:add') %}
<a class="btn btn-secondary" href="{{ path('bolt_user_add') }}" data-patience="virtue">
{{ macro.icon('user-plus') }} {{ __('action.add_user') }}
</a>
<a
class="btn btn-tertiary"
href="{{ path('bolt_file_edit', {'location': 'config', 'file': '/bolt/permissions.yaml'}) }}"
data-patience="virtue"
>
{{ macro.icon('user-cog') }} {{ __('action.edit_permissions') }}
</a>
{% endif %}
</p>
<form>
<div class="form-group">
<p>
<strong>{{ 'listing.title_sortby'|trans }}</strong>:
<select class="form-control" name="sortBy" title="{{ 'listing.title_sortby'|trans }}">
<option value="" {% if sortBy is empty %}selected{% endif %}>
{{ 'listing.option_select_sortby'|trans }}
</option>
{% for key, filterOption in filterOptions %}
<option value="{{ key }}" {% if sortBy == key %}selected{% endif %}>
{{ filterOption }}
</option>
<option value="-{{ key }}" {% if sortBy == '-' ~ key %}selected{% endif %}>
-{{ filterOption }}
</option>
{% endfor %}
</select>
</p>

<p>
<strong>{{ 'listing.title_filterby'|trans }}</strong>:
<input
class="form-control"
type="text"
name="filter"
id="content-filter"
value="{{ filterValue }}"
placeholder="{{ 'listing.placeholder_filter'|trans }}"
title="{{ 'listing.placeholder_filter'|trans }}"
/>
</p>
</div>

{{ macro.button('listing.button_filter', 'filter', 'secondary mb-0', {'type': 'submit'}) }}

{% if sortBy is not empty or filterValue is not empty %}
{{ macro.buttonlink('listing.button_clear', path('bolt_users'), 'times', 'tertiary mb-0') }}
{% endif %}
</form>
</div>
</div>
{% endif %}
{% endblock %}