From a162d77d7075da80c6fe11c029ef9915317dc768 Mon Sep 17 00:00:00 2001 From: Ross Poulton Date: Fri, 29 Aug 2008 09:11:02 +0000 Subject: [PATCH] * Create new help page for comment template context variables ( see /help/context/; also linked from comment form) * Refactor API help page to share template with context help * Allow a limited number of Ticket & Queue model fields to be accessible in comments, as per 'Help' page. * New function in lib.py to build a dict of 'safe' fields from ticket & queue, to prevent the power of the Django model API from exposing things like passwords (imagine if a user typed a comment containing {{ ticket.queue.email_box_password }} !!!! * When accessing the ticket list with no filter params (eg by clicking on the "Tickets" button in the menu), the default search is for tickets that aren't closed, rather than showing all tickets. * Updated English locale with changed message strings. --- lib.py | 46 ++++++ locale/en/LC_MESSAGES/django.mo | Bin 367 -> 367 bytes locale/en/LC_MESSAGES/django.po | 22 +-- .../helpdesk/{api_help.html => help_api.html} | 48 +----- templates/helpdesk/help_base.html | 46 ++++++ templates/helpdesk/help_context.html | 139 ++++++++++++++++++ templates/helpdesk/ticket.html | 2 +- urls.py | 17 ++- views/api.py | 4 +- views/staff.py | 25 +++- 10 files changed, 285 insertions(+), 64 deletions(-) rename templates/helpdesk/{api_help.html => help_api.html} (92%) create mode 100644 templates/helpdesk/help_base.html create mode 100644 templates/helpdesk/help_context.html diff --git a/lib.py b/lib.py index 6767511b..5ed56631 100644 --- a/lib.py +++ b/lib.py @@ -279,3 +279,49 @@ def apply_query(queryset, params): queryset = queryset.order_by(params['sorting']) return queryset + + +def safe_template_context(ticket): + """ + Return a dictionary that can be used as a template context to render + comments and other details with ticket or queue paramaters. Note that + we don't just provide the Ticket & Queue objects to the template as + they could reveal confidential information. Just imagine these two options: + * {{ ticket.queue.email_box_password }} + * {{ ticket.assigned_to.password }} + + Ouch! + + The downside to this is that if we make changes to the model, we will also + have to update this code. Perhaps we can find a better way in the future. + """ + + context = { + 'queue': {}, + 'ticket': {}, + } + queue = ticket.queue + + for field in ( 'title', 'slug', 'email_address', 'from_address'): + attr = getattr(queue, field, None) + if callable(attr): + context['queue'][field] = attr() + else: + context['queue'][field] = attr + + for field in ( 'title', 'created', 'modified', 'submitter_email', + 'status', 'get_status_display', 'on_hold', 'description', + 'resolution', 'priority', 'get_priority_display', + 'last_escalation', 'ticket', 'ticket_for_url', + 'get_status', 'ticket_url', 'staff_url', '_get_assigned_to' + ): + attr = getattr(ticket, field, None) + if callable(attr): + context['ticket'][field] = '%s' % attr() + else: + context['ticket'][field] = attr + + context['ticket']['queue'] = context['queue'] + context['ticket']['assigned_to'] = context['ticket']['_get_assigned_to'] + + return context diff --git a/locale/en/LC_MESSAGES/django.mo b/locale/en/LC_MESSAGES/django.mo index e8f5dc08c4a407d02a711f3b9bb0abcf103b1d2f..31ba04483f67255fca12f7df0afe6f9b0f3c072e 100644 GIT binary patch delta 16 YcmaFQ^qy(L1U5?r15+!*iPLTZ05qcpG5`Po delta 16 YcmaFQ^qy(L1U3r=10yTbiPLTZ05q5eG5`Po diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 65019906..84630416 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-08-28 02:50+0000\n" +"POT-Creation-Date: 2008-08-29 05:10+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -54,7 +54,7 @@ msgstr "" #: forms.py:58 models.py:301 management/commands/escalate_tickets.py:152 #: templates/helpdesk/public_view_ticket.html:29 #: templates/helpdesk/ticket.html:67 templates/helpdesk/ticket.html.py:159 -#: templates/helpdesk/ticket_list.html:43 views/staff.py:186 +#: templates/helpdesk/ticket_list.html:43 views/staff.py:192 msgid "Priority" msgstr "" @@ -108,7 +108,7 @@ msgstr "" #: models.py:29 models.py:239 models.py:446 models.py:740 models.py:771 #: templates/helpdesk/dashboard.html:26 templates/helpdesk/dashboard.html:41 #: templates/helpdesk/ticket.html:153 templates/helpdesk/ticket_list.html:40 -#: templates/helpdesk/ticket_list.html:115 views/staff.py:176 +#: templates/helpdesk/ticket_list.html:115 views/staff.py:182 msgid "Title" msgstr "" @@ -378,7 +378,7 @@ msgid "" msgstr "" #: models.py:321 templates/helpdesk/ticket.html:58 views/feeds.py:91 -#: views/feeds.py:117 views/feeds.py:171 views/staff.py:153 +#: views/feeds.py:117 views/feeds.py:171 views/staff.py:159 msgid "Unassigned" msgstr "" @@ -390,7 +390,7 @@ msgstr "" msgid "Date" msgstr "" -#: models.py:453 views/staff.py:167 +#: models.py:453 views/staff.py:173 msgid "Comment" msgstr "" @@ -1079,8 +1079,8 @@ msgstr "" #: templates/helpdesk/ticket.html:119 msgid "" -"You can use the {{ ticket }} and {{ queue }} template variables in your " -"message." +"You can insert ticket and queue details in your message. For more " +"information, see the context help page." msgstr "" #: templates/helpdesk/ticket.html:142 @@ -1289,19 +1289,19 @@ msgstr "" msgid "Accepted resolution and closed ticket" msgstr "" -#: views/staff.py:147 +#: views/staff.py:153 #, python-format msgid "Assigned to %(username)s" msgstr "" -#: views/staff.py:169 +#: views/staff.py:175 msgid "Updated" msgstr "" -#: views/staff.py:401 +#: views/staff.py:422 msgid "Ticket taken off hold" msgstr "" -#: views/staff.py:404 +#: views/staff.py:425 msgid "Ticket placed on hold" msgstr "" diff --git a/templates/helpdesk/api_help.html b/templates/helpdesk/help_api.html similarity index 92% rename from templates/helpdesk/api_help.html rename to templates/helpdesk/help_api.html index e2f97ac9..bdbd9186 100644 --- a/templates/helpdesk/api_help.html +++ b/templates/helpdesk/help_api.html @@ -1,46 +1,9 @@ - - - - Jutda Helpdesk API Documentation - - -

Jutda Helpdesk API Documentation

+{% block title %}Jutda Helpdesk API Documentation{% endblock %} +{% block heading %}Jutda Helpdesk API Documentation{% endblock %} +{% block content %}

Contents

    @@ -305,5 +268,4 @@ echo $result;

    This method responds with plain-text.

    If you receive a 200 OK response, then the content of the response will be the users ID.

    - - +{% endblock %} diff --git a/templates/helpdesk/help_base.html b/templates/helpdesk/help_base.html new file mode 100644 index 00000000..8fdaf1af --- /dev/null +++ b/templates/helpdesk/help_base.html @@ -0,0 +1,46 @@ + + + + {% block title %}Jutda Helpdesk Help{% endblock %} + + +

    {% block heading %}Jutda Helpdesk Help{% endblock %}

    + + {% block content %}{% endblock %} + + diff --git a/templates/helpdesk/help_context.html b/templates/helpdesk/help_context.html new file mode 100644 index 00000000..e2f79467 --- /dev/null +++ b/templates/helpdesk/help_context.html @@ -0,0 +1,139 @@ +{% extends "helpdesk/help_base.html" %} + +{% block title %}Jutda Helpdesk Context Listing{% endblock %} +{% block heading %}Jutda Helpdesk Context Listing{% endblock %} + +{% block content %} +

    Contents

    + + + +

    Introduction

    + +

    Jutda Helpdesk provides a powerful way for you to embed fields from the current ticket into your comments and ticket resolutions using a template language.

    + +

    For example, you may want to place the last escalation date into a ticket comment, or reproduce the original description sent to you by the submitter.

    + +

    Tech Note: This system uses the Django templating system with a sandboxed template context.

    + +

    Inserting Fields

    + +

    In your comment, enter the field name surrounded by double curly braces. For example, to get the ticket title, use:

    + +
    {{ ticket.title }}
    + +

    Queue Fields

    + +

    The following fields are available in the template context to display the details of the queue to which this ticket belongs:

    + +
    +
    queue.title
    +
    The display title of the queue
    + +
    queue.slug
    +
    The 'slug' of the queue, a single-word lowercase identifier used in URL's and e-mail subjects.
    + +
    queue.email_address
    +
    The e-mail address to which users can send an e-mail to open a new ticket automatically.
    + +
    queue.from_address
    +
    The e-mail address from which the queue will send e-mails to submitters.
    +
    + +

    Ticket Fields

    + +
    + +
    ticket.title
    +
    Title of the ticket, as provided by the submitter. A single line of text.
    + +
    ticket.created
    +
    The time/date this ticket was first opened
    + +
    ticket.modified
    +
    The time/date this ticket was most recently changed
    + +
    ticket.submitter_email
    +
    E-Mail address of the ticket submitter
    + +
    ticket.status
    +
    Status of this ticket (Integer)
    + +
    ticket.get_status_display
    +
    Status of this ticket (Text-based)
    + +
    ticket.get_status
    +
    Status of this ticket (Text-based, includes 'On Hold' if required)
    + +
    ticket.on_hold
    +
    Indicates whether this ticket is on hold (True/False)
    + +
    ticket.description
    +
    The description of the ticket, eg the body of the e-mail sent or the problem entered by the submitter
    + +
    ticket.resolution
    +
    The resolution of the ticket provided by staff. This may be blank.
    + +
    ticket.priority
    +
    The priority of this ticket (1-5, Integer only)
    + +
    ticket.get_priority_display
    +
    The priority of this ticket (Text-based)
    + +
    ticket.last_escalation
    +
    The time/date this ticket was last escalated
    + +
    ticket.ticket
    +
    The ticket ID in form '[queue_slug-id]', used in e-mail subjects.
    + +
    ticket.ticket_for_url
    +
    The ticket ID in form 'queue_slug-id', used in URLs
    + +
    ticket.ticket_url
    +
    Public URL for this ticket
    + +
    ticket.staff_url
    +
    Staff-only URL for this ticket
    + +
    ticket.assigned_to
    +
    Name of the staff member assigned to this ticket, or 'Unassigned' if it is unassigned.
    + +
    ticket.queue
    +
    Another dictionary of queue information, identical to queue above. For example, use ticket.queue.title.
    +
    + +{% endblock %} diff --git a/templates/helpdesk/ticket.html b/templates/helpdesk/ticket.html index 488aba0d..1f40a95f 100644 --- a/templates/helpdesk/ticket.html +++ b/templates/helpdesk/ticket.html @@ -116,7 +116,7 @@
    -
    {% trans "You can use the {{ ticket }} and {{ queue }} template variables in your message." %}
    +
    {% trans "You can insert ticket and queue details in your message. For more information, see the context help page." %}
    {% ifequal ticket.status 1 %} diff --git a/urls.py b/urls.py index a4b3d377..18020799 100644 --- a/urls.py +++ b/urls.py @@ -91,11 +91,6 @@ urlpatterns += patterns('', 'helpdesk.views.api.api', name='helpdesk_api'), - url(r'^api/$', - 'django.views.generic.simple.direct_to_template', - {'template': 'helpdesk/api_help.html',}, - name='helpdesk_api_help'), - url(r'^login/$', 'django.contrib.auth.views.login', name='login'), @@ -118,3 +113,15 @@ urlpatterns += patterns('helpdesk.views.kb', url(r'^kb/(?P[0-9]+)/vote/$', 'vote', name='helpdesk_kb_vote'), ) + +urlpatterns += patterns('', + url(r'^api/$', + 'django.views.generic.simple.direct_to_template', + {'template': 'helpdesk/help_api.html',}, + name='helpdesk_api_help'), + + url(r'^help/context/$', + 'django.views.generic.simple.direct_to_template', + {'template': 'helpdesk/help_context.html',}, + name='helpdesk_help_context'), +) diff --git a/views/api.py b/views/api.py index d74f183f..a5ea1f71 100644 --- a/views/api.py +++ b/views/api.py @@ -8,7 +8,7 @@ api.py - Wrapper around API calls, and core functions to provide complete The API documentation can be accessed by visiting http://helpdesk/api/help/ (obviously, substitute helpdesk for your Jutda Helpdesk URI), or by reading -through templates/helpdesk/api_help.html. +through templates/helpdesk/help_api.html. """ from datetime import datetime @@ -47,7 +47,7 @@ def api(request, method): """ if method == 'help': - return render_to_response('helpdesk/api_help.html') + return render_to_response('helpdesk/help_api.html') if request.method != 'POST': return api_return(STATUS_ERROR_BADMETHOD) diff --git a/views/staff.py b/views/staff.py index ed590e06..2b0acfa0 100644 --- a/views/staff.py +++ b/views/staff.py @@ -21,7 +21,7 @@ from django.template import loader, Context, RequestContext from django.utils.translation import ugettext as _ from helpdesk.forms import TicketForm -from helpdesk.lib import send_templated_mail, line_chart, bar_chart, query_to_dict, apply_query +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 @@ -133,6 +133,12 @@ def update_ticket(request, ticket_id): owner = int(request.POST.get('owner', None)) priority = int(request.POST.get('priority', ticket.priority)) + # We need to allow the 'ticket' and 'queue' contexts to be applied to the + # comment. + from django.template import loader, Context + context = Context(safe_template_context(ticket)) + comment = loader.get_template_from_string(comment).render(context) + if not owner and ticket.assigned_to: owner = ticket.assigned_to.id @@ -284,6 +290,8 @@ def ticket_list(request): 'other_filter': None, } + from_saved_query = False + if request.GET.get('saved_query', None): from_saved_query = True try: @@ -295,8 +303,21 @@ def ticket_list(request): import base64, cPickle query_params = cPickle.loads(base64.urlsafe_b64decode(str(saved_query.query))) + elif not ( request.GET.has_key('queue') + or request.GET.has_key('assigned_to') + or request.GET.has_key('status') + or request.GET.has_key('q') + or request.GET.has_key('sort') ): + + # Fall-back if no querying is being done, force the list to only + # show open/reopened/resolved (not closed) cases sorted by creation + # date. + + query_params = { + 'filtering': {'status__in': [1, 2, 3]}, + 'sorting': 'created', + } else: - from_saved_query = False queues = request.GET.getlist('queue') if queues: queues = [int(q) for q in queues]