forked from extern/django-helpdesk
Issue #102
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.
This commit is contained in:
parent
a9d5bb67a3
commit
f419d8e2d0
@ -1 +1,6 @@
|
||||
2009-09-09 r138 Issue #104 Add a CHANGELOG file
|
||||
|
||||
2009-09-09 r139 Issue #102 Add Ticket CC's
|
||||
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.
|
||||
|
7
forms.py
7
forms.py
@ -15,7 +15,7 @@ from django.contrib.auth.models import User
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from helpdesk.lib import send_templated_mail
|
||||
from helpdesk.models import Ticket, Queue, FollowUp, Attachment, IgnoreEmail
|
||||
from helpdesk.models import Ticket, Queue, FollowUp, Attachment, IgnoreEmail, TicketCC
|
||||
|
||||
class EditTicketForm(forms.ModelForm):
|
||||
class Meta:
|
||||
@ -356,3 +356,8 @@ class UserSettingsForm(forms.Form):
|
||||
class EmailIgnoreForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = IgnoreEmail
|
||||
|
||||
class TicketCCForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = TicketCC
|
||||
exclude = ('ticket',)
|
||||
|
57
models.py
57
models.py
@ -129,7 +129,7 @@ class Queue(models.Model):
|
||||
help_text=_('Whether to use SSL for IMAP or POP3 - the default ports '
|
||||
'when using SSL are 993 for IMAP and 995 for POP3.'),
|
||||
)
|
||||
|
||||
|
||||
email_box_user = models.CharField(
|
||||
_('E-Mail Username'),
|
||||
max_length=200,
|
||||
@ -999,3 +999,58 @@ class IgnoreEmail(models.Model):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
class TicketCC(models.Model):
|
||||
"""
|
||||
Often, there are people who wish to follow a ticket who aren't the
|
||||
person who originally submitted it. This model provides a way for those
|
||||
people to follow a ticket.
|
||||
|
||||
In this circumstance, a 'person' could be either an e-mail address or
|
||||
an existing system user.
|
||||
"""
|
||||
|
||||
ticket = models.ForeignKey(Ticket)
|
||||
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_('User who wishes to receive updates for this ticket.'),
|
||||
)
|
||||
|
||||
email = models.EmailField(
|
||||
_('E-Mail Address'),
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text=_('For non-user followers, enter their e-mail address'),
|
||||
)
|
||||
|
||||
can_view = models.BooleanField(
|
||||
_('Can View Ticket?'),
|
||||
blank=True,
|
||||
help_text=_('Can this CC login to view the ticket details?'),
|
||||
)
|
||||
|
||||
can_update = models.BooleanField(
|
||||
_('Can Update Ticket?'),
|
||||
blank=True,
|
||||
help_text=_('Can this CC login and update the ticket?'),
|
||||
)
|
||||
|
||||
def _email_address(self):
|
||||
if self.user and self.user.email is not None:
|
||||
return self.user.email
|
||||
else:
|
||||
return self.email
|
||||
email_address = property(_email_address)
|
||||
|
||||
def _display(self):
|
||||
if self.user:
|
||||
return self.user
|
||||
else:
|
||||
return self.email
|
||||
display = property(_display)
|
||||
|
||||
def __unicode__(self):
|
||||
return u'%s for %s' % (self.display, self.ticket.title)
|
||||
|
@ -5,7 +5,7 @@
|
||||
{% block helpdesk_body %}{% blocktrans with ignore.email_address as email_address %}
|
||||
<h2>Un-Ignore E-Mail Address</h2>
|
||||
|
||||
<p>Are you sure you wish to stop removing this email address (<em>{{ email_address }}</em>) and allow their e-mails to automatically create tickets in your system? You can re-add this e-mail address at any time.<?p>
|
||||
<p>Are you sure you wish to stop removing this email address (<em>{{ email_address }}</em>) and allow their e-mails to automatically create tickets in your system? You can re-add this e-mail address at any time.</p>
|
||||
{% endblocktrans %}
|
||||
|
||||
{% blocktrans %}<p><a href='../../'>Keep Ignoring It</a></p>
|
||||
|
@ -67,16 +67,21 @@
|
||||
</tr>
|
||||
|
||||
<tr class='row_odd'>
|
||||
<th>{% trans "Copies To" %}</th>
|
||||
<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>
|
||||
|
||||
<tr class='row_even'>
|
||||
<th colspan='2'>{% trans "Description" %}</th>
|
||||
</tr>
|
||||
<tr class='row_even'>
|
||||
<tr class='row_odd'>
|
||||
<td colspan='2'>{{ ticket.description|force_escape|linebreaksbr }}</td>
|
||||
</tr>
|
||||
|
||||
{% if ticket.resolution %}<tr class='row_odd'>
|
||||
{% if ticket.resolution %}<tr class='row_even'>
|
||||
<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_even'>
|
||||
<tr class='row_odd'>
|
||||
<td colspan='2'>{{ ticket.resolution|force_escape }}</td>
|
||||
</tr>{% endif %}
|
||||
|
||||
|
25
templates/helpdesk/ticket_cc_add.html
Normal file
25
templates/helpdesk/ticket_cc_add.html
Normal file
@ -0,0 +1,25 @@
|
||||
{% extends "helpdesk/base.html" %}{% load i18n %}
|
||||
|
||||
{% block helpdesk_title %}{% trans "Add Ticket CC" %}{% endblock %}
|
||||
|
||||
{% block helpdesk_body %}{% blocktrans %}
|
||||
<h2>Add Ticket CC</h2>
|
||||
|
||||
<p>To automatically send an email to a user or e-mail address when this ticket is updated, select the user or enter an e-mail address below.</p>{% endblocktrans %}
|
||||
|
||||
<form method='post' action='./'>
|
||||
|
||||
<fieldset>
|
||||
<dl>{% for field in form %}
|
||||
<dt><label for='id_{{ field.name }}'>{{ field.label }}</label></dt>
|
||||
<dd>{{ field }}</dd>
|
||||
{% if field.errors %}<dd class='error'>{{ field.errors }}</dd>{% endif %}
|
||||
{% if field.help_text %}<dd class='form_help_text'>{{ field.help_text }}</dd>{% endif %}
|
||||
{% endfor %}</dl>
|
||||
</fieldset>
|
||||
|
||||
<input type='submit' value='{% trans "Save Ticket CC" %}' />
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
14
templates/helpdesk/ticket_cc_del.html
Normal file
14
templates/helpdesk/ticket_cc_del.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% extends "helpdesk/base.html" %}{% load i18n %}
|
||||
|
||||
{% block helpdesk_title %}{% trans "Delete Ticket CC" %}{% endblock %}
|
||||
|
||||
{% block helpdesk_body %}{% blocktrans with cc.email_address as email_address %}
|
||||
<h2>Delete Ticket CC</h2>
|
||||
|
||||
<p>Are you sure you wish to delete this email address (<em>{{ email_address }}</em>) from the CC list for this ticket? They will stop receiving updates.</p>
|
||||
{% endblocktrans %}
|
||||
|
||||
{% blocktrans %}<p><a href='../../'>Don't Delete</a></p>
|
||||
|
||||
<form method='post' action='./'><input type='submit' value='Yes, Delete' /></form>
|
||||
{% endblocktrans %}{% endblock %}
|
31
templates/helpdesk/ticket_cc_list.html
Normal file
31
templates/helpdesk/ticket_cc_list.html
Normal file
@ -0,0 +1,31 @@
|
||||
{% extends "helpdesk/base.html" %}{% load i18n %}
|
||||
|
||||
{% block helpdesk_title %}{% trans "Ticket CC Settings" %}{% endblock %}
|
||||
|
||||
{% block helpdesk_body %}{% blocktrans with ticket.title as ticket_title and ticket.id as ticket_id %}
|
||||
<h2>Ticket CC Settings</h2>
|
||||
|
||||
<p>The following people will receive an e-mail whenever <em><a href='../'>{{ ticket_title }}</a></em> is updated. Some people can also view or edit the ticket via the public ticket views.</p>
|
||||
|
||||
<p>You can <a href='add/'>add a new e-mail address to the list</a> or delete any of the items below as required.</p>{% endblocktrans %}
|
||||
|
||||
<table width='100%'>
|
||||
<thead>
|
||||
<tr class='row_tablehead'><td colspan='4'>{% trans "Ticket CC List" %}</td></tr>
|
||||
<tr class='row_columnheads'><th>{% trans "E-Mail Address" %}</th><th>{% trans "View?" %}</th><th>{% trans "Update?" %}</th><th>{% trans "Delete" %}</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for person in copies_to %}
|
||||
<tr class='row_{% cycle odd,even %}'>
|
||||
<td>{{ person.display }}</td>
|
||||
<td>{{ person.can_view }}</td>
|
||||
<td>{{ person.can_update }}</td>
|
||||
<td><a href='{% url helpdesk_ticket_cc_del ticket.id person.id %}'>{% trans "Delete" %}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p><a href='{% url helpdesk_view ticket.id %}'>{% blocktrans with ticket.title as ticket_title %}Return to <em>{{ ticket_title }}</em>{% endblocktrans %}</a></p>
|
||||
|
||||
{% endblock %}
|
12
urls.py
12
urls.py
@ -55,6 +55,18 @@ urlpatterns = patterns('helpdesk.views.staff',
|
||||
'unhold_ticket',
|
||||
name='helpdesk_unhold'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/cc/$',
|
||||
'ticket_cc',
|
||||
name='helpdesk_ticket_cc'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/cc/add/$',
|
||||
'ticket_cc_add',
|
||||
name='helpdesk_ticket_cc_add'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/cc/delete/(?P<cc_id>[0-9]+)/$',
|
||||
'ticket_cc_del',
|
||||
name='helpdesk_ticket_cc_del'),
|
||||
|
||||
url(r'^raw/(?P<type>\w+)/$',
|
||||
'raw_details',
|
||||
name='helpdesk_raw'),
|
||||
|
23
views/api.py
23
views/api.py
@ -209,6 +209,18 @@ class API:
|
||||
)
|
||||
messages_sent_to.append(ticket.submitter_email)
|
||||
|
||||
if public:
|
||||
for cc in ticket.ticketcc_set.all():
|
||||
if cc.email_address not in messages_sent_to:
|
||||
send_templated_mail(
|
||||
'updated_submitter',
|
||||
context,
|
||||
recipients=cc.email_address,
|
||||
sender=ticket.queue.from_address,
|
||||
fail_silently=True,
|
||||
)
|
||||
messages_sent_to.append(cc.email_address)
|
||||
|
||||
if ticket.queue.updated_ticket_cc and ticket.queue.updated_ticket_cc not in messages_sent_to:
|
||||
send_templated_mail(
|
||||
'updated_cc',
|
||||
@ -274,6 +286,17 @@ class API:
|
||||
)
|
||||
messages_sent_to.append(ticket.submitter_email)
|
||||
|
||||
for cc in ticket.ticketcc_set.all():
|
||||
if cc.email_address not in messages_sent_to:
|
||||
send_templated_mail(
|
||||
'resolved_submitter',
|
||||
context,
|
||||
recipients=cc.email_address,
|
||||
sender=ticket.queue.from_address,
|
||||
fail_silently=True,
|
||||
)
|
||||
messages_sent_to.append(cc.email_address)
|
||||
|
||||
if ticket.queue.updated_ticket_cc and ticket.queue.updated_ticket_cc not in messages_sent_to:
|
||||
send_templated_mail(
|
||||
'resolved_cc',
|
||||
|
@ -22,9 +22,9 @@ from django.shortcuts import render_to_response, get_object_or_404
|
||||
from django.template import loader, Context, RequestContext
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from helpdesk.forms import TicketForm, UserSettingsForm, EmailIgnoreForm, EditTicketForm
|
||||
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
|
||||
from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch, IgnoreEmail, TicketCC
|
||||
|
||||
|
||||
staff_member_required = user_passes_test(lambda u: u.is_authenticated() and u.is_active and u.is_staff)
|
||||
@ -259,6 +259,17 @@ def update_ticket(request, ticket_id, public=False):
|
||||
)
|
||||
messages_sent_to.append(ticket.submitter_email)
|
||||
|
||||
for cc in ticket.ticketcc_set.all():
|
||||
if cc.email_address not in messages_sent_to:
|
||||
send_templated_mail(
|
||||
template,
|
||||
context,
|
||||
recipients=cc.email_address,
|
||||
sender=ticket.queue.from_address,
|
||||
fail_silently=True,
|
||||
)
|
||||
messages_sent_to.append(cc.email_address)
|
||||
|
||||
if ticket.assigned_to and request.user != ticket.assigned_to and ticket.assigned_to.email and ticket.assigned_to.email not in messages_sent_to:
|
||||
# We only send e-mails to staff members if the ticket is updated by
|
||||
# another user. The actual template varies, depending on what has been
|
||||
@ -364,6 +375,17 @@ def mass_update(request):
|
||||
)
|
||||
messages_sent_to.append(t.submitter_email)
|
||||
|
||||
for cc in ticket.ticketcc_set.all():
|
||||
if cc.email_address not in messages_sent_to:
|
||||
send_templated_mail(
|
||||
'closed_submitter',
|
||||
context,
|
||||
recipients=cc.email_address,
|
||||
sender=ticket.queue.from_address,
|
||||
fail_silently=True,
|
||||
)
|
||||
messages_sent_to.append(cc.email_address)
|
||||
|
||||
if t.assigned_to and request.user != t.assigned_to and t.assigned_to.email and t.assigned_to.email not in messages_sent_to:
|
||||
send_templated_mail(
|
||||
'closed_owner',
|
||||
@ -563,6 +585,7 @@ def edit_ticket(request, ticket_id):
|
||||
RequestContext(request, {
|
||||
'form': form,
|
||||
}))
|
||||
edit_ticket = staff_member_required(edit_ticket)
|
||||
|
||||
def create_ticket(request):
|
||||
if request.method == 'POST':
|
||||
@ -890,3 +913,42 @@ def email_ignore_del(request, id):
|
||||
'ignore': ignore,
|
||||
}))
|
||||
email_ignore_del = superuser_required(email_ignore_del)
|
||||
|
||||
def ticket_cc(request, ticket_id):
|
||||
ticket = get_object_or_404(Ticket, id=ticket_id)
|
||||
copies_to = ticket.ticketcc_set.all()
|
||||
return render_to_response('helpdesk/ticket_cc_list.html',
|
||||
RequestContext(request, {
|
||||
'copies_to': copies_to,
|
||||
'ticket': ticket,
|
||||
}))
|
||||
ticket_cc = staff_member_required(ticket_cc)
|
||||
|
||||
def ticket_cc_add(request, ticket_id):
|
||||
ticket = get_object_or_404(Ticket, id=ticket_id)
|
||||
if request.method == 'POST':
|
||||
form = TicketCCForm(request.POST)
|
||||
if form.is_valid():
|
||||
ticketcc = form.save(commit=False)
|
||||
ticketcc.ticket = ticket
|
||||
ticketcc.save()
|
||||
return HttpResponseRedirect(reverse('helpdesk_ticket_cc', kwargs={'ticket_id': ticket.id}))
|
||||
else:
|
||||
form = TicketCCForm()
|
||||
return render_to_response('helpdesk/ticket_cc_add.html',
|
||||
RequestContext(request, {
|
||||
'ticket': ticket,
|
||||
'form': form,
|
||||
}))
|
||||
ticket_cc_add = staff_member_required(ticket_cc_add)
|
||||
|
||||
def ticket_cc_del(request, ticket_id, cc_id):
|
||||
cc = get_object_or_404(TicketCC, ticket__id=ticket_id, id=cc_id)
|
||||
if request.POST:
|
||||
cc.delete()
|
||||
return HttpResponseRedirect(reverse('helpdesk_ticket_cc', kwargs={'ticket_id': cc.ticket.id}))
|
||||
return render_to_response('helpdesk/ticket_cc_del.html',
|
||||
RequestContext(request, {
|
||||
'cc': cc,
|
||||
}))
|
||||
ticket_cc_del = staff_member_required(ticket_cc_del)
|
||||
|
Loading…
Reference in New Issue
Block a user