2008-05-21 23:16:44 +02:00
|
|
|
"""
|
2011-01-26 00:08:41 +01:00
|
|
|
django-helpdesk - A Django powered ticket tracker for small enterprise.
|
2008-05-21 23:16:44 +02:00
|
|
|
|
|
|
|
(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.
|
|
|
|
"""
|
2020-02-06 10:10:07 +01:00
|
|
|
|
2022-07-22 03:26:41 +02:00
|
|
|
|
|
|
|
from django.conf import settings
|
|
|
|
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
|
2020-02-06 10:10:07 +01:00
|
|
|
from django.http import HttpResponseRedirect
|
2016-10-21 17:14:12 +02:00
|
|
|
from django.shortcuts import render
|
2022-07-22 03:26:41 +02:00
|
|
|
from django.urls import reverse
|
2022-03-17 03:29:09 +01:00
|
|
|
from django.utils.translation import gettext as _
|
2020-01-13 21:15:22 +01:00
|
|
|
from django.views.decorators.clickjacking import xframe_options_exempt
|
2020-01-14 16:02:00 +01:00
|
|
|
from django.views.decorators.csrf import csrf_exempt
|
2018-09-08 20:36:35 +02:00
|
|
|
from django.views.generic.base import TemplateView
|
2018-09-07 19:05:16 +02:00
|
|
|
from django.views.generic.edit import FormView
|
2011-06-09 17:24:33 +02:00
|
|
|
from helpdesk import settings as helpdesk_settings
|
2022-07-22 03:26:41 +02:00
|
|
|
from helpdesk.decorators import is_helpdesk_staff, protect_view
|
2016-10-21 17:14:12 +02:00
|
|
|
from helpdesk.lib import text_is_spam
|
2022-07-22 03:26:41 +02:00
|
|
|
from helpdesk.models import Queue, Ticket, UserSettings
|
2020-01-27 17:37:59 +01:00
|
|
|
from helpdesk.user import huser_from_request
|
2022-07-22 03:26:41 +02:00
|
|
|
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
|
|
|
|
|
2008-05-21 23:16:44 +02:00
|
|
|
|
2020-02-06 10:10:07 +01:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2008-08-19 10:50:38 +02:00
|
|
|
|
2018-09-07 19:05:16 +02:00
|
|
|
def create_ticket(request, *args, **kwargs):
|
|
|
|
if is_helpdesk_staff(request.user):
|
|
|
|
return staff.CreateTicketView.as_view()(request, *args, **kwargs)
|
|
|
|
else:
|
2023-11-23 22:51:42 +01:00
|
|
|
return protect_view(CreateTicketView.as_view())(request, *args, **kwargs)
|
2011-11-19 09:34:07 +01:00
|
|
|
|
2018-09-07 19:05:16 +02:00
|
|
|
|
2020-01-08 18:39:41 +01:00
|
|
|
class BaseCreateTicketView(abstract_views.AbstractCreateTicketMixin, FormView):
|
2021-03-02 12:06:21 +01:00
|
|
|
|
|
|
|
def get_form_class(self):
|
|
|
|
try:
|
2022-07-12 12:34:19 +02:00
|
|
|
the_module, the_form_class = helpdesk_settings.HELPDESK_PUBLIC_TICKET_FORM_CLASS.rsplit(
|
|
|
|
".", 1)
|
2021-03-02 12:06:21 +01:00
|
|
|
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
|
2018-09-07 19:05:16 +02:00
|
|
|
|
|
|
|
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:
|
2018-10-05 14:54:22 +02:00
|
|
|
if request.user.usersettings_helpdesk.login_view_ticketlist:
|
2018-09-07 19:05:16 +02:00
|
|
|
return HttpResponseRedirect(reverse('helpdesk:list'))
|
|
|
|
else:
|
|
|
|
return HttpResponseRedirect(reverse('helpdesk:dashboard'))
|
|
|
|
except UserSettings.DoesNotExist:
|
2015-12-22 10:10:00 +01:00
|
|
|
return HttpResponseRedirect(reverse('helpdesk:dashboard'))
|
2018-09-07 19:05:16 +02:00
|
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
|
|
|
|
def get_initial(self):
|
2020-01-08 18:39:41 +01:00
|
|
|
initial_data = super().get_initial()
|
2018-01-09 09:55:27 +01:00
|
|
|
|
|
|
|
# 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:
|
2020-02-06 10:10:07 +01:00
|
|
|
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
|
|
|
|
)
|
2022-07-12 12:34:19 +02:00
|
|
|
raise ImproperlyConfigured(
|
|
|
|
"Wrong public queue configuration") from e
|
2018-01-09 09:55:27 +01:00
|
|
|
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
|
2018-09-07 19:05:16 +02:00
|
|
|
return initial_data
|
|
|
|
|
2019-12-06 15:44:20 +01:00
|
|
|
def get_form_kwargs(self, *args, **kwargs):
|
|
|
|
kwargs = super().get_form_kwargs(*args, **kwargs)
|
2020-02-06 10:10:07 +01:00
|
|
|
if '_hide_fields_' in self.request.GET:
|
2022-07-12 12:34:19 +02:00
|
|
|
kwargs['hidden_fields'] = self.request.GET.get(
|
|
|
|
'_hide_fields_', '').split(',')
|
|
|
|
kwargs['readonly_fields'] = self.request.GET.get(
|
|
|
|
'_readonly_fields_', '').split(',')
|
2019-12-06 15:44:20 +01:00
|
|
|
return kwargs
|
|
|
|
|
2018-09-07 19:05:16 +02:00
|
|
|
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:
|
2022-07-12 12:34:19 +02:00
|
|
|
ticket = form.save(
|
|
|
|
user=self.request.user if self.request.user.is_authenticated else None)
|
2018-09-07 19:05:16 +02:00
|
|
|
try:
|
2018-09-08 20:36:35 +02:00
|
|
|
return HttpResponseRedirect('%s?ticket=%s&email=%s&key=%s' % (
|
2018-09-07 19:05:16 +02:00
|
|
|
reverse('helpdesk:public_view'),
|
|
|
|
ticket.ticket_for_url,
|
2022-03-17 03:29:09 +01:00
|
|
|
quote(ticket.submitter_email),
|
2018-09-08 20:36:35 +02:00
|
|
|
ticket.secret_key)
|
2018-09-07 19:05:16 +02:00
|
|
|
)
|
|
|
|
except ValueError:
|
|
|
|
# if someone enters a non-int string for the ticket
|
|
|
|
return HttpResponseRedirect(reverse('helpdesk:home'))
|
2009-08-04 14:04:13 +02:00
|
|
|
|
2012-03-01 23:28:54 +01:00
|
|
|
|
2019-12-12 15:22:12 +01:00
|
|
|
class CreateTicketIframeView(BaseCreateTicketView):
|
|
|
|
template_name = 'helpdesk/public_create_ticket_iframe.html'
|
|
|
|
|
2020-01-14 16:02:00 +01:00
|
|
|
@csrf_exempt
|
2020-01-13 21:15:22 +01:00
|
|
|
@xframe_options_exempt
|
|
|
|
def dispatch(self, *args, **kwargs):
|
|
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
|
2020-01-14 17:41:55 +01:00
|
|
|
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'
|
2020-01-13 21:15:22 +01:00
|
|
|
|
2020-01-16 18:23:41 +01:00
|
|
|
@xframe_options_exempt
|
|
|
|
def dispatch(self, *args, **kwargs):
|
|
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
|
2019-12-12 15:22:12 +01:00
|
|
|
|
|
|
|
class CreateTicketView(BaseCreateTicketView):
|
|
|
|
template_name = 'helpdesk/public_create_ticket.html'
|
|
|
|
|
2020-10-23 16:23:47 +02:00
|
|
|
def get_form(self, form_class=None):
|
|
|
|
form = super().get_form(form_class)
|
2022-07-12 12:34:19 +02:00
|
|
|
# Add the CSS error class to the form in order to better see them in
|
|
|
|
# the page
|
2020-10-23 16:23:47 +02:00
|
|
|
form.error_css_class = 'text-danger'
|
|
|
|
return form
|
|
|
|
|
2019-12-12 15:22:12 +01:00
|
|
|
|
2018-09-07 19:05:16 +02:00
|
|
|
class Homepage(CreateTicketView):
|
|
|
|
template_name = 'helpdesk/public_homepage.html'
|
2008-05-21 23:16:44 +02:00
|
|
|
|
2020-01-08 18:39:41 +01:00
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
context = super().get_context_data(**kwargs)
|
2022-07-12 12:34:19 +02:00
|
|
|
context['kb_categories'] = huser_from_request(
|
|
|
|
self.request).get_allowed_kb_categories()
|
2020-01-08 18:39:41 +01:00
|
|
|
return context
|
|
|
|
|
2008-08-19 10:50:38 +02:00
|
|
|
|
2023-11-23 20:52:27 +01:00
|
|
|
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
|
2018-09-08 20:36:35 +02:00
|
|
|
email = request.GET.get('email', None)
|
2023-11-23 20:52:27 +01:00
|
|
|
error_message = kwargs.get('error_message', None)
|
|
|
|
|
|
|
|
context.update({
|
2018-09-08 20:36:35 +02:00
|
|
|
'ticket': False,
|
|
|
|
'email': email,
|
|
|
|
'error_message': error_message,
|
|
|
|
'helpdesk_settings': helpdesk_settings,
|
|
|
|
})
|
2023-11-23 20:52:27 +01:00
|
|
|
return context
|
2018-09-08 20:36:35 +02:00
|
|
|
|
|
|
|
|
2023-11-23 20:37:45 +01:00
|
|
|
class ViewTicket(TemplateView):
|
|
|
|
template_name = 'helpdesk/public_view_ticket.html'
|
2018-09-08 20:36:35 +02:00
|
|
|
|
|
|
|
|
2023-11-23 20:37:45 +01:00
|
|
|
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:
|
2023-11-23 20:52:27 +01:00
|
|
|
return SearchForTicketView.as_view()(request)
|
2023-11-23 20:37:45 +01:00
|
|
|
else:
|
2023-11-23 20:52:27 +01:00
|
|
|
return SearchForTicketView.as_view()(request, _('Missing ticket ID or e-mail address. Please try again.'))
|
2023-11-23 20:37:45 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
queue, ticket_id = Ticket.queue_and_id_from_query(ticket_req)
|
2023-11-23 21:50:44 +01:00
|
|
|
if request.user.is_authenticated and request.user.email == email:
|
|
|
|
ticket = Ticket.objects.get(id=ticket_id, submitter_email__iexact=email)
|
2023-12-03 01:34:29 +01:00
|
|
|
elif hasattr(settings, 'HELPDESK_VIEW_A_TICKET_PUBLIC') and settings.HELPDESK_VIEW_A_TICKET_PUBLIC:
|
2023-11-23 20:37:45 +01:00
|
|
|
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):
|
2023-11-23 21:50:44 +01:00
|
|
|
return SearchForTicketView.as_view()(request, _('Invalid ticket ID or e-mail address. Please try again.'))
|
2023-11-23 20:37:45 +01:00
|
|
|
|
|
|
|
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
|
2016-10-21 17:14:12 +02:00
|
|
|
|
2008-08-19 10:50:38 +02:00
|
|
|
|
2023-11-23 21:50:44 +01:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
2011-11-27 09:26:56 +01:00
|
|
|
def change_language(request):
|
2011-11-27 09:43:51 +01:00
|
|
|
return_to = ''
|
2015-04-28 01:13:54 +02:00
|
|
|
if 'return_to' in request.GET:
|
2011-11-27 09:43:51 +01:00
|
|
|
return_to = request.GET['return_to']
|
|
|
|
|
2016-10-21 17:14:12 +02:00
|
|
|
return render(request, 'helpdesk/public_change_language.html', {'next': return_to})
|