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.
This commit is contained in:
Timothy Hobbs 2024-07-22 10:10:55 +02:00
parent 4adcc7f3d3
commit 27b7de5d18
6 changed files with 55 additions and 3 deletions

View File

@ -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'),
),
]

View File

@ -30,6 +30,7 @@ import os
import re import re
from rest_framework import serializers from rest_framework import serializers
import uuid import uuid
import json
class EscapeHtml(Extension): class EscapeHtml(Extension):
@ -926,6 +927,12 @@ class FollowUp(models.Model):
null=True, null=True,
) )
extra_fields = models.JSONField(
_('Extra Fields'),
blank=True,
null=True,
)
public = models.BooleanField( public = models.BooleanField(
_('Public'), _('Public'),
blank=True, blank=True,
@ -968,6 +975,9 @@ class FollowUp(models.Model):
blank=True, null=True blank=True, null=True
) )
def extra_fields_json(self):
return json.dumps(self.extra_fields)
class Meta: class Meta:
ordering = ('date',) ordering = ('date',)
verbose_name = _('Follow-up') verbose_name = _('Follow-up')

View File

@ -91,7 +91,7 @@ class FollowUpSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = FollowUp model = FollowUp
fields = ( 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', 'time_spent', 'attachments', 'followupattachment_set', 'date', 'message_id',
) )
@ -109,6 +109,7 @@ class FollowUpSerializer(serializers.ModelSerializer):
public=validated_data.get("public", False), public=validated_data.get("public", False),
new_status=validated_data.get("new_status", None), new_status=validated_data.get("new_status", None),
time_spent=validated_data.get("time_spent", None), time_spent=validated_data.get("time_spent", None),
extra_fields=validated_data.get("extra_fields", None),
) )

View File

@ -80,6 +80,9 @@
</small> </small>
{% endif %}{% endwith %} {% endif %}{% endwith %}
</div> </div>
<div style="display:none">
<pre id="followup_extra_fields_{{followup.id}}" class="followup_extra_field">{{ followup.extra_fields }}</pre>
</div>
<!-- /.list-group-item --> <!-- /.list-group-item -->
{% endfor %} {% endfor %}
</div> </div>
@ -111,6 +114,12 @@
{% blocktrans %} {% blocktrans %}
<dd class='form_help_text'>You can insert ticket and queue details in your message. For more information, see the <a href='{{ context_help_url }}'>context help page</a>.</dd> <dd class='form_help_text'>You can insert ticket and queue details in your message. For more information, see the <a href='{{ context_help_url }}'>context help page</a>.</dd>
{% endblocktrans %} {% endblocktrans %}
<div style="display:none;">
<dt><label for='extra_fields'>{% trans "Extra Fields" %}</label></dt>
<dd>
<textarea rows='8' cols='70' name='extra_fields' id='extra_fields'></textarea>
</dd>
</div>
<dt><label>{% trans "New Status" %}</label></dt> <dt><label>{% trans "New Status" %}</label></dt>
{% if not ticket.can_be_resolved %}<dd>{% trans "This ticket cannot be resolved or closed until the tickets it depends on are resolved." %}</dd>{% endif %} {% if not ticket.can_be_resolved %}<dd>{% trans "This ticket cannot be resolved or closed until the tickets it depends on are resolved." %}</dd>{% endif %}

View File

@ -206,6 +206,7 @@ def update_ticket(
due_date=None, due_date=None,
new_checklists=None, new_checklists=None,
message_id=None, message_id=None,
extra_fields=None,
): ):
# We need to allow the 'ticket' and 'queue' contexts to be applied to the # We need to allow the 'ticket' and 'queue' contexts to be applied to the
# comment. # comment.
@ -240,7 +241,8 @@ def update_ticket(
owner = ticket.assigned_to.id owner = ticket.assigned_to.id
f = FollowUp(ticket=ticket, date=timezone.now(), comment=comment, 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): if is_helpdesk_staff(user):
f.user = user f.user = user

View File

@ -306,12 +306,14 @@ def followup_edit(request, ticket_id, followup_id):
public = form.cleaned_data['public'] public = form.cleaned_data['public']
new_status = form.cleaned_data['new_status'] new_status = form.cleaned_data['new_status']
time_spent = form.cleaned_data['time_spent'] time_spent = form.cleaned_data['time_spent']
extra_fields = form.cleaned_data['extra_fields']
# will save previous date # will save previous date
old_date = followup.date old_date = followup.date
new_followup = FollowUp(title=title, date=old_date, ticket=_ticket, new_followup = FollowUp(title=title, date=old_date, ticket=_ticket,
comment=comment, public=public, comment=comment, public=public,
new_status=new_status, new_status=new_status,
time_spent=time_spent) time_spent=time_spent,
extra_fields=extra_fields)
# keep old user if one did exist before. # keep old user if one did exist before.
if followup.user: if followup.user:
new_followup.user = 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', '') comment = request.POST.get('comment', '')
new_status = int(request.POST.get('new_status', ticket.status)) new_status = int(request.POST.get('new_status', ticket.status))
title = request.POST.get('title', '') 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)) owner = int(request.POST.get('owner', -1))
priority = int(request.POST.get('priority', ticket.priority)) priority = int(request.POST.get('priority', ticket.priority))
queue = int(request.POST.get('queue', ticket.queue.id)) 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 request.FILES,
not comment, not comment,
not changes_in_checklists, not changes_in_checklists,
not extra_fields,
new_status == ticket.status, new_status == ticket.status,
title == ticket.title, title == ticket.title,
priority == int(ticket.priority), priority == int(ticket.priority),
@ -613,6 +624,7 @@ def update_ticket_view(request, ticket_id, public=False):
ticket, ticket,
title = title, title = title,
comment = comment, comment = comment,
extra_fields = extra_fields,
files = request.FILES.getlist('attachment'), files = request.FILES.getlist('attachment'),
public = request.POST.get('public', False), public = request.POST.get('public', False),
owner = int(request.POST.get('owner', -1)), owner = int(request.POST.get('owner', -1)),