From fced64727113b398754752daaf2adb20b58704bd Mon Sep 17 00:00:00 2001 From: Tony Zhu Date: Mon, 31 Mar 2014 14:43:59 -0400 Subject: [PATCH 01/16] Use json instead of deprecated django.utils.simplejson --- helpdesk/views/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helpdesk/views/api.py b/helpdesk/views/api.py index 64e162e7..2b386d77 100644 --- a/helpdesk/views/api.py +++ b/helpdesk/views/api.py @@ -11,13 +11,13 @@ The API documentation can be accessed by visiting http://helpdesk/api/help/ through templates/helpdesk/help_api.html. """ +import json from django import forms from django.contrib.auth import authenticate from django.contrib.auth.models import User from django.http import HttpResponse from django.shortcuts import render_to_response from django.template import loader, Context -from django.utils import simplejson from django.views.decorators.csrf import csrf_exempt try: @@ -116,7 +116,7 @@ class API: def api_public_list_queues(self): - return api_return(STATUS_OK, simplejson.dumps([{"id": "%s" % q.id, "title": "%s" % q.title} for q in Queue.objects.all()]), json=True) + return api_return(STATUS_OK, json.dumps([{"id": "%s" % q.id, "title": "%s" % q.title} for q in Queue.objects.all()]), json=True) def api_public_find_user(self): From dc86236136feaa8b901ac6488ebb4562a9acb815 Mon Sep 17 00:00:00 2001 From: Tony Zhu Date: Thu, 23 Oct 2014 10:44:59 -0400 Subject: [PATCH 02/16] Go back to the original helpdesk repo --- helpdesk/views/api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/helpdesk/views/api.py b/helpdesk/views/api.py index 1b682ff3..140c80c6 100644 --- a/helpdesk/views/api.py +++ b/helpdesk/views/api.py @@ -11,7 +11,6 @@ The API documentation can be accessed by visiting http://helpdesk/api/help/ through templates/helpdesk/help_api.html. """ -import json from django import forms from django.contrib.auth import authenticate try: @@ -121,7 +120,7 @@ class API: def api_public_list_queues(self): - return api_return(STATUS_OK, json.dumps([{"id": "%s" % q.id, "title": "%s" % q.title} for q in Queue.objects.all()]), json=True) + return api_return(STATUS_OK, simplejson.dumps([{"id": "%s" % q.id, "title": "%s" % q.title} for q in Queue.objects.all()]), json=True) def api_public_find_user(self): @@ -326,4 +325,3 @@ class API: ticket.save() return api_return(STATUS_OK) - From 8635742931f3ed78fdadd69fea563e5fb2efe5ae Mon Sep 17 00:00:00 2001 From: Tony Zhu Date: Thu, 23 Oct 2014 10:48:11 -0400 Subject: [PATCH 03/16] Rename the initial data to avoid django 1.7 flush bug --- helpdesk/fixtures/{initial_data.json => emailtemplates.json} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename helpdesk/fixtures/{initial_data.json => emailtemplates.json} (100%) diff --git a/helpdesk/fixtures/initial_data.json b/helpdesk/fixtures/emailtemplates.json similarity index 100% rename from helpdesk/fixtures/initial_data.json rename to helpdesk/fixtures/emailtemplates.json From 19ec7bee96fcf5c1e3a46cb1a18b286af4381d1c Mon Sep 17 00:00:00 2001 From: Ross Poulton Date: Sat, 14 Feb 2015 11:02:58 +1100 Subject: [PATCH 04/16] Extend attachment filefield max length in upload form. Fixes #301. --- helpdesk/forms.py | 1 + 1 file changed, 1 insertion(+) diff --git a/helpdesk/forms.py b/helpdesk/forms.py index 45f9ef75..23f08424 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -369,6 +369,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form): required=False, label=_('Attach File'), help_text=_('You can attach a file such as a document or screenshot to this ticket.'), + max_length=1000, ) def __init__(self, *args, **kwargs): From afe63359958a3ec004427e2815e96468a5b24162 Mon Sep 17 00:00:00 2001 From: Ross Poulton Date: Sat, 14 Feb 2015 11:10:20 +1100 Subject: [PATCH 05/16] Add install instructions for email templates. Fixes #294. --- docs/install.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index f9561984..36475b60 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -39,7 +39,7 @@ Adding To Your Django Project 'django.contrib.admin', # Required for helpdesk admin/maintenance 'django.contrib.markup', # Required for helpdesk text display 'django.contrib.humanize', # Required for elapsed time formatting - 'south', # Highly recommended to make database migrations simpler. + 'south', # Highly recommended to make database migrations simpler in Django < 1.7 'markdown_deux', # Required for Knowledgebase item formatting 'helpdesk', # This is new! ) @@ -58,7 +58,7 @@ Adding To Your Django Project ./manage.py syncdb - Then migrate using South + Then migrate using South / Django 1.7+ migrations:: ./manage.py migrate helpdesk @@ -98,3 +98,7 @@ Adding To Your Django Project LOGIN_URL = '/helpdesk/login/' Alter the URL to suit your installation path. + +9. Load initial e-mail templates, otherwise you will not be able to setnd e-mail:: + + python manage.py loaddata emailtemplate.json From bb2c240ef86eb95cab306aba4293df05e072e561 Mon Sep 17 00:00:00 2001 From: Ross Poulton Date: Sat, 14 Feb 2015 11:13:19 +1100 Subject: [PATCH 06/16] Use proper URL names for hold/unhold rather than manually building URL. Fixes #299 --- helpdesk/templates/helpdesk/ticket_desc_table.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpdesk/templates/helpdesk/ticket_desc_table.html b/helpdesk/templates/helpdesk/ticket_desc_table.html index d6016785..649c6f9c 100644 --- a/helpdesk/templates/helpdesk/ticket_desc_table.html +++ b/helpdesk/templates/helpdesk/ticket_desc_table.html @@ -4,7 +4,7 @@

{{ ticket.id }}. {{ ticket.title }} [{{ ticket.get_status }}]

Edit | Delete - {% if ticket.on_hold %} | {% trans "Unhold" %}{% else %} | {% trans "Hold" %}{% endif %} + {% if ticket.on_hold %} | {% trans "Unhold" %}{% else %} | {% trans "Hold" %}{% endif %} {% blocktrans with ticket.queue as queue %}Queue: {{ queue }}{% endblocktrans %} From e9555ff72ee9166efad4866449e78a6471bfcf73 Mon Sep 17 00:00:00 2001 From: Ross Poulton Date: Thu, 19 Feb 2015 09:54:36 +1100 Subject: [PATCH 07/16] Release 0.1.16 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 82fadd5e..8dccede2 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from distutils.util import convert_path from fnmatch import fnmatchcase from setuptools import setup, find_packages -version = '0.1.15' +version = '0.1.16' # Provided as an attribute, so you can append to these instead # of replicating them: From ae146c2e7fd7e9f19427f608c1f85a8f92848fd0 Mon Sep 17 00:00:00 2001 From: Ross Poulton Date: Mon, 23 Feb 2015 14:22:28 +1100 Subject: [PATCH 08/16] Remove reference to django.contrib.markup in install docs. (#255) --- docs/install.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/install.rst b/docs/install.rst index 36475b60..ea1fb9e8 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -29,7 +29,7 @@ Download, extract, and drop ``helpdesk`` into your ``PYTHONPATH`` Adding To Your Django Project ----------------------------- -1. Edit your ``settings.py`` file and add ``helpdesk`` to the ``INSTALLED_APPS`` setting. You also need ``django.contrib.admin`` and ``django.contrib.markup`` in ``INSTALLED_APPS`` if you haven't already added it. eg:: +1. Edit your ``settings.py`` file and add ``helpdesk`` to the ``INSTALLED_APPS`` setting. You also need ``django.contrib.admin`` in ``INSTALLED_APPS`` if you haven't already added it. eg:: INSTALLED_APPS = ( 'django.contrib.auth', @@ -37,7 +37,6 @@ Adding To Your Django Project 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.admin', # Required for helpdesk admin/maintenance - 'django.contrib.markup', # Required for helpdesk text display 'django.contrib.humanize', # Required for elapsed time formatting 'south', # Highly recommended to make database migrations simpler in Django < 1.7 'markdown_deux', # Required for Knowledgebase item formatting From 5e0ee1e37c9b7e514e382d87670f2b4b4d69001e Mon Sep 17 00:00:00 2001 From: Ross Poulton Date: Mon, 23 Feb 2015 14:24:53 +1100 Subject: [PATCH 09/16] Add bootstrapform to install docs. Closes #307. --- docs/install.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/install.rst b/docs/install.rst index ea1fb9e8..f386ea38 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -40,7 +40,8 @@ Adding To Your Django Project 'django.contrib.humanize', # Required for elapsed time formatting 'south', # Highly recommended to make database migrations simpler in Django < 1.7 'markdown_deux', # Required for Knowledgebase item formatting - 'helpdesk', # This is new! + 'bootstrapform', # Required for nicer formatting of forms with the default templates + 'helpdesk', # This is us! ) 2. Make sure django-helpdesk is accessible via ``urls.py``. Add the following line to ``urls.py``:: From f84da6c44336bab3457ce8534ae8756cbd17e96f Mon Sep 17 00:00:00 2001 From: Emmanuel Cohen Date: Fri, 27 Mar 2015 16:22:37 +0100 Subject: [PATCH 10/16] Preliminary support for python 3 & django 1.7 --- helpdesk/forms.py | 6 +- helpdesk/migrations/0001_initial.py | 247 +++++++++--------- .../migrations/0003_populate_usersettings.py | 7 +- helpdesk/models.py | 18 +- helpdesk/templates/helpdesk/public_base.html | 3 +- .../templates/helpdesk/public_homepage.html | 4 +- .../templatetags/load_helpdesk_settings.py | 12 +- helpdesk/templatetags/saved_queries.py | 12 +- helpdesk/views/public.py | 6 +- helpdesk/views/staff.py | 45 ++-- 10 files changed, 193 insertions(+), 167 deletions(-) diff --git a/helpdesk/forms.py b/helpdesk/forms.py index 23f08424..b766ceb6 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -6,8 +6,10 @@ django-helpdesk - A Django powered ticket tracker for small enterprise. forms.py - Definitions of newforms-based forms for creating and maintaining tickets. """ - -from StringIO import StringIO +try: + from StringIO import StringIO +except ImportError: + from io import StringIO from django import forms from django.forms import extras diff --git a/helpdesk/migrations/0001_initial.py b/helpdesk/migrations/0001_initial.py index dfd93b4b..2c157c6e 100644 --- a/helpdesk/migrations/0001_initial.py +++ b/helpdesk/migrations/0001_initial.py @@ -17,232 +17,235 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Attachment', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('file', models.FileField(upload_to=helpdesk.models.attachment_path, max_length=1000, verbose_name='File')), - ('filename', models.CharField(max_length=1000, verbose_name='Filename')), - ('mime_type', models.CharField(max_length=255, verbose_name='MIME Type')), - ('size', models.IntegerField(help_text='Size of this file in bytes', verbose_name='Size')), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('file', models.FileField(upload_to=helpdesk.models.attachment_path, verbose_name='File', max_length=1000)), + ('filename', models.CharField(verbose_name='Filename', max_length=1000)), + ('mime_type', models.CharField(verbose_name='MIME Type', max_length=255)), + ('size', models.IntegerField(verbose_name='Size', help_text='Size of this file in bytes')), ], options={ - 'ordering': ['filename'], - 'verbose_name': 'Attachment', 'verbose_name_plural': 'Attachments', + 'verbose_name': 'Attachment', + 'ordering': ['filename'], }, bases=(models.Model,), ), migrations.CreateModel( name='CustomField', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.SlugField(help_text='As used in the database and behind the scenes. Must be unique and consist of only lowercase letters with no punctuation.', unique=True, verbose_name='Field Name')), - ('label', models.CharField(help_text='The display label for this field', max_length=b'30', verbose_name='Label')), - ('help_text', models.TextField(help_text='Shown to the user when editing the ticket', null=True, verbose_name='Help Text', blank=True)), - ('data_type', models.CharField(help_text='Allows you to restrict the data entered into this field', max_length=100, verbose_name='Data Type', choices=[(b'varchar', 'Character (single line)'), (b'text', 'Text (multi-line)'), (b'integer', 'Integer'), (b'decimal', 'Decimal'), (b'list', 'List'), (b'boolean', 'Boolean (checkbox yes/no)'), (b'date', 'Date'), (b'time', 'Time'), (b'datetime', 'Date & Time'), (b'email', 'E-Mail Address'), (b'url', 'URL'), (b'ipaddress', 'IP Address'), (b'slug', 'Slug')])), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('name', models.SlugField(help_text='As used in the database and behind the scenes. Must be unique and consist of only lowercase letters with no punctuation.', verbose_name='Field Name', unique=True)), + ('label', models.CharField(verbose_name='Label', help_text='The display label for this field', max_length='30')), + ('help_text', models.TextField(null=True, verbose_name='Help Text', blank=True, help_text='Shown to the user when editing the ticket')), + ('data_type', models.CharField(choices=[('varchar', 'Character (single line)'), ('text', 'Text (multi-line)'), ('integer', 'Integer'), ('decimal', 'Decimal'), ('list', 'List'), ('boolean', 'Boolean (checkbox yes/no)'), ('date', 'Date'), ('time', 'Time'), ('datetime', 'Date & Time'), ('email', 'E-Mail Address'), ('url', 'URL'), ('ipaddress', 'IP Address'), ('slug', 'Slug')], verbose_name='Data Type', help_text='Allows you to restrict the data entered into this field', max_length=100)), ('max_length', models.IntegerField(null=True, verbose_name='Maximum Length (characters)', blank=True)), - ('decimal_places', models.IntegerField(help_text='Only used for decimal fields', null=True, verbose_name='Decimal Places', blank=True)), - ('empty_selection_list', models.BooleanField(default=False, help_text='Only for List: adds an empty first entry to the choices list, which enforces that the user makes an active choice.', verbose_name='Add empty first choice to List?')), - ('list_values', models.TextField(help_text='For list fields only. Enter one option per line.', null=True, verbose_name='List Values', blank=True)), - ('ordering', models.IntegerField(help_text='Lower numbers are displayed first; higher numbers are listed later', null=True, verbose_name='Ordering', blank=True)), - ('required', models.BooleanField(default=False, help_text='Does the user have to enter a value for this field?', verbose_name='Required?')), - ('staff_only', models.BooleanField(default=False, help_text='If this is ticked, then the public submission form will NOT show this field', verbose_name='Staff Only?')), + ('decimal_places', models.IntegerField(null=True, verbose_name='Decimal Places', blank=True, help_text='Only used for decimal fields')), + ('empty_selection_list', models.BooleanField(verbose_name='Add empty first choice to List?', default=False, help_text='Only for List: adds an empty first entry to the choices list, which enforces that the user makes an active choice.')), + ('list_values', models.TextField(null=True, verbose_name='List Values', blank=True, help_text='For list fields only. Enter one option per line.')), + ('ordering', models.IntegerField(null=True, verbose_name='Ordering', blank=True, help_text='Lower numbers are displayed first; higher numbers are listed later')), + ('required', models.BooleanField(verbose_name='Required?', default=False, help_text='Does the user have to enter a value for this field?')), + ('staff_only', models.BooleanField(verbose_name='Staff Only?', default=False, help_text='If this is ticked, then the public submission form will NOT show this field')), ], options={ - 'verbose_name': 'Custom field', 'verbose_name_plural': 'Custom fields', + 'verbose_name': 'Custom field', }, bases=(models.Model,), ), migrations.CreateModel( name='EmailTemplate', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('template_name', models.CharField(max_length=100, verbose_name='Template Name')), - ('subject', models.CharField(help_text='This will be prefixed with "[ticket.ticket] ticket.title". We recommend something simple such as "(Updated") or "(Closed)" - the same context is available as in plain_text, below.', max_length=100, verbose_name='Subject')), - ('heading', models.CharField(help_text='In HTML e-mails, this will be the heading at the top of the email - the same context is available as in plain_text, below.', max_length=100, verbose_name='Heading')), - ('plain_text', models.TextField(help_text='The context available to you includes {{ ticket }}, {{ queue }}, and depending on the time of the call: {{ resolution }} or {{ comment }}.', verbose_name='Plain Text')), - ('html', models.TextField(help_text='The same context is available here as in plain_text, above.', verbose_name='HTML')), - ('locale', models.CharField(help_text='Locale of this template.', max_length=10, null=True, verbose_name='Locale', blank=True)), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('template_name', models.CharField(verbose_name='Template Name', max_length=100)), + ('subject', models.CharField(verbose_name='Subject', help_text='This will be prefixed with "[ticket.ticket] ticket.title". We recommend something simple such as "(Updated") or "(Closed)" - the same context is available as in plain_text, below.', max_length=100)), + ('heading', models.CharField(verbose_name='Heading', help_text='In HTML e-mails, this will be the heading at the top of the email - the same context is available as in plain_text, below.', max_length=100)), + ('plain_text', models.TextField(verbose_name='Plain Text', help_text='The context available to you includes {{ ticket }}, {{ queue }}, and depending on the time of the call: {{ resolution }} or {{ comment }}.')), + ('html', models.TextField(verbose_name='HTML', help_text='The same context is available here as in plain_text, above.')), + ('locale', models.CharField(null=True, verbose_name='Locale', help_text='Locale of this template.', blank=True, max_length=10)), ], options={ - 'ordering': ['template_name', 'locale'], - 'verbose_name': 'e-mail template', 'verbose_name_plural': 'e-mail templates', + 'verbose_name': 'e-mail template', + 'ordering': ['template_name', 'locale'], }, bases=(models.Model,), ), migrations.CreateModel( name='EscalationExclusion', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=100, verbose_name='Name')), - ('date', models.DateField(help_text='Date on which escalation should not happen', verbose_name='Date')), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('name', models.CharField(verbose_name='Name', max_length=100)), + ('date', models.DateField(verbose_name='Date', help_text='Date on which escalation should not happen')), ], options={ - 'verbose_name': 'Escalation exclusion', 'verbose_name_plural': 'Escalation exclusions', + 'verbose_name': 'Escalation exclusion', }, bases=(models.Model,), ), migrations.CreateModel( name='FollowUp', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='Date')), - ('title', models.CharField(max_length=200, null=True, verbose_name='Title', blank=True)), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('date', models.DateTimeField(verbose_name='Date', default=django.utils.timezone.now)), + ('title', models.CharField(null=True, verbose_name='Title', blank=True, max_length=200)), ('comment', models.TextField(null=True, verbose_name='Comment', blank=True)), - ('public', models.BooleanField(default=False, help_text='Public tickets are viewable by the submitter and all staff, but non-public tickets can only be seen by staff.', verbose_name='Public')), - ('new_status', models.IntegerField(blank=True, help_text='If the status was changed, what was it changed to?', null=True, verbose_name='New Status', choices=[(1, 'Open'), (2, 'Reopened'), (3, 'Resolved'), (4, 'Closed'), (5, 'Duplicate')])), + ('public', models.BooleanField(verbose_name='Public', default=False, help_text='Public tickets are viewable by the submitter and all staff, but non-public tickets can only be seen by staff.')), + ('new_status', models.IntegerField(null=True, verbose_name='New Status', help_text='If the status was changed, what was it changed to?', blank=True, choices=[(1, 'Open'), (2, 'Reopened'), (3, 'Resolved'), (4, 'Closed'), (5, 'Duplicate')])), ], options={ - 'ordering': ['date'], - 'verbose_name': 'Follow-up', 'verbose_name_plural': 'Follow-ups', + 'verbose_name': 'Follow-up', + 'ordering': ['date'], }, bases=(models.Model,), ), migrations.CreateModel( name='IgnoreEmail', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=100, verbose_name='Name')), - ('date', models.DateField(help_text='Date on which this e-mail address was added', verbose_name='Date', editable=False, blank=True)), - ('email_address', models.CharField(help_text='Enter a full e-mail address, or portions with wildcards, eg *@domain.com or postmaster@*.', max_length=150, verbose_name='E-Mail Address')), - ('keep_in_mailbox', models.BooleanField(default=False, help_text='Do you want to save emails from this address in the mailbox? If this is unticked, emails from this address will be deleted.', verbose_name='Save Emails in Mailbox?')), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('name', models.CharField(verbose_name='Name', max_length=100)), + ('date', models.DateField(editable=False, verbose_name='Date', blank=True, help_text='Date on which this e-mail address was added')), + ('email_address', models.CharField(verbose_name='E-Mail Address', help_text='Enter a full e-mail address, or portions with wildcards, eg *@domain.com or postmaster@*.', max_length=150)), + ('keep_in_mailbox', models.BooleanField(verbose_name='Save Emails in Mailbox?', default=False, help_text='Do you want to save emails from this address in the mailbox? If this is unticked, emails from this address will be deleted.')), ], options={ - 'verbose_name': 'Ignored e-mail address', 'verbose_name_plural': 'Ignored e-mail addresses', + 'verbose_name': 'Ignored e-mail address', }, bases=(models.Model,), ), migrations.CreateModel( name='KBCategory', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=100, verbose_name='Title')), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('title', models.CharField(verbose_name='Title', max_length=100)), ('slug', models.SlugField(verbose_name='Slug')), ('description', models.TextField(verbose_name='Description')), ], options={ - 'ordering': ['title'], - 'verbose_name': 'Knowledge base category', 'verbose_name_plural': 'Knowledge base categories', + 'verbose_name': 'Knowledge base category', + 'ordering': ['title'], }, bases=(models.Model,), ), migrations.CreateModel( name='KBItem', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=100, verbose_name='Title')), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('title', models.CharField(verbose_name='Title', max_length=100)), ('question', models.TextField(verbose_name='Question')), ('answer', models.TextField(verbose_name='Answer')), - ('votes', models.IntegerField(default=0, help_text='Total number of votes cast for this item', verbose_name='Votes')), - ('recommendations', models.IntegerField(default=0, help_text='Number of votes for this item which were POSITIVE.', verbose_name='Positive Votes')), - ('last_updated', models.DateTimeField(help_text='The date on which this question was most recently changed.', verbose_name='Last Updated', blank=True)), + ('votes', models.IntegerField(verbose_name='Votes', default=0, help_text='Total number of votes cast for this item')), + ('recommendations', models.IntegerField(verbose_name='Positive Votes', default=0, help_text='Number of votes for this item which were POSITIVE.')), + ('last_updated', models.DateTimeField(verbose_name='Last Updated', blank=True, help_text='The date on which this question was most recently changed.')), ('category', models.ForeignKey(verbose_name='Category', to='helpdesk.KBCategory')), ], options={ - 'ordering': ['title'], - 'verbose_name': 'Knowledge base item', 'verbose_name_plural': 'Knowledge base items', + 'verbose_name': 'Knowledge base item', + 'ordering': ['title'], }, bases=(models.Model,), ), migrations.CreateModel( name='PreSetReply', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('name', models.CharField(help_text='Only used to assist users with selecting a reply - not shown to the user.', max_length=100, verbose_name='Name')), - ('body', models.TextField(help_text='Context available: {{ ticket }} - ticket object (eg {{ ticket.title }}); {{ queue }} - The queue; and {{ user }} - the current user.', verbose_name='Body')), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('name', models.CharField(verbose_name='Name', help_text='Only used to assist users with selecting a reply - not shown to the user.', max_length=100)), + ('body', models.TextField(verbose_name='Body', help_text='Context available: {{ ticket }} - ticket object (eg {{ ticket.title }}); {{ queue }} - The queue; and {{ user }} - the current user.')), ], options={ - 'ordering': ['name'], - 'verbose_name': 'Pre-set reply', 'verbose_name_plural': 'Pre-set replies', + 'verbose_name': 'Pre-set reply', + 'ordering': ['name'], }, bases=(models.Model,), ), migrations.CreateModel( name='Queue', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=100, verbose_name='Title')), - ('slug', models.SlugField(help_text="This slug is used when building ticket ID's. Once set, try not to change it or e-mailing may get messy.", verbose_name='Slug')), - ('email_address', models.EmailField(help_text='All outgoing e-mails for this queue will use this e-mail address. If you use IMAP or POP3, this should be the e-mail address for that mailbox.', max_length=75, null=True, verbose_name='E-Mail Address', blank=True)), - ('locale', models.CharField(help_text='Locale of this queue. All correspondence in this queue will be in this language.', max_length=10, null=True, verbose_name='Locale', blank=True)), - ('allow_public_submission', models.BooleanField(default=False, help_text='Should this queue be listed on the public submission form?', verbose_name='Allow Public Submission?')), - ('allow_email_submission', models.BooleanField(default=False, help_text='Do you want to poll the e-mail box below for new tickets?', verbose_name='Allow E-Mail Submission?')), - ('escalate_days', models.IntegerField(help_text='For tickets which are not held, how often do you wish to increase their priority? Set to 0 for no escalation.', null=True, verbose_name='Escalation Days', blank=True)), - ('new_ticket_cc', models.CharField(help_text='If an e-mail address is entered here, then it will receive notification of all new tickets created for this queue. Enter a comma between multiple e-mail addresses.', max_length=200, null=True, verbose_name='New Ticket CC Address', blank=True)), - ('updated_ticket_cc', models.CharField(help_text='If an e-mail address is entered here, then it will receive notification of all activity (new tickets, closed tickets, updates, reassignments, etc) for this queue. Separate multiple addresses with a comma.', max_length=200, null=True, verbose_name='Updated Ticket CC Address', blank=True)), - ('email_box_type', models.CharField(choices=[(b'pop3', 'POP 3'), (b'imap', 'IMAP')], max_length=5, blank=True, help_text='E-Mail server type for creating tickets automatically from a mailbox - both POP3 and IMAP are supported.', null=True, verbose_name='E-Mail Box Type')), - ('email_box_host', models.CharField(help_text='Your e-mail server address - either the domain name or IP address. May be "localhost".', max_length=200, null=True, verbose_name='E-Mail Hostname', blank=True)), - ('email_box_port', models.IntegerField(help_text='Port number to use for accessing e-mail. Default for POP3 is "110", and for IMAP is "143". This may differ on some servers. Leave it blank to use the defaults.', null=True, verbose_name='E-Mail Port', blank=True)), - ('email_box_ssl', models.BooleanField(default=False, help_text='Whether to use SSL for IMAP or POP3 - the default ports when using SSL are 993 for IMAP and 995 for POP3.', verbose_name='Use SSL for E-Mail?')), - ('email_box_user', models.CharField(help_text='Username for accessing this mailbox.', max_length=200, null=True, verbose_name='E-Mail Username', blank=True)), - ('email_box_pass', models.CharField(help_text='Password for the above username', max_length=200, null=True, verbose_name='E-Mail Password', blank=True)), - ('email_box_imap_folder', models.CharField(help_text='If using IMAP, what folder do you wish to fetch messages from? This allows you to use one IMAP account for multiple queues, by filtering messages on your IMAP server into separate folders. Default: INBOX.', max_length=100, null=True, verbose_name='IMAP Folder', blank=True)), - ('email_box_interval', models.IntegerField(default=b'5', help_text='How often do you wish to check this mailbox? (in Minutes)', null=True, verbose_name='E-Mail Check Interval', blank=True)), - ('email_box_last_check', models.DateTimeField(null=True, editable=False, blank=True)), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('title', models.CharField(verbose_name='Title', max_length=100)), + ('slug', models.SlugField(verbose_name='Slug', help_text="This slug is used when building ticket ID's. Once set, try not to change it or e-mailing may get messy.")), + ('email_address', models.EmailField(null=True, verbose_name='E-Mail Address', help_text='All outgoing e-mails for this queue will use this e-mail address. If you use IMAP or POP3, this should be the e-mail address for that mailbox.', blank=True, max_length=75)), + ('locale', models.CharField(null=True, verbose_name='Locale', help_text='Locale of this queue. All correspondence in this queue will be in this language.', blank=True, max_length=10)), + ('allow_public_submission', models.BooleanField(verbose_name='Allow Public Submission?', default=False, help_text='Should this queue be listed on the public submission form?')), + ('allow_email_submission', models.BooleanField(verbose_name='Allow E-Mail Submission?', default=False, help_text='Do you want to poll the e-mail box below for new tickets?')), + ('escalate_days', models.IntegerField(null=True, verbose_name='Escalation Days', blank=True, help_text='For tickets which are not held, how often do you wish to increase their priority? Set to 0 for no escalation.')), + ('new_ticket_cc', models.CharField(null=True, verbose_name='New Ticket CC Address', help_text='If an e-mail address is entered here, then it will receive notification of all new tickets created for this queue. Enter a comma between multiple e-mail addresses.', blank=True, max_length=200)), + ('updated_ticket_cc', models.CharField(null=True, verbose_name='Updated Ticket CC Address', help_text='If an e-mail address is entered here, then it will receive notification of all activity (new tickets, closed tickets, updates, reassignments, etc) for this queue. Separate multiple addresses with a comma.', blank=True, max_length=200)), + ('email_box_type', models.CharField(null=True, verbose_name='E-Mail Box Type', help_text='E-Mail server type for creating tickets automatically from a mailbox - both POP3 and IMAP are supported.', blank=True, max_length=5, choices=[('pop3', 'POP 3'), ('imap', 'IMAP')])), + ('email_box_host', models.CharField(null=True, verbose_name='E-Mail Hostname', help_text='Your e-mail server address - either the domain name or IP address. May be "localhost".', blank=True, max_length=200)), + ('email_box_port', models.IntegerField(null=True, verbose_name='E-Mail Port', blank=True, help_text='Port number to use for accessing e-mail. Default for POP3 is "110", and for IMAP is "143". This may differ on some servers. Leave it blank to use the defaults.')), + ('email_box_ssl', models.BooleanField(verbose_name='Use SSL for E-Mail?', default=False, help_text='Whether to use SSL for IMAP or POP3 - the default ports when using SSL are 993 for IMAP and 995 for POP3.')), + ('email_box_user', models.CharField(null=True, verbose_name='E-Mail Username', help_text='Username for accessing this mailbox.', blank=True, max_length=200)), + ('email_box_pass', models.CharField(null=True, verbose_name='E-Mail Password', help_text='Password for the above username', blank=True, max_length=200)), + ('email_box_imap_folder', models.CharField(null=True, verbose_name='IMAP Folder', help_text='If using IMAP, what folder do you wish to fetch messages from? This allows you to use one IMAP account for multiple queues, by filtering messages on your IMAP server into separate folders. Default: INBOX.', blank=True, max_length=100)), + ('email_box_interval', models.IntegerField(null=True, verbose_name='E-Mail Check Interval', blank=True, default='5', help_text='How often do you wish to check this mailbox? (in Minutes)')), + ('email_box_last_check', models.DateTimeField(editable=False, null=True, blank=True)), + ('socks_proxy_type', models.CharField(null=True, verbose_name='Socks Proxy Type', help_text='SOCKS4 or SOCKS5 allows you to proxy your connections through a SOCKS server.', blank=True, max_length=8, choices=[('socks4', 'SOCKS4'), ('socks5', 'SOCKS5')])), + ('socks_proxy_host', models.GenericIPAddressField(null=True, verbose_name='Socks Proxy Host', help_text='Socks proxy IP address. Default: 127.0.0.1', blank=True)), + ('socks_proxy_port', models.IntegerField(null=True, verbose_name='Socks Proxy Port', blank=True, help_text='Socks proxy port number. Default: 9150 (default TOR port)')), ], options={ - 'ordering': ('title',), - 'verbose_name': 'Queue', 'verbose_name_plural': 'Queues', + 'verbose_name': 'Queue', + 'ordering': ('title',), }, bases=(models.Model,), ), migrations.CreateModel( name='SavedSearch', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(help_text='User-provided name for this query', max_length=100, verbose_name='Query Name')), - ('shared', models.BooleanField(default=False, help_text='Should other users see this query?', verbose_name='Shared With Other Users?')), - ('query', models.TextField(help_text='Pickled query object. Be wary changing this.', verbose_name='Search Query')), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('title', models.CharField(verbose_name='Query Name', help_text='User-provided name for this query', max_length=100)), + ('shared', models.BooleanField(verbose_name='Shared With Other Users?', default=False, help_text='Should other users see this query?')), + ('query', models.TextField(verbose_name='Search Query', help_text='Pickled query object. Be wary changing this.')), ('user', models.ForeignKey(verbose_name='User', to=settings.AUTH_USER_MODEL)), ], options={ - 'verbose_name': 'Saved search', 'verbose_name_plural': 'Saved searches', + 'verbose_name': 'Saved search', }, bases=(models.Model,), ), migrations.CreateModel( name='Ticket', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('title', models.CharField(max_length=200, verbose_name='Title')), - ('created', models.DateTimeField(help_text='Date this ticket was first created', verbose_name='Created', blank=True)), - ('modified', models.DateTimeField(help_text='Date this ticket was most recently changed.', verbose_name='Modified', blank=True)), - ('submitter_email', models.EmailField(help_text='The submitter will receive an email for all public follow-ups left for this task.', max_length=75, null=True, verbose_name='Submitter E-Mail', blank=True)), - ('status', models.IntegerField(default=1, verbose_name='Status', choices=[(1, 'Open'), (2, 'Reopened'), (3, 'Resolved'), (4, 'Closed'), (5, 'Duplicate')])), - ('on_hold', models.BooleanField(default=False, help_text='If a ticket is on hold, it will not automatically be escalated.', verbose_name='On Hold')), - ('description', models.TextField(help_text='The content of the customers query.', null=True, verbose_name='Description', blank=True)), - ('resolution', models.TextField(help_text='The resolution provided to the customer by our staff.', null=True, verbose_name='Resolution', blank=True)), - ('priority', models.IntegerField(default=3, help_text='1 = Highest Priority, 5 = Low Priority', blank=3, verbose_name='Priority', choices=[(1, '1. Critical'), (2, '2. High'), (3, '3. Normal'), (4, '4. Low'), (5, '5. Very Low')])), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('title', models.CharField(verbose_name='Title', max_length=200)), + ('created', models.DateTimeField(verbose_name='Created', blank=True, help_text='Date this ticket was first created')), + ('modified', models.DateTimeField(verbose_name='Modified', blank=True, help_text='Date this ticket was most recently changed.')), + ('submitter_email', models.EmailField(null=True, verbose_name='Submitter E-Mail', help_text='The submitter will receive an email for all public follow-ups left for this task.', blank=True, max_length=75)), + ('status', models.IntegerField(verbose_name='Status', default=1, choices=[(1, 'Open'), (2, 'Reopened'), (3, 'Resolved'), (4, 'Closed'), (5, 'Duplicate')])), + ('on_hold', models.BooleanField(verbose_name='On Hold', default=False, help_text='If a ticket is on hold, it will not automatically be escalated.')), + ('description', models.TextField(null=True, verbose_name='Description', blank=True, help_text='The content of the customers query.')), + ('resolution', models.TextField(null=True, verbose_name='Resolution', blank=True, help_text='The resolution provided to the customer by our staff.')), + ('priority', models.IntegerField(verbose_name='Priority', help_text='1 = Highest Priority, 5 = Low Priority', blank=3, default=3, choices=[(1, '1. Critical'), (2, '2. High'), (3, '3. Normal'), (4, '4. Low'), (5, '5. Very Low')])), ('due_date', models.DateTimeField(null=True, verbose_name='Due on', blank=True)), - ('last_escalation', models.DateTimeField(help_text='The date this ticket was last escalated - updated automatically by management/commands/escalate_tickets.py.', null=True, editable=False, blank=True)), - ('assigned_to', models.ForeignKey(related_name=b'assigned_to', verbose_name='Assigned to', blank=True, to=settings.AUTH_USER_MODEL, null=True)), + ('last_escalation', models.DateTimeField(editable=False, null=True, blank=True, help_text='The date this ticket was last escalated - updated automatically by management/commands/escalate_tickets.py.')), + ('assigned_to', models.ForeignKey(null=True, verbose_name='Assigned to', blank=True, related_name='assigned_to', to=settings.AUTH_USER_MODEL)), ('queue', models.ForeignKey(verbose_name='Queue', to='helpdesk.Queue')), ], options={ + 'verbose_name_plural': 'Tickets', + 'verbose_name': 'Ticket', 'ordering': ('id',), 'get_latest_by': 'created', - 'verbose_name': 'Ticket', - 'verbose_name_plural': 'Tickets', }, bases=(models.Model,), ), migrations.CreateModel( name='TicketCC', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('email', models.EmailField(help_text='For non-user followers, enter their e-mail address', max_length=75, null=True, verbose_name='E-Mail Address', blank=True)), - ('can_view', models.BooleanField(default=False, help_text='Can this CC login to view the ticket details?', verbose_name='Can View Ticket?')), - ('can_update', models.BooleanField(default=False, help_text='Can this CC login and update the ticket?', verbose_name='Can Update Ticket?')), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('email', models.EmailField(null=True, verbose_name='E-Mail Address', help_text='For non-user followers, enter their e-mail address', blank=True, max_length=75)), + ('can_view', models.BooleanField(verbose_name='Can View Ticket?', default=False, help_text='Can this CC login to view the ticket details?')), + ('can_update', models.BooleanField(verbose_name='Can Update Ticket?', default=False, help_text='Can this CC login and update the ticket?')), ('ticket', models.ForeignKey(verbose_name='Ticket', to='helpdesk.Ticket')), - ('user', models.ForeignKey(blank=True, to=settings.AUTH_USER_MODEL, help_text='User who wishes to receive updates for this ticket.', null=True, verbose_name='User')), + ('user', models.ForeignKey(null=True, verbose_name='User', blank=True, to=settings.AUTH_USER_MODEL, help_text='User who wishes to receive updates for this ticket.')), ], options={ }, @@ -251,55 +254,55 @@ class Migration(migrations.Migration): migrations.CreateModel( name='TicketChange', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('field', models.CharField(max_length=100, verbose_name='Field')), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('field', models.CharField(verbose_name='Field', max_length=100)), ('old_value', models.TextField(null=True, verbose_name='Old Value', blank=True)), ('new_value', models.TextField(null=True, verbose_name='New Value', blank=True)), ('followup', models.ForeignKey(verbose_name='Follow-up', to='helpdesk.FollowUp')), ], options={ - 'verbose_name': 'Ticket change', 'verbose_name_plural': 'Ticket changes', + 'verbose_name': 'Ticket change', }, bases=(models.Model,), ), migrations.CreateModel( name='TicketCustomFieldValue', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), ('value', models.TextField(null=True, blank=True)), ('field', models.ForeignKey(verbose_name='Field', to='helpdesk.CustomField')), ('ticket', models.ForeignKey(verbose_name='Ticket', to='helpdesk.Ticket')), ], options={ - 'verbose_name': 'Ticket custom field value', 'verbose_name_plural': 'Ticket custom field values', + 'verbose_name': 'Ticket custom field value', }, bases=(models.Model,), ), migrations.CreateModel( name='TicketDependency', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('depends_on', models.ForeignKey(related_name=b'depends_on', verbose_name='Depends On Ticket', to='helpdesk.Ticket')), - ('ticket', models.ForeignKey(related_name=b'ticketdependency', verbose_name='Ticket', to='helpdesk.Ticket')), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('depends_on', models.ForeignKey(related_name='depends_on', verbose_name='Depends On Ticket', to='helpdesk.Ticket')), + ('ticket', models.ForeignKey(related_name='ticketdependency', verbose_name='Ticket', to='helpdesk.Ticket')), ], options={ - 'verbose_name': 'Ticket dependency', 'verbose_name_plural': 'Ticket dependencies', + 'verbose_name': 'Ticket dependency', }, bases=(models.Model,), ), migrations.CreateModel( name='UserSettings', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('settings_pickled', models.TextField(help_text='This is a base64-encoded representation of a pickled Python dictionary. Do not change this field via the admin.', null=True, verbose_name='Settings Dictionary', blank=True)), + ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), + ('settings_pickled', models.TextField(null=True, verbose_name='Settings Dictionary', blank=True, help_text='This is a base64-encoded representation of a pickled Python dictionary. Do not change this field via the admin.')), ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)), ], options={ - 'verbose_name': 'User Setting', 'verbose_name_plural': 'User Settings', + 'verbose_name': 'User Setting', }, bases=(models.Model,), ), @@ -310,13 +313,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='presetreply', name='queues', - field=models.ManyToManyField(help_text='Leave blank to allow this reply to be used for all queues, or select those queues you wish to limit this reply to.', to='helpdesk.Queue', null=True, blank=True), + field=models.ManyToManyField(null=True, to='helpdesk.Queue', blank=True, help_text='Leave blank to allow this reply to be used for all queues, or select those queues you wish to limit this reply to.'), preserve_default=True, ), migrations.AddField( model_name='ignoreemail', name='queues', - field=models.ManyToManyField(help_text='Leave blank for this e-mail to be ignored on all queues, or select those queues you wish to ignore this e-mail for.', to='helpdesk.Queue', null=True, blank=True), + field=models.ManyToManyField(null=True, to='helpdesk.Queue', blank=True, help_text='Leave blank for this e-mail to be ignored on all queues, or select those queues you wish to ignore this e-mail for.'), preserve_default=True, ), migrations.AddField( @@ -328,13 +331,13 @@ class Migration(migrations.Migration): migrations.AddField( model_name='followup', name='user', - field=models.ForeignKey(verbose_name='User', blank=True, to=settings.AUTH_USER_MODEL, null=True), + field=models.ForeignKey(null=True, verbose_name='User', blank=True, to=settings.AUTH_USER_MODEL), preserve_default=True, ), migrations.AddField( model_name='escalationexclusion', name='queues', - field=models.ManyToManyField(help_text='Leave blank for this exclusion to be applied to all queues, or select those queues you wish to exclude with this entry.', to='helpdesk.Queue', null=True, blank=True), + field=models.ManyToManyField(null=True, to='helpdesk.Queue', blank=True, help_text='Leave blank for this exclusion to be applied to all queues, or select those queues you wish to exclude with this entry.'), preserve_default=True, ), migrations.AddField( diff --git a/helpdesk/migrations/0003_populate_usersettings.py b/helpdesk/migrations/0003_populate_usersettings.py index ebf57a8b..36b98310 100644 --- a/helpdesk/migrations/0003_populate_usersettings.py +++ b/helpdesk/migrations/0003_populate_usersettings.py @@ -9,9 +9,12 @@ from helpdesk.settings import DEFAULT_USER_SETTINGS def picke_settings(data): """Pickling as defined at migration's creation time""" - import cPickle + try: + import pickle + except ImportError: + import cPickle as pickle from helpdesk.lib import b64encode - return b64encode(cPickle.dumps(data)) + return b64encode(pickle.dumps(data)) # https://docs.djangoproject.com/en/1.7/topics/migrations/#data-migrations diff --git a/helpdesk/models.py b/helpdesk/models.py index 505b3cba..9f9b32fb 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -646,7 +646,7 @@ def attachment_path(instance, filename): att_path = os.path.join(settings.MEDIA_ROOT, path) if settings.DEFAULT_FILE_STORAGE == "django.core.files.storage.FileSystemStorage": if not os.path.exists(att_path): - os.makedirs(att_path, 0777) + os.makedirs(att_path, 0o777) return os.path.join(path, filename) @@ -1005,17 +1005,23 @@ class UserSettings(models.Model): def _set_settings(self, data): # data should always be a Python dictionary. - import cPickle + try: + import pickle + except ImportError: + import cPickle as pickle from helpdesk.lib import b64encode - self.settings_pickled = b64encode(cPickle.dumps(data)) + self.settings_pickled = b64encode(pickle.dumps(data)) def _get_settings(self): # return a python dictionary representing the pickled data. - import cPickle + try: + import pickle + except ImportError: + import cPickle as pickle from helpdesk.lib import b64decode try: - return cPickle.loads(b64decode(str(self.settings_pickled))) - except cPickle.UnpicklingError: + return pickle.loads(b64decode(str(self.settings_pickled))) + except pickle.UnpicklingError: return {} settings = property(_get_settings, _set_settings) diff --git a/helpdesk/templates/helpdesk/public_base.html b/helpdesk/templates/helpdesk/public_base.html index 6c9fa75d..d1c4e265 100644 --- a/helpdesk/templates/helpdesk/public_base.html +++ b/helpdesk/templates/helpdesk/public_base.html @@ -1,4 +1,5 @@ -{% load i18n %}{% load url from future %} +{% load i18n %} +{% load url from future %} {% load load_helpdesk_settings %} {% with request|load_helpdesk_settings as helpdesk_settings %} diff --git a/helpdesk/templates/helpdesk/public_homepage.html b/helpdesk/templates/helpdesk/public_homepage.html index d991f0f4..786d5264 100644 --- a/helpdesk/templates/helpdesk/public_homepage.html +++ b/helpdesk/templates/helpdesk/public_homepage.html @@ -1,4 +1,6 @@ -{% extends "helpdesk/public_base.html" %}{% load i18n bootstrap %}{% load url from future %} +{% extends "helpdesk/public_base.html" %} +{% load i18n bootstrap %} +{% load url from future %} {% block helpdesk_body %} diff --git a/helpdesk/templatetags/load_helpdesk_settings.py b/helpdesk/templatetags/load_helpdesk_settings.py index 74f3647f..c00579e0 100644 --- a/helpdesk/templatetags/load_helpdesk_settings.py +++ b/helpdesk/templatetags/load_helpdesk_settings.py @@ -9,13 +9,13 @@ from django.template import Library from helpdesk import settings as helpdesk_settings_config def load_helpdesk_settings(request): - try: +# try: return helpdesk_settings_config - except Exception, e: - import sys - print >> sys.stderr, "'load_helpdesk_settings' template tag (django-helpdesk) crashed with following error:" - print >> sys.stderr, e - return '' +# except Exception, e: +# import sys +# print >> sys.stderr, "'load_helpdesk_settings' template tag (django-helpdesk) crashed with following error:" +# print >> sys.stderr, e +# return '' register = Library() register.filter('load_helpdesk_settings', load_helpdesk_settings) diff --git a/helpdesk/templatetags/saved_queries.py b/helpdesk/templatetags/saved_queries.py index 2a742623..5c8d3a34 100644 --- a/helpdesk/templatetags/saved_queries.py +++ b/helpdesk/templatetags/saved_queries.py @@ -12,14 +12,14 @@ from helpdesk.models import SavedSearch def saved_queries(user): - try: +# try: user_saved_queries = SavedSearch.objects.filter(Q(user=user) | Q(shared__exact=True)) return user_saved_queries - except Exception, e: - import sys - print >> sys.stderr, "'saved_queries' template tag (django-helpdesk) crashed with following error:" - print >> sys.stderr, e - return '' +# except Exception, e: +# import sys +# print >> sys.stderr, "'saved_queries' template tag (django-helpdesk) crashed with following error:" +# print >> sys.stderr, e +# return '' register = Library() register.filter('saved_queries', saved_queries) diff --git a/helpdesk/views/public.py b/helpdesk/views/public.py index e164f059..b020cfb2 100644 --- a/helpdesk/views/public.py +++ b/helpdesk/views/public.py @@ -92,11 +92,11 @@ def view_ticket(request): if request.user.is_staff: redirect_url = reverse('helpdesk_view', args=[ticket_id]) - if request.GET.has_key('close'): + if 'close' in request.GET: redirect_url += '?close' return HttpResponseRedirect(redirect_url) - if request.GET.has_key('close') and ticket.status == Ticket.RESOLVED_STATUS: + if 'close' in request.GET and ticket.status == Ticket.RESOLVED_STATUS: from helpdesk.views.staff import update_ticket # Trick the update_ticket() view into thinking it's being called with # a valid POST. @@ -134,7 +134,7 @@ def view_ticket(request): def change_language(request): return_to = '' - if request.GET.has_key('return_to'): + if 'return_to' in request.GET: return_to = request.GET['return_to'] return render_to_response('helpdesk/public_change_language.html', diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index 3b5acb1a..a41c7d2d 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -196,7 +196,7 @@ followup_delete = staff_member_required(followup_delete) def view_ticket(request, ticket_id): ticket = get_object_or_404(Ticket, id=ticket_id) - if request.GET.has_key('take'): + if 'take' in request.GET: # Allow the user to assign the ticket to themselves whilst viewing it. # Trick the update_ticket() view into thinking it's being called with @@ -209,14 +209,14 @@ def view_ticket(request, ticket_id): } return update_ticket(request, ticket_id) - if request.GET.has_key('subscribe'): + if 'subscribe' in request.GET: # Allow the user to subscribe him/herself to the ticket whilst viewing it. ticketcc_string, SHOW_SUBSCRIBE = return_ticketccstring_and_show_subscribe(request.user, ticket) if SHOW_SUBSCRIBE: subscribe_staff_member_to_ticket(ticket, request.user) return HttpResponseRedirect(reverse('helpdesk_view', args=[ticket.id])) - if request.GET.has_key('close') and ticket.status == Ticket.RESOLVED_STATUS: + if 'close' in request.GET and ticket.status == Ticket.RESOLVED_STATUS: if not ticket.assigned_to: owner = 0 else: @@ -702,15 +702,18 @@ def ticket_list(request): if not (saved_query.shared or saved_query.user == request.user): return HttpResponseRedirect(reverse('helpdesk_list')) - import cPickle + try: + import pickle + except ImportError: + import cPickle as pickle from helpdesk.lib import b64decode - query_params = cPickle.loads(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') - or request.GET.has_key('sortreverse') + query_params = pickle.loads(b64decode(str(saved_query.query))) + elif not ( 'queue' in request.GET + or 'assigned_to' in request.GET + or 'status' in request.GET + or 'q' in request.GET + or 'sort' in request.GET + or 'sortreverse' in request.GET ): # Fall-back if no querying is being done, force the list to only @@ -799,13 +802,16 @@ def ticket_list(request): tickets = ticket_paginator.page(ticket_paginator.num_pages) search_message = '' - if context.has_key('query') and settings.DATABASES['default']['ENGINE'].endswith('sqlite'): + if 'query' in context and settings.DATABASES['default']['ENGINE'].endswith('sqlite'): search_message = _('

Note: Your keyword search is case sensitive because of your database. This means the search will not be accurate. By switching to a different database system you will gain better searching! For more information, read the Django Documentation on string matching in SQLite.') - import cPickle + try: + import pickle + except ImportError: + import cPickle as pickle from helpdesk.lib import b64encode - urlsafe_query = b64encode(cPickle.dumps(query_params)) + urlsafe_query = b64encode(pickle.dumps(query_params)) user_saved_queries = SavedSearch.objects.filter(Q(user=request.user) | Q(shared__exact=True)) @@ -864,7 +870,7 @@ def create_ticket(request): initial_data = {} if request.user.usersettings.settings.get('use_email_as_submitter', False) and request.user.email: initial_data['submitter_email'] = request.user.email - if request.GET.has_key('queue'): + if 'queue' in request.GET: initial_data['queue'] = request.GET['queue'] form = TicketForm(initial=initial_data) @@ -966,9 +972,12 @@ def run_report(request, report): if not (saved_query.shared or saved_query.user == request.user): return HttpResponseRedirect(reverse('helpdesk_report_index')) - import cPickle + try: + import pickle + except ImportError: + import cPickle as pickle from helpdesk.lib import b64decode - query_params = cPickle.loads(b64decode(str(saved_query.query))) + query_params = pickle.loads(b64decode(str(saved_query.query))) report_queryset = apply_query(report_queryset, query_params) from collections import defaultdict @@ -1249,7 +1258,7 @@ def ticket_dependency_add(request, ticket_id): if form.is_valid(): ticketdependency = form.save(commit=False) ticketdependency.ticket = ticket - if ticketdependency.ticket <> ticketdependency.depends_on: + if ticketdependency.ticket != ticketdependency.depends_on: ticketdependency.save() return HttpResponseRedirect(reverse('helpdesk_view', args=[ticket.id])) else: From eb3beb0dc902ec8f28539d6faa349b0a55208128 Mon Sep 17 00:00:00 2001 From: Emmanuel Cohen Date: Fri, 27 Mar 2015 16:33:31 +0100 Subject: [PATCH 11/16] Fix statistics for python 3 (still compatible with python 2) --- helpdesk/views/staff.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index a41c7d2d..b36bd5d5 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -6,7 +6,8 @@ django-helpdesk - A Django powered ticket tracker for small enterprise. views/staff.py - The bulk of the application - provides most business logic and renders all staff-facing views. """ - +from __future__ import unicode_literals +from django.utils.encoding import python_2_unicode_compatible from datetime import datetime, timedelta import sys @@ -1012,7 +1013,7 @@ def run_report(request, report): if report == 'userpriority': title = _('User by Priority') col1heading = _('User') - possible_options = [t[1].__unicode__() for t in Ticket.PRIORITY_CHOICES] + possible_options = [t[1].__str__() for t in Ticket.PRIORITY_CHOICES] charttype = 'bar' elif report == 'userqueue': @@ -1024,7 +1025,7 @@ def run_report(request, report): elif report == 'userstatus': title = _('User by Status') col1heading = _('User') - possible_options = [s[1].__unicode__() for s in Ticket.STATUS_CHOICES] + possible_options = [s[1].__str__() for s in Ticket.STATUS_CHOICES] charttype = 'bar' elif report == 'usermonth': @@ -1036,13 +1037,13 @@ def run_report(request, report): elif report == 'queuepriority': title = _('Queue by Priority') col1heading = _('Queue') - possible_options = [t[1].__unicode__() for t in Ticket.PRIORITY_CHOICES] + possible_options = [t[1].__str__() for t in Ticket.PRIORITY_CHOICES] charttype = 'bar' elif report == 'queuestatus': title = _('Queue by Status') col1heading = _('Queue') - possible_options = [s[1].__unicode__() for s in Ticket.STATUS_CHOICES] + possible_options = [s[1].__str__() for s in Ticket.STATUS_CHOICES] charttype = 'bar' elif report == 'queuemonth': From 9600f457d838521fc5b7011a6fea3bdf25e88213 Mon Sep 17 00:00:00 2001 From: Tony Zhu Date: Tue, 14 Apr 2015 16:25:20 -0400 Subject: [PATCH 12/16] Remove no effect model property to silence django 1.8 system check warning --- helpdesk/models.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/helpdesk/models.py b/helpdesk/models.py index 505b3cba..69ebecbe 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -715,7 +715,6 @@ class PreSetReply(models.Model): queues = models.ManyToManyField( Queue, blank=True, - null=True, help_text=_('Leave blank to allow this reply to be used for all ' 'queues, or select those queues you wish to limit this reply to.'), ) @@ -757,7 +756,6 @@ class EscalationExclusion(models.Model): queues = models.ManyToManyField( Queue, blank=True, - null=True, help_text=_('Leave blank for this exclusion to be applied to all ' 'queues, or select those queues you wish to exclude with this ' 'entry.'), @@ -1061,7 +1059,6 @@ class IgnoreEmail(models.Model): queues = models.ManyToManyField( Queue, blank=True, - null=True, help_text=_('Leave blank for this e-mail to be ignored on all ' 'queues, or select those queues you wish to ignore this e-mail ' 'for.'), From 807e0c5ea98add83e1d74034fe06ff370780495d Mon Sep 17 00:00:00 2001 From: Tony Zhu Date: Tue, 14 Apr 2015 16:29:47 -0400 Subject: [PATCH 13/16] Remove extra space line --- helpdesk/views/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helpdesk/views/api.py b/helpdesk/views/api.py index 04e32c4b..341337ac 100644 --- a/helpdesk/views/api.py +++ b/helpdesk/views/api.py @@ -324,4 +324,4 @@ class API: ticket.save() - return api_return(STATUS_OK) + return api_return(STATUS_OK) \ No newline at end of file From ed9581e5de6d57400b32589d4c7b4441d97f4f9e Mon Sep 17 00:00:00 2001 From: Tony Zhu Date: Thu, 16 Apr 2015 11:58:24 -0400 Subject: [PATCH 14/16] Remove deprecated load url from future --- helpdesk/templates/helpdesk/base.html | 1 - helpdesk/templates/helpdesk/email_ignore_list.html | 2 +- helpdesk/templates/helpdesk/help_api.html | 2 +- helpdesk/templates/helpdesk/help_base.html | 1 - helpdesk/templates/helpdesk/include/stats.html | 2 +- helpdesk/templates/helpdesk/include/tickets.html | 2 +- helpdesk/templates/helpdesk/include/unassigned.html | 2 +- helpdesk/templates/helpdesk/navigation.html | 2 +- helpdesk/templates/helpdesk/public_base.html | 2 +- helpdesk/templates/helpdesk/public_homepage.html | 2 +- helpdesk/templates/helpdesk/public_view_form.html | 2 +- helpdesk/templates/helpdesk/rss_list.html | 2 +- helpdesk/templates/helpdesk/system_settings.html | 2 +- helpdesk/templates/helpdesk/ticket.html | 2 +- helpdesk/templates/helpdesk/ticket_cc_add.html | 2 +- helpdesk/templates/helpdesk/ticket_cc_del.html | 2 +- helpdesk/templates/helpdesk/ticket_cc_list.html | 2 +- helpdesk/templates/helpdesk/ticket_dependency_add.html | 2 +- helpdesk/templates/helpdesk/ticket_dependency_del.html | 2 +- helpdesk/templates/helpdesk/ticket_desc_table.html | 2 +- helpdesk/templates/helpdesk/ticket_list.html | 2 +- helpdesk/templates/helpdesk/user_settings.html | 2 +- 22 files changed, 20 insertions(+), 22 deletions(-) diff --git a/helpdesk/templates/helpdesk/base.html b/helpdesk/templates/helpdesk/base.html index 71ad129e..9f9e1eb7 100644 --- a/helpdesk/templates/helpdesk/base.html +++ b/helpdesk/templates/helpdesk/base.html @@ -1,5 +1,4 @@ {% load i18n %} -{% load url from future %} {% load saved_queries %} {% load load_helpdesk_settings %} {% with request|load_helpdesk_settings as helpdesk_settings %} diff --git a/helpdesk/templates/helpdesk/email_ignore_list.html b/helpdesk/templates/helpdesk/email_ignore_list.html index 80d30c1d..5fbf3e2f 100644 --- a/helpdesk/templates/helpdesk/email_ignore_list.html +++ b/helpdesk/templates/helpdesk/email_ignore_list.html @@ -1,4 +1,4 @@ -{% extends "helpdesk/base.html" %}{% load i18n %}{% load url from future %} +{% extends "helpdesk/base.html" %}{% load i18n %} {% block helpdesk_title %}{% trans "Ignored E-Mail Addresses" %}{% endblock %} diff --git a/helpdesk/templates/helpdesk/help_api.html b/helpdesk/templates/helpdesk/help_api.html index 888992f2..bcb7cb92 100644 --- a/helpdesk/templates/helpdesk/help_api.html +++ b/helpdesk/templates/helpdesk/help_api.html @@ -1,4 +1,4 @@ -{% extends "helpdesk/help_base.html" %}{% load url from future %} +{% extends "helpdesk/help_base.html" %} {% block title %}django-helpdesk API Documentation{% endblock %} {% block heading %}django-helpdesk API Documentation{% endblock %} diff --git a/helpdesk/templates/helpdesk/help_base.html b/helpdesk/templates/helpdesk/help_base.html index 11903d2d..03dbee2b 100644 --- a/helpdesk/templates/helpdesk/help_base.html +++ b/helpdesk/templates/helpdesk/help_base.html @@ -1,4 +1,3 @@ -{% load url from future %}