Skip to content

Commit

Permalink
Add filters to admin user list
Browse files Browse the repository at this point in the history
  • Loading branch information
timobrembeck committed May 25, 2022
1 parent 57569a9 commit 6790643
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 76 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ UNRELEASED

* [ [#1454](https://github.com/digitalfabrik/integreat-cms/issues/1454) ] Enable recurring events for non-expert users
* [ [#1416](https://github.com/digitalfabrik/integreat-cms/issues/1416) ] Hide staff users from region user list
* [ [#1483](https://github.com/digitalfabrik/integreat-cms/issues/1483) ] Add filters to admin user list


2022.5.3
Expand Down
1 change: 1 addition & 0 deletions integreat_cms/cms/forms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
from .users.region_user_form import RegionUserForm
from .users.user_email_form import UserEmailForm
from .users.user_form import UserForm
from .users.user_filter_form import UserFilterForm
from .users.user_password_form import UserPasswordForm
from .users.password_reset_form import CustomPasswordResetForm

Expand Down
95 changes: 95 additions & 0 deletions integreat_cms/cms/forms/users/user_filter_form.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import logging

from django import forms
from django.db.models.fields import BLANK_CHOICE_DASH
from django.utils.translation import ugettext_lazy as _

from ...constants import roles
from ...models import Region
from ...utils.user_utils import search_users

logger = logging.getLogger(__name__)


class UserFilterForm(forms.Form):
"""
Form for filtering user objects
"""

role = forms.ChoiceField(
label=_("Role"),
choices=BLANK_CHOICE_DASH + roles.CHOICES,
required=False,
)
permissions = forms.ChoiceField(
label=_("Permissions"),
choices=BLANK_CHOICE_DASH
+ [
("is_superuser", _("Administrator")),
("is_staff", _("Integreat team member")),
],
required=False,
)
region = forms.ModelChoiceField(
label=_("Region"),
queryset=Region.objects.all(),
required=False,
)
query = forms.CharField(required=False)

@property
def is_enabled(self):
"""
This function determines whether the filters are applied.
:return: Whether filtering should be performed
:rtype: bool
"""
return self.is_valid() and self.has_changed()

@property
def filters_visible(self):
"""
This function determines whether the filter form is visible by default.
:return: Whether any filters (other than search) were changed
:rtype: bool
"""
return self.is_enabled and self.changed_data != ["query"]

def apply(self, users):
"""
Filter the users list according to the given filter data
:param users: The list of users
:type users: list
:return: The filtered page list
:rtype: list
"""
if self.is_enabled:
logger.debug("User list filtered with changed data %r", self.changed_data)
if "query" in self.changed_data:
users = self.filter_by_query(users)
if "role" in self.changed_data:
users = users.filter(groups__name=self.cleaned_data["role"])
if "permissions" in self.changed_data:
users = users.filter(**{self.cleaned_data["permissions"]: True})
if "region" in self.changed_data:
users = users.filter(regions=self.cleaned_data["region"])
return users

def filter_by_query(self, users):
"""
Filter the pages list by a given search query
:param users: The list of users
:type users: list
:return: The filtered page list
:rtype: list
"""
query = self.cleaned_data["query"].lower()
user_keys = search_users(region=None, query=query).values("pk")
users = users.filter(pk__in=user_keys)
return users
23 changes: 23 additions & 0 deletions integreat_cms/cms/templates/users/_user_filter_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{% load i18n %}
{% load widget_tweaks %}

<form class="p-4" id="user-filter-form">
<div class="grid grid-cols-3 gap-4 mb-4">
<div>
<label for="{{ filter_form.role.id_for_label }}">{{ filter_form.role.label }}</label>
{% render_field filter_form.role %}
</div>
<div>
<label for="{{ filter_form.permissions.id_for_label }}">{{ filter_form.permissions.label }}</label>
{% render_field filter_form.permissions %}
</div>
<div>
<label for="{{ filter_form.region.id_for_label }}">{{ filter_form.region.label }}</label>
{% render_field filter_form.region %}
</div>
</div>
<div class="flex justify-end gap-4">
<a id="filter-reset" class="btn btn-light-blue">{% trans 'Reset filter' %}</a>
<button class="btn">{% trans 'Apply filter' %}</button>
</div>
</form>
52 changes: 30 additions & 22 deletions integreat_cms/cms/templates/users/user_list.html
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
{% extends "_base.html" %}
{% load i18n %}
{% block content %}
{% load static %}

{% block content %}
<div class="table-header">
<h1 class="heading">{% trans 'User Management' %}</h1>
<div class="flex justify-between">
{% include "search_input_form.html" with object_type="user" %}
{% if perms.cms.change_user %}
<a href="{% url 'new_user' %}" class="btn">
{% trans 'Create user' %}
</a>
{% endif %}
<div class="flex justify-between gap-4">
{% include "_search_input.html" with object_type="user" related_form='user-filter-form' search_query=filter_form.cleaned_data.query %}
<div class="flex flex-wrap gap-4">
<button id="filter-toggle" class="btn btn-light-blue">
<span class="filter-toggle-text {% if filter_form.filters_visible %}hidden{% endif %}">{% trans 'Show filters' %}</span>
<span class="filter-toggle-text {% if not filter_form.filters_visible %}hidden{% endif %}">{% trans 'Hide filters' %}</span>
</button>
{% if perms.cms.change_user %}
<a href="{% url 'new_user' %}" class="btn">{% trans 'Create user' %}</a>
{% endif %}
</div>
</div>
</div>

<div id="filter-form-container"
class="{% if not filter_form.filters_visible %}hidden{% endif %} w-full mt-4 rounded border border-solid border-gray-200 shadow bg-white">
{% include 'users/_user_filter_form.html' %}
</div>
<div class="table-listing">
<table class="w-full mt-4 rounded border border-solid border-gray-200 shadow bg-white">
<thead>
Expand All @@ -34,19 +42,19 @@ <h1 class="heading">{% trans 'User Management' %}</h1>
</tr>
</thead>
<tbody>
{% for user in users %}
{% include "users/user_list_row.html" %}
{% empty %}
<tr>
<td colspan="6" class="px-2 py-3">
{% if search_query %}
{% trans 'No users found with these filters.' %}
{% else %}
{% trans 'No users available yet.' %}
{% endif %}
</td>
</tr>
{% endfor %}
{% for user in users %}
{% include "users/user_list_row.html" %}
{% empty %}
<tr>
<td colspan="6" class="px-2 py-3">
{% if filter_form.is_enabled %}
{% trans 'No users found with these filters.' %}
{% else %}
{% trans 'No users available yet.' %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
Expand Down
32 changes: 7 additions & 25 deletions integreat_cms/cms/views/users/user_list_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
from django.views.generic import TemplateView

from ...decorators import permission_required
from ...forms import ObjectSearchForm
from ...utils.user_utils import search_users
from ...forms import UserFilterForm

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -48,14 +47,12 @@ def get(self, request, *args, **kwargs):
.prefetch_related("groups__role")
.order_by("username")
)
query = None

search_data = kwargs.get("search_data")
search_form = ObjectSearchForm(search_data)
if search_form.is_valid():
query = search_form.cleaned_data["query"]
user_keys = search_users(region=None, query=query).values("pk")
users = users.filter(pk__in=user_keys)
# Initialize user filter form
filter_form = UserFilterForm(data=request.GET)

# Filter users according to given filters, if any
users = filter_form.apply(users)

chunk_size = int(request.GET.get("size", settings.PER_PAGE))
# for consistent pagination querysets should be ordered
Expand All @@ -68,21 +65,6 @@ def get(self, request, *args, **kwargs):
{
**self.get_context_data(**kwargs),
"users": user_chunk,
"search_query": query,
"filter_form": filter_form,
},
)

def post(self, request, *args, **kwargs):
r"""
Apply the query and filter the rendered users
:param request: The current request
:type request: ~django.http.HttpResponse
:param \*args: The supplied arguments
:type \*args: list
:param \**kwargs: The supplied keyword arguments
:type \**kwargs: dict
:return: The rendered template response
:rtype: ~django.template.response.TemplateResponse
"""
return self.get(request, *args, **kwargs, search_data=request.POST)

0 comments on commit 6790643

Please sign in to comment.