Thanks for being here. Hopefully you've helped resolve a few tickets and make the world a better place.
8 |
9 | {% endblocktrans %}{% endblock %}
10 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | django-helpdesk was originally written by Ross Poulton. Since publishing the
2 | code a number of people have made some fantastic improvements and provided
3 | bug fixes and updates as the Django codebase has moved on and caused small
4 | portions of this application to break.
5 |
6 | To these people, and any more, my sincere thanks:
7 |
8 | Andreas Kotowicz
9 | Chris Etcp
10 | David Clymer
11 | Loe Spee
12 | Maxim Litnitskiy
13 | Nikolay Panov
14 |
--------------------------------------------------------------------------------
/helpdesk/poll_helpdesk_email_queues.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 |
4 | # don't forget to add this script to the /etc/crontab:
5 | #
6 | # */1 * * * * username /home/username/django/project/poll_helpdesk_email_queues.sh >> /tmp/foo.log 2>&1
7 |
8 | # set your django and project paths here
9 | PATHTODJANGO="/home/username/django/libraries/lib/python"
10 | PATHTOPROJECT="/home/username/django/project/"
11 |
12 |
13 | export PYTHONPATH=$PYTHONPATH:$PATHTODJANGO:$PATHTOPROJECT:
14 |
15 | cd $PATHTOPROJECT
16 | /usr/bin/python manage.py get_email
17 |
18 |
19 |
--------------------------------------------------------------------------------
/helpdesk/templates/helpdesk/ticket_dependency_del.html:
--------------------------------------------------------------------------------
1 | {% extends "helpdesk/base.html" %}{% load i18n %}{% load url from future %}
2 |
3 | {% block helpdesk_title %}{% trans "Delete Ticket Dependency" %}{% endblock %}
4 |
5 | {% block helpdesk_body %}{% blocktrans %}
6 |
Delete Ticket Dependency
7 |
8 |
Are you sure you wish to remove the dependency on this ticket?
12 |
13 |
14 | {% endblock %}
15 |
--------------------------------------------------------------------------------
/docs/custom_fields.rst:
--------------------------------------------------------------------------------
1 | Custom Fields
2 | =============
3 |
4 | django-helpdesk supports custom fields on the ``Ticket`` model. These fields are created by using the Django administration tool, and are shown on both the public and staff submission forms. You can use most Django field types including text, integer, boolean, and list.
5 |
6 | The demo at http://django-helpdesk-demo.herokuapp.com contains an example of each type of custom field, including a mix of mandatory and optional fields.
7 |
8 | Custom fields are relatively inefficient, and you cannot search by them. They can be useful for tracking extra information that your organisation needs but that isn't supported out of the box.
9 |
--------------------------------------------------------------------------------
/helpdesk/templates/helpdesk/ticket_cc_del.html:
--------------------------------------------------------------------------------
1 | {% extends "helpdesk/base.html" %}{% load i18n %}{% load url from future %}
2 |
3 | {% block helpdesk_title %}{% trans "Delete Ticket CC" %}{% endblock %}
4 |
5 | {% block helpdesk_body %}{% blocktrans with cc.email_address as email_address %}
6 |
Delete Ticket CC
7 |
8 |
Are you sure you wish to delete this email address ({{ email_address }}) from the CC list for this ticket? They will stop receiving updates.
{% blocktrans with ticket.title as ticket_title %}Are you sure you want to delete this ticket ({{ ticket_title }})? All traces of the ticket, including followups, attachments, and updates will be irreversibly removed.{% endblocktrans %}
{% blocktrans %}Use the following options to change the way your helpdesk system works for you. These settings do not impact any other user.{% endblocktrans %}
9 |
10 |
17 |
18 | {% endblock %}
19 |
--------------------------------------------------------------------------------
/helpdesk/templatetags/user_admin_url.py:
--------------------------------------------------------------------------------
1 | """
2 | django-helpdesk - A Django powered ticket tracker for small enterprise.
3 |
4 | (c) Copyright 2008 Jutda. All Rights Reserved. See LICENSE for details.
5 |
6 | templatetags/admin_url.py - Very simple template tag allow linking to the
7 | right auth user model urls.
8 |
9 | {% url 'changelist'|user_admin_url %}
10 | """
11 |
12 | from django import template
13 | from django.contrib.auth import get_user_model
14 |
15 | def user_admin_url(action):
16 | user = get_user_model()
17 | return 'admin:%s_%s_%s' % (
18 | user._meta.app_label, user._meta.module_name.lower(),
19 | action)
20 |
21 | register = template.Library()
22 | register.filter(user_admin_url)
23 |
--------------------------------------------------------------------------------
/helpdesk/templates/helpdesk/public_spam.html:
--------------------------------------------------------------------------------
1 | {% extends "helpdesk/public_base.html" %}{% load i18n %}
2 |
3 | {% block helpdesk_body %}
4 |
{% trans "Unable To Open Ticket" %}
5 |
{% trans "Sorry, but there has been an error trying to submit your ticket." %}
6 |
{% blocktrans %}Our system has marked your submission as spam, so we are unable to save it. If this is not spam, please press back and re-type your message. Be careful to avoid sounding 'spammy', and if you have heaps of links please try removing them if possible.{% endblocktrans %}
7 |
{% blocktrans %}We are sorry for any inconvenience, however this check is required to avoid our helpdesk resources being overloaded by spammers.{% endblocktrans %}
8 | {% endblock helpdesk_body %}
9 |
--------------------------------------------------------------------------------
/helpdesk/templatetags/in_list.py:
--------------------------------------------------------------------------------
1 | """
2 | django-helpdesk - A Django powered ticket tracker for small enterprise.
3 |
4 | (c) Copyright 2008 Jutda. All Rights Reserved. See LICENSE for details.
5 |
6 | templatetags/in_list.py - Very simple template tag to allow us to use the
7 | equivilent of 'if x in y' in templates. eg:
8 |
9 | Assuming 'food' = 'pizza' and 'best_foods' = ['pizza', 'pie', 'cake]:
10 |
11 | {% if food|in_list:best_foods %}
12 | You've selected one of our favourite foods!
13 | {% else %}
14 | Your food isn't one of our favourites.
15 | {% endif %}
16 | """
17 |
18 | from django import template
19 |
20 | def in_list(value, arg):
21 | return value in ( arg or [] )
22 |
23 | register = template.Library()
24 | register.filter(in_list)
25 |
--------------------------------------------------------------------------------
/helpdesk/templates/helpdesk/ru/email_html_base.html:
--------------------------------------------------------------------------------
1 |
{% blocktrans with ignore.email_address as email_address %}Are you sure you wish to stop removing this email address ({{ email_address }}) and allow their e-mails to automatically create tickets in your system? You can re-add this e-mail address at any time.{% endblocktrans %}
This e-mail was sent to you as a user of our support service, in accordance with our privacy policy. Please advise us if you believe you have received this e-mail in error.
{% 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." %}
7 |
8 |
9 |
{% trans "Knowledgebase Categories" %}
10 |
11 |
{% trans "Category" %}
12 |
13 |
14 | {% for category in kb_categories %}
15 |
Diese E-Mail wurde, im Einklang mit unserer Privacy-Policy an Sie als Benutzer unseres Support-Dienstes gesendet. Bitte teilen Sie uns mit wenn Sie diese E-Mail nicht hätten bekommen sollen.
Ce courriel vous a été envoyé en tant qu'utilisateur de notre service de support, en accord avec notre politique de confidentialité. Merci de nous informer si vous pensez que ce message ne vous était pas destiné.
Questa email vi è stata inviata in quanto utenti del nostro servizio di supporto clienti, in conformità con la nostra policy sulla privacy. Vi preghiamo di informarci se pensate di aver ricevuto questa email per errore.
{% blocktrans with query.title as query_title %}Are you sure you want to delete this saved filter ({{ query_title }})? To re-create it, you will need to manually re-filter your ticket listing.{% endblocktrans %}
9 |
10 | {% if query.shared %}
11 |
{% blocktrans %}You have shared this query, so other users may be using it. If you delete it, they will have to manually create their own query.{% endblocktrans %}
{% trans "To log in simply enter your username and password below." %}
15 |
16 |
27 |
28 |
29 | {% endif %}
30 | {% endblock %}
31 |
--------------------------------------------------------------------------------
/docs/spam.rst:
--------------------------------------------------------------------------------
1 | Spam Filtering
2 | ==============
3 |
4 | django-helpdesk includes a copy of ``akismet.py`` by `Michael Foord `_, which lets incoming ticket submissions be automatically checked against either the `Akismet `_ or `TypePad Anti-Spam `_ services.
5 |
6 | To enable this functionality, sign up for an API key with one of these two services.
7 |
8 | Akismet
9 | ~~~~~~~
10 |
11 | * Sign up at http://akismet.com/
12 | * Save your API key in ``settings.py`` as ``AKISMET_API_KEY``
13 |
14 | **Note**: Akismet is only free for personal use. Paid commercial accounts are available.
15 |
16 | TypePad AntiSpam
17 | ~~~~~~~~~~~~~~~~
18 | * Sign up at http://antispam.typepad.com/
19 | * Save your API key in ``settings.py`` as ``TYPEPAD_ANTISPAM_API_KEY``
20 |
21 | This service is free to use, within their terms and conditions.
22 |
23 | If you have either of these settings enabled, the spam filtering will be done automatically. If you have *both* settings configured, TypePad will be used instead of Akismet.
24 |
25 |
26 | Example
27 | ~~~~~~~
28 |
29 | A sample configuration in ``settings.py`` may be::
30 |
31 | TYPEPAD_ANTISPAM_API_KEY = 'abc123'
32 |
33 |
--------------------------------------------------------------------------------
/helpdesk/templates/helpdesk/help_base.html:
--------------------------------------------------------------------------------
1 | {% load url from future %}
2 |
3 |
4 |
40 | {% block title %}django-helpdesk Help{% endblock %}
41 |
42 |
43 |
{% blocktrans %}To ignore an e-mail address and prevent any emails from that address creating tickets automatically, enter the e-mail address below.{% endblocktrans %}
9 |
10 |
{% blocktrans %}You can either enter a whole e-mail address such as email@domain.com or a portion of an e-mail address with a wildcard, such as *@domain.com or user@*.{% endblocktrans %}
- {% trans "Average number of days until ticket is closed (all tickets): " %}{{ basic_ticket_stats.average_nbr_days_until_ticket_closed }}.
6 |
- {% trans "Average number of days until ticket is closed (tickets opened in last 60 days): " %}{{ basic_ticket_stats.average_nbr_days_until_ticket_closed_last_60_days }}.
7 | {% trans "Click" %} here {% trans "for detailed average by month." %}
8 |
- {% trans "Distribution of open tickets, grouped by time period:" %}
9 |
{% trans "Days since opened" %}
{% trans "Number of open tickets" %}
10 |
11 |
12 | {% for entry in basic_ticket_stats.open_ticket_stats %}
13 |
{% trans "Welcome to your Helpdesk Dashboard! From here you can quickly see tickets submitted by you, tickets you are working on, and those tickets that have no owner." %}
The following people will receive an e-mail whenever {{ ticket_title }} is updated. Some people can also view or edit the ticket via the public ticket views.
The following e-mail addresses are currently being ignored by the incoming e-mail processor. You can add a new e-mail address to the list or delete any of the items below as required.
{% endblocktrans %}
9 |
10 |
11 |
{% trans "Ignored E-Mail Addresses" %}
12 |
13 |
{% trans "Name" %}
{% trans "E-Mail Address" %}
{% trans "Date Added" %}
{% trans "Queues" %}
{% trans "Keep in mailbox?" %}
{% trans "Delete" %}
14 |
15 |
16 | {% for ignore in ignore_list %}
17 |
18 |
{{ ignore.name }}
19 |
{{ ignore.email_address }}
20 |
{{ ignore.date }}
21 |
{% for queue in ignore.queues.all %}{{ queue.slug }}{% if not forloop.last %}, {% endif %}{% empty %}{% trans "All" %}{% endfor %}
22 |
{% if ignore.keep_in_mailbox %}{% trans "Keep" %}{% endif %}
{% 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?" %}
{% trans "Unless otherwise stated, all fields are required." %} {% trans "Please provide as descriptive a title and description as possible." %}
12 |
13 |
{% trans "Note" %}: {% blocktrans %}Editing a ticket does not send an e-mail to the ticket owner or submitter. No new details should be entered, this form should only be used to fix incorrect details or clean up the submission.{% endblocktrans %}
14 |
15 |
37 |
38 |
39 |
40 | {% endblock %}
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2008, Ross Poulton (Trading as Jutda)
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without modification,
5 | are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 |
10 | 2. Redistributions in binary form must reproduce the above copyright
11 | notice, this list of conditions and the following disclaimer in the
12 | documentation and/or other materials provided with the distribution.
13 |
14 | 3. Neither the name of Ross Poulton, Jutda, nor the names of any
15 | of its contributors may be used to endorse or promote products
16 | derived from this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
29 | EXCEPTIONS
30 |
31 | This software is distributed with some third-party software which is not distributed under the above license. See LICENSE.3RDPARTY for further details.
32 |
--------------------------------------------------------------------------------
/helpdesk/templatetags/ticket_to_link.py:
--------------------------------------------------------------------------------
1 | """
2 | django-helpdesk - A Django powered ticket tracker for small enterprise.
3 |
4 | (c) Copyright 2008 Jutda. All Rights Reserved. See LICENSE for details.
5 |
6 | templatetags/ticket_to_link.py - Used in ticket comments to allow wiki-style
7 | linking to other tickets. Including text such
8 | as '#3180' in a comment automatically links
9 | that text to ticket number 3180, with styling
10 | to show the status of that ticket (eg a closed
11 | ticket would have a strikethrough).
12 | """
13 |
14 | import re
15 |
16 | from django import template
17 | from django.core.urlresolvers import reverse
18 | from django.utils.safestring import mark_safe
19 |
20 | from helpdesk.models import Ticket
21 |
22 |
23 | class ReverseProxy:
24 | def __init__(self, sequence):
25 | self.sequence = sequence
26 |
27 | def __iter__(self):
28 | length = len(self.sequence)
29 | i = length
30 | while i > 0:
31 | i = i - 1
32 | yield self.sequence[i]
33 |
34 |
35 | def num_to_link(text):
36 | if text == '':
37 | return text
38 |
39 | matches = []
40 | for match in re.finditer(r"(?:[^&]|\b|^)#(\d+)\b", text):
41 | matches.append(match)
42 |
43 | for match in ReverseProxy(matches):
44 | start = match.start()
45 | end = match.end()
46 | number = match.groups()[0]
47 | url = reverse('helpdesk_view', args=[number])
48 | try:
49 | ticket = Ticket.objects.get(id=number)
50 | except Ticket.DoesNotExist:
51 | ticket = None
52 |
53 | if ticket:
54 | style = ticket.get_status_display()
55 | text = "%s #%s%s" % (text[:match.start()], url, style, match.groups()[0], text[match.end():])
56 | return mark_safe(text)
57 |
58 | register = template.Library()
59 | register.filter(num_to_link)
60 |
--------------------------------------------------------------------------------
/helpdesk/views/kb.py:
--------------------------------------------------------------------------------
1 | """
2 | django-helpdesk - A Django powered ticket tracker for small enterprise.
3 |
4 | (c) Copyright 2008 Jutda. All Rights Reserved. See LICENSE for details.
5 |
6 | views/kb.py - Public-facing knowledgebase views. The knowledgebase is a
7 | simple categorised question/answer system to show common
8 | resolutions to common problems.
9 | """
10 |
11 | from datetime import datetime
12 |
13 | from django.http import HttpResponseRedirect
14 | from django.shortcuts import render_to_response, get_object_or_404
15 | from django.template import RequestContext
16 | from django.utils.translation import ugettext as _
17 |
18 | from helpdesk import settings as helpdesk_settings
19 | from helpdesk.models import KBCategory, KBItem
20 |
21 |
22 | def index(request):
23 | category_list = KBCategory.objects.all()
24 | # TODO: It'd be great to have a list of most popular items here.
25 | return render_to_response('helpdesk/kb_index.html',
26 | RequestContext(request, {
27 | 'kb_categories': category_list,
28 | 'helpdesk_settings': helpdesk_settings,
29 | }))
30 |
31 |
32 | def category(request, slug):
33 | category = get_object_or_404(KBCategory, slug__iexact=slug)
34 | items = category.kbitem_set.all()
35 | return render_to_response('helpdesk/kb_category.html',
36 | RequestContext(request, {
37 | 'category': category,
38 | 'items': items,
39 | 'helpdesk_settings': helpdesk_settings,
40 | }))
41 |
42 |
43 | def item(request, item):
44 | item = get_object_or_404(KBItem, pk=item)
45 | return render_to_response('helpdesk/kb_item.html',
46 | RequestContext(request, {
47 | 'item': item,
48 | 'helpdesk_settings': helpdesk_settings,
49 | }))
50 |
51 |
52 | def vote(request, item):
53 | item = get_object_or_404(KBItem, pk=item)
54 | vote = request.GET.get('vote', None)
55 | if vote in ('up', 'down'):
56 | item.votes += 1
57 | if vote == 'up':
58 | item.recommendations += 1
59 | item.save()
60 |
61 | return HttpResponseRedirect(item.get_absolute_url())
62 |
63 |
--------------------------------------------------------------------------------
/helpdesk/static/helpdesk/jquery.jqplot/plugins/jqplot.trendline.min.js:
--------------------------------------------------------------------------------
1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com
2 | jsDate | (c) 2010-2013 Chris Leonello
3 | */(function(f){f.jqplot.Trendline=function(){this.show=f.jqplot.config.enablePlugins;this.color="#666666";this.renderer=new f.jqplot.LineRenderer();this.rendererOptions={marker:{show:false}};this.label="";this.type="linear";this.shadow=true;this.markerRenderer={show:false};this.lineWidth=1.5;this.shadowAngle=45;this.shadowOffset=1;this.shadowAlpha=0.07;this.shadowDepth=3;this.isTrendline=true};f.jqplot.postSeriesInitHooks.push(e);f.jqplot.postDrawSeriesHooks.push(g);f.jqplot.addLegendRowHooks.push(a);function a(k){var j=null;if(k.trendline&&k.trendline.show){var i=k.trendline.label.toString();if(i){j={label:i,color:k.trendline.color}}}return j}function e(m,k,j,i,l){if(this._type&&(this._type==="line"||this._type=="bar")){this.trendline=new f.jqplot.Trendline();i=i||{};f.extend(true,this.trendline,{color:this.color},j.trendline,i.trendline);this.trendline.renderer.init.call(this.trendline,null)}}function g(m,i){i=f.extend(true,{},this.trendline,i);if(this.trendline&&i.show){var k;var l=i.data||this.data;k=c(l,this.trendline.type);var j=i.gridData||this.renderer.makeGridData.call(this,k.data);this.trendline.renderer.draw.call(this.trendline,m,j,{showLine:true,shadow:this.trendline.shadow})}}function b(w,v,n){var u=(n==null)?"linear":n;var s=w.length;var t;var z;var o=0;var m=0;var r=0;var q=0;var l=0;var j=[];var k=[];if(u=="linear"){k=w;j=v}else{if(u=="exp"||u=="exponential"){for(var p=0;p{% trans "RSS Feeds" %}
5 |
6 |
{% trans "The following RSS feeds are available for you to monitor using your preferred RSS software. With the exception of the 'Latest Activity' feed, all feeds provide information only on Open and Reopened cases. This ensures your RSS reader isn't full of information about closed or historical tasks." %}
{% trans "All unassigned tickets - useful for being alerted to new tickets opened by the public via the web or via e-mail" %}
17 |
18 |
19 |
{% trans "These RSS feeds allow you to view a summary of either your own tickets, or all tickets, for each of the queues in your helpdesk. For example, if you manage the staff who utilise a particular queue, this may be used to view new tickets coming into that queue." %}
20 |
21 |
22 |
{% trans "Per-Queue Feeds" %}
23 |
24 |
{% trans "Queue" %}
{% trans "All Open Tickets" %}
{% trans "My Open Tickets" %}
25 |
26 |
27 | {% for queue in queues %}
28 |
29 |
{{ queue.title }}
30 |
31 |
32 | {% endfor %}
33 |
34 |
35 | {% endblock %}
36 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | 2009-09-09 r138 Issue #104 Add a CHANGELOG file
2 |
3 | 2009-09-09 r139 Issue #102 Add Ticket CC's
4 | Add rudimentary CC: functionality on tickets, controlled by staff users. CC's
5 | can be e-mail addresses or users, who will receive copies of all emails sent
6 | to the Submitter. This is a work in progress.
7 |
8 | 2009-09-09 r140 Issue #13 Add Tags to tickets
9 | Patch courtesy of david@zettazebra.com, adds the ability to add tags to
10 | tickets if django-tagging is installed and in use. If django-tagging isn't
11 | being used, no change is visible to the user.
12 |
13 | 2009-10-13 r141 Issue #118 Incorrect locale handling on email templates
14 | Patch courtesy of hgeerts. Corrects the handling of locale on email
15 | templaets, defaulting to English if no locale is provided.
16 |
17 | 2009-10-13 r142 Issue #117 Incorrect I18N usage in a few spots
18 | Patch thanks to hgeerts.
19 |
20 | 2009-10-13 r143 Issue #113 - Clicking Queue names on the Dashboard was showing
21 | all tickets; now only shows open tickets. Thanks to Andreas Kotowicz for the
22 | sugestion.
23 |
24 | 2009-12-16 r144 Issue #122 - Infinite loop when most recent ticket was
25 | opened in December. Thanks to Chris Vigelius for the report.
26 |
27 | 2009-12-16 r145 issue #123 - Google Chart doesn't show when there is a large
28 | volume of data in the system. This patch restricts the chart to 1000px wide.
29 |
30 | 2009-12-16 r146 Issue #121 Formatting fix for email subjects. Thanks,
31 | Andreas Kotowicz.
32 |
33 | 2009-12-16 r147 Issue #119 Update Russian translation, thanks to Alex Yakovlev
34 |
35 | 2009-12-23 r148 Issue #125 Errors occurring when running reports with no data
36 |
37 | 2010-01-20 r149 Issue #126 Reports didn't work with transalations.
38 |
39 | 2010-01-20 r150 Issue #127 Add german transalation, courtesy of openinformation.org
40 |
41 | 2009-01-20 r151 Issue #128 If queue name has a dash in it, email imported failed. Thanks to enix.org for the patch.
42 |
43 | 2010-01-21 r152 Fix indentation error caused by issue #126.
44 |
45 | 2010-01-26 r153 Fix issue #129 - can not 'unassign' tickets. Thanks to
46 | lukeman for the patch.
47 |
48 | 2010-01-26 r154 Fix bug in the code from Issue #102 where TicketCC's couldn't
49 | be deleted. Thanks again to lukeman.
50 |
51 | 2010-01-31 r155 Fix bug caused by issue #129 - ticket followup titles being
52 | set incorrectly. Thanks to Lukeman for the fix.
53 |
54 | 2010-07-16 r157 Fix issues #141, #142 - IMAP infinite loops and ticket
55 | pagination issues. Thanks to Walter Doekes for the patches.
56 |
57 | 2010-07-16 r158 New CSRF functionality for Django 1.1+. Thanks to
58 | 'litchfield4' for the patch.
59 |
60 | 2010-09-04 r159 Error when updating multiple tickets. Issue #135.
61 |
62 | 2010-09-04 r160 Fix translation blocks in deletion templates. Some translation strings will need to be updated. Thanks to william88 for the bug report.
63 |
64 | 2010-09-04 r161 Fix jQuery filename in public templates. Thanks to bruno.braga for the fix.
65 |
--------------------------------------------------------------------------------
/docs/contributing.rst:
--------------------------------------------------------------------------------
1 | Contributing
2 | ============
3 |
4 | django-helpdesk is an open-source project and as such contributions from the community are welcomed and encouraged.
5 |
6 | Licensing
7 | ---------
8 |
9 | All contributions to django-helpdesk must be under the BSD license documented in our :doc:`license` page. All code submitted (in any way: via e-mail, via GitHub forks, attachments, etc) are assumed to be open-source and licensed under the BSD license.
10 |
11 | If you or your organisation does not accept these license terms then we cannot accept your contribution. Please reconsider!
12 |
13 | Translations
14 | ------------
15 |
16 | .. image:: http://www.transifex.net/projects/p/django-helpdesk/resource/core/chart/image_png
17 |
18 | Although django-helpdesk has originally been written for the English language, there are already multiple translations to Spanish, Polish, and German and more translations are welcomed.
19 |
20 | Translations are handled using the excellent Transifex service which is much easier for most users than manually editing .po files. It also allows collaborative translation. If you want to help translate django-helpdesk into languages other than English, we encourage you to make use of our Transifex project:
21 |
22 | http://www.transifex.net/projects/p/django-helpdesk/resource/core/
23 |
24 | Once you have translated content via Transifex, please raise an issue on the project Github page to let us know it's ready to import.
25 |
26 | Code changes
27 | ------------
28 |
29 | Please fork the project on GitHub, make your changes, and log a pull request to get the changes pulled back into my repository.
30 |
31 | Wherever possible please break git commits up into small chunks that are specific to a single bit of functionality. For example, a commit should not contain both new functionality *and* a bugfix; the new function and the bugfix should be separate commits wherever possible.
32 |
33 | Commit messages should also explain *what*, precisely, has been changed.
34 |
35 | If you have any questions, please contact the project co-ordinator, Ross Poulton, at ross@rossp.org.
36 |
37 | Tests
38 | -----
39 |
40 | Currently, test coverage is very low. We're working on increasing this, and to make life easier we are using `Travis CI`_ for continuous integration. This means that the test suite is run every time a code change is made, so we can try and make sure we avoid basic bugs and other regressions.
41 |
42 | Please include tests in the ``tests/`` folder when committing code changes.
43 |
44 | .. _Travis CI: http://travis-ci.org/
45 |
46 | Database schema changes
47 | -----------------------
48 |
49 | As well as making your normal code changes to ``models.py``, please generate a South migration file and commit it with your code. You will want to use a command similar to the following::
50 |
51 | ./manage.py migrate helpdesk --auto [migration_name]
52 |
53 | Make sure that ``migration_name`` is a sensible single-string explanation of what this migration does, such as *add_priority_options* or *add_basket_table*
54 |
55 | This will add a file to the ``migrations/`` folder, which must be committed to git with your other code changes.
56 |
--------------------------------------------------------------------------------
/helpdesk/static/helpdesk/jquery.jqplot/plugins/jqplot.canvasAxisTickRenderer.min.js:
--------------------------------------------------------------------------------
1 | /* jqPlot 1.0.8r1250 | (c) 2009-2013 Chris Leonello | jplot.com
2 | jsDate | (c) 2010-2013 Chris Leonello
3 | */(function(a){a.jqplot.CanvasAxisTickRenderer=function(b){this.mark="outside";this.showMark=true;this.showGridline=true;this.isMinorTick=false;this.angle=0;this.markSize=4;this.show=true;this.showLabel=true;this.labelPosition="auto";this.label="";this.value=null;this._styles={};this.formatter=a.jqplot.DefaultTickFormatter;this.formatString="";this.prefix="";this.fontFamily='"Trebuchet MS", Arial, Helvetica, sans-serif';this.fontSize="10pt";this.fontWeight="normal";this.fontStretch=1;this.textColor="#666666";this.enableFontSupport=true;this.pt2px=null;this._elem;this._ctx;this._plotWidth;this._plotHeight;this._plotDimensions={height:null,width:null};a.extend(true,this,b);var c={fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily};if(this.pt2px){c.pt2px=this.pt2px}if(this.enableFontSupport){if(a.jqplot.support_canvas_text()){this._textRenderer=new a.jqplot.CanvasFontRenderer(c)}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}}else{this._textRenderer=new a.jqplot.CanvasTextRenderer(c)}};a.jqplot.CanvasAxisTickRenderer.prototype.init=function(b){a.extend(true,this,b);this._textRenderer.init({fontSize:this.fontSize,fontWeight:this.fontWeight,fontStretch:this.fontStretch,fillStyle:this.textColor,angle:this.getAngleRad(),fontFamily:this.fontFamily})};a.jqplot.CanvasAxisTickRenderer.prototype.getWidth=function(d){if(this._elem){return this._elem.outerWidth(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.sin(f.angle)*e)+Math.abs(Math.cos(f.angle)*c);return b}};a.jqplot.CanvasAxisTickRenderer.prototype.getHeight=function(d){if(this._elem){return this._elem.outerHeight(true)}else{var f=this._textRenderer;var c=f.getWidth(d);var e=f.getHeight(d);var b=Math.abs(Math.cos(f.angle)*e)+Math.abs(Math.sin(f.angle)*c);return b}};a.jqplot.CanvasAxisTickRenderer.prototype.getTop=function(b){if(this._elem){return this._elem.position().top}else{return null}};a.jqplot.CanvasAxisTickRenderer.prototype.getAngleRad=function(){var b=this.angle*Math.PI/180;return b};a.jqplot.CanvasAxisTickRenderer.prototype.setTick=function(b,d,c){this.value=b;if(c){this.isMinorTick=true}return this};a.jqplot.CanvasAxisTickRenderer.prototype.draw=function(c,f){if(!this.label){this.label=this.prefix+this.formatter(this.formatString,this.value)}if(this._elem){if(a.jqplot.use_excanvas&&window.G_vmlCanvasManager.uninitElement!==undefined){window.G_vmlCanvasManager.uninitElement(this._elem.get(0))}this._elem.emptyForce();this._elem=null}var e=f.canvasManager.getCanvas();this._textRenderer.setText(this.label,c);var b=this.getWidth(c);var d=this.getHeight(c);e.width=b;e.height=d;e.style.width=b;e.style.height=d;e.style.textAlign="left";e.style.position="absolute";e=f.canvasManager.initCanvas(e);this._elem=a(e);this._elem.css(this._styles);this._elem.addClass("jqplot-"+this.axis+"-tick");e=null;return this._elem};a.jqplot.CanvasAxisTickRenderer.prototype.pack=function(){this._textRenderer.draw(this._elem.get(0).getContext("2d"),this.label)}})(jQuery);
--------------------------------------------------------------------------------
/helpdesk/templates/helpdesk/public_view_ticket.html:
--------------------------------------------------------------------------------
1 | {% extends "helpdesk/public_base.html" %}{% load i18n humanize %}
2 | {% block helpdesk_title %}{% trans "View a Ticket" %}{% endblock %}
3 |
4 | {% block helpdesk_body %}
5 |
6 |
64 | {% for change in followup.ticketchange_set.all %}
65 |
{% 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 %}
66 | {% endfor %}
67 |
{% endif %}
68 | {% for attachment in followup.attachment_set.all %}{% if forloop.first %}
{% trans "You can run this query on filtered data by using one of your saved queries." %}
20 |
28 | {% else %}
29 |
{% trans "Want to filter this report to just show a subset of data? Go to the Ticket List, filter your query, and save your query." %}
30 | {% endif %}
31 |
32 |
33 |
{{ title }}
34 |
35 |
{% for h in headings %}
{% if forloop.first %}{{ h|title }}{% else %}{{ h }}{% endif %}
{% endfor %}
36 |
37 |
38 | {% for d in data %}
39 |
{% for f in d %}
{{ f }}
{% endfor %}
{% endfor %}
40 |
41 |
42 |
43 |
44 | {% ifequal charttype "date" %}
45 |
73 | {% endifequal %}
74 | {% ifequal charttype "bar" %}
75 |
104 | {% endifequal %}
105 |
106 |
107 |
108 | {% endblock %}
109 |
--------------------------------------------------------------------------------
/LICENSE.3RDPARTY:
--------------------------------------------------------------------------------
1 | This file contains license details for 3rd party software which is
2 | distributed with Jutda Helpdesk.
3 |
4 | 1. License for jQuery & jQuery UI
5 | 2. License for jQuery UI 'Smoothness' theme
6 | 3. License for akismet.py
7 | 4. License for jqPlot
8 |
9 | ----------------------------------------------------------------------
10 |
11 | 1. License for jQuery & jQuery UI
12 |
13 | Copyright (c) 2007 John Resig, http://jquery.com/
14 |
15 | Permission is hereby granted, free of charge, to any person obtaining
16 | a copy of this software and associated documentation files (the
17 | "Software"), to deal in the Software without restriction, including
18 | without limitation the rights to use, copy, modify, merge, publish,
19 | distribute, sublicense, and/or sell copies of the Software, and to
20 | permit persons to whom the Software is furnished to do so, subject to
21 | the following conditions:
22 |
23 | The above copyright notice and this permission notice shall be
24 | included in all copies or substantial portions of the Software.
25 |
26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 |
34 | ----------------------------------------------------------------------
35 |
36 | 2. License for jQuery UI 'Smoothness' theme
37 |
38 | /*
39 | * jQuery UI screen structure and presentation
40 | * This CSS file was generated by ThemeRoller, a Filament Group Project for jQuery UI
41 | * Author: Scott Jehl, scott@filamentgroup.com, http://www.filamentgroup.com
42 | * Visit ThemeRoller.com
43 | */
44 |
45 | ----------------------------------------------------------------------
46 |
47 | 3. License for akismet.py
48 |
49 | Copyright (c) 2003-2009, Michael Foord
50 | All rights reserved.
51 | E-mail : fuzzyman AT voidspace DOT org DOT uk
52 |
53 | Redistribution and use in source and binary forms, with or without
54 | modification, are permitted provided that the following conditions are
55 | met:
56 |
57 |
58 | * Redistributions of source code must retain the above copyright
59 | notice, this list of conditions and the following disclaimer.
60 |
61 | * Redistributions in binary form must reproduce the above
62 | copyright notice, this list of conditions and the following
63 | disclaimer in the documentation and/or other materials provided
64 | with the distribution.
65 |
66 | * Neither the name of Michael Foord nor the name of Voidspace
67 | may be used to endorse or promote products derived from this
68 | software without specific prior written permission.
69 |
70 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
71 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
72 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
73 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
74 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
75 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
76 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
77 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
78 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
79 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
80 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
81 |
82 | ----------------------------------------------------------------------
83 |
84 | 4. License for jqPlot
85 |
86 | Copyright (c) 2009 - 2010 Chris Leonello
87 |
88 | Permission is hereby granted, free of charge, to any person obtaining a copy
89 | of this software and associated documentation files (the "Software"), to deal
90 | in the Software without restriction, including without limitation the rights
91 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
92 | copies of the Software, and to permit persons to whom the Software is
93 | furnished to do so, subject to the following conditions:
94 |
95 | The above copyright notice and this permission notice shall be included in
96 | all copies or substantial portions of the Software.
97 |
98 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
99 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
100 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
101 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
102 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
103 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
104 | THE SOFTWARE.
105 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | PAPER =
8 | BUILDDIR = .
9 |
10 | # Internal variables.
11 | PAPEROPT_a4 = -D latex_paper_size=a4
12 | PAPEROPT_letter = -D latex_paper_size=letter
13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
14 |
15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
16 |
17 | help:
18 | @echo "Please use \`make ' where is one of"
19 | @echo " html to make standalone HTML files"
20 | @echo " dirhtml to make HTML files named index.html in directories"
21 | @echo " singlehtml to make a single large HTML file"
22 | @echo " pickle to make pickle files"
23 | @echo " json to make JSON files"
24 | @echo " htmlhelp to make HTML files and a HTML help project"
25 | @echo " qthelp to make HTML files and a qthelp project"
26 | @echo " devhelp to make HTML files and a Devhelp project"
27 | @echo " epub to make an epub"
28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
29 | @echo " latexpdf to make LaTeX files and run them through pdflatex"
30 | @echo " text to make text files"
31 | @echo " man to make manual pages"
32 | @echo " changes to make an overview of all changed/added/deprecated items"
33 | @echo " linkcheck to check all external links for integrity"
34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)"
35 |
36 | clean:
37 | -rm -rf $(BUILDDIR)/*
38 |
39 | html:
40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
41 | @echo
42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
43 |
44 | dirhtml:
45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
46 | @echo
47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
48 |
49 | singlehtml:
50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
51 | @echo
52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
53 |
54 | pickle:
55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
56 | @echo
57 | @echo "Build finished; now you can process the pickle files."
58 |
59 | json:
60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
61 | @echo
62 | @echo "Build finished; now you can process the JSON files."
63 |
64 | htmlhelp:
65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
66 | @echo
67 | @echo "Build finished; now you can run HTML Help Workshop with the" \
68 | ".hhp project file in $(BUILDDIR)/htmlhelp."
69 |
70 | qthelp:
71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
72 | @echo
73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \
74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-helpdesk.qhcp"
76 | @echo "To view the help file:"
77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-helpdesk.qhc"
78 |
79 | devhelp:
80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
81 | @echo
82 | @echo "Build finished."
83 | @echo "To view the help file:"
84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/django-helpdesk"
85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-helpdesk"
86 | @echo "# devhelp"
87 |
88 | epub:
89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
90 | @echo
91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
92 |
93 | latex:
94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
95 | @echo
96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \
98 | "(use \`make latexpdf' here to do that automatically)."
99 |
100 | latexpdf:
101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
102 | @echo "Running LaTeX files through pdflatex..."
103 | make -C $(BUILDDIR)/latex all-pdf
104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
105 |
106 | text:
107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
108 | @echo
109 | @echo "Build finished. The text files are in $(BUILDDIR)/text."
110 |
111 | man:
112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
113 | @echo
114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
115 |
116 | changes:
117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
118 | @echo
119 | @echo "The overview file is in $(BUILDDIR)/changes."
120 |
121 | linkcheck:
122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
123 | @echo
124 | @echo "Link check complete; look for any errors in the above output " \
125 | "or in $(BUILDDIR)/linkcheck/output.txt."
126 |
127 | doctest:
128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
129 | @echo "Testing of doctests in the sources finished, look at the " \
130 | "results in $(BUILDDIR)/doctest/output.txt."
131 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | django-helpdesk - A Django powered ticket tracker for small enterprise.
2 | =======================================================================
3 |
4 | .. image:: https://travis-ci.org/rossp/django-helpdesk.png?branch=master
5 | :target: https://travis-ci.org/rossp/django-helpdesk
6 |
7 | Copyright 2009-11 Jutda and Ross Poulton. All Rights Reserved. See LICENSE for details.
8 |
9 | django-helpdesk was formerly known as Jutda Helpdesk, named after the
10 | company who originally created it. As of January 2011 the name has been
11 | changed to reflect what it really is: a Django-powered ticket tracker with
12 | contributors reaching far beyond Jutda.
13 |
14 | Complete documentation is available in the docs/ directory, or online at http://django-helpdesk.readthedocs.org/.
15 |
16 | You can see a demo installation at http://django-helpdesk-demo.herokuapp.com/
17 |
18 | Licensing
19 | ---------
20 |
21 | See the file 'LICENSE' for licensing terms. Note that django-helpdesk is
22 | distributed with 3rd party products which have their own licenses. See
23 | LICENSE.3RDPARTY for license terms for included packages.
24 |
25 | Dependencies (pre-flight checklist)
26 | -----------------------------------
27 |
28 | 1. Python 2.6+
29 | 2. Django (1.4 or newer)
30 | 3. South for database migrations (highly recommended, but not required). Download from http://south.aeracode.org/
31 | 4. An existing WORKING Django project with database etc. If you
32 | cannot log into the Admin, you won't get this product working.
33 | 5. `pip install django-bootstrap-form` and add `bootstrapform` to `settings.INSTALLED_APPS`
34 | 6. `pip install django-markdown-deux` and add `markdown_deux` to `settings.INSTALLED_APPS`
35 | 7. `pip install email-reply-parser` to get smart email reply handling
36 |
37 | **NOTE REGARDING SQLITE AND SEARCHING:**
38 | If you use sqlite as your database, the search function will not work as
39 | effectively as it will with other databases due to its inability to do
40 | case-insensitive searches. It's recommended that you use PostgreSQL or MySQL
41 | if possible. For more information, see this note in the Django documentation:
42 | http://docs.djangoproject.com/en/dev/ref/databases/#sqlite-string-matching
43 |
44 | When you try to do a keyword search using sqlite, a message will be displayed
45 | to alert you to this shortcoming. There is no way around it, sorry.
46 |
47 | **NOTE REGARDING MySQL:**
48 | If you use MySQL, with most default configurations you will receive an error
49 | when creating the database tables as we populate a number of default templates
50 | in languages other than English.
51 |
52 | You must create the database the holds the django-helpdesk tables using the
53 | UTF-8 collation; see the MySQL manual for more information:
54 | http://dev.mysql.com/doc/refman/5.1/en/charset-database.html
55 |
56 | If you do NOT do this step, and you only want to use English-language templates,
57 | you can continue however you will receive a warning when running the 'migrate'
58 | commands.
59 |
60 | Fresh Django Installations
61 | --------------------------
62 |
63 | If you're on a brand new Django installation, make sure you do a ``syncdb``
64 | **before** adding ``helpdesk`` to your ``INSTALLED_APPS``. This will avoid
65 | errors with trying to create User settings.
66 |
67 | Upgrading from previous versions
68 | --------------------------------
69 |
70 | We highly recommend that you use South (available
71 | from http://south.aeracode.org/) to assist with management of database schema
72 | changes.
73 |
74 | If you are upgrading from a previous version that did NOT use South for
75 | migrations (i.e. prior to April 2011) then you will need to 'fake' the first
76 | migration::
77 |
78 | python manage.py migrate helpdesk 0001 --fake
79 |
80 | If you are upgrading from a previous version of django-helpdesk that DID use
81 | South, simply download an up to date version of the code base (eg by using
82 | `git pull` or `pip install --upgrade django-helpdesk`) then migrate the database::
83 |
84 | python manage.py migrate helpdesk --db-dry-run # DB untouched
85 | python manage.py migrate helpdesk
86 |
87 | Lastly, restart your web server software (eg Apache) or FastCGI instance, to
88 | ensure the latest changes are in use.
89 |
90 | You can continue to the 'Initial Configuration' area, if needed.
91 |
92 | Installation
93 | ------------
94 |
95 | ``pip install django-helpdesk``
96 |
97 | For further installation information see docs/install.html and docs/configuration.html
98 |
99 | Contributing
100 | ------------
101 |
102 | If you want to help translate django-helpdesk into languages other than English, we encourage you to make use of our Transifex project.
103 |
104 | http://www.transifex.net/projects/p/django-helpdesk/resource/core/
105 |
106 | Feel free to request access to contribute your translations.
107 |
108 | Pull requests for all other changes are welcome. We're currently trying to add test cases wherever possible, so please continue to include tests with pull requests.
109 |
110 | .. image:: https://secure.travis-ci.org/rossp/django-helpdesk.png?branch=master
111 | :target: https://travis-ci.org/rossp/django-helpdesk
112 |
--------------------------------------------------------------------------------
/helpdesk/south_migrations/0011_populate_usersettings.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from south.utils import datetime_utils as datetime
3 | from south.db import db
4 | from south.v2 import DataMigration
5 | from django.db import models
6 | from django.contrib.auth import get_user_model
7 | from helpdesk.settings import DEFAULT_USER_SETTINGS
8 |
9 |
10 | def pickle_settings(data):
11 | """Pickling as defined at migration's creation time"""
12 | import cPickle
13 | from helpdesk.lib import b64encode
14 | return b64encode(cPickle.dumps(data))
15 |
16 |
17 | # https://docs.djangoproject.com/en/1.7/topics/migrations/#data-migrations
18 | def populate_usersettings(orm):
19 | """Create a UserSettings entry for each existing user.
20 | This will only happen once (at install time, or at upgrade)
21 | when the UserSettings model doesn't already exist."""
22 |
23 | _User = get_user_model()
24 |
25 | # Import historical version of models
26 | User = orm[_User._meta.app_label+'.'+_User._meta.model_name]
27 | UserSettings = orm["helpdesk"+'.'+"UserSettings"]
28 | settings_pickled = pickle_settings(DEFAULT_USER_SETTINGS)
29 |
30 | for u in User.objects.all():
31 | try:
32 | UserSettings.objects.get(user=u)
33 | except UserSettings.DoesNotExist:
34 | UserSettings.objects.create(user=u, settings_pickled=settings_pickled)
35 |
36 | class Migration(DataMigration):
37 |
38 | def forwards(self, orm):
39 | populate_usersettings(orm)
40 |
41 | def backwards(self, orm):
42 | pass
43 |
44 | models = {
45 | u'auth.group': {
46 | 'Meta': {'object_name': 'Group'},
47 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
48 | 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
49 | 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
50 | },
51 | u'auth.permission': {
52 | 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
53 | 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
54 | 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
55 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
56 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
57 | },
58 | u'auth.user': {
59 | 'Meta': {'object_name': 'User'},
60 | 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
61 | 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
62 | 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
63 | 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
64 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
65 | 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
66 | 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
67 | 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
68 | 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
69 | 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
70 | 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
71 | 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
72 | 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
73 | },
74 | u'contenttypes.contenttype': {
75 | 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
76 | 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
77 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
78 | 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
79 | 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
80 | },
81 | u'helpdesk.usersettings': {
82 | 'Meta': {'object_name': 'UserSettings'},
83 | u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
84 | 'settings_pickled': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
85 | 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'})
86 | }
87 | }
88 |
89 | complete_apps = ['helpdesk']
90 | symmetrical = True
91 |
--------------------------------------------------------------------------------
/helpdesk/management/commands/create_escalation_exclusions.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | """
3 | Jutda Helpdesk - A Django powered ticket tracker for small enterprise.
4 |
5 | (c) Copyright 2008 Jutda. All Rights Reserved. See LICENSE for details.
6 |
7 | scripts/create_escalation_exclusion.py - Easy way to routinely add particular
8 | days to the list of days on which no
9 | escalation should take place.
10 | """
11 |
12 | from datetime import timedelta, date
13 | import getopt
14 | from optparse import make_option
15 | import sys
16 |
17 | from django.core.management.base import BaseCommand, CommandError
18 | from django.db.models import Q
19 |
20 | from helpdesk.models import EscalationExclusion, Queue
21 |
22 |
23 | class Command(BaseCommand):
24 | def __init__(self):
25 | BaseCommand.__init__(self)
26 |
27 | self.option_list += (
28 | make_option(
29 | '--days', '-d',
30 | help='Days of week (monday, tuesday, etc)'),
31 | make_option(
32 | '--occurrences', '-o',
33 | type='int',
34 | default=1,
35 | help='Occurrences: How many weeks ahead to exclude this day'),
36 | make_option(
37 | '--queues', '-q',
38 | help='Queues to include (default: all). Use queue slugs'),
39 | make_option(
40 | '--escalate-verbosely', '-x',
41 | action='store_true',
42 | default=False,
43 | help='Display a list of dates excluded'),
44 | )
45 |
46 | def handle(self, *args, **options):
47 | days = options['days']
48 | occurrences = options['occurrences']
49 | verbose = False
50 | queue_slugs = options['queues']
51 | queues = []
52 |
53 | if options['escalate-verbosely']:
54 | verbose = True
55 |
56 | # this should already be handled by optparse
57 | if not occurrences: occurrences = 1
58 | if not (days and occurrences):
59 | raise CommandError('One or more occurrences must be specified.')
60 |
61 | if queue_slugs is not None:
62 | queue_set = queue_slugs.split(',')
63 | for queue in queue_set:
64 | try:
65 | q = Queue.objects.get(slug__exact=queue)
66 | except Queue.DoesNotExist:
67 | raise CommandError("Queue %s does not exist." % queue)
68 | queues.append(q)
69 |
70 | create_exclusions(days=days, occurrences=occurrences, verbose=verbose, queues=queues)
71 |
72 |
73 | day_names = {
74 | 'monday': 0,
75 | 'tuesday': 1,
76 | 'wednesday': 2,
77 | 'thursday': 3,
78 | 'friday': 4,
79 | 'saturday': 5,
80 | 'sunday': 6,
81 | }
82 |
83 |
84 | def create_exclusions(days, occurrences, verbose, queues):
85 | days = days.split(',')
86 | for day in days:
87 | day_name = day
88 | day = day_names[day]
89 | workdate = date.today()
90 | i = 0
91 | while i < occurrences:
92 | if day == workdate.weekday():
93 | if EscalationExclusion.objects.filter(date=workdate).count() == 0:
94 | esc = EscalationExclusion(name='Auto Exclusion for %s' % day_name, date=workdate)
95 | esc.save()
96 |
97 | if verbose:
98 | print "Created exclusion for %s %s" % (day_name, workdate)
99 |
100 | for q in queues:
101 | esc.queues.add(q)
102 | if verbose:
103 | print " - for queue %s" % q
104 |
105 | i += 1
106 | workdate += timedelta(days=1)
107 |
108 |
109 | def usage():
110 | print "Options:"
111 | print " --days, -d: Days of week (monday, tuesday, etc)"
112 | print " --occurrences, -o: Occurrences: How many weeks ahead to exclude this day"
113 | print " --queues, -q: Queues to include (default: all). Use queue slugs"
114 | print " --verbose, -v: Display a list of dates excluded"
115 |
116 |
117 | if __name__ == '__main__':
118 | # This script can be run from the command-line or via Django's manage.py.
119 | try:
120 | opts, args = getopt.getopt(sys.argv[1:], 'd:o:q:v', ['days=', 'occurrences=', 'verbose', 'queues='])
121 | except getopt.GetoptError:
122 | usage()
123 | sys.exit(2)
124 |
125 | days = None
126 | occurrences = None
127 | verbose = False
128 | queue_slugs = None
129 | queues = []
130 |
131 | for o, a in opts:
132 | if o in ('-x', '--escalate-verbosely'):
133 | verbose = True
134 | if o in ('-d', '--days'):
135 | days = a
136 | if o in ('-q', '--queues'):
137 | queue_slugs = a
138 | if o in ('-o', '--occurrences'):
139 | occurrences = int(a)
140 |
141 | if not occurrences: occurrences = 1
142 | if not (days and occurrences):
143 | usage()
144 | sys.exit(2)
145 |
146 | if queue_slugs is not None:
147 | queue_set = queue_slugs.split(',')
148 | for queue in queue_set:
149 | try:
150 | q = Queue.objects.get(slug__exact=queue)
151 | except Queue.DoesNotExist:
152 | print "Queue %s does not exist." % queue
153 | sys.exit(2)
154 | queues.append(q)
155 |
156 | create_exclusions(days=days, occurrences=occurrences, verbose=verbose, queues=queues)
157 |
--------------------------------------------------------------------------------
/helpdesk/templates/helpdesk/navigation.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}{% load url from future %}
2 |
3 |
79 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from distutils.util import convert_path
4 | from fnmatch import fnmatchcase
5 | from setuptools import setup, find_packages
6 |
7 | version = '0.1.16'
8 |
9 | # Provided as an attribute, so you can append to these instead
10 | # of replicating them:
11 | standard_exclude = ('*.py', '*.pyc', '*$py.class', '*~', '.*', '*.bak')
12 | standard_exclude_directories = ('.*', 'CVS', '_darcs', './build',
13 | './dist', 'EGG-INFO', '*.egg-info')
14 |
15 | # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
16 | # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
17 | # Note: you may want to copy this into your setup.py file verbatim, as
18 | # you can't import this from another package, when you don't know if
19 | # that package is installed yet.
20 | def find_package_data(
21 | where='.', package='',
22 | exclude=standard_exclude,
23 | exclude_directories=standard_exclude_directories,
24 | only_in_packages=True,
25 | show_ignored=False):
26 | """
27 | Return a dictionary suitable for use in ``package_data``
28 | in a distutils ``setup.py`` file.
29 |
30 | The dictionary looks like::
31 |
32 | {'package': [files]}
33 |
34 | Where ``files`` is a list of all the files in that package that
35 | don't match anything in ``exclude``.
36 |
37 | If ``only_in_packages`` is true, then top-level directories that
38 | are not packages won't be included (but directories under packages
39 | will).
40 |
41 | Directories matching any pattern in ``exclude_directories`` will
42 | be ignored; by default directories with leading ``.``, ``CVS``,
43 | and ``_darcs`` will be ignored.
44 |
45 | If ``show_ignored`` is true, then all the files that aren't
46 | included in package data are shown on stderr (for debugging
47 | purposes).
48 |
49 | Note patterns use wildcards, or can be exact paths (including
50 | leading ``./``), and all searching is case-insensitive.
51 | """
52 |
53 | out = {}
54 | stack = [(convert_path(where), '', package, only_in_packages)]
55 | while stack:
56 | where, prefix, package, only_in_packages = stack.pop(0)
57 | for name in os.listdir(where):
58 | fn = os.path.join(where, name)
59 | if os.path.isdir(fn):
60 | bad_name = False
61 | for pattern in exclude_directories:
62 | if (fnmatchcase(name, pattern)
63 | or fn.lower() == pattern.lower()):
64 | bad_name = True
65 | if show_ignored:
66 | print >> sys.stderr, (
67 | "Directory %s ignored by pattern %s"
68 | % (fn, pattern))
69 | break
70 | if bad_name:
71 | continue
72 | if (os.path.isfile(os.path.join(fn, '__init__.py'))
73 | and not prefix):
74 | if not package:
75 | new_package = name
76 | else:
77 | new_package = package + '.' + name
78 | stack.append((fn, '', new_package, False))
79 | else:
80 | stack.append((fn, prefix + name + '/', package, only_in_packages))
81 | elif package or not only_in_packages:
82 | # is a file
83 | bad_name = False
84 | for pattern in exclude:
85 | if (fnmatchcase(name, pattern)
86 | or fn.lower() == pattern.lower()):
87 | bad_name = True
88 | if show_ignored:
89 | print >> sys.stderr, (
90 | "File %s ignored by pattern %s"
91 | % (fn, pattern))
92 | break
93 | if bad_name:
94 | continue
95 | out.setdefault(package, []).append(prefix+name)
96 | return out
97 |
98 |
99 | def get_requirements():
100 | with open(os.path.join(os.path.dirname(__file__), "requirements.txt")) as f:
101 | requirements_list = [req.strip() for req in f.readlines()]
102 |
103 | requirements_list.append("setuptools")
104 | requirements_list.append("pytz")
105 | return requirements_list
106 |
107 |
108 | LONG_DESCRIPTION = """
109 | ===============
110 | django-helpdesk
111 | ===============
112 |
113 | This is a Django-powered helpdesk ticket tracker, designed to
114 | plug into an existing Django website and provide you with
115 | internal (or, perhaps, external) helpdesk management.
116 | """
117 |
118 | setup(
119 | name='django-helpdesk',
120 | version=version,
121 | description="Django-powered ticket tracker for your helpdesk",
122 | long_description=LONG_DESCRIPTION,
123 | classifiers=[
124 | "Programming Language :: Python",
125 | "Topic :: Software Development :: Libraries :: Python Modules",
126 | "Framework :: Django",
127 | "Environment :: Web Environment",
128 | "Operating System :: OS Independent",
129 | "Intended Audience :: Customer Service",
130 | "License :: OSI Approved :: BSD License",
131 | "Natural Language :: English",
132 | "Topic :: Office/Business",
133 | "Topic :: Software Development :: Bug Tracking",
134 | ],
135 | keywords=['django', 'helpdesk', 'tickets', 'incidents', 'cases'],
136 | author='Ross Poulton',
137 | author_email='ross@rossp.org',
138 | url='http://github.com/rossp/django-helpdesk',
139 | license='BSD',
140 | packages=find_packages(),
141 | package_data=find_package_data("helpdesk", only_in_packages=False),
142 | include_package_data=True,
143 | zip_safe=False,
144 | install_requires=get_requirements(),
145 | )
146 |
147 |
--------------------------------------------------------------------------------