mirror of
https://github.com/django-helpdesk/django-helpdesk.git
synced 2025-05-20 09:20:46 +02:00
First attempt at fixing issue #55, which adds the ability to update
multiple tickets at once (eg to delete them, or assign them to somebody else).
This commit is contained in:
parent
7bff65d693
commit
684115cf19
@ -7,6 +7,21 @@
|
|||||||
<script type='text/javascript' language='javascript'>
|
<script type='text/javascript' language='javascript'>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$("#tabset > ul").tabs();
|
$("#tabset > ul").tabs();
|
||||||
|
|
||||||
|
$("#select_all").click(function() {
|
||||||
|
$(".ticket_multi_select").attr('checked', true);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$("#select_none").click(function() {
|
||||||
|
$(".ticket_multi_select").attr('checked', false);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$("#select_inverse").click(function() {
|
||||||
|
$(".ticket_multi_select").each(function() {
|
||||||
|
$(this).attr('checked', !$(this).attr('checked'));
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -112,12 +127,14 @@ $(document).ready(function() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ search_message|safe }}
|
{{ search_message|safe }}
|
||||||
|
<form method='post' action='{% url helpdesk_mass_update %}'>
|
||||||
<table width='100%'>
|
<table width='100%'>
|
||||||
<tr class='row_tablehead'><td colspan='7'>{% trans "Tickets" %}</td></tr>
|
<tr class='row_tablehead'><td colspan='8'>{% trans "Tickets" %}</td></tr>
|
||||||
<tr class='row_columnheads'><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_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>
|
||||||
{% if tickets %}{% for ticket in tickets %}
|
{% if tickets %}{% for ticket in tickets %}
|
||||||
<tr class='row_{% cycle odd,even %} row_hover'>
|
<tr class='row_{% cycle odd,even %} row_hover'>
|
||||||
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.ticket }}</a></th>
|
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.ticket }}</a></th>
|
||||||
|
<td><input type='checkbox' name='ticket_id' value='{{ ticket.id }}' class='ticket_multi_select' /></td>
|
||||||
<td>{{ ticket.get_priority_span }}</td>
|
<td>{{ ticket.get_priority_span }}</td>
|
||||||
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.title }}</a></th>
|
<th><a href='{{ ticket.get_absolute_url }}'>{{ ticket.title }}</a></th>
|
||||||
<td>{{ ticket.queue }}</td>
|
<td>{{ ticket.queue }}</td>
|
||||||
@ -130,4 +147,8 @@ $(document).ready(function() {
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<p><label>Select: </label> <a href='#select_all' id='select_all'>All</a> <a href='#select_none' id='select_none'>None</a> <a href='#select_inverse' id='select_inverse'>Inverse</a></p>
|
||||||
|
|
||||||
|
<p><label for='id_mass_action'>With Selected Tickets:</label> <select name='action' id='id_mass_action'><option value='take'>Take (Assign to me)</option><option value='delete'>Delete</option><optgroup label='Close'><option value='close'>Close (Don't Send E-Mail)</option><option value='close_public'>Close (Send E-Mail)</option></optgroup><optgroup label='Assign To'><option value='unassign'>Nobody (Unassign)</option>{% for u in user_choices %}<option value='assign_{{ u.id }}'>{{ u.username }}</option>{% endfor %}</optgroup></select> <input type='submit' value='Go' /></p>
|
||||||
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
106
views/staff.py
106
views/staff.py
@ -149,10 +149,10 @@ def update_ticket(request, ticket_id):
|
|||||||
owner = ticket.assigned_to.id
|
owner = ticket.assigned_to.id
|
||||||
|
|
||||||
f = FollowUp(ticket=ticket, date=datetime.now(), comment=comment)
|
f = FollowUp(ticket=ticket, date=datetime.now(), comment=comment)
|
||||||
|
|
||||||
if request.user.is_staff:
|
if request.user.is_staff:
|
||||||
f.user = request.user
|
f.user = request.user
|
||||||
|
|
||||||
f.public = public
|
f.public = public
|
||||||
|
|
||||||
reassigned = False
|
reassigned = False
|
||||||
@ -185,7 +185,7 @@ def update_ticket(request, ticket_id):
|
|||||||
f.title = _('Updated')
|
f.title = _('Updated')
|
||||||
|
|
||||||
f.save()
|
f.save()
|
||||||
|
|
||||||
files = []
|
files = []
|
||||||
if request.FILES:
|
if request.FILES:
|
||||||
import mimetypes, os
|
import mimetypes, os
|
||||||
@ -199,9 +199,9 @@ def update_ticket(request, ticket_id):
|
|||||||
)
|
)
|
||||||
a.file.save(file.name, file, save=False)
|
a.file.save(file.name, file, save=False)
|
||||||
a.save()
|
a.save()
|
||||||
|
|
||||||
if file.size < getattr(settings, 'MAX_EMAIL_ATTACHMENT_SIZE', 512000):
|
if file.size < getattr(settings, 'MAX_EMAIL_ATTACHMENT_SIZE', 512000):
|
||||||
# Only files smaller than 512kb (or as defined in
|
# Only files smaller than 512kb (or as defined in
|
||||||
# settings.MAX_EMAIL_ATTACHMENT_SIZE) are sent via email.
|
# settings.MAX_EMAIL_ATTACHMENT_SIZE) are sent via email.
|
||||||
files.append(a.file.path)
|
files.append(a.file.path)
|
||||||
|
|
||||||
@ -303,10 +303,85 @@ def update_ticket(request, ticket_id):
|
|||||||
update_ticket = staff_member_required(update_ticket)
|
update_ticket = staff_member_required(update_ticket)
|
||||||
|
|
||||||
|
|
||||||
|
def mass_update(request):
|
||||||
|
tickets = request.POST.getlist('ticket_id')
|
||||||
|
action = request.POST.get('action', None)
|
||||||
|
if not (tickets and action):
|
||||||
|
return HttpResponseRedirect(reverse('helpdesk_list'))
|
||||||
|
|
||||||
|
if action.startswith('assign_'):
|
||||||
|
parts = action.split('_')
|
||||||
|
user = User.objects.get(id=parts[1])
|
||||||
|
action = 'assign'
|
||||||
|
elif action == 'take':
|
||||||
|
user = request.user
|
||||||
|
action = 'assign'
|
||||||
|
|
||||||
|
for t in Ticket.objects.filter(id__in=tickets):
|
||||||
|
if action == 'assign' and t.assigned_to != user:
|
||||||
|
t.assigned_to = user
|
||||||
|
t.save()
|
||||||
|
f = FollowUp(ticket=t, date=datetime.now(), title=_('Assigned to %(username)s in bulk update' % {'username': user.username}), public=True, user=request.user)
|
||||||
|
f.save()
|
||||||
|
elif action == 'unassign' and t.assigned_to is not None:
|
||||||
|
t.assigned_to = None
|
||||||
|
t.save()
|
||||||
|
f = FollowUp(ticket=t, date=datetime.now(), title=_('Unassigned in bulk update'), public=True, user=request.user)
|
||||||
|
f.save()
|
||||||
|
elif action == 'close' and t.status != Ticket.CLOSED_STATUS:
|
||||||
|
t.status = Ticket.CLOSED_STATUS
|
||||||
|
t.save()
|
||||||
|
f = FollowUp(ticket=t, date=datetime.now(), title=_('Closed in bulk update'), public=False, user=request.user, new_status=Ticket.CLOSED_STATUS)
|
||||||
|
f.save()
|
||||||
|
elif action == 'close_public' and t.status != Ticket.CLOSED_STATUS:
|
||||||
|
t.status = Ticket.CLOSED_STATUS
|
||||||
|
t.save()
|
||||||
|
f = FollowUp(ticket=t, date=datetime.now(), title=_('Closed in bulk update'), public=True, user=request.user, new_status=Ticket.CLOSED_STATUS)
|
||||||
|
f.save()
|
||||||
|
# Send email to Submitter, Owner, Queue CC
|
||||||
|
context = {
|
||||||
|
'ticket': t,
|
||||||
|
'queue': t.queue,
|
||||||
|
'resolution': t.resolution,
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.submitter_email:
|
||||||
|
send_templated_mail(
|
||||||
|
'closed_submitter',
|
||||||
|
context,
|
||||||
|
recipients=t.submitter_email,
|
||||||
|
sender=t.queue.from_address,
|
||||||
|
fail_silently=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if t.assigned_to and request.user != t.assigned_to and t.assigned_to.email:
|
||||||
|
send_templated_mail(
|
||||||
|
'closed_owner',
|
||||||
|
context,
|
||||||
|
recipients=t.assigned_to.email,
|
||||||
|
sender=t.queue.from_address,
|
||||||
|
fail_silently=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if t.queue.updated_ticket_cc:
|
||||||
|
send_templated_mail(
|
||||||
|
'closed_cc',
|
||||||
|
context,
|
||||||
|
recipients=t.queue.updated_ticket_cc,
|
||||||
|
sender=t.queue.from_address,
|
||||||
|
fail_silently=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
elif action == 'delete':
|
||||||
|
t.delete()
|
||||||
|
|
||||||
|
return HttpResponseRedirect(reverse('helpdesk_list'))
|
||||||
|
mass_update = staff_member_required(mass_update)
|
||||||
|
|
||||||
def ticket_list(request):
|
def ticket_list(request):
|
||||||
context = {}
|
context = {}
|
||||||
|
|
||||||
# Query_params will hold a dictionary of paramaters relating to
|
# Query_params will hold a dictionary of paramaters relating to
|
||||||
# a query, to be saved if needed:
|
# a query, to be saved if needed:
|
||||||
query_params = {
|
query_params = {
|
||||||
'filtering': {},
|
'filtering': {},
|
||||||
@ -371,7 +446,7 @@ def ticket_list(request):
|
|||||||
or request.GET.has_key('sort')
|
or request.GET.has_key('sort')
|
||||||
or request.GET.has_key('sortreverse') ):
|
or request.GET.has_key('sortreverse') ):
|
||||||
|
|
||||||
# Fall-back if no querying is being done, force the list to only
|
# Fall-back if no querying is being done, force the list to only
|
||||||
# show open/reopened/resolved (not closed) cases sorted by creation
|
# show open/reopened/resolved (not closed) cases sorted by creation
|
||||||
# date.
|
# date.
|
||||||
|
|
||||||
@ -406,7 +481,7 @@ def ticket_list(request):
|
|||||||
Q(submitter_email__icontains=q)
|
Q(submitter_email__icontains=q)
|
||||||
)
|
)
|
||||||
context = dict(context, query=q)
|
context = dict(context, query=q)
|
||||||
|
|
||||||
query_params['other_filter'] = qset
|
query_params['other_filter'] = qset
|
||||||
|
|
||||||
### SORTING
|
### SORTING
|
||||||
@ -414,7 +489,7 @@ def ticket_list(request):
|
|||||||
if sort not in ('status', 'assigned_to', 'created', 'title', 'queue', 'priority'):
|
if sort not in ('status', 'assigned_to', 'created', 'title', 'queue', 'priority'):
|
||||||
sort = 'created'
|
sort = 'created'
|
||||||
query_params['sorting'] = sort
|
query_params['sorting'] = sort
|
||||||
|
|
||||||
sortreverse = request.GET.get('sortreverse', None)
|
sortreverse = request.GET.get('sortreverse', None)
|
||||||
query_params['sortreverse'] = sortreverse
|
query_params['sortreverse'] = sortreverse
|
||||||
|
|
||||||
@ -424,6 +499,7 @@ def ticket_list(request):
|
|||||||
if context.has_key('query') and settings.DATABASE_ENGINE.startswith('sqlite'):
|
if context.has_key('query') and settings.DATABASE_ENGINE.startswith('sqlite'):
|
||||||
search_message = _('<p><strong>Note:</strong> Your keyword search is case sensitive because of your database. This means the search will <strong>not</strong> be accurate. By switching to a different database system you will gain better searching! For more information, read the <a href="http://docs.djangoproject.com/en/dev/ref/databases/#sqlite-string-matching">Django Documentation on string matching in SQLite</a>.')
|
search_message = _('<p><strong>Note:</strong> Your keyword search is case sensitive because of your database. This means the search will <strong>not</strong> be accurate. By switching to a different database system you will gain better searching! For more information, read the <a href="http://docs.djangoproject.com/en/dev/ref/databases/#sqlite-string-matching">Django Documentation on string matching in SQLite</a>.')
|
||||||
|
|
||||||
|
|
||||||
import cPickle
|
import cPickle
|
||||||
from helpdesk.lib import b64encode
|
from helpdesk.lib import b64encode
|
||||||
urlsafe_query = b64encode(cPickle.dumps(query_params))
|
urlsafe_query = b64encode(cPickle.dumps(query_params))
|
||||||
@ -596,8 +672,8 @@ def run_report(request, report):
|
|||||||
desc = '%s %s' % (months[low_bound[1]-1], low_bound[0])
|
desc = '%s %s' % (months[low_bound[1]-1], low_bound[0])
|
||||||
month_sql.append("""
|
month_sql.append("""
|
||||||
COUNT(
|
COUNT(
|
||||||
CASE 1 = 1
|
CASE 1 = 1
|
||||||
WHEN (date(t.created) >= date('%s')
|
WHEN (date(t.created) >= date('%s')
|
||||||
AND date(t.created) < date('%s')) THEN t.id END) AS "%s"
|
AND date(t.created) < date('%s')) THEN t.id END) AS "%s"
|
||||||
""" % (low_sqlmonth, upper_sqlmonth, desc))
|
""" % (low_sqlmonth, upper_sqlmonth, desc))
|
||||||
month_columns.append(desc)
|
month_columns.append(desc)
|
||||||
@ -691,13 +767,13 @@ def save_query(request):
|
|||||||
title = request.POST.get('title', None)
|
title = request.POST.get('title', None)
|
||||||
shared = request.POST.get('shared', False)
|
shared = request.POST.get('shared', False)
|
||||||
query_encoded = request.POST.get('query_encoded', None)
|
query_encoded = request.POST.get('query_encoded', None)
|
||||||
|
|
||||||
if not title or not query_encoded:
|
if not title or not query_encoded:
|
||||||
return HttpResponseRedirect(reverse('helpdesk_list'))
|
return HttpResponseRedirect(reverse('helpdesk_list'))
|
||||||
|
|
||||||
query = SavedSearch(title=title, shared=shared, query=query_encoded, user=request.user)
|
query = SavedSearch(title=title, shared=shared, query=query_encoded, user=request.user)
|
||||||
query.save()
|
query.save()
|
||||||
|
|
||||||
return HttpResponseRedirect('%s?saved_query=%s' % (reverse('helpdesk_list'), query.id))
|
return HttpResponseRedirect('%s?saved_query=%s' % (reverse('helpdesk_list'), query.id))
|
||||||
save_query = staff_member_required(save_query)
|
save_query = staff_member_required(save_query)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user