diff --git a/CHANGELOG b/CHANGELOG index 6f63438c..b208c928 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,3 +4,8 @@ Add rudimentary CC: functionality on tickets, controlled by staff users. CC's can be e-mail addresses or users, who will receive copies of all emails sent to the Submitter. This is a work in progress. + +2009-09-09 r140 Issue #13 Add Tags to tickets +Patch courtesy of david@zettazebra.com, adds the ability to add tags to +tickets if django-tagging is installed and in use. If django-tagging isn't +being used, no change is visible to the user. diff --git a/forms.py b/forms.py index bf533568..33156796 100644 --- a/forms.py +++ b/forms.py @@ -16,6 +16,7 @@ from django.utils.translation import ugettext as _ from helpdesk.lib import send_templated_mail from helpdesk.models import Ticket, Queue, FollowUp, Attachment, IgnoreEmail, TicketCC +from helpdesk.settings import HAS_TAG_SUPPORT class EditTicketForm(forms.ModelForm): class Meta: @@ -72,6 +73,17 @@ class TicketForm(forms.Form): help_text=_('You can attach a file such as a document or screenshot to this ticket.'), ) + if HAS_TAG_SUPPORT: + tags = forms.CharField( + max_length=255, + required=False, + widget=forms.TextInput(), + label=_('Tags'), + help_text=_('Words, separated by spaces, or phrases separated by commas. ' + 'These should communicate significant characteristics of this ' + 'ticket'), + ) + def save(self, user): """ Writes and returns a Ticket() object @@ -88,6 +100,9 @@ class TicketForm(forms.Form): priority = self.cleaned_data['priority'], ) + if HAS_TAG_SUPPORT: + t.tags = self.cleaned_data['tags'] + if self.cleaned_data['assigned_to']: try: u = User.objects.get(id=self.cleaned_data['assigned_to']) diff --git a/models.py b/models.py index 2ec8da2d..fbeaa3f1 100644 --- a/models.py +++ b/models.py @@ -13,7 +13,10 @@ from django.contrib.auth.models import User from django.db import models from django.conf import settings from django.utils.translation import ugettext_lazy as _ +from helpdesk.settings import HAS_TAG_SUPPORT +if HAS_TAG_SUPPORT: + from tagging.fields import TagField class Queue(models.Model): """ @@ -400,6 +403,9 @@ class Ticket(models.Model): ) staff_url = property(_get_staff_url) + if HAS_TAG_SUPPORT: + tags = TagField(blank=True) + class Meta: get_latest_by = "created" diff --git a/settings.py b/settings.py new file mode 100644 index 00000000..dbbbd3e9 --- /dev/null +++ b/settings.py @@ -0,0 +1,14 @@ + +""" +Default settings for jutda-helpdesk. + +""" + +from django.conf import settings + +# check for django-tagging support +HAS_TAG_SUPPORT = 'tagging' in settings.INSTALLED_APPS +try: + import tagging +except ImportError: + HAS_TAG_SUPPORT = False diff --git a/templates/helpdesk/public_view_ticket.html b/templates/helpdesk/public_view_ticket.html index c9b8dc3a..ea80c8bf 100644 --- a/templates/helpdesk/public_view_ticket.html +++ b/templates/helpdesk/public_view_ticket.html @@ -22,17 +22,24 @@ {{ ticket.get_priority_display }} +{% if tags_enabled %} + {% trans "Tags" %} + {{ ticket.tags }} + +{% endif %} + + {% trans "Description" %} - + {{ ticket.description|linebreaksbr }} -{% if ticket.resolution %} +{% if ticket.resolution %} {% trans "Resolution" %}{% ifequal ticket.get_status_display "Resolved" %} {% trans "Accept" %}{% endifequal %} - + {{ ticket.resolution }} {% endif %} diff --git a/templates/helpdesk/ticket.html b/templates/helpdesk/ticket.html index eb2f8366..0d7d26e4 100644 --- a/templates/helpdesk/ticket.html +++ b/templates/helpdesk/ticket.html @@ -71,17 +71,25 @@ {% for ticketcc in ticket.ticketcc_set.all %}{{ ticketcc.display }}{% if not forloop.last %}, {% endif %}{% endfor %} {% trans "Manage" %} +{% if tags_enabled %} + {% trans "Tags" %} + {{ ticket.tags }} + +{% endif %} + + + {% trans "Description" %} - + {{ ticket.description|force_escape|linebreaksbr }} -{% if ticket.resolution %} +{% if ticket.resolution %} {% trans "Resolution" %}{% ifequal ticket.get_status_display "Resolved" %} {% trans "Accept" %}{% endifequal %} - + {{ ticket.resolution|force_escape }} {% endif %} @@ -161,6 +169,10 @@
+ {% if tags_enabled %} +
+
+ {% endif %} diff --git a/templates/helpdesk/ticket_list.html b/templates/helpdesk/ticket_list.html index e4394f50..43958d78 100644 --- a/templates/helpdesk/ticket_list.html +++ b/templates/helpdesk/ticket_list.html @@ -54,6 +54,9 @@ $(document).ready(function() { + {% if tags_enabled %} + + {% endif %} @@ -112,6 +115,13 @@ $(document).ready(function() { +{% if tags_enabled %} +
+ +

Ctrl-click to select multiple options

+ +
+{% endif %}
@@ -169,8 +179,8 @@ $(document).ready(function() { {{ search_message|safe }}
- - + +{% if tags_enabled %}{% endif %} {% if tickets %}{% for ticket in tickets.object_list %} @@ -181,6 +191,7 @@ $(document).ready(function() { +{% if tags_enabled %}{% endif %} {% endfor %}{% else %} diff --git a/views/staff.py b/views/staff.py index 8f181713..945e6f1f 100644 --- a/views/staff.py +++ b/views/staff.py @@ -25,7 +25,10 @@ from django.utils.translation import ugettext as _ from helpdesk.forms import TicketForm, UserSettingsForm, EmailIgnoreForm, EditTicketForm, TicketCCForm from helpdesk.lib import send_templated_mail, line_chart, bar_chart, query_to_dict, apply_query, safe_template_context from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch, IgnoreEmail, TicketCC - +from helpdesk.settings import HAS_TAG_SUPPORT + +if HAS_TAG_SUPPORT: + from tagging.models import Tag, TaggedItem staff_member_required = user_passes_test(lambda u: u.is_authenticated() and u.is_active and u.is_staff) superuser_required = user_passes_test(lambda u: u.is_authenticated() and u.is_active and u.is_superuser) @@ -126,6 +129,7 @@ def view_ticket(request, ticket_id): 'active_users': User.objects.filter(is_active=True).filter(is_staff=True), 'priorities': Ticket.PRIORITY_CHOICES, 'preset_replies': PreSetReply.objects.filter(Q(queues=ticket.queue) | Q(queues__isnull=True)), + 'tags_enabled': HAS_TAG_SUPPORT })) view_ticket = staff_member_required(view_ticket) @@ -142,6 +146,7 @@ def update_ticket(request, ticket_id, public=False): public = request.POST.get('public', False) owner = int(request.POST.get('owner', None)) priority = int(request.POST.get('priority', ticket.priority)) + tags = request.POST.get('tags', '') # We need to allow the 'ticket' and 'queue' contexts to be applied to the # comment. @@ -230,6 +235,17 @@ def update_ticket(request, ticket_id, public=False): c.save() ticket.priority = priority + if HAS_TAG_SUPPORT: + if tags != ticket.tags: + c = TicketChange( + followup=f, + field=_('Tags'), + old_value=ticket.tags, + new_value=tags, + ) + c.save() + ticket.tags = tags + if f.new_status == Ticket.RESOLVED_STATUS: ticket.resolution = comment @@ -477,7 +493,8 @@ def ticket_list(request): or request.GET.has_key('status') or request.GET.has_key('q') or request.GET.has_key('sort') - or request.GET.has_key('sortreverse') ): + or request.GET.has_key('sortreverse') + or request.GET.has_key('tags') ): # Fall-back if no querying is being done, force the list to only # show open/reopened/resolved (not closed) cases sorted by creation @@ -527,6 +544,14 @@ def ticket_list(request): query_params['sortreverse'] = sortreverse ticket_qs = apply_query(Ticket.objects.select_related(), query_params) + + ## TAG MATCHING + if HAS_TAG_SUPPORT: + tags = request.GET.getlist('tags') + if tags: + ticket_qs = TaggedItem.objects.get_by_model(ticket_qs, tags) + query_params['tags'] = tags + ticket_paginator = paginator.Paginator(ticket_qs, request.user.usersettings.settings.get('tickets_per_page') or 20) try: page = int(request.GET.get('page', '1')) @@ -554,6 +579,11 @@ def ticket_list(request): if get_key != "page": query_string.append("%s=%s" % (get_key, get_value)) + tag_choices = [] + if HAS_TAG_SUPPORT: + # FIXME: restrict this to tags that are actually in use + tag_choices = Tag.objects.all() + return render_to_response('helpdesk/ticket_list.html', RequestContext(request, dict( context, @@ -562,11 +592,13 @@ def ticket_list(request): user_choices=User.objects.filter(is_active=True), queue_choices=Queue.objects.all(), status_choices=Ticket.STATUS_CHOICES, + tag_choices=tag_choices, urlsafe_query=urlsafe_query, user_saved_queries=user_saved_queries, query_params=query_params, from_saved_query=from_saved_query, search_message=search_message, + tags_enabled=HAS_TAG_SUPPORT ))) ticket_list = staff_member_required(ticket_list) @@ -584,6 +616,7 @@ def edit_ticket(request, ticket_id): return render_to_response('helpdesk/edit_ticket.html', RequestContext(request, { 'form': form, + 'tags_enabled': HAS_TAG_SUPPORT, })) edit_ticket = staff_member_required(edit_ticket) @@ -607,6 +640,7 @@ def create_ticket(request): return render_to_response('helpdesk/create_ticket.html', RequestContext(request, { 'form': form, + 'tags_enabled': HAS_TAG_SUPPORT, })) create_ticket = staff_member_required(create_ticket)
{% trans "Tickets" %}
# {% trans "Pr" %}{% trans "Title" %}{% trans "Queue" %}{% trans "Status" %}{% trans "Created" %}{% trans "Owner" %}
{% trans "Tickets" %}
# {% trans "Pr" %}{% trans "Title" %}{% trans "Queue" %}{% trans "Status" %}{% trans "Created" %}{% trans "Owner" %}{% trans "Tags" %}
{{ ticket.ticket }}{{ ticket.get_status }} {{ ticket.created|timesince }} ago {{ ticket.get_assigned_to }}{{ ticket.tags }}
{% trans "No Tickets Match Your Selection" %}