Further refactor datatables code

This commit is contained in:
Timothy Hobbs 2019-10-11 17:12:39 +02:00
parent 4ca6adbe39
commit 8a57f72349
3 changed files with 107 additions and 97 deletions

View File

@ -1,4 +1,5 @@
from django.db.models import Q
from django.core.cache import cache
from model_utils import Choices
@ -87,6 +88,18 @@ def apply_query(queryset, params):
return queryset
def get_query(query, huser):
# Prefilter the allowed tickets
objects = cache.get(huser.user.email + query)
if objects is not None:
return objects
tickets = huser.get_tickets_in_queues().select_related()
query_params = query_from_base64(query)
ticket_qs = apply_query(tickets, query_params)
cache.set(huser.user.email + query, ticket_qs, timeout=60*60)
return ticket_qs
ORDER_COLUMN_CHOICES = Choices(
('0', 'id'),
('2', 'priority'),

60
helpdesk/user.py Normal file
View File

@ -0,0 +1,60 @@
from helpdesk.models import (
Ticket,
Queue
)
from helpdesk import settings as helpdesk_settings
class HelpdeskUser:
def __init__(self, user):
self.user = user
def get_queues(self):
"""Return the list of Queues the user can access.
:param user: The User (the class should have the has_perm method)
:return: A Python list of Queues
"""
user = self.user
all_queues = Queue.objects.all()
public_ids = [q.pk for q in
Queue.objects.filter(allow_public_submission=True)]
limit_queues_by_user = \
helpdesk_settings.HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION \
and not user.is_superuser
if limit_queues_by_user:
id_list = [q.pk for q in all_queues if user.has_perm(q.permission_name)]
id_list += public_ids
return all_queues.filter(pk__in=id_list)
else:
return all_queues
def get_tickets_in_queues(self):
return Ticket.objects.filter(queue__in=self.get_queues())
def can_access_queue(self, queue):
"""Check if a certain user can access a certain queue.
:param user: The User (the class should have the has_perm method)
:param queue: The django-helpdesk Queue instance
:return: True if the user has permission (either by default or explicitly), false otherwise
"""
user = self.user
if user.is_superuser or not helpdesk_settings.HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION:
return True
else:
return user.has_perm(queue.permission_name)
def can_access_ticket(self, ticket):
"""Check to see if the user has permission to access
a ticket. If not then deny access."""
user = self.user
if self.can_access_queue(ticket.queue):
return True
elif user.is_superuser or user.is_staff or \
(ticket.assigned_to and user.id == ticket.assigned_to.id):
return True
else:
return False

View File

@ -25,16 +25,18 @@ from django.utils.html import escape
from django import forms
from django.utils import timezone
from django.views.generic.edit import FormView, UpdateView
from django.core.cache import cache
from helpdesk.query import (
query_to_dict,
get_query,
apply_query,
query_tickets_by_args,
query_to_base64,
query_from_base64,
)
from helpdesk.user import HelpdeskUser
from helpdesk.serializers import DatatablesTicketSerializer
from helpdesk.decorators import (
@ -96,44 +98,10 @@ def _get_queue_choices(queues):
return queue_choices
def _get_user_queues(user):
"""Return the list of Queues the user can access.
:param user: The User (the class should have the has_perm method)
:return: A Python list of Queues
"""
all_queues = Queue.objects.all()
public_ids = [q.pk for q in
Queue.objects.filter(allow_public_submission=True)]
limit_queues_by_user = \
helpdesk_settings.HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION \
and not user.is_superuser
if limit_queues_by_user:
id_list = [q.pk for q in all_queues if user.has_perm(q.permission_name)]
id_list += public_ids
return all_queues.filter(pk__in=id_list)
else:
return all_queues
def _has_access_to_queue(user, queue):
"""Check if a certain user can access a certain queue.
:param user: The User (the class should have the has_perm method)
:param queue: The django-helpdesk Queue instance
:return: True if the user has permission (either by default or explicitly), false otherwise
"""
if user.is_superuser or not helpdesk_settings.HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION:
return True
else:
return user.has_perm(queue.permission_name)
def _is_my_ticket(user, ticket):
"""Check to see if the user has permission to access
a ticket. If not then deny access."""
if _has_access_to_queue(user, ticket.queue):
if (user, ticket.queue):
return True
elif user.is_superuser or user.is_staff or \
(ticket.assigned_to and user.id == ticket.assigned_to.id):
@ -163,7 +131,7 @@ def dashboard(request):
assigned_to=request.user,
status__in=[Ticket.CLOSED_STATUS, Ticket.RESOLVED_STATUS])
user_queues = _get_user_queues(request.user)
user_queues = HelpdeskUser(request.user).get_queues()
unassigned_tickets = active_tickets.filter(
assigned_to__isnull=True,
@ -189,7 +157,7 @@ def dashboard(request):
# Queue 1 10 4
# Queue 2 4 12
queues = _get_user_queues(request.user).values_list('id', flat=True)
queues = HelpdeskUser(request.user).get_queues().values_list('id', flat=True)
from_clause = """FROM helpdesk_ticket t,
helpdesk_queue q"""
@ -210,14 +178,17 @@ def dashboard(request):
dashboard = staff_member_required(dashboard)
def ticket_perm_check(request, ticket):
huser = HelpdeskUser(request.user)
if not huser.can_access_queue(ticket.queue):
raise PermissionDenied()
if not huser.can_access_ticket(ticket):
raise PermissionDenied()
@helpdesk_staff_member_required
def delete_ticket(request, ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
if not _has_access_to_queue(request.user, ticket.queue):
raise PermissionDenied()
if not _is_my_ticket(request.user, ticket):
raise PermissionDenied()
ticket_perm_check(request, ticket)
if request.method == 'GET':
return render(request, 'helpdesk/delete_ticket.html', {
@ -236,10 +207,7 @@ def followup_edit(request, ticket_id, followup_id):
"""Edit followup options with an ability to change the ticket."""
followup = get_object_or_404(FollowUp, id=followup_id)
ticket = get_object_or_404(Ticket, id=ticket_id)
if not _has_access_to_queue(request.user, ticket.queue):
raise PermissionDenied()
if not _is_my_ticket(request.user, ticket):
raise PermissionDenied()
ticket_perm_check(request, ticket)
if request.method == 'GET':
form = EditFollowUpForm(initial={
@ -311,10 +279,7 @@ followup_delete = staff_member_required(followup_delete)
@helpdesk_staff_member_required
def view_ticket(request, ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
if not _has_access_to_queue(request.user, ticket.queue):
raise PermissionDenied()
if not _is_my_ticket(request.user, ticket):
raise PermissionDenied()
ticket_perm_check(request, ticket)
if 'take' in request.GET:
# Allow the user to assign the ticket to themselves whilst viewing it.
@ -360,7 +325,7 @@ def view_ticket(request, ticket_id):
else:
users = User.objects.filter(is_active=True).order_by(User.USERNAME_FIELD)
queues = _get_user_queues(request.user)
queues = HelpdeskUser(request.user).get_queues()
queue_choices = _get_queue_choices(queues)
# TODO: shouldn't this template get a form to begin with?
form = TicketForm(initial={'due_date': ticket.due_date},
@ -757,8 +722,9 @@ def mass_update(request):
user = request.user
action = 'assign'
huser = HelpdeskUser(request.user)
for t in Ticket.objects.filter(id__in=tickets):
if not _has_access_to_queue(request.user, t.queue):
if not huser.can_access_queue(t.queue):
continue
if action == 'assign' and t.assigned_to != user:
@ -838,9 +804,7 @@ mass_update = staff_member_required(mass_update)
def ticket_list(request):
context = {}
user_queues = _get_user_queues(request.user)
# Prefilter the allowed tickets
base_tickets = Ticket.objects.filter(queue__in=user_queues)
huser = HelpdeskUser(request.user)
# Query_params will hold a dictionary of parameters relating to
# a query, to be saved if needed:
@ -885,7 +849,7 @@ def ticket_list(request):
if filter:
try:
ticket = base_tickets.get(**filter)
ticket = huser.get_tickets_in_queues.get(**filter)
return HttpResponseRedirect(ticket.staff_url)
except Ticket.DoesNotExist:
# Go on to standard keyword searching
@ -940,17 +904,9 @@ def ticket_list(request):
sortreverse = request.GET.get('sortreverse', None)
query_params['sortreverse'] = sortreverse
tickets = base_tickets.select_related()
try:
ticket_qs = apply_query(tickets, query_params)
except ValidationError:
# invalid parameters in query, return default query
ticket_qs = apply_query(tickets, default_query_params)
urlsafe_query = query_to_base64(query_params)
cache.set(request.user.email + urlsafe_query, ticket_qs, timeout=60*60)
get_query(urlsafe_query, huser)
user_saved_queries = SavedSearch.objects.filter(Q(user=request.user) | Q(shared__exact=True))
@ -969,7 +925,7 @@ def ticket_list(request):
context,
default_tickets_per_page=request.user.usersettings_helpdesk.tickets_per_page,
user_choices=User.objects.filter(is_active=True, is_staff=True),
queue_choices=user_queues,
queue_choices=huser.get_queues(),
status_choices=Ticket.STATUS_CHOICES,
urlsafe_query=urlsafe_query,
user_saved_queries=user_saved_queries,
@ -1019,8 +975,7 @@ def datatables_ticket_list(request, query):
on the table. query_tickets_by_args is at lib.py, DatatablesTicketSerializer is in
serializers.py. The serializers and this view use django-rest_framework methods
"""
objects = cache.get(request.user.email + query)
query_params = query_from_base64(query)
objects = get_query(query, HelpdeskUser(request.user))
model_object = query_tickets_by_args(objects, '-date_created', **request.query_params)
serializer = DatatablesTicketSerializer(model_object['items'], many=True)
result = dict()
@ -1034,10 +989,7 @@ def datatables_ticket_list(request, query):
@helpdesk_staff_member_required
def edit_ticket(request, ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
if not _has_access_to_queue(request.user, ticket.queue):
raise PermissionDenied()
if not _is_my_ticket(request.user, ticket):
raise PermissionDenied()
ticket_perm_check(request, ticket)
if request.method == 'POST':
form = EditTicketForm(request.POST, instance=ticket)
@ -1068,7 +1020,7 @@ class CreateTicketView(MustBeStaffMixin, FormView):
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
queues = _get_user_queues(self.request.user)
queues = HelpdeskUser(self.request.user).get_queues()
kwargs["queue_choices"] = _get_queue_choices(queues)
return kwargs
@ -1078,7 +1030,7 @@ class CreateTicketView(MustBeStaffMixin, FormView):
def get_success_url(self):
request = self.request
if _has_access_to_queue(request.user, self.ticket.queue):
if HelpdeskUser(request.user).can_access_queue(self.ticket.queue):
return self.ticket.get_absolute_url()
else:
return reverse('helpdesk:dashboard')
@ -1109,10 +1061,7 @@ raw_details = staff_member_required(raw_details)
@helpdesk_staff_member_required
def hold_ticket(request, ticket_id, unhold=False):
ticket = get_object_or_404(Ticket, id=ticket_id)
if not _has_access_to_queue(request.user, ticket.queue):
raise PermissionDenied()
if not _is_my_ticket(request.user, ticket):
raise PermissionDenied()
ticket_perm_check(request, ticket)
if unhold:
ticket.on_hold = False
@ -1159,7 +1108,7 @@ def report_index(request):
number_tickets = Ticket.objects.all().count()
saved_query = request.GET.get('saved_query', None)
user_queues = _get_user_queues(request.user)
user_queues = HelpdeskUser(request.user).get_queues()
Tickets = Ticket.objects.filter(queue__in=user_queues)
basic_ticket_stats = calc_basic_ticket_stats(Tickets)
@ -1202,7 +1151,7 @@ def run_report(request, report):
return HttpResponseRedirect(reverse("helpdesk:report_index"))
report_queryset = Ticket.objects.all().select_related().filter(
queue__in=_get_user_queues(request.user)
queue__in=HelpdeskUser(request.user).get_queues()
)
try:
@ -1252,7 +1201,7 @@ def run_report(request, report):
elif report == 'userqueue':
title = _('User by Queue')
col1heading = _('User')
queue_options = _get_user_queues(request.user)
queue_options = HelpdeskUser(request.user).get_queues()
possible_options = [q.title for q in queue_options]
charttype = 'bar'
@ -1467,10 +1416,7 @@ email_ignore_del = superuser_required(email_ignore_del)
@helpdesk_staff_member_required
def ticket_cc(request, ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
if not _has_access_to_queue(request.user, ticket.queue):
raise PermissionDenied()
if not _is_my_ticket(request.user, ticket):
raise PermissionDenied()
ticket_perm_check(request, ticket)
copies_to = ticket.ticketcc_set.all()
return render(request, 'helpdesk/ticket_cc_list.html', {
@ -1485,10 +1431,7 @@ ticket_cc = staff_member_required(ticket_cc)
@helpdesk_staff_member_required
def ticket_cc_add(request, ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
if not _has_access_to_queue(request.user, ticket.queue):
raise PermissionDenied()
if not _is_my_ticket(request.user, ticket):
raise PermissionDenied()
ticket_perm_check(request, ticket)
if request.method == 'POST':
form = TicketCCForm(request.POST)
@ -1528,10 +1471,7 @@ ticket_cc_del = staff_member_required(ticket_cc_del)
@helpdesk_staff_member_required
def ticket_dependency_add(request, ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
if not _has_access_to_queue(request.user, ticket.queue):
raise PermissionDenied()
if not _is_my_ticket(request.user, ticket):
raise PermissionDenied()
ticket_perm_check(request, ticket)
if request.method == 'POST':
form = TicketDependencyForm(request.POST)
if form.is_valid():
@ -1566,10 +1506,7 @@ ticket_dependency_del = staff_member_required(ticket_dependency_del)
@helpdesk_staff_member_required
def attachment_del(request, ticket_id, attachment_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
if not _has_access_to_queue(request.user, ticket.queue):
raise PermissionDenied()
if not _is_my_ticket(request.user, ticket):
raise PermissionDenied()
ticket_perm_check(request, ticket)
attachment = get_object_or_404(FollowUpAttachment, id=attachment_id)
if request.method == 'POST':