diff --git a/helpdesk/forms.py b/helpdesk/forms.py index 0c6ac3d3..b9a00ee3 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -607,23 +607,34 @@ class MultipleTicketSelectForm(forms.Form): class ChecklistForm(forms.ModelForm): - checklist_template = forms.ModelChoiceField( - label=_("Template"), - queryset=ChecklistTemplate.objects.all(), - widget=forms.Select(attrs={'class': 'form-wontrol'}), - required=False, - ) name = forms.CharField( - widget=forms.TextInput(attrs={'class': 'form-wontrol'}), - required=False, + widget=forms.TextInput(attrs={'class': 'form-control'}), + required=True, ) class Meta: model = Checklist fields = ('name',) + +class CreateChecklistForm(ChecklistForm): + checklist_template = forms.ModelChoiceField( + label=_("Template"), + queryset=ChecklistTemplate.objects.all(), + widget=forms.Select(attrs={'class': 'form-control'}), + required=False, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['name'].required = False + def clean(self): if not self.cleaned_data.get('checklist_template') and not self.cleaned_data.get('name'): raise ValidationError(_('Please choose at least a name or a template for the new checklist')) if self.cleaned_data.get('checklist_template') and self.cleaned_data.get('name'): raise ValidationError(_('Please choose either a name or a template for the new checklist')) + + +class FormControlDeleteFormSet(forms.BaseInlineFormSet): + deletion_widget = forms.CheckboxInput(attrs={'class': 'form-control'}) diff --git a/helpdesk/migrations/0038_checklist_checklisttemplate_checklisttask.py b/helpdesk/migrations/0038_checklist_checklisttemplate_checklisttask.py index 3bfd0156..a3c80616 100644 --- a/helpdesk/migrations/0038_checklist_checklisttemplate_checklisttask.py +++ b/helpdesk/migrations/0038_checklist_checklisttemplate_checklisttask.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2 on 2023-04-22 20:50 +# Generated by Django 4.2 on 2023-04-28 21:23 from django.db import migrations, models import django.db.models.deletion @@ -42,11 +42,13 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('description', models.CharField(max_length=250, verbose_name='Description')), ('completion_date', models.DateTimeField(blank=True, null=True, verbose_name='Completion Date')), + ('position', models.PositiveSmallIntegerField(db_index=True, verbose_name='Position')), ('checklist', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tasks', to='helpdesk.checklist', verbose_name='Checklist')), ], options={ 'verbose_name': 'Checklist Task', 'verbose_name_plural': 'Checklist Tasks', + 'ordering': ('position',), }, ), ] diff --git a/helpdesk/models.py b/helpdesk/models.py index d090529d..7f9e2072 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -2030,8 +2030,8 @@ class ChecklistTemplate(models.Model): def create_checklist_for_ticket(self, ticket): checklist = ticket.checklists.create(name=self.name) - for task in self.task_list: - checklist.tasks.create(description=task) + for position, task in enumerate(self.task_list): + checklist.tasks.create(description=task, position=position) return checklist @@ -2079,12 +2079,17 @@ class ChecklistTask(models.Model): null=True, blank=True ) + position = models.PositiveSmallIntegerField( + verbose_name=_('Position'), + db_index=True + ) objects = ChecklistTaskQuerySet.as_manager() class Meta: verbose_name = _('Checklist Task') verbose_name_plural = _('Checklist Tasks') + ordering = ('position',) def __str__(self): return self.description diff --git a/helpdesk/templates/helpdesk/checklist_confirm_delete.html b/helpdesk/templates/helpdesk/checklist_confirm_delete.html new file mode 100644 index 00000000..1e5a697f --- /dev/null +++ b/helpdesk/templates/helpdesk/checklist_confirm_delete.html @@ -0,0 +1,45 @@ +{% extends "helpdesk/base.html" %} + +{% load i18n %} + +{% block helpdesk_title %}{% trans "Delete Checklist" %}{% endblock %} + +{% block helpdesk_breadcrumb %} + + + +{% endblock %} + +{% block helpdesk_body %} +
+
+
+
+

+ {% trans "Delete Checklist" %} +

+
+
+
+ {% csrf_token %} +

{% trans "Are you sure your want to delete checklist" %} {{ checklist.name }} ?

+
+ + + {% trans "Don't Delete" %} + + +
+
+
+
+
+
+{% endblock %} diff --git a/helpdesk/templates/helpdesk/checklist_form.html b/helpdesk/templates/helpdesk/checklist_form.html new file mode 100644 index 00000000..fbc9a8e7 --- /dev/null +++ b/helpdesk/templates/helpdesk/checklist_form.html @@ -0,0 +1,106 @@ +{% extends "helpdesk/base.html" %} + +{% load i18n %} + +{% block helpdesk_title %}{% trans "Edit Checklist" %}{% endblock %} + +{% block helpdesk_breadcrumb %} + + + +{% endblock %} + +{% block helpdesk_body %} +
+
+

+ {% trans "Edit Checklist" %} + + + {% trans "Delete checklist" %} + +

+
+
+ {% if form.non_field_errors %} +

+ {{ form.non_field_errors }} +

+ {% endif %} +
+ {% csrf_token %} +
+
+ {{ form.name.label_tag }} + {{ form.name }} + {{ form.name.errors }} +
+
+

Tasks

+ {{ formset.management_form }} + + + + + + + + + + {% for form in formset %} + + {{ form.id }} + + + + + {% endfor %} + +
{% trans "Description" %}{% trans "Position" %}{% trans "Delete?" %}
+ {{ form.description }} + {{ form.description.errors }} + + {{ form.position }} + {{ form.position.errors }} + + {{ form.DELETE }} + {{ form.DELETE.errors }} +
+ +
+ + + {% trans "Cancel Changes" %} + + +
+
+
+
+{% endblock %} + +{% block helpdesk_js %} + +{% endblock %} diff --git a/helpdesk/templates/helpdesk/ticket.html b/helpdesk/templates/helpdesk/ticket.html index 265a449d..84185a9d 100644 --- a/helpdesk/templates/helpdesk/ticket.html +++ b/helpdesk/templates/helpdesk/ticket.html @@ -7,6 +7,11 @@ {% block helpdesk_title %}{{ ticket.queue.slug }}-{{ ticket.id }} : {% trans "View Ticket Details" %}{% endblock %} {% block helpdesk_head %} + {% endblock %} {% block h1_title %}{{ ticket.ticket_for_url }}{% endblock %} diff --git a/helpdesk/templates/helpdesk/ticket_desc_table.html b/helpdesk/templates/helpdesk/ticket_desc_table.html index 36eba139..f7a2f710 100644 --- a/helpdesk/templates/helpdesk/ticket_desc_table.html +++ b/helpdesk/templates/helpdesk/ticket_desc_table.html @@ -178,14 +178,17 @@
{% for checklist in ticket.checklists.all %}
-
-
-

+
+
+
{{ checklist }} - -
+

@@ -214,9 +217,9 @@
{% endfor %}
-
+
-

Add a checklist

+
Add a checklist
diff --git a/helpdesk/urls.py b/helpdesk/urls.py index 1d001e18..762ac7af 100644 --- a/helpdesk/urls.py +++ b/helpdesk/urls.py @@ -93,6 +93,16 @@ urlpatterns = [ staff.attachment_del, name="attachment_del", ), + path( + "tickets//checklists//", + staff.edit_ticket_checklist, + name="edit_ticket_checklist" + ), + path( + "tickets//checklists//delete/", + staff.delete_ticket_checklist, + name="delete_ticket_checklist" + ), re_path(r"^raw/(?P\w+)/$", staff.raw_details, name="raw"), path("rss/", staff.rss_list, name="rss_index"), path("reports/", staff.report_index, name="report_index"), diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index 40e8dbe1..e0576add 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -20,6 +20,7 @@ from django.core.exceptions import PermissionDenied, ValidationError from django.core.handlers.wsgi import WSGIRequest from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator from django.db.models import Q +from django.forms import inlineformset_factory, TextInput, NumberInput from django.http import Http404, HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import get_object_or_404, redirect, render from django.urls import reverse, reverse_lazy @@ -49,7 +50,9 @@ from helpdesk.forms import ( TicketDependencyForm, TicketForm, UserSettingsForm, - ChecklistForm + CreateChecklistForm, + ChecklistForm, + FormControlDeleteFormSet ) from helpdesk.lib import process_attachments, queue_template_context, safe_template_context from helpdesk.models import ( @@ -65,7 +68,9 @@ from helpdesk.models import ( TicketChange, TicketCustomFieldValue, TicketDependency, - UserSettings + UserSettings, + Checklist, + ChecklistTask ) from helpdesk.query import get_query_class, query_from_base64, query_to_base64 from helpdesk.user import HelpdeskUser @@ -407,7 +412,7 @@ def view_ticket(request, ticket_id): else: submitter_userprofile_url = None - checklist_form = ChecklistForm(request.POST or None) + checklist_form = CreateChecklistForm(request.POST or None) if checklist_form.is_valid(): checklist_template = checklist_form.cleaned_data.get('checklist_template') if checklist_template: @@ -432,6 +437,55 @@ def view_ticket(request, ticket_id): }) +@helpdesk_staff_member_required +def edit_ticket_checklist(request, ticket_id, checklist_id): + ticket = get_object_or_404(Ticket, id=ticket_id) + ticket_perm_check(request, ticket) + checklist = get_object_or_404(ticket.checklists.all(), id=checklist_id) + + form = ChecklistForm(request.POST or None, instance=checklist) + TaskFormSet = inlineformset_factory( + Checklist, + ChecklistTask, + formset=FormControlDeleteFormSet, + fields=['description', 'position'], + widgets={ + 'description': TextInput(attrs={'class': 'form-control'}), + 'position': NumberInput(attrs={'class': 'form-control'}), + }, + can_delete=True, + extra=1 + ) + formset = TaskFormSet(request.POST or None, instance=checklist) + if form.is_valid() and formset.is_valid(): + form.save() + formset.save() + return redirect(ticket) + + return render(request, 'helpdesk/checklist_form.html', { + 'ticket': ticket, + 'checklist': checklist, + 'form': form, + 'formset': formset, + }) + + +@helpdesk_staff_member_required +def delete_ticket_checklist(request, ticket_id, checklist_id): + ticket = get_object_or_404(Ticket, id=ticket_id) + ticket_perm_check(request, ticket) + checklist = get_object_or_404(ticket.checklists.all(), id=checklist_id) + + if request.POST: + checklist.delete() + return redirect(ticket) + + return render(request, 'helpdesk/checklist_confirm_delete.html', { + 'ticket': ticket, + 'checklist': checklist, + }) + + def return_ticketccstring_and_show_subscribe(user, ticket): """used in view_ticket() and followup_edit()""" # create the ticketcc_string and check whether current user is already