forked from extern/django-helpdesk
Merge 0.2.8 bugfixes
This commit is contained in:
commit
dff14d40d3
@ -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
|
||||
|
@ -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']
|
||||
|
@ -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.
|
||||
|
@ -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'):
|
||||
|
@ -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')
|
||||
|
@ -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,6 +280,12 @@ class Queue(models.Model):
|
||||
in the sender name field, so hopefully the admin can see and fix it.
|
||||
"""
|
||||
if not self.email_address:
|
||||
# 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)
|
||||
|
@ -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 %}
|
||||
|
@ -37,4 +37,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$( function() {
|
||||
$( "#id_due_date" ).datepicker();
|
||||
} );
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 %}
|
||||
|
@ -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,10 +404,24 @@ 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 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:
|
||||
# old way, probably deprecated?
|
||||
if not (due_date_year and due_date_month and due_date_day):
|
||||
due_date = ticket.due_date
|
||||
else:
|
||||
# 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:
|
||||
@ -859,6 +864,10 @@ def ticket_list(request):
|
||||
from helpdesk.lib import b64decode
|
||||
try:
|
||||
if six.PY3:
|
||||
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)))
|
||||
@ -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,6 +1183,10 @@ def run_report(request, report):
|
||||
from helpdesk.lib import b64decode
|
||||
try:
|
||||
if six.PY3:
|
||||
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)))
|
||||
|
3
setup.py
3
setup.py
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user