Merge 0.2.8 bugfixes

This commit is contained in:
Garret Wassermann 2018-07-03 00:59:33 -04:00
commit dff14d40d3
13 changed files with 85 additions and 63 deletions

View File

@ -7,7 +7,7 @@ python:
- "3.6"
env:
- DJANGO=1.11.9
- DJANGO=1.11.13
install:
- pip install -q Django==$DJANGO
@ -15,7 +15,7 @@ install:
- pip install -q -r requirements-testing.txt
before_script:
- "pycodestyle --exclude=migrations --ignore=E501 helpdesk"
- "pycodestyle --exclude=migrations --ignore=E501,W503,W504 helpdesk"
script:
- coverage run --source='.' quicktest.py helpdesk

View File

@ -19,11 +19,9 @@ AUTHOR = 'django-helpdesk team'
URL = 'https://github.com/django-helpdesk/django-helpdesk'
CLASSIFIERS = ['Development Status :: 4 - Beta',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Framework :: Django :: 1.11',
'Framework :: Django :: 2.0']
KEYWORDS = []
PACKAGES = ['demodesk']

View File

@ -215,4 +215,4 @@ The following settings were defined in previous versions and are no longer suppo
- **HELPDESK_FOOTER_SHOW_CHANGE_LANGUAGE_LINK** Is never shown. Use your own template if required.
- **HELPDESK_ENABLE_PER_QUEUE_MEMBERSHIP** Discontinued in favor of HELPDESK_ENABLE_PER_QUEUE_PERMISSION.
- **HELPDESK_ENABLE_PER_QUEUE_MEMBERSHIP** Discontinued in favor of HELPDESK_ENABLE_PER_QUEUE_STAFF_PERMISSION.

View File

@ -12,6 +12,9 @@
# Released subject to the BSD License
# See http://www.voidspace.org.uk/python/license.shtml
# Updated by django-helpdesk developers, 2018
# to be compatible with python 3
"""
A python interface to the `Akismet <http://akismet.com>`_ API.
@ -56,7 +59,10 @@ Usage example::
import os
from urllib import urlencode
try:
from urllib import urlencode # python2
except ImportError:
from urllib.parse import urlencode # python3
import socket
if hasattr(socket, 'setdefaulttimeout'):

View File

@ -216,7 +216,7 @@ def process_queue(q, logger):
q.email_box_pass or
settings.QUEUE_EMAIL_BOX_PASSWORD)
server.select(q.email_box_imap_folder)
except imaplib.IMAP.abort:
except imaplib.IMAP4.abort:
logger.error("IMAP login failed. Check that the server is accessible and that the username and password are correct.")
server.logout()
sys.exit()
@ -332,7 +332,7 @@ def ticket_from_message(message, queue, logger):
return False
return True
matchobj = re.match(r".*\[" + queue.slug + "-(?P<id>\d+)\]", subject)
matchobj = re.match(r".*\[" + queue.slug + r"-(?P<id>\d+)\]", subject)
if matchobj:
# This is a reply or forward.
ticket = matchobj.group('id')

View File

@ -17,6 +17,7 @@ from django.utils import timezone
from django.utils import six
from django.utils.translation import ugettext_lazy as _, ugettext
from django.utils.encoding import python_2_unicode_compatible
import re
import six
@ -279,7 +280,13 @@ class Queue(models.Model):
in the sender name field, so hopefully the admin can see and fix it.
"""
if not self.email_address:
return u'NO QUEUE EMAIL ADDRESS DEFINED <%s>' % settings.DEFAULT_FROM_EMAIL
# must check if given in format "Foo <foo@example.com>"
default_email = re.match(".*<(?P<email>.*@*.)>", settings.DEFAULT_FROM_EMAIL)
if default_email is not None:
# already in the right format, so just include it here
return u'NO QUEUE EMAIL ADDRESS DEFINED %s' % settings.DEFAULT_FROM_EMAIL
else:
return u'NO QUEUE EMAIL ADDRESS DEFINED <%s>' % settings.DEFAULT_FROM_EMAIL
else:
return u'%s <%s>' % (self.title, self.email_address)
from_address = property(_from_address)

View File

@ -11,12 +11,11 @@
<p>{% 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 %}</p>
{% endif %}
<p><a href='{% url 'helpdesk:list' %}?saved_query={{ query.id }}'><button class="btn btn-primary btn-lg">{% trans "No, Don't Delete It" %}</button></a></p>
<p><a href='../'><button class="btn btn-primary btn-lg">{% trans "No, Don't Delete It" %}</button></a></p>
<form method='post' action='./'>
<form method='post' action='./'>{% csrf_token %}
<button class="btn btn-danger" type='submit'>{% trans "Yes I Understand - Delete It Anyway" %}</button>
{% csrf_token %}</form>
</form>
{% endblock %}

View File

@ -37,4 +37,11 @@
</div>
</div>
</div>
<script>
$( function() {
$( "#id_due_date" ).datepicker();
} );
</script>
{% endblock %}

View File

@ -214,6 +214,7 @@ $(document).on('change', ':file', function() {
<dt><label for='id_priority'>{% trans "Priority" %}</label></dt>
<dd><select id='id_priority' name='priority'>{% for p in priorities %}<option value='{{ p.0 }}'{% ifequal p.0 ticket.priority %} selected='selected'{% endifequal %}>{{ p.1 }}</option>{% endfor %}</select></dd>
<dt><label for='id_due_date'>{% trans "Due on" %}</label></dt>
<dd>{{ form.due_date }}</dd>

View File

@ -38,6 +38,11 @@
<tr>
<td colspan='2'>{{ ticket.resolution|force_escape|urlizetrunc:50|linebreaksbr }}</td>
</tr>{% endif %}
<tr>
<th>{% trans "Due Date" %}</th>
<td>{{ ticket.due_date|date:"r" }} ({{ ticket.due_date|naturaltime }})</td>
</tr>
<tr>
<th>{% trans "Submitted On" %}</th>
<td>{{ ticket.created|date:"r" }} ({{ ticket.created|naturaltime }})</td>

View File

@ -223,6 +223,7 @@ $(document).ready(function() {
<th>{% trans "Queue" %}</th>
<th>{% trans "Status" %}</th>
<th>{% trans "Created" %}</th>
<th>{% trans "Due Date" %}</th>
<th>{% trans "Owner" %}</th>
</tr>
</thead>
@ -236,6 +237,7 @@ $(document).ready(function() {
<td>{{ ticket.queue }}</td>
<td>{{ ticket.get_status }}</td>
<td data-order='{{ ticket.created|date:"U" }}'><span title='{{ ticket.created|date:"r" }}'>{{ ticket.created|naturaltime }}</span></td>
<td data-order='{{ ticket.due_date|date:"U" }}'><span title='{{ ticket.due_date|date:"r" }}'>{{ ticket.due_date|naturaltime }}</span></td>
<td>{{ ticket.get_assigned_to }}</td>
</tr>
{% endfor %}

View File

@ -7,8 +7,10 @@ views/staff.py - The bulk of the application - provides most business logic and
renders all staff-facing views.
"""
from __future__ import unicode_literals
from datetime import datetime, timedelta
from datetime import date, datetime, timedelta
import re
from django import VERSION as DJANGO_VERSION
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import user_passes_test
@ -155,21 +157,6 @@ def dashboard(request):
else:
where_clause = """WHERE q.id = t.queue_id"""
cursor = connection.cursor()
cursor.execute("""
SELECT q.id as queue,
q.title AS name,
COUNT(CASE t.status WHEN '1' THEN t.id WHEN '2' THEN t.id END) AS open,
COUNT(CASE t.status WHEN '3' THEN t.id END) AS resolved,
COUNT(CASE t.status WHEN '4' THEN t.id END) AS closed
%s
%s
GROUP BY queue, name
ORDER BY q.id;
""" % (from_clause, where_clause))
dash_tickets = query_to_dict(cursor.fetchall(), cursor.description)
return render(request, 'helpdesk/dashboard.html', {
'user_tickets': tickets,
'user_tickets_closed_resolved': tickets_closed_resolved,
@ -404,6 +391,10 @@ def update_ticket(request, ticket_id, public=False):
ticket = get_object_or_404(Ticket, id=ticket_id)
date_re = re.compile(
r'(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<year>\d{4})$'
)
comment = request.POST.get('comment', '')
new_status = int(request.POST.get('new_status', ticket.status))
title = request.POST.get('title', '')
@ -413,15 +404,29 @@ def update_ticket(request, ticket_id, public=False):
due_date_year = int(request.POST.get('due_date_year', 0))
due_date_month = int(request.POST.get('due_date_month', 0))
due_date_day = int(request.POST.get('due_date_day', 0))
# NOTE: jQuery's default for dates is mm/dd/yy
# very US-centric but for now that's the only format supported
# until we clean up code to internationalize a little more
due_date = request.POST.get('due_date', None)
if not (due_date_year and due_date_month and due_date_day):
due_date = ticket.due_date
if due_date is not None:
# based on Django code to parse dates:
# https://docs.djangoproject.com/en/2.0/_modules/django/utils/dateparse/
match = date_re.match(due_date)
if match:
kw = {k: int(v) for k, v in match.groupdict().items()}
due_date = date(**kw)
else:
if ticket.due_date:
# old way, probably deprecated?
if not (due_date_year and due_date_month and due_date_day):
due_date = ticket.due_date
else:
due_date = timezone.now()
due_date = due_date.replace(due_date_year, due_date_month, due_date_day)
# NOTE: must be an easier way to create a new date than doing it this way?
if ticket.due_date:
due_date = ticket.due_date
else:
due_date = timezone.now()
due_date = due_date.replace(due_date_year, due_date_month, due_date_day)
no_changes = all([
not request.FILES,
@ -859,7 +864,11 @@ def ticket_list(request):
from helpdesk.lib import b64decode
try:
if six.PY3:
query_params = json.loads(b64decode(str(saved_query.query)).decode())
if DJANGO_VERSION[0] > 1:
# if Django >= 2.0
query_params = json.loads(b64decode(str(saved_query.query).lstrip("b\\'")).decode())
else:
query_params = json.loads(b64decode(str(saved_query.query)).decode())
else:
query_params = json.loads(b64decode(str(saved_query.query)))
except ValueError:
@ -1123,31 +1132,18 @@ def report_index(request):
# Open Resolved
# Queue 1 10 4
# Queue 2 4 12
Queues = user_queues if user_queues else Queue.objects.all()
queues = _get_user_queues(request.user).values_list('id', flat=True)
from_clause = """FROM helpdesk_ticket t,
helpdesk_queue q"""
if queues:
where_clause = """WHERE q.id = t.queue_id AND
q.id IN (%s)""" % (",".join(("%d" % pk for pk in queues)))
else:
where_clause = """WHERE q.id = t.queue_id"""
cursor = connection.cursor()
cursor.execute("""
SELECT q.id as queue,
q.title AS name,
COUNT(CASE t.status WHEN '1' THEN t.id WHEN '2' THEN t.id END) AS open,
COUNT(CASE t.status WHEN '3' THEN t.id END) AS resolved,
COUNT(CASE t.status WHEN '4' THEN t.id END) AS closed
%s
%s
GROUP BY queue, name
ORDER BY q.id;
""" % (from_clause, where_clause))
dash_tickets = query_to_dict(cursor.fetchall(), cursor.description)
dash_tickets = []
for queue in Queues:
dash_ticket = {
'queue': queue.id,
'name': queue.title,
'open': queue.ticket_set.filter(status__in=[1, 2]).count(),
'resolved': queue.ticket_set.filter(status=3).count(),
'closed': queue.ticket_set.filter(status=4).count(),
}
dash_tickets.append(dash_ticket)
return render(request, 'helpdesk/report_index.html', {
'number_tickets': number_tickets,
@ -1187,7 +1183,11 @@ def run_report(request, report):
from helpdesk.lib import b64decode
try:
if six.PY3:
query_params = json.loads(b64decode(str(saved_query.query)).decode())
if DJANGO_VERSION[0] > 1:
# if Django >= 2.0
query_params = json.loads(b64decode(str(saved_query.query).lstrip("b\\'")).decode())
else:
query_params = json.loads(b64decode(str(saved_query.query)).decode())
else:
query_params = json.loads(b64decode(str(saved_query.query)))
except json.JSONDecodeError:

View File

@ -128,14 +128,11 @@ setup(
classifiers=[
"Development Status :: 4 - Beta",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Framework :: Django",
"Framework :: Django :: 1.11",
"Framework :: Django :: 2.0",
"Environment :: Web Environment",
"Operating System :: OS Independent",