mirror of
https://github.com/django-helpdesk/django-helpdesk.git
synced 2025-01-31 10:29:15 +01:00
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.
This commit is contained in:
parent
f419d8e2d0
commit
f418e97efc
@ -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.
|
||||
|
15
forms.py
15
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'])
|
||||
|
@ -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"
|
||||
|
||||
|
14
settings.py
Normal file
14
settings.py
Normal file
@ -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
|
@ -22,17 +22,24 @@
|
||||
<td>{{ ticket.get_priority_display }}</td>
|
||||
</tr>
|
||||
|
||||
{% if tags_enabled %}
|
||||
<tr class='row_even'>
|
||||
<th>{% trans "Tags" %}</th>
|
||||
<td>{{ ticket.tags }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<tr class='row_{% if tags_enabled %}odd{% else %}even{% endif %}'>
|
||||
<th colspan='2'>{% trans "Description" %}</th>
|
||||
</tr>
|
||||
<tr class='row_odd'>
|
||||
<tr class='row_{% if tags_enabled %}odd{% else %}even{% endif %}'>
|
||||
<td colspan='2'>{{ ticket.description|linebreaksbr }}</td>
|
||||
</tr>
|
||||
|
||||
{% if ticket.resolution %}<tr class='row_even'>
|
||||
{% if ticket.resolution %}<tr class='row_{% if tags_enabled %}even{% else %}odd{% endif %}'>
|
||||
<th colspan='2'>{% trans "Resolution" %}{% ifequal ticket.get_status_display "Resolved" %} <a href='{{ request.get_full_path }}&close'><img src='{{ MEDIA_URL }}/helpdesk/buttons/accept.png' alt='{% trans "Accept" %}' title='{% trans "Accept and Close" %}' width='60' height='15' /></a>{% endifequal %}</th>
|
||||
</tr>
|
||||
<tr class='row_odd'>
|
||||
<tr class='row_{% if tags_enabled %}even{% else %}odd{% endif %}'>
|
||||
<td colspan='2'>{{ ticket.resolution }}</td>
|
||||
</tr>{% endif %}
|
||||
|
||||
|
@ -71,17 +71,25 @@
|
||||
<td>{% for ticketcc in ticket.ticketcc_set.all %}{{ ticketcc.display }}{% if not forloop.last %}, {% endif %}{% endfor %} <strong><a href='{% url helpdesk_ticket_cc ticket.id %}'>{% trans "Manage" %}</a></strong></td>
|
||||
</tr>
|
||||
|
||||
{% if tags_enabled %}
|
||||
<tr class='row_even'>
|
||||
<th>{% trans "Tags" %}</th>
|
||||
<td>{{ ticket.tags }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<tr class='row_even'>
|
||||
<tr class='row_{% if tags_enabled %}odd{% else %}even{% endif %}'>
|
||||
<th colspan='2'>{% trans "Description" %}</th>
|
||||
</tr>
|
||||
<tr class='row_odd'>
|
||||
<tr class='row_{% if tags_enabled %}even{% else %}odd{% endif %}'>
|
||||
<td colspan='2'>{{ ticket.description|force_escape|linebreaksbr }}</td>
|
||||
</tr>
|
||||
|
||||
{% if ticket.resolution %}<tr class='row_even'>
|
||||
{% if ticket.resolution %}<tr class='row_{% if tags_enabled %}odd{% else %}even{% endif %}'>
|
||||
<th colspan='2'>{% trans "Resolution" %}{% ifequal ticket.get_status_display "Resolved" %} <a href='?close'><img src='{{ MEDIA_URL }}helpdesk/buttons/accept.png' alt='{% trans "Accept" %}' title='{% trans "Accept and Close" %}' width='60' height='15' /></a>{% endifequal %}</th>
|
||||
</tr>
|
||||
<tr class='row_odd'>
|
||||
<tr class='row_{% if tags_enabled %}even{% else %}odd{% endif %}'>
|
||||
<td colspan='2'>{{ ticket.resolution|force_escape }}</td>
|
||||
</tr>{% endif %}
|
||||
|
||||
@ -161,6 +169,10 @@
|
||||
|
||||
<dt><label for='id_priority'>{% trans "Priority" %}</label></dt>
|
||||
<dd><select id='id_priority' name='priority'>{% for p in priorities %}<option value='{{ p.0 }}'{% ifequal p.0 ticket.priority %} selected='selected'{% endifequal %}>{{ p.1 }}</option>{% endfor %}</select></dd>
|
||||
{% if tags_enabled %}
|
||||
<dt><label for='id_tags'>{% trans "Tags" %}</label></dt>
|
||||
<dd><input type='text' id='id_tags' name='tags' value='{{ ticket.tags }}'/></dd>
|
||||
{% endif %}
|
||||
|
||||
</dl>
|
||||
|
||||
|
@ -54,6 +54,9 @@ $(document).ready(function() {
|
||||
<option value='Queue'>{% trans "Queue" %}</option>
|
||||
<option value='Status'>{% trans "Status" %}</option>
|
||||
<option value='Keywords'>{% trans "Keywords" %}</option>
|
||||
{% if tags_enabled %}
|
||||
<option value='Tags'>{% trans "Tags" %}</option>
|
||||
{% endif %}
|
||||
</select>
|
||||
<input type='button' id='filterBuilderButton' value='+' />
|
||||
</form>
|
||||
@ -112,6 +115,13 @@ $(document).ready(function() {
|
||||
<input type='button' class='filterBuilderRemove' value='-' />
|
||||
</div>
|
||||
|
||||
{% if tags_enabled %}
|
||||
<div class='filterBox{% if query_params.tags %} filterBoxShow{% endif %}' id='filterBoxTags'>
|
||||
<label for='id_tags'>{% trans "Tag(s)" %}</label><select id='id_tags' name='tags' multiple='selected' size='5'>{% for t in tag_choices %}<option value='{{t.name}}'{% if t.name|in_list:query_params.tags %} selected='selected'{% endif %}>{{ t.name }}</option>{% endfor %}</select>
|
||||
<p class='filterHelp'>Ctrl-click to select multiple options</p>
|
||||
<input type='button' class='filterBuilderRemove' value='-' />
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class='filterBox{% if query %} filterBoxShow{% endif %}' id='filterBoxKeywords'>
|
||||
<label for='id_query'>{% trans "Keywords" %}</label><input type='text' name='q' value='{{ query }}' id='id_query' />
|
||||
@ -169,8 +179,8 @@ $(document).ready(function() {
|
||||
{{ search_message|safe }}
|
||||
<form method='post' action='{% url helpdesk_mass_update %}'>
|
||||
<table width='100%'>
|
||||
<tr class='row_tablehead'><td colspan='8'>{% trans "Tickets" %}</td></tr>
|
||||
<tr class='row_columnheads'><th>#</th><th> </th><th>{% trans "Pr" %}</th><th>{% trans "Title" %}</th><th>{% trans "Queue" %}</th><th>{% trans "Status" %}</th><th>{% trans "Created" %}</th><th>{% trans "Owner" %}</th></tr>
|
||||
<tr class='row_tablehead'><td colspan='9'>{% trans "Tickets" %}</td></tr>
|
||||
<tr class='row_columnheads'><th>#</th><th> </th><th>{% trans "Pr" %}</th><th>{% trans "Title" %}</th><th>{% trans "Queue" %}</th><th>{% trans "Status" %}</th><th>{% trans "Created" %}</th><th>{% trans "Owner" %}</th>{% if tags_enabled %}<th>{% trans "Tags" %}</th>{% endif %}</tr>
|
||||
{% if tickets %}{% for ticket in tickets.object_list %}
|
||||
<tr class='row_{% cycle odd,even %} row_hover'>
|
||||
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.ticket }}</a></th>
|
||||
@ -181,6 +191,7 @@ $(document).ready(function() {
|
||||
<td>{{ ticket.get_status }}</td>
|
||||
<td><span title='{{ ticket.created|date:"r" }}'>{{ ticket.created|timesince }} ago</span></td>
|
||||
<td>{{ ticket.get_assigned_to }}</td>
|
||||
{% if tags_enabled %}<td>{{ ticket.tags }}</td>{% endif %}
|
||||
</tr>
|
||||
{% endfor %}{% else %}
|
||||
<tr class='row_odd'><td colspan='5'>{% trans "No Tickets Match Your Selection" %}</td></tr>
|
||||
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user