mirror of
https://gitea.mueller.network/extern/django-helpdesk.git
synced 2025-02-16 10:19:17 +01:00
Note: This is a breaking change as it forces pagination on the API endoints. This should have been done from the start as the API without pagination is useless when there are large numbers of tickets.
269 lines
10 KiB
Python
269 lines
10 KiB
Python
"""
|
|
django-helpdesk - A Django powered ticket tracker for small enterprise.
|
|
|
|
(c) Copyright 2008 Jutda. All Rights Reserved. See LICENSE for details.
|
|
|
|
views/public.py - All public facing views, eg non-staff (no authentication
|
|
required) views.
|
|
"""
|
|
|
|
|
|
from django.conf import settings
|
|
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
|
|
from django.http import HttpResponseRedirect
|
|
from django.shortcuts import render
|
|
from django.urls import reverse
|
|
from django.utils.translation import gettext as _
|
|
from django.views.decorators.clickjacking import xframe_options_exempt
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from django.views.generic.base import TemplateView
|
|
from django.views.generic.edit import FormView
|
|
from helpdesk import settings as helpdesk_settings
|
|
from helpdesk.decorators import is_helpdesk_staff, protect_view
|
|
from helpdesk.lib import text_is_spam
|
|
from helpdesk.models import Queue, Ticket, UserSettings
|
|
from helpdesk.user import huser_from_request
|
|
import helpdesk.views.abstract_views as abstract_views
|
|
import helpdesk.views.staff as staff
|
|
from importlib import import_module
|
|
import logging
|
|
from urllib.parse import quote
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def create_ticket(request, *args, **kwargs):
|
|
if is_helpdesk_staff(request.user):
|
|
return staff.CreateTicketView.as_view()(request, *args, **kwargs)
|
|
else:
|
|
return CreateTicketView.as_view()(request, *args, **kwargs)
|
|
|
|
|
|
class BaseCreateTicketView(abstract_views.AbstractCreateTicketMixin, FormView):
|
|
|
|
def get_form_class(self):
|
|
try:
|
|
the_module, the_form_class = helpdesk_settings.HELPDESK_PUBLIC_TICKET_FORM_CLASS.rsplit(
|
|
".", 1)
|
|
the_module = import_module(the_module)
|
|
the_form_class = getattr(the_module, the_form_class)
|
|
except Exception as e:
|
|
raise ImproperlyConfigured(
|
|
f"Invalid custom form class {helpdesk_settings.HELPDESK_PUBLIC_TICKET_FORM_CLASS}"
|
|
) from e
|
|
return the_form_class
|
|
|
|
def dispatch(self, *args, **kwargs):
|
|
request = self.request
|
|
if not request.user.is_authenticated and helpdesk_settings.HELPDESK_REDIRECT_TO_LOGIN_BY_DEFAULT:
|
|
return HttpResponseRedirect(reverse('login'))
|
|
|
|
if is_helpdesk_staff(request.user) or \
|
|
(request.user.is_authenticated and
|
|
helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE):
|
|
try:
|
|
if request.user.usersettings_helpdesk.login_view_ticketlist:
|
|
return HttpResponseRedirect(reverse('helpdesk:list'))
|
|
else:
|
|
return HttpResponseRedirect(reverse('helpdesk:dashboard'))
|
|
except UserSettings.DoesNotExist:
|
|
return HttpResponseRedirect(reverse('helpdesk:dashboard'))
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
def get_initial(self):
|
|
initial_data = super().get_initial()
|
|
|
|
# add pre-defined data for public ticket
|
|
if hasattr(settings, 'HELPDESK_PUBLIC_TICKET_QUEUE'):
|
|
# get the requested queue; return an error if queue not found
|
|
try:
|
|
initial_data['queue'] = Queue.objects.get(
|
|
slug=settings.HELPDESK_PUBLIC_TICKET_QUEUE,
|
|
allow_public_submission=True
|
|
).id
|
|
except Queue.DoesNotExist as e:
|
|
logger.fatal(
|
|
"Public queue '%s' is configured as default but can't be found",
|
|
settings.HELPDESK_PUBLIC_TICKET_QUEUE
|
|
)
|
|
raise ImproperlyConfigured(
|
|
"Wrong public queue configuration") from e
|
|
if hasattr(settings, 'HELPDESK_PUBLIC_TICKET_PRIORITY'):
|
|
initial_data['priority'] = settings.HELPDESK_PUBLIC_TICKET_PRIORITY
|
|
if hasattr(settings, 'HELPDESK_PUBLIC_TICKET_DUE_DATE'):
|
|
initial_data['due_date'] = settings.HELPDESK_PUBLIC_TICKET_DUE_DATE
|
|
return initial_data
|
|
|
|
def get_form_kwargs(self, *args, **kwargs):
|
|
kwargs = super().get_form_kwargs(*args, **kwargs)
|
|
if '_hide_fields_' in self.request.GET:
|
|
kwargs['hidden_fields'] = self.request.GET.get(
|
|
'_hide_fields_', '').split(',')
|
|
kwargs['readonly_fields'] = self.request.GET.get(
|
|
'_readonly_fields_', '').split(',')
|
|
return kwargs
|
|
|
|
def form_valid(self, form):
|
|
request = self.request
|
|
if text_is_spam(form.cleaned_data['body'], request):
|
|
# This submission is spam. Let's not save it.
|
|
return render(request, template_name='helpdesk/public_spam.html')
|
|
else:
|
|
ticket = form.save(
|
|
user=self.request.user if self.request.user.is_authenticated else None)
|
|
try:
|
|
return HttpResponseRedirect('%s?ticket=%s&email=%s&key=%s' % (
|
|
reverse('helpdesk:public_view'),
|
|
ticket.ticket_for_url,
|
|
quote(ticket.submitter_email),
|
|
ticket.secret_key)
|
|
)
|
|
except ValueError:
|
|
# if someone enters a non-int string for the ticket
|
|
return HttpResponseRedirect(reverse('helpdesk:home'))
|
|
|
|
|
|
class CreateTicketIframeView(BaseCreateTicketView):
|
|
template_name = 'helpdesk/public_create_ticket_iframe.html'
|
|
|
|
@csrf_exempt
|
|
@xframe_options_exempt
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
def form_valid(self, form):
|
|
if super().form_valid(form).status_code == 302:
|
|
return HttpResponseRedirect(reverse('helpdesk:success_iframe'))
|
|
|
|
|
|
class SuccessIframeView(TemplateView):
|
|
template_name = 'helpdesk/success_iframe.html'
|
|
|
|
@xframe_options_exempt
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
|
|
class CreateTicketView(BaseCreateTicketView):
|
|
template_name = 'helpdesk/public_create_ticket.html'
|
|
|
|
def get_form(self, form_class=None):
|
|
form = super().get_form(form_class)
|
|
# Add the CSS error class to the form in order to better see them in
|
|
# the page
|
|
form.error_css_class = 'text-danger'
|
|
return form
|
|
|
|
|
|
class Homepage(CreateTicketView):
|
|
template_name = 'helpdesk/public_homepage.html'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['kb_categories'] = huser_from_request(
|
|
self.request).get_allowed_kb_categories()
|
|
return context
|
|
|
|
|
|
class SearchForTicketView(TemplateView):
|
|
template_name = 'helpdesk/public_view_form.html'
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
if hasattr(settings, 'HELPDESK_VIEW_A_TICKET_PUBLIC') and settings.HELPDESK_VIEW_A_TICKET_PUBLIC:
|
|
context = self.get_context_data(**kwargs)
|
|
return self.render_to_response(context)
|
|
else:
|
|
raise PermissionDenied("Public viewing of tickets without a secret key is forbidden.")
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
request = self.request
|
|
email = request.GET.get('email', None)
|
|
error_message = kwargs.get('error_message', None)
|
|
|
|
context.update({
|
|
'ticket': False,
|
|
'email': email,
|
|
'error_message': error_message,
|
|
'helpdesk_settings': helpdesk_settings,
|
|
})
|
|
return context
|
|
|
|
|
|
class ViewTicket(TemplateView):
|
|
template_name = 'helpdesk/public_view_ticket.html'
|
|
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
ticket_req = request.GET.get('ticket', None)
|
|
email = request.GET.get('email', None)
|
|
key = request.GET.get('key', '')
|
|
|
|
if not (ticket_req and email):
|
|
if ticket_req is None and email is None:
|
|
return SearchForTicketView.as_view()(request)
|
|
else:
|
|
return SearchForTicketView.as_view()(request, _('Missing ticket ID or e-mail address. Please try again.'))
|
|
|
|
try:
|
|
queue, ticket_id = Ticket.queue_and_id_from_query(ticket_req)
|
|
if request.user.is_authenticated and request.user.email == email:
|
|
ticket = Ticket.objects.get(id=ticket_id, submitter_email__iexact=email)
|
|
if hasattr(settings, 'HELPDESK_VIEW_A_TICKET_PUBLIC') and settings.HELPDESK_VIEW_A_TICKET_PUBLIC:
|
|
ticket = Ticket.objects.get(id=ticket_id, submitter_email__iexact=email)
|
|
else:
|
|
ticket = Ticket.objects.get(id=ticket_id, submitter_email__iexact=email, secret_key__iexact=key)
|
|
except (ObjectDoesNotExist, ValueError):
|
|
return SearchForTicketView.as_view()(request, _('Invalid ticket ID or e-mail address. Please try again.'))
|
|
|
|
if 'close' in request.GET and ticket.status == Ticket.RESOLVED_STATUS:
|
|
from helpdesk.update_ticket import update_ticket
|
|
update_ticket(
|
|
request.user,
|
|
ticket,
|
|
public=True,
|
|
comment=_('Submitter accepted resolution and closed ticket'),
|
|
new_status=Ticket.CLOSED_STATUS,
|
|
)
|
|
return HttpResponseRedirect(ticket.ticket_url)
|
|
|
|
# Prepare context for rendering
|
|
context = {
|
|
'key': key,
|
|
'mail': email,
|
|
'ticket': ticket,
|
|
'helpdesk_settings': helpdesk_settings,
|
|
'next': self.get_next_url(ticket_id)
|
|
}
|
|
return self.render_to_response(context)
|
|
|
|
def get_next_url(self, ticket_id):
|
|
redirect_url = ''
|
|
if is_helpdesk_staff(self.request.user):
|
|
redirect_url = reverse('helpdesk:view', args=[ticket_id])
|
|
if 'close' in self.request.GET:
|
|
redirect_url += '?close'
|
|
elif helpdesk_settings.HELPDESK_NAVIGATION_ENABLED:
|
|
redirect_url = reverse('helpdesk:view', args=[ticket_id])
|
|
return redirect_url
|
|
|
|
|
|
class MyTickets(TemplateView):
|
|
template_name = 'helpdesk/my_tickets.html'
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
if not request.user.is_authenticated:
|
|
return HttpResponseRedirect(reverse('helpdesk:login'))
|
|
|
|
context = self.get_context_data(**kwargs)
|
|
return self.render_to_response(context)
|
|
|
|
|
|
def change_language(request):
|
|
return_to = ''
|
|
if 'return_to' in request.GET:
|
|
return_to = request.GET['return_to']
|
|
|
|
return render(request, 'helpdesk/public_change_language.html', {'next': return_to})
|