{% trans "We have listed a number of knowledgebase articles for your perusal in the following categories. Please check to see if any of these articles address your problem prior to opening a support ticket." %}
{% trans "We give our users an opportunity to vote for items that they believe have helped them out, in order for us to better serve future customers. We would appreciate your feedback on this article. Did you find it useful?" %}
diff --git a/templates/helpdesk/public_view_form.html b/templates/helpdesk/public_view_form.html
index b53f1c39..f24c5001 100644
--- a/templates/helpdesk/public_view_form.html
+++ b/templates/helpdesk/public_view_form.html
@@ -16,7 +16,7 @@
-
+
diff --git a/templates/helpdesk/ticket.html b/templates/helpdesk/ticket.html
index 891653f1..ffdc03af 100644
--- a/templates/helpdesk/ticket.html
+++ b/templates/helpdesk/ticket.html
@@ -93,7 +93,7 @@
{% if followup.comment %}{{ followup.comment|num_to_link|safe }}{% endif %}
{% for change in followup.ticketchange_set.all %}
{% if forloop.first %}
{% endif %}
-
{% blocktrans %}Changed {{ change.field }} from {{ change.old_value }} to {{ change.new_value }}.{% endblocktrans %}
+
{% blocktrans with change.field as field and change.old_value as old_value and change.new_value as new_value %}Changed {{ field }} from {{ old_value }} to {{ new_value }}.{% endblocktrans %}
{% if forloop.last %}
{% endif %}
{% endfor %}
{% for attachment in followup.attachment_set.all %}{% if forloop.first %}
{% endif %}
@@ -116,7 +116,7 @@
-
{% trans "You can use the ticket and queue template variables in your message." %}
+
{% trans "You can use the {{ ticket }} and {{ queue }} template variables in your message." %}
{% ifequal ticket.status 1 %}
diff --git a/urls.py b/urls.py
index 5f862c16..8ba2d1e3 100644
--- a/urls.py
+++ b/urls.py
@@ -11,14 +11,14 @@ from django.conf.urls.defaults import *
from django.contrib.auth.decorators import login_required
-from feeds import feed_setup
+from helpdesk.views.feeds import feed_setup
from django.contrib.syndication.views import feed as django_feed
-urlpatterns = patterns('helpdesk.views',
- url(r'^$',
+urlpatterns = patterns('helpdesk.views.staff',
+ url(r'^dashboard/$',
'dashboard',
- name='helpdesk_home'),
+ name='helpdesk_dashboard'),
url(r'^tickets/$',
'ticket_list',
@@ -51,10 +51,6 @@ urlpatterns = patterns('helpdesk.views',
url(r'^raw/(?P\w+)/$',
'raw_details',
name='helpdesk_raw'),
-
- url(r'^view/$',
- 'public_view',
- name='helpdesk_public_view'),
url(r'^rss/$',
'rss_list',
@@ -69,16 +65,24 @@ urlpatterns = patterns('helpdesk.views',
name='helpdesk_run_report'),
)
+urlpatterns += patterns('helpdesk.views.public',
+ url(r'^$',
+ 'homepage',
+ name='helpdesk_home'),
+
+ url(r'^view/$',
+ 'view_ticket',
+ name='helpdesk_public_view'),
+)
+
urlpatterns += patterns('',
url(r'^rss/(?P.*)/$',
login_required(django_feed),
{'feed_dict': feed_setup},
name='helpdesk_rss'),
-)
-
-urlpatterns += patterns('',
+
url(r'^api/(?P[a-z_-]+)/$',
- 'helpdesk.api.api',
+ 'helpdesk.views.api.api',
name='helpdesk_api'),
url(r'^api/$',
@@ -94,3 +98,17 @@ urlpatterns += patterns('',
'django.contrib.auth.views.logout',
name='logout'),
)
+
+urlpatterns += patterns('helpdesk.views.kb',
+ url(r'^kb/$',
+ 'index', name='helpdesk_kb_index'),
+
+ url(r'^kb/(?P[A-Za-z_-]+)/$',
+ 'category', name='helpdesk_kb_category'),
+
+ url(r'^kb/(?P[0-9]+)/$',
+ 'item', name='helpdesk_kb_item'),
+
+ url(r'^kb/(?P[0-9]+)/vote/$',
+ 'vote', name='helpdesk_kb_vote'),
+)
diff --git a/views/__init__.py b/views/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/api.py b/views/api.py
similarity index 100%
rename from api.py
rename to views/api.py
diff --git a/feeds.py b/views/feeds.py
similarity index 98%
rename from feeds.py
rename to views/feeds.py
index c2ecc954..7f40a8c1 100644
--- a/feeds.py
+++ b/views/feeds.py
@@ -4,7 +4,7 @@ from django.core.urlresolvers import reverse
from django.db.models import Q
from django.utils.translation import ugettext as _
-from models import Ticket, FollowUp, Queue
+from helpdesk.models import Ticket, FollowUp, Queue
class OpenTicketsByUser(Feed):
diff --git a/views/kb.py b/views/kb.py
new file mode 100644
index 00000000..4183d5c7
--- /dev/null
+++ b/views/kb.py
@@ -0,0 +1,52 @@
+"""
+Jutda Helpdesk - A Django powered ticket tracker for small enterprise.
+
+(c) Copyright 2008 Jutda. All Rights Reserved. See LICENSE for details.
+
+views/kb.py - Public-facing knowledgebase views. The knowledgebase is a
+ simple categorised question/answer system to show common
+ resolutions to common problems.
+"""
+from datetime import datetime
+
+from django.http import HttpResponseRedirect
+from django.shortcuts import render_to_response, get_object_or_404
+from django.template import RequestContext
+from django.utils.translation import ugettext as _
+
+from helpdesk.models import KBCategory, KBItem
+
+def index(request):
+ category_list = KBCategory.objects.all()
+ # Add most popular items here.
+ return render_to_response('helpdesk/kb_index.html',
+ RequestContext(request, {
+ 'categories': category_list,
+ }))
+
+def category(request, slug):
+ category = get_object_or_404(KBCategory, slug__iexact=slug)
+ items = category.kbitem_set.all()
+ return render_to_response('helpdesk/kb_category.html',
+ RequestContext(request, {
+ 'category': category,
+ 'items': items,
+ }))
+
+def item(request, item):
+ item = get_object_or_404(KBItem, pk=item)
+ return render_to_response('helpdesk/kb_item.html',
+ RequestContext(request, {
+ 'item': item,
+ }))
+
+def vote(request, item):
+ item = get_object_or_404(KBItem, pk=item)
+ vote = request.GET.get('vote', None)
+ if vote in ('up', 'down'):
+ item.votes += 1
+ if vote == 'up':
+ item.recommendations += 1
+ item.save()
+
+ return HttpResponseRedirect(item.get_absolute_url())
diff --git a/views/public.py b/views/public.py
new file mode 100644
index 00000000..66312da9
--- /dev/null
+++ b/views/public.py
@@ -0,0 +1,60 @@
+"""
+Jutda Helpdesk - A Django powered ticket tracker for small enterprise.
+
+(c) Copyright 2008 Jutda. All Rights Reserved. See LICENSE for details.
+
+views/public.py - All public facing views, eg non-staff (no authentication
+ required) views.
+"""
+from datetime import datetime
+
+from django.core.urlresolvers import reverse
+from django.http import HttpResponseRedirect, Http404, HttpResponse
+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 PublicTicketForm
+from helpdesk.lib import send_templated_mail
+from helpdesk.models import Ticket, Queue
+
+def homepage(request):
+ if request.user.is_authenticated():
+ return HttpResponseRedirect(reverse('helpdesk_dashboard'))
+
+ if request.method == 'POST':
+ form = PublicTicketForm(request.POST)
+ form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.filter(allow_public_submission=True)]
+ if form.is_valid():
+ ticket = form.save()
+ return HttpResponseRedirect('%s?ticket=%s&email=%s'% (reverse('helpdesk_public_view'), ticket.ticket_for_url, ticket.submitter_email))
+ else:
+ form = PublicTicketForm()
+ form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.filter(allow_public_submission=True)]
+
+ return render_to_response('helpdesk/public_homepage.html',
+ RequestContext(request, {
+ 'form': form,
+ }))
+
+def view_ticket(request):
+ ticket = request.GET.get('ticket', '')
+ email = request.GET.get('email', '')
+ error_message = ''
+
+ if ticket and email:
+ try:
+ queue, ticket_id = ticket.split('-')
+ t = Ticket.objects.get(id=ticket_id, queue__slug__iexact=queue, submitter_email__iexact=email)
+ return render_to_response('helpdesk/public_view_ticket.html',
+ RequestContext(request, {'ticket': t,}))
+ except:
+ t = False;
+ error_message = _('Invalid ticket ID or e-mail address. Please try again.')
+
+ return render_to_response('helpdesk/public_view_form.html',
+ RequestContext(request, {
+ 'ticket': ticket,
+ 'email': email,
+ 'error_message': error_message,
+ }))
diff --git a/views.py b/views/staff.py
similarity index 83%
rename from views.py
rename to views/staff.py
index ef972f48..cbae660c 100644
--- a/views.py
+++ b/views/staff.py
@@ -3,8 +3,8 @@ Jutda Helpdesk - A Django powered ticket tracker for small enterprise.
(c) Copyright 2008 Jutda. All Rights Reserved. See LICENSE for details.
-views.py - The bulk of the application - provides most business logic and
- renders all user-facing views.
+views/staff.py - The bulk of the application - provides most business logic and
+ renders all staff-facing views.
"""
from datetime import datetime
@@ -17,57 +17,42 @@ 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, PublicTicketForm
+from helpdesk.forms import TicketForm
from helpdesk.lib import send_templated_mail, line_chart, bar_chart, query_to_dict
from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment
def dashboard(request):
"""
- This isn't always truly a "dashboard" view. If the user is not logged in,
- we instead show the user a "public submission" form and a way to view
- existing tickets.
+ A quick summary overview for users: A list of their own tickets, a table
+ showing ticket counts by queue/status, and a list of unassigned tickets
+ with options for them to 'Take' ownership of said tickets.
"""
- if request.user.is_authenticated():
- 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)
- from django.db import connection
- cursor = connection.cursor()
- cursor.execute("""
- SELECT q.id as queue,
- q.title AS name,
- COUNT(CASE t.status WHEN '1' THEN t.id WHEN '2' THEN t.id END) AS open,
- COUNT(CASE t.status WHEN '3' THEN t.id END) AS resolved
- FROM helpdesk_ticket t,
- helpdesk_queue q
- WHERE q.id = t.queue_id
- GROUP BY queue, name
- ORDER BY q.id;
- """)
- dash_tickets = query_to_dict(cursor.fetchall(), cursor.description)
-
- return render_to_response('helpdesk/dashboard.html',
- RequestContext(request, {
- 'user_tickets': tickets,
- 'unassigned_tickets': unassigned_tickets,
- 'dash_tickets': dash_tickets,
- }))
- else:
- # Not a logged in user
- if request.method == 'POST':
- form = PublicTicketForm(request.POST)
- form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.all()]
- if form.is_valid():
- ticket = form.save()
- return HttpResponseRedirect('%s?ticket=%s&email=%s'% (reverse('helpdesk_public_view'), ticket.ticket_for_url, ticket.submitter_email))
- else:
- form = PublicTicketForm()
- form.fields['queue'].choices = [('', '--------')] + [[q.id, q.title] for q in Queue.objects.all()]
+ 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)
- return render_to_response('helpdesk/public_homepage.html',
- RequestContext(request, {
- 'form': form,
- }))
+ from django.db import connection
+ cursor = connection.cursor()
+ cursor.execute("""
+ SELECT q.id as queue,
+ q.title AS name,
+ COUNT(CASE t.status WHEN '1' THEN t.id WHEN '2' THEN t.id END) AS open,
+ COUNT(CASE t.status WHEN '3' THEN t.id END) AS resolved
+ FROM helpdesk_ticket t,
+ helpdesk_queue q
+ WHERE q.id = t.queue_id
+ GROUP BY queue, name
+ ORDER BY q.id;
+ """)
+ dash_tickets = query_to_dict(cursor.fetchall(), cursor.description)
+
+ 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 delete_ticket(request, ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
@@ -303,28 +288,6 @@ def raw_details(request, type):
raise Http404
raw_details = login_required(raw_details)
-def public_view(request):
- ticket = request.GET.get('ticket', '')
- email = request.GET.get('email', '')
- error_message = ''
-
- if ticket and email:
- queue, ticket_id = ticket.split('-')
- try:
- t = Ticket.objects.get(id=ticket_id, queue__slug__iexact=queue, submitter_email__iexact=email)
- return render_to_response('helpdesk/public_view_ticket.html',
- RequestContext(request, {'ticket': t,}))
- except:
- t = False;
- error_message = _('Invalid ticket ID or e-mail address. Please try again.')
-
- return render_to_response('helpdesk/public_view_form.html',
- RequestContext(request, {
- 'ticket': ticket,
- 'email': email,
- 'error_message': error_message,
- }))
-
def hold_ticket(request, ticket_id, unhold=False):
ticket = get_object_or_404(Ticket, id=ticket_id)