diff --git a/helpdesk/forms.py b/helpdesk/forms.py index ff76d435..55b004e9 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -177,6 +177,17 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form): help_text=_('You can attach a file such as a document or screenshot to this ticket.'), ) + def __init__(self, kbcategory=None, *args, **kwargs): + super().__init__(*args, **kwargs) + if kbcategory: + self.fields['kbitem'] = forms.ChoiceField( + widget=forms.Select(attrs={'class': 'form-control'}), + required=False, + label=_('Knowedge Base Item'), + choices=[(kbi.pk, kbi.title) for kbi in KBItem.objects.filter(category=kbcategory.pk)], + ) + + def _add_form_custom_fields(self, staff_only_filter=None): if staff_only_filter is None: queryset = CustomField.objects.all() @@ -184,11 +195,8 @@ class AbstractTicketForm(CustomFieldMixin, forms.Form): queryset = CustomField.objects.filter(staff_only=staff_only_filter) for field in queryset: - instanceargs = { - 'label': field.label, - 'help_text': field.help_text, - 'required': field.required, - } + instanceargs = { 'label': field.label, 'help_text': + field.help_text, 'required': field.required, } self.customfield_to_field(field, instanceargs) @@ -341,22 +349,13 @@ class PublicTicketForm(AbstractTicketForm): help_text=_('We will e-mail you when your ticket is updated.'), ) - def __init__(self, hidden_fields=(), readonly_fields=(), kbcategory=None, *args, **kwargs): + def __init__(self, hidden_fields=(), readonly_fields=(), *args, **kwargs): """ Add any (non-staff) custom fields that are defined to the form """ super(PublicTicketForm, self).__init__(*args, **kwargs) self._add_form_custom_fields(False) - if kbcategory: - self.fields['kbitem'] = forms.ChoiceField( - widget=forms.Select(attrs={'class': 'form-control'}), - required=False, - label=_('Knowedge Base Item'), - choices=[(kbi.pk, kbi.title) for kbi in KBItem.objects.filter(category=kbcategory.pk)], - ) - - field_hide_table = { 'queue': 'HELPDESK_PUBLIC_TICKET_QUEUE', 'priority': 'HELPDESK_PUBLIC_TICKET_PRIORITY', diff --git a/helpdesk/models.py b/helpdesk/models.py index 09d2436b..1ab47c74 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -1320,6 +1320,11 @@ class KBItem(models.Model): from django.urls import reverse return str(reverse('helpdesk:kb_category', args=(self.category.slug,)))+"?kbitem="+str(self.pk) + def query_url(self): + from django.urls import reverse + return str(reverse('helpdesk:list')) +"?kbitem="+str(self.pk) + + def get_markdown(self): return get_markdown(self.answer) diff --git a/helpdesk/templates/helpdesk/filters/kbitems.html b/helpdesk/templates/helpdesk/filters/kbitems.html new file mode 100644 index 00000000..fa79dd33 --- /dev/null +++ b/helpdesk/templates/helpdesk/filters/kbitems.html @@ -0,0 +1,15 @@ +{% load i18n humanize %} +{% load static from staticfiles %} +{% load in_list %} +
+
+ +
+
+ +
+
+ +
+
{% trans "Ctrl-click to select multiple options" %}
+
diff --git a/helpdesk/templates/helpdesk/kb_category.html b/helpdesk/templates/helpdesk/kb_category.html index 953fe183..38ddd413 100644 --- a/helpdesk/templates/helpdesk/kb_category.html +++ b/helpdesk/templates/helpdesk/kb_category.html @@ -43,6 +43,7 @@ {% blocktrans with recommendations=item.recommendations votes=item.votes %}{{ recommendations }} people found this answer useful of {{votes}}. {% endblocktrans %} {% endif %} + @@ -51,7 +52,7 @@ {% endfor %} {% if category.queue %} - + {% endif %} {% endblock %} diff --git a/helpdesk/templates/helpdesk/ticket_desc_table.html b/helpdesk/templates/helpdesk/ticket_desc_table.html index 52b3000e..4a3dbfe5 100644 --- a/helpdesk/templates/helpdesk/ticket_desc_table.html +++ b/helpdesk/templates/helpdesk/ticket_desc_table.html @@ -68,6 +68,12 @@ {% trans "Total time spent" %} {{ ticket.time_spent_formated }} + {% if ticket.kbitem %} + + {% trans "Knowlegebase item" %} + {{ticket.kbitem.title}} + + {% endif %} {% trans "Attachments" %} diff --git a/helpdesk/templates/helpdesk/ticket_list.html b/helpdesk/templates/helpdesk/ticket_list.html index 3e56eb7c..03be65e2 100644 --- a/helpdesk/templates/helpdesk/ticket_list.html +++ b/helpdesk/templates/helpdesk/ticket_list.html @@ -171,6 +171,7 @@ + {% csrf_token %} @@ -196,6 +197,9 @@
  • {% include './filters/keywords.html' %}
  • +
  • + {% include './filters/kbitems.html' %} +
  • @@ -318,8 +322,8 @@ var name = data.split(" ")[1]; if (type === 'display') { - data = '
    ' + - row.id + '. ' + + data = '
    ' + + row.id + '. ' + row.title + '
    '; } return data @@ -348,7 +352,7 @@ "render": function(data, type, row, meta) { if (data != "None") { return data; - } + } else { return ""; } @@ -396,6 +400,9 @@ {% if query_params.search_string %} $("#filterBuilderSelect-Keywords")[0].disabled = "disabled"; {% endif %} + {% if query_params.filtering.kbitem__in %} + $("#filterBuilderSelect-KBItems")[0].disabled = "disabled"; + {% endif %} }); {% for f in query_params.filtering %} diff --git a/helpdesk/views/abstract_views.py b/helpdesk/views/abstract_views.py new file mode 100644 index 00000000..648996b3 --- /dev/null +++ b/helpdesk/views/abstract_views.py @@ -0,0 +1,37 @@ +from django.views.generic.edit import FormView + +from helpdesk.models import CustomField, KBItem, Queue + + +class AbstractCreateTicketMixin(): + def get_initial(self): + initial_data = {} + request = self.request + try: + initial_data['queue'] = Queue.objects.get(slug=request.GET.get('queue', None)).id + except Queue.DoesNotExist: + pass + if request.user.is_authenticated and request.user.usersettings_helpdesk.use_email_as_submitter and request.user.email: + initial_data['submitter_email'] = request.user.email + + + query_param_fields = ['submitter_email', 'title', 'body', 'queue', 'kbitem'] + custom_fields = ["custom_%s" % f.name for f in CustomField.objects.filter(staff_only=False)] + query_param_fields += custom_fields + for qpf in query_param_fields: + initial_data[qpf] = request.GET.get(qpf, initial_data.get(qpf, "")) + + return initial_data + + def get_form_kwargs(self, *args, **kwargs): + kwargs = super().get_form_kwargs(*args, **kwargs) + kbitem = self.request.GET.get( + 'kbitem', + self.request.POST.get('kbitem', None), + ) + if kbitem: + try: + kwargs['kbcategory'] = KBItem.objects.get(pk=int(kbitem)).category + except (ValueError, KBItem.DoesNotExist): + pass + return kwargs diff --git a/helpdesk/views/kb.py b/helpdesk/views/kb.py index 78666767..1b2626d0 100644 --- a/helpdesk/views/kb.py +++ b/helpdesk/views/kb.py @@ -30,12 +30,18 @@ def category(request, slug): selected_item = request.GET.get('kbitem', None) try: selected_item = int(selected_item) - except ValueError: + except TypeError: + pass + qparams = request.GET.copy() + try: + del qparams['kbitem'] + except KeyError: pass return render(request, 'helpdesk/kb_category.html', { 'category': category, 'items': items, 'selected_item': selected_item, + 'query_param_string': qparams.urlencode(), 'helpdesk_settings': helpdesk_settings, }) diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index 2ffcdc38..b9f5279b 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -19,6 +19,7 @@ from django.views.generic.edit import FormView from helpdesk import settings as helpdesk_settings from helpdesk.decorators import protect_view, is_helpdesk_staff import helpdesk.views.staff as staff +import helpdesk.views.abstract_views as abstract_views from helpdesk.forms import PublicTicketForm from helpdesk.lib import text_is_spam from helpdesk.models import CustomField, Ticket, Queue, UserSettings, KBCategory, KBItem @@ -31,7 +32,7 @@ def create_ticket(request, *args, **kwargs): return CreateTicketView.as_view()(request, *args, **kwargs) -class BaseCreateTicketView(FormView): +class BaseCreateTicketView(abstract_views.AbstractCreateTicketMixin, FormView): form_class = PublicTicketForm def dispatch(self, *args, **kwargs): @@ -51,54 +52,27 @@ class BaseCreateTicketView(FormView): return HttpResponseRedirect(reverse('helpdesk:dashboard')) return super().dispatch(*args, **kwargs) - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context['kb_categories'] = KBCategory.objects.all() - return context - def get_initial(self): request = self.request - initial_data = {} - try: - queue = Queue.objects.get(slug=request.GET.get('queue', None)) - except Queue.DoesNotExist: - queue = None + 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: - queue = Queue.objects.get(slug=settings.HELPDESK_PUBLIC_TICKET_QUEUE) + initial_data['queue'] = Queue.objects.get(slug=settings.HELPDESK_PUBLIC_TICKET_QUEUE).id except Queue.DoesNotExist: return HttpResponse(status=500) 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 - - if queue: - initial_data['queue'] = queue.id - - if request.user.is_authenticated and request.user.email: - initial_data['submitter_email'] = request.user.email - - query_param_fields = ['submitter_email', 'title', 'body', 'queue', 'kbitem'] - custom_fields = ["custom_%s" % f.name for f in CustomField.objects.filter(staff_only=False)] - query_param_fields += custom_fields - for qpf in query_param_fields: - initial_data[qpf] = request.GET.get(qpf, initial_data.get(qpf, "")) return initial_data def get_form_kwargs(self, *args, **kwargs): kwargs = super().get_form_kwargs(*args, **kwargs) kwargs['hidden_fields'] = self.request.GET.get('_hide_fields_', '').split(',') kwargs['readonly_fields'] = self.request.GET.get('_readonly_fields_', '').split(',') - kbitem = self.request.GET.get('kbitem', None) - if kbitem: - try: - kwargs['kbcategory'] = KBItem.objects.get(pk=int(kbitem)) - except (ValueError, KBItem.DoesNotExist): - pass return kwargs def form_valid(self, form): @@ -134,6 +108,11 @@ class CreateTicketView(BaseCreateTicketView): class Homepage(CreateTicketView): template_name = 'helpdesk/public_homepage.html' + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['kb_categories'] = KBCategory.objects.all() + return context + def search_for_ticket(request, error_message=None): if hasattr(settings, 'HELPDESK_VIEW_A_TICKET_PUBLIC') and settings.HELPDESK_VIEW_A_TICKET_PUBLIC: diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index c374b51b..0ea2f481 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -52,9 +52,10 @@ from helpdesk.lib import ( ) from helpdesk.models import ( Ticket, Queue, FollowUp, TicketChange, PreSetReply, FollowUpAttachment, SavedSearch, - IgnoreEmail, TicketCC, TicketDependency, UserSettings, + IgnoreEmail, TicketCC, TicketDependency, UserSettings, KBItem, ) from helpdesk import settings as helpdesk_settings +import helpdesk.views.abstract_views as abstract_views from helpdesk.views.permissions import MustBeStaffMixin from ..lib import format_time_spent @@ -802,7 +803,9 @@ def ticket_list(request): 'search_string': '', } default_query_params = { - 'filtering': {'status__in': [1, 2, 3]}, + 'filtering': { + 'status__in': [1, 2, 3], + }, 'sorting': 'created', 'search_string': '', 'sortreverse': False, @@ -849,7 +852,7 @@ def ticket_list(request): if saved_query: pass - elif not {'queue', 'assigned_to', 'status', 'q', 'sort', 'sortreverse'}.intersection(request.GET): + elif not {'queue', 'assigned_to', 'status', 'q', 'sort', 'sortreverse', 'kbitem'}.intersection(request.GET): # Fall-back if no querying is being done all_queues = Queue.objects.all() query_params = deepcopy(default_query_params) @@ -858,6 +861,7 @@ def ticket_list(request): ('queue', 'queue__id__in'), ('assigned_to', 'assigned_to__id__in'), ('status', 'status__in'), + ('kbitem', 'kbitem__in'), ] for param, filter_command in filter_in_params: @@ -884,7 +888,7 @@ def ticket_list(request): # SORTING sort = request.GET.get('sort', None) - if sort not in ('status', 'assigned_to', 'created', 'title', 'queue', 'priority'): + if sort not in ('status', 'assigned_to', 'created', 'title', 'queue', 'priority', 'kbitem'): sort = 'created' query_params['sorting'] = sort @@ -907,12 +911,15 @@ def ticket_list(request): '' 'Django Documentation on string matching in SQLite.') + kbitem_choices = [(item.pk, item.title) for item in KBItem.objects.all()] + return render(request, 'helpdesk/ticket_list.html', dict( 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=huser.get_queues(), status_choices=Ticket.STATUS_CHOICES, + kbitem_choices=kbitem_choices, urlsafe_query=urlsafe_query, user_saved_queries=user_saved_queries, query_params=query_params, @@ -992,17 +999,12 @@ def edit_ticket(request, ticket_id): edit_ticket = staff_member_required(edit_ticket) -class CreateTicketView(MustBeStaffMixin, FormView): +class CreateTicketView(MustBeStaffMixin, abstract_views.AbstractCreateTicketMixin, FormView): template_name = 'helpdesk/create_ticket.html' form_class = TicketForm def get_initial(self): - initial_data = {} - request = self.request - if request.user.usersettings_helpdesk.use_email_as_submitter and request.user.email: - initial_data['submitter_email'] = request.user.email - if 'queue' in request.GET: - initial_data['queue'] = request.GET['queue'] + initial_data = super().get_initial() return initial_data def get_form_kwargs(self):