From 27b7de5d1888236b3f9d7a0001fc737af647662c Mon Sep 17 00:00:00 2001 From: Timothy Hobbs Date: Mon, 22 Jul 2024 10:10:55 +0200 Subject: [PATCH] Add JSON extra_fields field to followup module This is extremely useful when writing custom frontends on top of django helpdesk. This is similar to the custom_fields except it is normalized and attached to followups and not tickets. The fact that it is just JSON means that there is no need for updating the database with new CustomField entries making for more easily maintained custom frontends. --- .../migrations/0039_followup_extra_fields.py | 18 ++++++++++++++++++ helpdesk/models.py | 10 ++++++++++ helpdesk/serializers.py | 3 ++- helpdesk/templates/helpdesk/ticket.html | 9 +++++++++ helpdesk/update_ticket.py | 4 +++- helpdesk/views/staff.py | 14 +++++++++++++- 6 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 helpdesk/migrations/0039_followup_extra_fields.py diff --git a/helpdesk/migrations/0039_followup_extra_fields.py b/helpdesk/migrations/0039_followup_extra_fields.py new file mode 100644 index 00000000..69854ad9 --- /dev/null +++ b/helpdesk/migrations/0039_followup_extra_fields.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-07-22 07:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('helpdesk', '0038_checklist_checklisttemplate_checklisttask'), + ] + + operations = [ + migrations.AddField( + model_name='followup', + name='extra_fields', + field=models.JSONField(blank=True, null=True, verbose_name='Extra Fields'), + ), + ] diff --git a/helpdesk/models.py b/helpdesk/models.py index a9fca8ad..568b8575 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -30,6 +30,7 @@ import os import re from rest_framework import serializers import uuid +import json class EscapeHtml(Extension): @@ -926,6 +927,12 @@ class FollowUp(models.Model): null=True, ) + extra_fields = models.JSONField( + _('Extra Fields'), + blank=True, + null=True, + ) + public = models.BooleanField( _('Public'), blank=True, @@ -968,6 +975,9 @@ class FollowUp(models.Model): blank=True, null=True ) + def extra_fields_json(self): + return json.dumps(self.extra_fields) + class Meta: ordering = ('date',) verbose_name = _('Follow-up') diff --git a/helpdesk/serializers.py b/helpdesk/serializers.py index c8c4dbb7..eacca8b7 100644 --- a/helpdesk/serializers.py +++ b/helpdesk/serializers.py @@ -91,7 +91,7 @@ class FollowUpSerializer(serializers.ModelSerializer): class Meta: model = FollowUp fields = ( - 'id', 'ticket', 'user', 'title', 'comment', 'public', 'new_status', + 'id', 'ticket', 'user', 'title', 'comment', 'public', 'new_status', 'extra_fields', 'time_spent', 'attachments', 'followupattachment_set', 'date', 'message_id', ) @@ -109,6 +109,7 @@ class FollowUpSerializer(serializers.ModelSerializer): public=validated_data.get("public", False), new_status=validated_data.get("new_status", None), time_spent=validated_data.get("time_spent", None), + extra_fields=validated_data.get("extra_fields", None), ) diff --git a/helpdesk/templates/helpdesk/ticket.html b/helpdesk/templates/helpdesk/ticket.html index ce7d61e4..f5b25cda 100644 --- a/helpdesk/templates/helpdesk/ticket.html +++ b/helpdesk/templates/helpdesk/ticket.html @@ -80,6 +80,9 @@ {% endif %}{% endwith %} +
+
{{ followup.extra_fields }}
+
{% endfor %} @@ -111,6 +114,12 @@ {% blocktrans %}
You can insert ticket and queue details in your message. For more information, see the context help page.
{% endblocktrans %} +
+
+
+ +
+
{% if not ticket.can_be_resolved %}
{% trans "This ticket cannot be resolved or closed until the tickets it depends on are resolved." %}
{% endif %} diff --git a/helpdesk/update_ticket.py b/helpdesk/update_ticket.py index 989e04e4..53525669 100644 --- a/helpdesk/update_ticket.py +++ b/helpdesk/update_ticket.py @@ -206,6 +206,7 @@ def update_ticket( due_date=None, new_checklists=None, message_id=None, + extra_fields=None, ): # We need to allow the 'ticket' and 'queue' contexts to be applied to the # comment. @@ -240,7 +241,8 @@ def update_ticket( owner = ticket.assigned_to.id f = FollowUp(ticket=ticket, date=timezone.now(), comment=comment, - time_spent=time_spent, message_id=message_id, title=title) + time_spent=time_spent, message_id=message_id, title=title, + extra_fields=extra_fields) if is_helpdesk_staff(user): f.user = user diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index f89b5d08..0595f386 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -306,12 +306,14 @@ def followup_edit(request, ticket_id, followup_id): public = form.cleaned_data['public'] new_status = form.cleaned_data['new_status'] time_spent = form.cleaned_data['time_spent'] + extra_fields = form.cleaned_data['extra_fields'] # will save previous date old_date = followup.date new_followup = FollowUp(title=title, date=old_date, ticket=_ticket, comment=comment, public=public, new_status=new_status, - time_spent=time_spent) + time_spent=time_spent, + extra_fields=extra_fields) # keep old user if one did exist before. if followup.user: new_followup.user = followup.user @@ -574,6 +576,14 @@ def update_ticket_view(request, ticket_id, public=False): comment = request.POST.get('comment', '') new_status = int(request.POST.get('new_status', ticket.status)) title = request.POST.get('title', '') + try: + efs = request.POST.get('extra_fields', '{}') + if efs: + extra_fields = json.loads(efs) + else: + extra_fields = {} + except ValueError: + extra_fields = {"error": "Invalid JSON"} owner = int(request.POST.get('owner', -1)) priority = int(request.POST.get('priority', ticket.priority)) queue = int(request.POST.get('queue', ticket.queue.id)) @@ -597,6 +607,7 @@ def update_ticket_view(request, ticket_id, public=False): not request.FILES, not comment, not changes_in_checklists, + not extra_fields, new_status == ticket.status, title == ticket.title, priority == int(ticket.priority), @@ -613,6 +624,7 @@ def update_ticket_view(request, ticket_id, public=False): ticket, title = title, comment = comment, + extra_fields = extra_fields, files = request.FILES.getlist('attachment'), public = request.POST.get('public', False), owner = int(request.POST.get('owner', -1)),