import re
from ajax_datatable import AjaxDatatableView
from atom.views import ActionMessageMixin, ActionView
from braces.views import LoginRequiredMixin, StaffuserRequiredMixin, UserFormKwargsMixin
from dal import autocomplete
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, RedirectView, TemplateView, UpdateView
from django_filters.views import FilterView
from poradnia.cases.models import Case, CaseUserObjectPermission
from poradnia.utils.mixins import ExprAutocompleteMixin
from .filters import UserFilter
from .forms import ProfileForm, UserForm
from .models import Profile, User
from .utils import PermissionMixin
[dokumentacja]
class UserDetailView(PermissionRequiredMixin, DetailView):
model = User
slug_field = "username"
slug_url_kwarg = "username"
permission_required = "users.can_view_other"
raise_exception = True
[dokumentacja]
def has_permission(self, *args, **kwargs):
if self.kwargs["username"] == self.request.user.username:
return True
return super().has_permission(*args, **kwargs)
def handle_no_permission(self):
if self.request.user.is_authenticated:
url = reverse(
"users:user_info", kwargs={"username": self.kwargs["username"]}
)
return HttpResponseRedirect(url)
else:
raise PermissionDenied
[dokumentacja]
class UserInfoView(DetailView):
model = User
slug_field = "username"
slug_url_kwarg = "username"
template_name = "users/user_info.html"
fields = ["picture", "profile"]
[dokumentacja]
def get_queryset(self):
queryset = super().get_queryset()
return queryset.only(*self.fields)
[dokumentacja]
class UserRedirectView(LoginRequiredMixin, RedirectView):
permanent = False
[dokumentacja]
def get_redirect_url(self):
return reverse("users:detail", kwargs={"username": self.request.user.username})
[dokumentacja]
class UserUpdateView(LoginRequiredMixin, UpdateView):
form_class = UserForm
# we already imported User in the view code above, remember?
model = User
# send the user back to their own page after a successful update
[dokumentacja]
def get_success_url(self):
return reverse("users:detail", kwargs={"username": self.request.user.username})
[dokumentacja]
def get_object(self):
# Only get the User record for the user making the request
return User.objects.get(username=self.request.user.username)
[dokumentacja]
class ProfileUpdateView(UserFormKwargsMixin, LoginRequiredMixin, UpdateView):
form_class = ProfileForm
model = Profile
[dokumentacja]
def get_success_url(self):
return reverse("users:detail", kwargs={"username": self.request.user.username})
[dokumentacja]
def get_object(self):
# Only get the User record for the user making the request
return Profile.objects.get_or_create(user=self.request.user)[0]
[dokumentacja]
class UserListView(StaffuserRequiredMixin, PermissionMixin, FilterView):
model = User
slug_field = "username"
slug_url_kwarg = "username"
filterset_class = UserFilter
paginate_by = 25
IS_STAFF_FILTER = (
(_("All users"), {}),
(_("Staff"), {"is_staff": True}),
((_("Clients"), {"is_staff": False})),
)
def get_is_staff_choice(self):
if "is_staff" not in self.request.GET:
return 0
if not self.request.GET["is_staff"].isdigit():
return 0
num = int(self.request.GET["is_staff"])
if len(self.IS_STAFF_FILTER) < num:
return 0
return num
[dokumentacja]
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
qs = qs.filter(**self.IS_STAFF_FILTER[self.get_is_staff_choice()][1])
qs = qs.with_case_count()
qs = qs.with_case_count_assigned()
return qs
[dokumentacja]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["is_staff"] = dict(
choices=enumerate(self.IS_STAFF_FILTER), selected=self.get_is_staff_choice()
)
return context
[dokumentacja]
class UserAutocomplete(
PermissionMixin, ExprAutocompleteMixin, autocomplete.Select2QuerySetView
):
model = User
search_expr = [
"first_name__icontains",
"last_name__icontains",
"username__icontains",
]
[dokumentacja]
class UserDeassignView(
LoginRequiredMixin,
PermissionRequiredMixin,
ActionMessageMixin,
ActionView,
):
model = User
slug_field = "username"
slug_url_kwarg = "username"
permission_required = "cases.can_assign"
success_message = _("{subject} deassigned")
template_name_suffix = "_deassign"
raise_exception = True
def get_success_url(self):
return self.object.get_absolute_url()
def action(self):
self.count = int(
Case.objects.filter(caseuserobjectpermission__user=self.object)
.exclude(client=self.object)
.distinct()
.count()
)
CaseUserObjectPermission.objects.filter(user=self.object).exclude(
content_object__client=self.object
).delete()
def get_success_message(self):
return _("{object} deassigned from {count} cases").format(
object=self.object, count=self.count
)
[dokumentacja]
class UserTableView(StaffuserRequiredMixin, PermissionMixin, TemplateView):
"""
View for displaying template with Users table.
"""
template_name = "users/user_table.html"
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(**kwargs)
context["header_label"] = mark_safe(_("Users search table"))
context["ajax_datatable_url"] = reverse("users:users_table_ajax_data")
return context
[dokumentacja]
class UserAjaxDatatableView(StaffuserRequiredMixin, PermissionMixin, AjaxDatatableView):
"""
View to provide table list of all Users with ajax data.
"""
model = User
title = _("Users")
initial_order = [
["username", "asc"],
]
length_menu = [[20, 50, 100], [20, 50, 100]]
search_values_separator = "|"
column_defs = [
{"name": "pk", "title": "ID", "visible": False},
{
"name": "username",
"title": _("Username"),
"visible": True,
"searchable": True,
"orderable": True,
},
{
"name": "nicename",
"title": _("Nice Name"),
"visible": True,
"searchable": True,
"orderable": True,
},
{
"name": "email",
"title": _("Email"),
"visible": True,
"searchable": True,
"orderable": True,
},
{
"name": "is_staff",
"title": _("Staff"),
"visible": True,
"searchable": True,
# (value, label) pairs.
"choices": [(1, _("Yes")), (0, _("No"))],
},
{
"name": "case_count",
"title": _("Client cases") + " (e.g. x, >y)",
"visible": True,
"searchable": True,
"orderable": True,
},
{
"name": "case_assigned_sum",
"title": _("Assigned cases sum") + " (e.g. x, >y)",
"visible": True,
"searchable": True,
"orderable": True,
},
{
"name": "case_assigned_moderated",
"title": _("Moderated"),
"visible": True,
"searchable": True,
"orderable": True,
},
{
"name": "case_assigned_active",
"title": _("Active_"),
"visible": True,
"searchable": True,
"orderable": True,
},
{
"name": "case_assigned_closed",
"title": _("Closed"),
"visible": True,
"searchable": True,
"orderable": True,
},
]
def get_initial_queryset(self, request=None):
qs = super().get_initial_queryset(request)
qs = qs.with_case_count()
qs = qs.with_case_count_assigned()
return qs
[dokumentacja]
def filter_queryset_by_column(self, column_name, search_value, qs):
"""
Allow simple boolean expressions in case columns, e.g. >10.
"""
if (
column_name
in [
"case_count",
"case_assigned_sum",
"case_assigned_moderated",
"case_assigned_active",
"case_assigned_closed",
]
and search_value
):
if (
self.search_values_separator
and self.search_values_separator in search_value
):
search_values = [
t.strip() for t in search_value.split(self.search_values_separator)
]
else:
search_values = [search_value.strip()]
column_filters = Q()
for val in search_values:
match = re.match(r"^(?P<operator>[<>!=]=?)?\s*(?P<value>\d+)$", val)
if match:
operator = match.group("operator")
value = int(match.group("value"))
lookup = {
">": "__gt",
">=": "__gte",
"<": "__lt",
"<=": "__lte",
"=": "",
"!=": "",
}.get(operator, "")
# Handle `=` and `!=` separately - there's no builtin qs operator.
if operator == "!=":
column_filters |= ~Q(**{f"{column_name}{lookup}": value})
else:
column_filters |= Q(**{f"{column_name}{lookup}": value})
if column_filters:
return qs.filter(column_filters)
return super().filter_queryset_by_column(column_name, search_value, qs)
def customize_row(self, row, obj):
row["username"] = mark_safe(
f'<a href="{obj.get_absolute_url()}">{obj.username}</a>'
)