forked from extern/django-helpdesk
Initial import of Python files & templates
This commit is contained in:
commit
015a7dd166
0
__init__.py
Normal file
0
__init__.py
Normal file
67
forms.py
Normal file
67
forms.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
""" ..
|
||||||
|
.,::;::::::
|
||||||
|
..,::::::::,,,,::: Jutda Helpdesk - A Django
|
||||||
|
.,,::::::,,,,,,,,,,,,,:: powered ticket tracker for
|
||||||
|
.,::::::,,,,,,,,,,,,,,,,,,:;r. small enterprise
|
||||||
|
.::::,,,,,,,,,,,,,,,,,,,,,,:;;rr.
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,,,,:;;;;;rr (c) Copyright 2008
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,,,,:;;;:::;;rr
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,. ,;;;::::::;;rr Jutda
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,. .:;;:::::::::;;rr
|
||||||
|
.:::,,,,,,,,,,,,,,,. .;r;::::::::::::;r; All Rights Reserved
|
||||||
|
.:::,,,,,,,,,,,,,,, .;r;;:::::::::::;;:.
|
||||||
|
.:::,,,,,,,,,,,,,,,. .;r;;::::::::::::;:.
|
||||||
|
.;:,,,,,,,,,,,,,,, .,;rr;::::::::::::;:. This software is released
|
||||||
|
.,:,,,,,,,,,,,,,. .,:;rrr;;::::::::::::;;. under a limited-use license that
|
||||||
|
:,,,,,,,,,,,,,..:;rrrrr;;;::::::::::::;;. allows you to freely download this
|
||||||
|
:,,,,,,,:::;;;rr;;;;;;:::::::::::::;;, software from it's manufacturer and
|
||||||
|
::::;;;;;;;;;;;:::::::::::::::::;;, use it yourself, however you may not
|
||||||
|
.r;;;;:::::::::::::::::::::::;;;, distribute it. For further details, see
|
||||||
|
.r;::::::::::::::::::::;;;;;:, the enclosed LICENSE file.
|
||||||
|
.;;::::::::::::::;;;;;:,.
|
||||||
|
.;;:::::::;;;;;;:,. Please direct people who wish to download this
|
||||||
|
.r;;;;;;;;:,. software themselves to www.jutda.com.au.
|
||||||
|
,,,..
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django import newforms as forms
|
||||||
|
from helpdesk.models import Ticket, Queue
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
class TicketForm(forms.Form):
|
||||||
|
queue = forms.ChoiceField(label=u'Queue', required=True, choices=())
|
||||||
|
|
||||||
|
title = forms.CharField(max_length=100, required=True,
|
||||||
|
widget=forms.TextInput(),
|
||||||
|
label=u'Summary of the problem')
|
||||||
|
|
||||||
|
submitter_email = forms.EmailField(required=False,
|
||||||
|
label=u'Submitter E-Mail Address')
|
||||||
|
|
||||||
|
body = forms.CharField(widget=forms.Textarea(),
|
||||||
|
label=u'Description of Issue', required=True)
|
||||||
|
|
||||||
|
assigned_to = forms.ChoiceField(choices=(), required=False,
|
||||||
|
label=u'Case owner')
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
"""
|
||||||
|
Writes and returns a Ticket() object
|
||||||
|
|
||||||
|
"""
|
||||||
|
q = Queue.objects.get(id=int(self.cleaned_data['queue']))
|
||||||
|
t = Ticket( title=self.cleaned_data['title'],
|
||||||
|
submitter_email=self.cleaned_data['submitter_email'],
|
||||||
|
created=datetime.now(),
|
||||||
|
status = Ticket.OPEN_STATUS,
|
||||||
|
queue = q,
|
||||||
|
description = self.cleaned_data['body'],
|
||||||
|
)
|
||||||
|
if self.cleaned_data['assigned_to']:
|
||||||
|
t.assigned_to = self.cleaned_data['assigned_to']
|
||||||
|
t.save()
|
||||||
|
|
||||||
|
return t
|
139
models.py
Normal file
139
models.py
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
""" ..
|
||||||
|
.,::;::::::
|
||||||
|
..,::::::::,,,,::: Jutda Helpdesk - A Django
|
||||||
|
.,,::::::,,,,,,,,,,,,,:: powered ticket tracker for
|
||||||
|
.,::::::,,,,,,,,,,,,,,,,,,:;r. small enterprise
|
||||||
|
.::::,,,,,,,,,,,,,,,,,,,,,,:;;rr.
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,,,,:;;;;;rr (c) Copyright 2008
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,,,,:;;;:::;;rr
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,. ,;;;::::::;;rr Jutda
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,. .:;;:::::::::;;rr
|
||||||
|
.:::,,,,,,,,,,,,,,,. .;r;::::::::::::;r; All Rights Reserved
|
||||||
|
.:::,,,,,,,,,,,,,,, .;r;;:::::::::::;;:.
|
||||||
|
.:::,,,,,,,,,,,,,,,. .;r;;::::::::::::;:.
|
||||||
|
.;:,,,,,,,,,,,,,,, .,;rr;::::::::::::;:. This software is released
|
||||||
|
.,:,,,,,,,,,,,,,. .,:;rrr;;::::::::::::;;. under a limited-use license that
|
||||||
|
:,,,,,,,,,,,,,..:;rrrrr;;;::::::::::::;;. allows you to freely download this
|
||||||
|
:,,,,,,,:::;;;rr;;;;;;:::::::::::::;;, software from it's manufacturer and
|
||||||
|
::::;;;;;;;;;;;:::::::::::::::::;;, use it yourself, however you may not
|
||||||
|
.r;;;;:::::::::::::::::::::::;;;, distribute it. For further details, see
|
||||||
|
.r;::::::::::::::::::::;;;;;:, the enclosed LICENSE file.
|
||||||
|
.;;::::::::::::::;;;;;:,.
|
||||||
|
.;;:::::::;;;;;;:,. Please direct people who wish to download this
|
||||||
|
.r;;;;;;;;:,. software themselves to www.jutda.com.au.
|
||||||
|
,,,..
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
"""
|
||||||
|
from django.db import models
|
||||||
|
from datetime import datetime
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.db.models import permalink
|
||||||
|
|
||||||
|
class Queue(models.Model):
|
||||||
|
title = models.CharField(maxlength=100)
|
||||||
|
slug = models.SlugField()
|
||||||
|
email_address = models.EmailField(blank=True, null=True)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return u"%s" % self.title
|
||||||
|
|
||||||
|
class Admin:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Ticket(models.Model):
|
||||||
|
"""
|
||||||
|
To allow a ticket to be entered as quickly as possible, only the
|
||||||
|
bare minimum fields are required. These basically allow us to
|
||||||
|
sort and manage the ticket. The user can always go back and
|
||||||
|
enter more information later.
|
||||||
|
|
||||||
|
A good example of this is when a customer is on the phone, and
|
||||||
|
you want to give them a ticket ID as quickly as possible. You can
|
||||||
|
enter some basic info, save the ticket, give the customer the ID
|
||||||
|
and get off the phone, then add in further detail at a later time
|
||||||
|
(once the customer is not on the line).
|
||||||
|
"""
|
||||||
|
|
||||||
|
OPEN_STATUS = 1
|
||||||
|
REOPENED_STATUS = 2
|
||||||
|
RESOLVED_STATUS = 3
|
||||||
|
CLOSED_STATUS = 4
|
||||||
|
|
||||||
|
STATUS_CHOICES = (
|
||||||
|
(OPEN_STATUS, 'Open'),
|
||||||
|
(REOPENED_STATUS, 'Reopened'),
|
||||||
|
(RESOLVED_STATUS, 'Resolved'),
|
||||||
|
(CLOSED_STATUS, 'Closed'),
|
||||||
|
)
|
||||||
|
|
||||||
|
title = models.CharField(maxlength=200)
|
||||||
|
queue = models.ForeignKey(Queue)
|
||||||
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
submitter_email = models.EmailField(blank=True, null=True, help_text='The submitter will receive an email for all public follow-ups left for this task.')
|
||||||
|
assigned_to = models.ForeignKey(User, related_name='assigned_to', blank=True, null=True)
|
||||||
|
status = models.IntegerField(choices=STATUS_CHOICES, default=OPEN_STATUS)
|
||||||
|
|
||||||
|
description = models.TextField(blank=True, null=True)
|
||||||
|
resolution = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
|
def _get_assigned_to(self):
|
||||||
|
if not self.assigned_to:
|
||||||
|
return 'Unassigned'
|
||||||
|
else:
|
||||||
|
if self.assigned_to.get_full_name():
|
||||||
|
return self.assigned_to.get_full_name()
|
||||||
|
else:
|
||||||
|
return self.assigned_to
|
||||||
|
get_assigned_to = property(_get_assigned_to)
|
||||||
|
|
||||||
|
class Admin:
|
||||||
|
list_display = ('title', 'status', 'assigned_to',)
|
||||||
|
date_hierarchy = 'created'
|
||||||
|
list_filter = ('assigned_to',)
|
||||||
|
search_fields = ('title',)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
get_latest_by = "created"
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return '%s' % self.title
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return ('helpdesk.views.view_ticket', [str(self.id)])
|
||||||
|
get_absolute_url = permalink(get_absolute_url)
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
if not self.id:
|
||||||
|
# This is a new ticket as no ID yet exists.
|
||||||
|
self.created = datetime.now()
|
||||||
|
|
||||||
|
super(Ticket, self).save()
|
||||||
|
|
||||||
|
|
||||||
|
class FollowUp(models.Model):
|
||||||
|
ticket = models.ForeignKey(Ticket)
|
||||||
|
date = models.DateTimeField(auto_now_add=True)
|
||||||
|
title = models.CharField(maxlength=200, blank=True, null=True)
|
||||||
|
comment = models.TextField(blank=True, null=True)
|
||||||
|
public = models.BooleanField(blank=True, null=True)
|
||||||
|
user = models.ForeignKey(User)
|
||||||
|
|
||||||
|
new_status = models.IntegerField(choices=Ticket.STATUS_CHOICES, blank=True, null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['date']
|
||||||
|
|
||||||
|
class Admin:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return '%s' % self.title
|
||||||
|
|
||||||
|
class TicketChange(models.Model):
|
||||||
|
followup = models.ForeignKey(FollowUp, edit_inline=models.TABULAR)
|
||||||
|
field = models.CharField(maxlength=100, core=True)
|
||||||
|
old_value = models.TextField(blank=True, null=True, core=True)
|
||||||
|
new_value = models.TextField(blank=True, null=True, core=True)
|
27
templates/helpdesk/base.html
Normal file
27
templates/helpdesk/base.html
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>{% block helpdesk_title %}Helpdesk{% endblock %}</title>
|
||||||
|
<script src='{{ MEDIA_URL }}/helpdesk/jquery.js' type='text/javascript' language='javascript'></script>
|
||||||
|
<link rel='stylesheet' href='{{ MEDIA_URL }}/helpdesk/helpdesk.css' type='text/css' />
|
||||||
|
{% block helpdesk_head %}{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id='container'>
|
||||||
|
<div id='header'>
|
||||||
|
<h1>Helpdesk</h1>
|
||||||
|
<ul>
|
||||||
|
<li><a href='{% url helpdesk_home %}'>Dashboard</a></li>
|
||||||
|
<li><a href='{% url helpdesk_list %}'>Tickets</a></li>
|
||||||
|
<li><a href='{% url helpdesk_submit %}'>Submit Ticket</a></li>
|
||||||
|
<li>Search</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div id='body'>
|
||||||
|
{% block helpdesk_body %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
<div id='footer'>
|
||||||
|
<p>Powered by <a href='http://www.jutda.com.au/'>Jutda HelpDesk</a></p>
|
||||||
|
</div>
|
||||||
|
</div>{% include "helpdesk/debug.html" %}
|
||||||
|
</body>
|
||||||
|
</html>
|
42
templates/helpdesk/create_ticket.html
Normal file
42
templates/helpdesk/create_ticket.html
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{% extends "helpdesk/base.html" %}
|
||||||
|
{% block helpdesk_title %}Helpdesk{% endblock %}
|
||||||
|
|
||||||
|
{% block helpdesk_body %}
|
||||||
|
<h2>Submit a Ticket</h2>
|
||||||
|
|
||||||
|
<form method='post' action='./'>
|
||||||
|
<fieldset>
|
||||||
|
<dl>
|
||||||
|
<dt><label for='id_queue'>{{ form.queue.label }}</label></dt>
|
||||||
|
<dd>{{ form.queue }}</dd>
|
||||||
|
{% if form.queue.errors %}
|
||||||
|
<dd class='error'>{{ form.queue.errors }}</dd>{% endif %}
|
||||||
|
|
||||||
|
<dt><label for='id_title'>{{ form.title.label }}</label></dt>
|
||||||
|
<dd>{{ form.title }}</dd>
|
||||||
|
{% if form.title.errors %}
|
||||||
|
<dd class='error'>{{ form.title.errors }}</dd>{% endif %}
|
||||||
|
|
||||||
|
<dt><label for='id_submitter_email'>{{ form.submitter_email.label }}</label></dt>
|
||||||
|
<dd>{{ form.submitter_email }}</dd>
|
||||||
|
{% if form.submitter_email.errors %}
|
||||||
|
<dd class='error'>{{ form.submitter_email.errors }}</dd>{% endif %}
|
||||||
|
|
||||||
|
<dt><label for='id_body'>{{ form.body.label }}</label></dt>
|
||||||
|
<dd>{{ form.body }}</dd>
|
||||||
|
{% if form.body.errors %}
|
||||||
|
<dd class='error'>{{ form.body.errors }}</dd>{% endif %}
|
||||||
|
|
||||||
|
<dt><label for='id_assigned_to'>{{ form.assigned_to.label }}</label></dt>
|
||||||
|
<dd>{{ form.assigned_to }}</dd>
|
||||||
|
{% if form.assigned_to.errors %}
|
||||||
|
<dd class='error'>{{ form.assigned_to.errors }}</dd>{% endif %}
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<div class='buttons'>
|
||||||
|
<input type='submit' value='Submit' />
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
49
templates/helpdesk/dashboard.html
Normal file
49
templates/helpdesk/dashboard.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{% extends "helpdesk/base.html" %}
|
||||||
|
{% block helpdesk_title %}Helpdesk Dashboard{% endblock %}
|
||||||
|
{% block helpdesk_head %}<script type='text/javascript' language='javascript'>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$("tr.row_hover").mouseover(function() {$(this).addClass("hover");}).mouseout(function() {$(this).removeClass("hover");});
|
||||||
|
});
|
||||||
|
</script>{% endblock %}
|
||||||
|
{% block helpdesk_body %}
|
||||||
|
<table width='30%'>
|
||||||
|
<tr class='row_tablehead'><td colspan='4'>Helpdesk Summary</td></tr>
|
||||||
|
<tr class='row_columnheads'><th>Queue</th><th>Open</th><th>Resolved</th></tr>
|
||||||
|
{% for queue in dash_tickets %}
|
||||||
|
<tr class='row_{% cycle odd,even %} row_hover '>
|
||||||
|
<th><a href='{% url helpdesk_list %}?queue={{ queue.queue.id }}'>{{ queue.queue }}</a></th>
|
||||||
|
<td>{% if queue.open %}<a href='{% url helpdesk_list %}?queue={{ queue.queue.id }}&status=1&status=2'>{% endif %}{{ queue.open }}{% if queue.open %}</a>{% endif %}</td>
|
||||||
|
<td>{% if queue.resolved %}<a href='{% url helpdesk_list %}?queue={{ queue.queue.id }}&status=3'>{% endif %}{{ queue.resolved }}{% if queue.resolved %}</a>{% endif %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table width='100%'>
|
||||||
|
<tr class='row_tablehead'><td colspan='5'>Your Tickets</td></tr>
|
||||||
|
<tr class='row_columnheads'><th>#</th><th>Title</th><th>Queue</th><th>Status</th><th>Last Update</th></tr>
|
||||||
|
{% for ticket in user_tickets %}
|
||||||
|
<tr class='row_{% cycle odd,even %} row_hover'>
|
||||||
|
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.id }}</a></th>
|
||||||
|
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.title }}</a></th>
|
||||||
|
<td>{{ ticket.queue }}</td>
|
||||||
|
<td>{{ ticket.get_status_display }}</td>
|
||||||
|
<td>{{ ticket.last_update|timesince }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table width='100%'>
|
||||||
|
<tr class='row_tablehead'><td colspan='5'>Unassigned Tickets</td></tr>
|
||||||
|
<tr class='row_columnheads'><th>#</th><th>Title</th><th>Queue</th><th>Created</th><th> </th></tr>
|
||||||
|
{% for ticket in unassigned_tickets %}
|
||||||
|
<tr class='row_{% cycle odd,even %} row_hover'>
|
||||||
|
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.id }}</a></th>
|
||||||
|
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.title }}</a></th>
|
||||||
|
<td>{{ ticket.queue }}</td>
|
||||||
|
<td>{{ ticket.created|timesince }} ago</td>
|
||||||
|
<th><a href='{{ ticket.get_absolute_url }}?take'>Take</a></th>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock %}
|
30
templates/helpdesk/debug.html
Normal file
30
templates/helpdesk/debug.html
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
{% if debug %}
|
||||||
|
<div id="debug" style='clear: both;padding-top: 200px'>
|
||||||
|
<h2>Queries</h2>
|
||||||
|
<p>
|
||||||
|
{{ sql_queries|length }} Quer{{ sql_queries|pluralize:"y,ies" }}
|
||||||
|
{% ifnotequal sql_queries|length 0 %}
|
||||||
|
(<span style="cursor: pointer;" onclick="var s=document.getElementById('debugQueryTable').style;s.display=s.display=='none'?'':'none';this.innerHTML=this.innerHTML=='Show'?'Hide':'Show';">Show</span>)
|
||||||
|
{% endifnotequal %}
|
||||||
|
</p>
|
||||||
|
<table id="debugQueryTable" style="display: none;">
|
||||||
|
<col width="1"></col>
|
||||||
|
<col></col>
|
||||||
|
<col width="1"></col>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">#</th>
|
||||||
|
<th scope="col">SQL</th>
|
||||||
|
<th scope="col">Time</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for query in sql_queries %}<tr class="{% cycle odd,even %}">
|
||||||
|
<td>{{ forloop.counter }}</td>
|
||||||
|
<td>{{ query.sql|escape }}</td>
|
||||||
|
<td>{{ query.time }}</td>
|
||||||
|
</tr>{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
101
templates/helpdesk/ticket.html
Normal file
101
templates/helpdesk/ticket.html
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
|
||||||
|
{% extends "helpdesk/base.html" %}
|
||||||
|
{% block helpdesk_title %}Helpdesk{% endblock %}
|
||||||
|
{% block helpdesk_head %}
|
||||||
|
<script src="http://media.jutda.com.au/helpdesk/nicEdit.js" type="text/javascript"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function() {
|
||||||
|
new nicEditor({buttonList: ['bold','italic','underline','strikeThrough','undo','redo','subscript','superscript','html']}).panelInstance('commentBox');
|
||||||
|
$("#ShowFurtherEditOptions").click(function() {
|
||||||
|
$("#FurtherEditOptions").toggle();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style type='text/css'>#commentBox { width: 100%; }</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block helpdesk_body %}
|
||||||
|
|
||||||
|
<table width='100%'>
|
||||||
|
<tr class='row_tablehead'><td colspan='2'>{{ ticket.id }}. {{ ticket.title }} [{{ ticket.get_status_display }}]</td>
|
||||||
|
<tr class='row_columnheads'><td colspan='2'>Queue: {{ ticket.queue }}</td></tr>
|
||||||
|
|
||||||
|
<tr class='row_odd'>
|
||||||
|
<th>Submitted On</th>
|
||||||
|
<td>{{ ticket.created }} ({{ ticket.created|timesince }} ago)</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr class='row_even'>
|
||||||
|
<th>Assigned To</th>
|
||||||
|
<td>{{ ticket.get_assigned_to }}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% if ticket.submitter_email %}<tr class='row_odd'>
|
||||||
|
<th>Submitter E-Mail</th>
|
||||||
|
<td>{{ ticket.submitter_email }}</td>
|
||||||
|
</tr>{% endif %}
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% if ticket.followup_set.all %}
|
||||||
|
<h3>Follow-Ups</h3>
|
||||||
|
{% load ticket_to_link %}
|
||||||
|
{% for followup in ticket.followup_set.all %}
|
||||||
|
<div class='followup'>
|
||||||
|
<div class='title'>{{ followup.title }} <span class='byline'>by {{ followup.user }} <span title='at {{ followup.date|date:"r" }}'>{{ followup.date|timesince }} ago</span>{% if not followup.public %} <span class='private'>(Private)</span>{% endif %}</span></div>
|
||||||
|
<p>{{ followup.comment|num_to_link }}</p>
|
||||||
|
{% if followup.ticketchange_set.all %}<div class='changes'>
|
||||||
|
{% for change in followup.ticketchange_set.all %}
|
||||||
|
Changed {{ change.field }} from {{ change.old_value }} to {{ change.new_value }}.<br />
|
||||||
|
{% endfor %}
|
||||||
|
</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method='post' action='update/'>
|
||||||
|
<h3>Add a comment</h3>
|
||||||
|
<textarea rows='8' cols='70' name='comment' id='commentBox'></textarea>
|
||||||
|
<p>New Status
|
||||||
|
{% ifequal ticket.status 1 %}
|
||||||
|
<input type='radio' name='new_status' value='1' id='st_open' checked='checked'><label for='st_open' class='active'>Open</label> »
|
||||||
|
<input type='radio' name='new_status' value='3' id='st_resolved'><label for='st_resolved'>Resolved</label> »
|
||||||
|
<input type='radio' name='new_status' value='4' id='st_closed'><label for='st_closed'>Closed</label>
|
||||||
|
{% endifequal %}
|
||||||
|
{% ifequal ticket.status 2 %}
|
||||||
|
<input type='radio' name='new_status' value='2' id='st_reopened' checked='checked'><label for='st_reopened' class='active'>Reopened</label>
|
||||||
|
<input type='radio' name='new_status' value='3' id='st_resolved'><label for='st_resolved'>Resolved</label>
|
||||||
|
<input type='radio' name='new_status' value='4' id='st_closed'><label for='st_closed'>Closed</label>
|
||||||
|
{% endifequal %}
|
||||||
|
{% ifequal ticket.status 3 %}
|
||||||
|
<input type='radio' name='new_status' value='2' id='st_reopened'><label for='st_reopened'>Reopened</label>
|
||||||
|
<input type='radio' name='new_status' value='3' id='st_resolved' checked='checked'><label for='st_resolved' class='active'>Resolved</label>
|
||||||
|
<input type='radio' name='new_status' value='4' id='st_closed'><label for='st_closed'>Closed</label>
|
||||||
|
{% endifequal %}
|
||||||
|
|
||||||
|
{% ifequal ticket.status 4 %}
|
||||||
|
<input type='radio' name='new_status' value='2' id='st_reopened'><label for='st_reopened'>Reopened</label>
|
||||||
|
<input type='radio' name='new_status' value='4' id='st_closed' checked='checked'><label for='st_closed'>Closed</label>
|
||||||
|
{% endifequal %}
|
||||||
|
|
||||||
|
<p><a href='#' id='ShowFurtherEditOptions'>Change Other Details</a></p>
|
||||||
|
|
||||||
|
<div id='FurtherEditOptions' style='display: none;'>
|
||||||
|
|
||||||
|
<label for='id_title'>Title</label>
|
||||||
|
<input type='text' name='title' value='{{ ticket.title|escape }}' />
|
||||||
|
|
||||||
|
<label for='id_owner'>Owner</label>
|
||||||
|
<select id='id_owner' name='owner'><option value='0'>Unassign</option>{% for u in active_users %}<option value='{{ u.id }}' {% ifequal u.id ticket.assigned_to.id %}selected{% endifequal %}>{{ u }}</option>{% endfor %}</select>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label for='id_public'>Is this update public?</label> <input type='checkbox' name='public' value='1' checked='checked' />
|
||||||
|
|
||||||
|
<input type='submit' value='Add' />
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
47
templates/helpdesk/ticket_list.html
Normal file
47
templates/helpdesk/ticket_list.html
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
{% extends "helpdesk/base.html" %}
|
||||||
|
{% block helpdesk_title %}Ticket Listing{% endblock %}
|
||||||
|
{% block helpdesk_head %}<script type='text/javascript' language='javascript'>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$("tr.row_hover").mouseover(function() {$(this).addClass("hover");}).mouseout(function() {$(this).removeClass("hover");});
|
||||||
|
});
|
||||||
|
</script>{% endblock %}
|
||||||
|
{% block helpdesk_body %}
|
||||||
|
|
||||||
|
{% load in_list %}
|
||||||
|
|
||||||
|
<form method='get' action='./'>
|
||||||
|
<label for='id_sort'>Sorting</label> <select id='id_sort' name='sort'>
|
||||||
|
<option value='created'{% ifequal sort "created"%} selected='selected'{% endifequal %}>Created</option>
|
||||||
|
<option value='title'{% ifequal sort "title"%} selected='selected'{% endifequal %}>Title</option>
|
||||||
|
<option value='queue'{% ifequal sort "queue"%} selected='selected'{% endifequal %}>Queue</option>
|
||||||
|
<option value='status'{% ifequal sort "status"%} selected='selected'{% endifequal %}>Status</option>
|
||||||
|
<option value='assigned_to'{% ifequal sort "assigned_to"%} selected='selected'{% endifequal %}>Owner</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label for='id_owners'>Owner(s)</label> <select id='id_owners' name='assigned_to' multiple='selected' size='5'>{% for u in user_choices %}<option value='{{ u.id }}'{% if u.id|in_list:owners %} selected='selected'{% endif %}>{{ u.username }}</option>{% endfor %}</select>
|
||||||
|
|
||||||
|
<label for='id_queues'>Queue(s)</label> <select id='id_queues' name='queue' multiple='selected' size='5'>{% for q in queue_choices %}<option value='{{ q.id }}'{% if q.id|in_list:queues %} selected='selected'{% endif %}>{{ q.title }}</option>{% endfor %}</select>
|
||||||
|
|
||||||
|
<label for='id_statuses'>Status(es)</label> {% for s in status_choices %}<input type='checkbox' name='status' value='{{ s.0 }}'{% if s.0|in_list:statuses %} checked='checked'{% endif %}> {{ s.1 }}{% endfor %}
|
||||||
|
|
||||||
|
<input type='submit' value='Go!' />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<table width='100%'>
|
||||||
|
<tr class='row_tablehead'><td colspan='6'>Tickets</td></tr>
|
||||||
|
<tr class='row_columnheads'><th>#</th><th>Title</th><th>Queue</th><th>Status</th><th>Created</th><th>Owner</th></tr>
|
||||||
|
{% if tickets %}{% for ticket in tickets %}
|
||||||
|
<tr class='row_{% cycle odd,even %} row_hover'>
|
||||||
|
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.id }}</a></th>
|
||||||
|
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.title }}</a></th>
|
||||||
|
<td>{{ ticket.queue }}</td>
|
||||||
|
<td>{{ ticket.get_status_display }}</td>
|
||||||
|
<td>{{ ticket.created }}</td>
|
||||||
|
<td>{{ ticket.get_assigned_to }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}{% else %}
|
||||||
|
<tr class='row_odd'><td colspan='5'>No Tickets Match Your Selection</td></tr>
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock %}
|
20
templates/registration/login.html
Normal file
20
templates/registration/login.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{% extends "helpdesk/base.html" %}
|
||||||
|
{% block helpdesk_title %}Helpdesk Login{% endblock %}
|
||||||
|
|
||||||
|
{% block helpdesk_body %}
|
||||||
|
<h2>Login</h2>
|
||||||
|
|
||||||
|
<p>To log in and begin responding to cases, simply enter your username and password below.</p>
|
||||||
|
|
||||||
|
<form method='post' action='./'>
|
||||||
|
{% if form.has_errors %}<p>Your username and password didn't match. Please try again.</p>{% endif %}
|
||||||
|
<dl>
|
||||||
|
<dt><label>Username</label></dt>
|
||||||
|
<dd>{{ form.username }}</dd>
|
||||||
|
<dt><label>Password</label></dt>
|
||||||
|
<dd>{{ form.password }}</dd>
|
||||||
|
</dl>
|
||||||
|
<input type='submit' value='Login' />
|
||||||
|
<input type="hidden" name="next" value="{% if next %}{{ next }}{% else %}../{% endif %}" />
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
0
templatetags/__init__.py
Normal file
0
templatetags/__init__.py
Normal file
34
templatetags/in_list.py
Normal file
34
templatetags/in_list.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
""" ..
|
||||||
|
.,::;::::::
|
||||||
|
..,::::::::,,,,::: Jutda Helpdesk - A Django
|
||||||
|
.,,::::::,,,,,,,,,,,,,:: powered ticket tracker for
|
||||||
|
.,::::::,,,,,,,,,,,,,,,,,,:;r. small enterprise
|
||||||
|
.::::,,,,,,,,,,,,,,,,,,,,,,:;;rr.
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,,,,:;;;;;rr (c) Copyright 2008
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,,,,:;;;:::;;rr
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,. ,;;;::::::;;rr Jutda
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,. .:;;:::::::::;;rr
|
||||||
|
.:::,,,,,,,,,,,,,,,. .;r;::::::::::::;r; All Rights Reserved
|
||||||
|
.:::,,,,,,,,,,,,,,, .;r;;:::::::::::;;:.
|
||||||
|
.:::,,,,,,,,,,,,,,,. .;r;;::::::::::::;:.
|
||||||
|
.;:,,,,,,,,,,,,,,, .,;rr;::::::::::::;:. This software is released
|
||||||
|
.,:,,,,,,,,,,,,,. .,:;rrr;;::::::::::::;;. under a limited-use license that
|
||||||
|
:,,,,,,,,,,,,,..:;rrrrr;;;::::::::::::;;. allows you to freely download this
|
||||||
|
:,,,,,,,:::;;;rr;;;;;;:::::::::::::;;, software from it's manufacturer and
|
||||||
|
::::;;;;;;;;;;;:::::::::::::::::;;, use it yourself, however you may not
|
||||||
|
.r;;;;:::::::::::::::::::::::;;;, distribute it. For further details, see
|
||||||
|
.r;::::::::::::::::::::;;;;;:, the enclosed LICENSE file.
|
||||||
|
.;;::::::::::::::;;;;;:,.
|
||||||
|
.;;:::::::;;;;;;:,. Please direct people who wish to download this
|
||||||
|
.r;;;;;;;;:,. software themselves to www.jutda.com.au.
|
||||||
|
,,,..
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
def in_list(value, arg):
|
||||||
|
return value in arg
|
||||||
|
register = template.Library()
|
||||||
|
register.filter(in_list)
|
68
templatetags/ticket_to_link.py
Normal file
68
templatetags/ticket_to_link.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
""" ..
|
||||||
|
.,::;::::::
|
||||||
|
..,::::::::,,,,::: Jutda Helpdesk - A Django
|
||||||
|
.,,::::::,,,,,,,,,,,,,:: powered ticket tracker for
|
||||||
|
.,::::::,,,,,,,,,,,,,,,,,,:;r. small enterprise
|
||||||
|
.::::,,,,,,,,,,,,,,,,,,,,,,:;;rr.
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,,,,:;;;;;rr (c) Copyright 2008
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,,,,:;;;:::;;rr
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,. ,;;;::::::;;rr Jutda
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,. .:;;:::::::::;;rr
|
||||||
|
.:::,,,,,,,,,,,,,,,. .;r;::::::::::::;r; All Rights Reserved
|
||||||
|
.:::,,,,,,,,,,,,,,, .;r;;:::::::::::;;:.
|
||||||
|
.:::,,,,,,,,,,,,,,,. .;r;;::::::::::::;:.
|
||||||
|
.;:,,,,,,,,,,,,,,, .,;rr;::::::::::::;:. This software is released
|
||||||
|
.,:,,,,,,,,,,,,,. .,:;rrr;;::::::::::::;;. under a limited-use license that
|
||||||
|
:,,,,,,,,,,,,,..:;rrrrr;;;::::::::::::;;. allows you to freely download this
|
||||||
|
:,,,,,,,:::;;;rr;;;;;;:::::::::::::;;, software from it's manufacturer and
|
||||||
|
::::;;;;;;;;;;;:::::::::::::::::;;, use it yourself, however you may not
|
||||||
|
.r;;;;:::::::::::::::::::::::;;;, distribute it. For further details, see
|
||||||
|
.r;::::::::::::::::::::;;;;;:, the enclosed LICENSE file.
|
||||||
|
.;;::::::::::::::;;;;;:,.
|
||||||
|
.;;:::::::;;;;;;:,. Please direct people who wish to download this
|
||||||
|
.r;;;;;;;;:,. software themselves to www.jutda.com.au.
|
||||||
|
,,,..
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
from helpdesk.models import Ticket
|
||||||
|
|
||||||
|
class ReverseProxy:
|
||||||
|
def __init__(self, sequence):
|
||||||
|
self.sequence = sequence
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
length = len(self.sequence)
|
||||||
|
i = length
|
||||||
|
while i > 0:
|
||||||
|
i = i - 1
|
||||||
|
yield self.sequence[i]
|
||||||
|
|
||||||
|
def num_to_link(text):
|
||||||
|
import re
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
matches = []
|
||||||
|
for match in re.finditer("#(\d+)", text):
|
||||||
|
matches.append(match)
|
||||||
|
|
||||||
|
for match in ReverseProxy(matches):
|
||||||
|
start = match.start()
|
||||||
|
end = match.end()
|
||||||
|
number = match.groups()[0]
|
||||||
|
url = reverse('helpdesk_view', args=[number])
|
||||||
|
try:
|
||||||
|
ticket = Ticket.objects.get(id=number)
|
||||||
|
except:
|
||||||
|
ticket = None
|
||||||
|
|
||||||
|
if ticket:
|
||||||
|
style = ticket.get_status_display()
|
||||||
|
text = "%s<a href='%s' class='ticket_link_status ticket_link_status_%s'>#%s</a>%s" % (text[:match.start()], url, style, match.groups()[0], text[match.end():])
|
||||||
|
return text
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
register.filter(num_to_link)
|
58
urls.py
Normal file
58
urls.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
""" ..
|
||||||
|
.,::;::::::
|
||||||
|
..,::::::::,,,,::: Jutda Helpdesk - A Django
|
||||||
|
.,,::::::,,,,,,,,,,,,,:: powered ticket tracker for
|
||||||
|
.,::::::,,,,,,,,,,,,,,,,,,:;r. small enterprise
|
||||||
|
.::::,,,,,,,,,,,,,,,,,,,,,,:;;rr.
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,,,,:;;;;;rr (c) Copyright 2008
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,,,,:;;;:::;;rr
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,. ,;;;::::::;;rr Jutda
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,. .:;;:::::::::;;rr
|
||||||
|
.:::,,,,,,,,,,,,,,,. .;r;::::::::::::;r; All Rights Reserved
|
||||||
|
.:::,,,,,,,,,,,,,,, .;r;;:::::::::::;;:.
|
||||||
|
.:::,,,,,,,,,,,,,,,. .;r;;::::::::::::;:.
|
||||||
|
.;:,,,,,,,,,,,,,,, .,;rr;::::::::::::;:. This software is released
|
||||||
|
.,:,,,,,,,,,,,,,. .,:;rrr;;::::::::::::;;. under a limited-use license that
|
||||||
|
:,,,,,,,,,,,,,..:;rrrrr;;;::::::::::::;;. allows you to freely download this
|
||||||
|
:,,,,,,,:::;;;rr;;;;;;:::::::::::::;;, software from it's manufacturer and
|
||||||
|
::::;;;;;;;;;;;:::::::::::::::::;;, use it yourself, however you may not
|
||||||
|
.r;;;;:::::::::::::::::::::::;;;, distribute it. For further details, see
|
||||||
|
.r;::::::::::::::::::::;;;;;:, the enclosed LICENSE file.
|
||||||
|
.;;::::::::::::::;;;;;:,.
|
||||||
|
.;;:::::::;;;;;;:,. Please direct people who wish to download this
|
||||||
|
.r;;;;;;;;:,. software themselves to www.jutda.com.au.
|
||||||
|
,,,..
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.conf.urls.defaults import *
|
||||||
|
|
||||||
|
urlpatterns = patterns('helpdesk.views',
|
||||||
|
url(r'^$',
|
||||||
|
'dashboard',
|
||||||
|
name='helpdesk_home'),
|
||||||
|
|
||||||
|
url(r'^tickets/$',
|
||||||
|
'ticket_list',
|
||||||
|
name='helpdesk_list'),
|
||||||
|
|
||||||
|
url(r'^tickets/submit/$',
|
||||||
|
'create_ticket',
|
||||||
|
name='helpdesk_submit'),
|
||||||
|
|
||||||
|
url(r'^tickets/(?P<ticket_id>[0-9]+)/$',
|
||||||
|
'view_ticket',
|
||||||
|
name='helpdesk_view'),
|
||||||
|
|
||||||
|
url(r'^tickets/(?P<ticket_id>[0-9]+)/update/$',
|
||||||
|
'update_ticket',
|
||||||
|
name='helpdesk_view'),
|
||||||
|
)
|
||||||
|
|
||||||
|
urlpatterns += patterns('',
|
||||||
|
url(r'^login/$', 'django.contrib.auth.views.login', name='login'),
|
||||||
|
|
||||||
|
url(r'^logout/$', 'django.contrib.auth.views.logout', name='logout'),
|
||||||
|
)
|
185
views.py
Normal file
185
views.py
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
""" ..
|
||||||
|
.,::;::::::
|
||||||
|
..,::::::::,,,,::: Jutda Helpdesk - A Django
|
||||||
|
.,,::::::,,,,,,,,,,,,,:: powered ticket tracker for
|
||||||
|
.,::::::,,,,,,,,,,,,,,,,,,:;r. small enterprise
|
||||||
|
.::::,,,,,,,,,,,,,,,,,,,,,,:;;rr.
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,,,,:;;;;;rr (c) Copyright 2008
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,,,,:;;;:::;;rr
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,,,. ,;;;::::::;;rr Jutda
|
||||||
|
.:::,,,,,,,,,,,,,,,,,,. .:;;:::::::::;;rr
|
||||||
|
.:::,,,,,,,,,,,,,,,. .;r;::::::::::::;r; All Rights Reserved
|
||||||
|
.:::,,,,,,,,,,,,,,, .;r;;:::::::::::;;:.
|
||||||
|
.:::,,,,,,,,,,,,,,,. .;r;;::::::::::::;:.
|
||||||
|
.;:,,,,,,,,,,,,,,, .,;rr;::::::::::::;:. This software is released
|
||||||
|
.,:,,,,,,,,,,,,,. .,:;rrr;;::::::::::::;;. under a limited-use license that
|
||||||
|
:,,,,,,,,,,,,,..:;rrrrr;;;::::::::::::;;. allows you to freely download this
|
||||||
|
:,,,,,,,:::;;;rr;;;;;;:::::::::::::;;, software from it's manufacturer and
|
||||||
|
::::;;;;;;;;;;;:::::::::::::::::;;, use it yourself, however you may not
|
||||||
|
.r;;;;:::::::::::::::::::::::;;;, distribute it. For further details, see
|
||||||
|
.r;::::::::::::::::::::;;;;;:, the enclosed LICENSE file.
|
||||||
|
.;;::::::::::::::;;;;;:,.
|
||||||
|
.;;:::::::;;;;;;:,. Please direct people who wish to download this
|
||||||
|
.r;;;;;;;;:,. software themselves to www.jutda.com.au.
|
||||||
|
,,,..
|
||||||
|
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Python imports
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# Django imports
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.template import loader, Context, RequestContext
|
||||||
|
from django.shortcuts import render_to_response, get_object_or_404
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
# Helpdesk imports
|
||||||
|
from helpdesk.forms import TicketForm
|
||||||
|
from helpdesk.models import Ticket, Queue, FollowUp, TicketChange
|
||||||
|
|
||||||
|
def dashboard(request):
|
||||||
|
tickets = Ticket.objects.filter(assigned_to=request.user).exclude(status=Ticket.CLOSED_STATUS)
|
||||||
|
unassigned_tickets = Ticket.objects.filter(assigned_to__isnull=True).exclude(status=Ticket.CLOSED_STATUS)
|
||||||
|
|
||||||
|
dash_tickets = []
|
||||||
|
for q in Queue.objects.all():
|
||||||
|
dash_tickets.append({
|
||||||
|
'queue': q,
|
||||||
|
'open': q.ticket_set.filter(Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS)).count(),
|
||||||
|
'resolved': q.ticket_set.filter(status=Ticket.RESOLVED_STATUS).count(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return render_to_response('helpdesk/dashboard.html',
|
||||||
|
RequestContext(request, {
|
||||||
|
'user_tickets': tickets,
|
||||||
|
'unassigned_tickets': unassigned_tickets,
|
||||||
|
'dash_tickets': dash_tickets,
|
||||||
|
}))
|
||||||
|
dashboard = login_required(dashboard)
|
||||||
|
|
||||||
|
def view_ticket(request, ticket_id):
|
||||||
|
ticket = get_object_or_404(Ticket, id=ticket_id)
|
||||||
|
if request.GET.has_key('take'):
|
||||||
|
ticket.assigned_to = request.user
|
||||||
|
ticket.save()
|
||||||
|
|
||||||
|
return render_to_response('helpdesk/ticket.html',
|
||||||
|
RequestContext(request, {
|
||||||
|
'ticket': ticket,
|
||||||
|
'active_users': User.objects.filter(is_active=True),
|
||||||
|
}))
|
||||||
|
view_ticket = login_required(view_ticket)
|
||||||
|
|
||||||
|
def update_ticket(request, ticket_id):
|
||||||
|
ticket = get_object_or_404(Ticket, id=ticket_id)
|
||||||
|
|
||||||
|
comment = request.POST.get('comment', '')
|
||||||
|
new_status = int(request.POST.get('new_status', ticket.status))
|
||||||
|
title = request.POST.get('title', '')
|
||||||
|
public = request.POST.get('public', False)
|
||||||
|
owner = int(request.POST.get('owner', None))
|
||||||
|
if not owner and ticket.assigned_to:
|
||||||
|
owner = ticket.assigned_to.id
|
||||||
|
|
||||||
|
f = FollowUp(ticket=ticket, date=datetime.now(), comment=comment, user=request.user)
|
||||||
|
if public:
|
||||||
|
f.public = True
|
||||||
|
|
||||||
|
if owner:
|
||||||
|
if owner != 0 and (ticket.assigned_to and owner != ticket.assigned_to.id) or not ticket.assigned_to:
|
||||||
|
new_user = User.objects.get(id=owner)
|
||||||
|
f.title = 'Assigned to %s' % new_user.username
|
||||||
|
ticket.assigned_to = new_user
|
||||||
|
else:
|
||||||
|
f.title = 'Unassigned'
|
||||||
|
ticket.assigned_to = None
|
||||||
|
|
||||||
|
if new_status != ticket.status:
|
||||||
|
ticket.status = new_status
|
||||||
|
ticket.save()
|
||||||
|
f.new_status = new_status
|
||||||
|
if f.title:
|
||||||
|
f.title += ' and %s' % ticket.get_status_display()
|
||||||
|
else:
|
||||||
|
f.title = '%s' % ticket.get_status_display()
|
||||||
|
|
||||||
|
if not f.title:
|
||||||
|
if f.comment:
|
||||||
|
f.title = 'Comment'
|
||||||
|
else:
|
||||||
|
f.title = 'Updated'
|
||||||
|
|
||||||
|
f.save()
|
||||||
|
|
||||||
|
if title != ticket.title:
|
||||||
|
c = TicketChange(followup=f, field='Title', old_value=ticket.title, new_value=title)
|
||||||
|
c.save()
|
||||||
|
ticket.title = title
|
||||||
|
|
||||||
|
ticket.save()
|
||||||
|
|
||||||
|
return HttpResponseRedirect(ticket.get_absolute_url())
|
||||||
|
update_ticket = login_required(update_ticket)
|
||||||
|
|
||||||
|
def ticket_list(request):
|
||||||
|
tickets = Ticket.objects.select_related()
|
||||||
|
context = {}
|
||||||
|
|
||||||
|
### FILTERING
|
||||||
|
queues = request.GET.getlist('queue')
|
||||||
|
if queues:
|
||||||
|
queues = [int(q) for q in queues]
|
||||||
|
tickets = tickets.filter(queue__id__in=queues)
|
||||||
|
context = dict(context, queues=queues)
|
||||||
|
|
||||||
|
owners = request.GET.getlist('assigned_to')
|
||||||
|
if owners:
|
||||||
|
owners = [int(u) for u in owners]
|
||||||
|
tickets = tickets.filter(assigned_to__id__in=owners)
|
||||||
|
context = dict(context, owners=owners)
|
||||||
|
|
||||||
|
statuses = request.GET.getlist('status')
|
||||||
|
if statuses:
|
||||||
|
statuses = [int(s) for s in statuses]
|
||||||
|
tickets = tickets.filter(status__in=statuses)
|
||||||
|
context = dict(context, statuses=statuses)
|
||||||
|
|
||||||
|
### SORTING
|
||||||
|
sort = request.GET.get('sort', None)
|
||||||
|
if sort not in ('status', 'assigned_to', 'created', 'title', 'queue'):
|
||||||
|
sort = 'created'
|
||||||
|
tickets = tickets.order_by(sort)
|
||||||
|
context = dict(context, sort=sort)
|
||||||
|
|
||||||
|
return render_to_response('helpdesk/ticket_list.html',
|
||||||
|
RequestContext(request, dict(
|
||||||
|
context,
|
||||||
|
tickets=tickets,
|
||||||
|
user_choices=User.objects.filter(is_active=True),
|
||||||
|
queue_choices=Queue.objects.all(),
|
||||||
|
status_choices=Ticket.STATUS_CHOICES,
|
||||||
|
)))
|
||||||
|
ticket_list = login_required(ticket_list)
|
||||||
|
|
||||||
|
def create_ticket(request):
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = TicketForm(request.POST)
|
||||||
|
form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.all()]
|
||||||
|
form.fields['assigned_to'].choices = [('', '--------')] + [[u.id, u.username] for u in User.objects.filter(is_active=True)]
|
||||||
|
if form.is_valid():
|
||||||
|
ticket = form.save()
|
||||||
|
return HttpResponseRedirect(ticket.get_absolute_url())
|
||||||
|
else:
|
||||||
|
form = TicketForm()
|
||||||
|
form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.all()]
|
||||||
|
form.fields['assigned_to'].choices = [('', '--------')] + [[u.id, u.username] for u in User.objects.filter(is_active=True)]
|
||||||
|
|
||||||
|
return render_to_response('helpdesk/create_ticket.html',
|
||||||
|
RequestContext(request, {
|
||||||
|
'form': form,
|
||||||
|
}))
|
||||||
|
create_ticket = login_required(create_ticket)
|
Loading…
Reference in New Issue
Block a user