forked from extern/django-helpdesk
Merge #1009 Django 4 support into unstable
This commit is contained in:
commit
224f1cb83e
@ -13,7 +13,7 @@ Including another URLconf
|
||||
1. Import the include() function: from django.conf.urls import url, include
|
||||
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.conf.urls import url, include
|
||||
from django.urls import include, path
|
||||
from django.contrib import admin
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
@ -26,7 +26,7 @@ from django.conf.urls.static import static
|
||||
# https://docs.djangoproject.com/en/1.10/howto/static-files/
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^admin/', admin.site.urls),
|
||||
url(r'^', include('helpdesk.urls', namespace='helpdesk')),
|
||||
url(r'^api/auth/', include('rest_framework.urls', namespace='rest_framework'))
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('helpdesk.urls', namespace='helpdesk')),
|
||||
path('api/auth/', include('rest_framework.urls', namespace='rest_framework'))
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
@ -1,7 +1,7 @@
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from helpdesk.models import Queue, Ticket, FollowUp, PreSetReply
|
||||
from helpdesk.models import EscalationExclusion, EmailTemplate
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from helpdesk.models import Queue, Ticket, FollowUp, PreSetReply, KBCategory
|
||||
from helpdesk.models import EscalationExclusion, EmailTemplate, KBItem
|
||||
from helpdesk.models import TicketChange, KBIAttachment, FollowUpAttachment, IgnoreEmail
|
||||
from helpdesk.models import CustomField
|
||||
from helpdesk import settings as helpdesk_settings
|
||||
|
@ -27,7 +27,7 @@ from django.core.exceptions import ValidationError
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.db.models import Q
|
||||
from django.utils import encoding, timezone
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from email_reply_parser import EmailReplyParser
|
||||
|
||||
from helpdesk import settings
|
||||
@ -129,7 +129,7 @@ def pop3_sync(q, logger, server):
|
||||
if type(raw_content[0]) is bytes:
|
||||
full_message = "\n".join([elm.decode('utf-8') for elm in raw_content])
|
||||
else:
|
||||
full_message = encoding.force_text("\n".join(raw_content), errors='replace')
|
||||
full_message = encoding.force_str("\n".join(raw_content), errors='replace')
|
||||
ticket = object_from_message(message=full_message, queue=q, logger=logger)
|
||||
|
||||
if ticket:
|
||||
@ -175,7 +175,7 @@ def imap_sync(q, logger, server):
|
||||
for num in msgnums:
|
||||
logger.info("Processing message %s" % num)
|
||||
status, data = server.fetch(num, '(RFC822)')
|
||||
full_message = encoding.force_text(data[0][1], errors='replace')
|
||||
full_message = encoding.force_str(data[0][1], errors='replace')
|
||||
try:
|
||||
ticket = object_from_message(message=full_message, queue=q, logger=logger)
|
||||
except TypeError:
|
||||
@ -268,7 +268,7 @@ def process_queue(q, logger):
|
||||
for i, m in enumerate(mail, 1):
|
||||
logger.info("Processing message %d" % i)
|
||||
with open(m, 'r') as f:
|
||||
full_message = encoding.force_text(f.read(), errors='replace')
|
||||
full_message = encoding.force_str(f.read(), errors='replace')
|
||||
ticket = object_from_message(message=full_message, queue=q, logger=logger)
|
||||
if ticket:
|
||||
logger.info("Successfully processed message %d, ticket/comment created.", i)
|
||||
@ -579,9 +579,9 @@ def object_from_message(message, queue, logger):
|
||||
logger.debug("Discovered plain text MIME part")
|
||||
else:
|
||||
try:
|
||||
email_body = encoding.smart_text(part.get_payload(decode=True))
|
||||
email_body = encoding.smart_str(part.get_payload(decode=True))
|
||||
except UnicodeDecodeError:
|
||||
email_body = encoding.smart_text(part.get_payload(decode=False))
|
||||
email_body = encoding.smart_str(part.get_payload(decode=False))
|
||||
|
||||
if not body and not full_body:
|
||||
# no text has been parsed so far - try such deep parsing for some messages
|
||||
|
@ -12,7 +12,7 @@ from datetime import datetime, date, time
|
||||
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.utils import timezone
|
||||
|
||||
|
@ -11,7 +11,7 @@ import mimetypes
|
||||
from datetime import datetime, date, time
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.encoding import smart_text
|
||||
from django.utils.encoding import smart_str
|
||||
|
||||
from helpdesk.settings import CUSTOMFIELD_DATETIME_FORMAT, CUSTOMFIELD_DATE_FORMAT, CUSTOMFIELD_TIME_FORMAT
|
||||
|
||||
@ -117,13 +117,13 @@ def text_is_spam(text, request):
|
||||
if ak.verify_key():
|
||||
ak_data = {
|
||||
'user_ip': request.META.get('REMOTE_ADDR', '127.0.0.1'),
|
||||
'user_agent': request.META.get('HTTP_USER_AGENT', ''),
|
||||
'referrer': request.META.get('HTTP_REFERER', ''),
|
||||
'user_agent': request.headers.get('User-Agent', ''),
|
||||
'referrer': request.headers.get('Referer', ''),
|
||||
'comment_type': 'comment',
|
||||
'comment_author': '',
|
||||
}
|
||||
|
||||
return ak.comment_check(smart_text(text), data=ak_data)
|
||||
return ak.comment_check(smart_str(text), data=ak_data)
|
||||
|
||||
return False
|
||||
|
||||
@ -135,8 +135,9 @@ def process_attachments(followup, attached_files):
|
||||
for attached in attached_files:
|
||||
|
||||
if attached.size:
|
||||
filename = smart_text(attached.name)
|
||||
att = followup.followupattachment_set.create(
|
||||
filename = smart_str(attached.name)
|
||||
att = FollowUpAttachment(
|
||||
followup=followup,
|
||||
file=attached,
|
||||
filename=filename,
|
||||
mime_type=attached.content_type or
|
||||
|
@ -19,7 +19,7 @@ from django.contrib.auth.models import Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db.utils import IntegrityError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from helpdesk.models import Queue
|
||||
|
||||
|
@ -8,7 +8,7 @@ create_usersettings.py - Easy way to create helpdesk-specific settings for
|
||||
users who don't yet have them.
|
||||
"""
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
|
@ -15,7 +15,7 @@ import sys
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils import timezone
|
||||
|
||||
from helpdesk.models import Queue, Ticket, FollowUp, EscalationExclusion, TicketChange
|
||||
|
@ -2,7 +2,7 @@
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db import migrations
|
||||
from django.db.utils import IntegrityError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
def create_and_assign_permissions(apps, schema_editor):
|
||||
|
@ -14,7 +14,7 @@ from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _, ugettext
|
||||
from django.utils.translation import gettext_lazy as _, gettext
|
||||
from io import StringIO
|
||||
import re
|
||||
import os
|
||||
@ -1031,11 +1031,11 @@ class TicketChange(models.Model):
|
||||
def __str__(self):
|
||||
out = '%s ' % self.field
|
||||
if not self.new_value:
|
||||
out += ugettext('removed')
|
||||
out += gettext('removed')
|
||||
elif not self.old_value:
|
||||
out += ugettext('set to %s') % self.new_value
|
||||
out += gettext('set to %s') % self.new_value
|
||||
else:
|
||||
out += ugettext('changed from "%(old_value)s" to "%(new_value)s"') % {
|
||||
out += gettext('changed from "%(old_value)s" to "%(new_value)s"') % {
|
||||
'old_value': self.old_value,
|
||||
'new_value': self.new_value
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
from django.db.models import Q
|
||||
from django.urls import reverse
|
||||
from django.utils.html import escape
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from base64 import b64encode
|
||||
from base64 import b64decode
|
||||
|
@ -3,9 +3,9 @@
|
||||
<h2>Queries</h2>
|
||||
<p>
|
||||
{{ sql_queries|length }} Quer{{ sql_queries|pluralize:"y,ies" }}
|
||||
{% ifnotequal sql_queries|length 0 %}
|
||||
{% if sql_queries|length != 0 %}
|
||||
(<span style="cursor: pointer;" onclick="var s=document.getElementById('debugQueryTable').style;s.display=s.display=='none'?'':'none';this.innerHTML=this.innerHTML=='Show'?'Hide':'Show';">Show</span>)
|
||||
{% endifnotequal %}
|
||||
{% endif %}
|
||||
</p>
|
||||
<table id="debugQueryTable" style="display: none;">
|
||||
<col width="1"></col>
|
||||
|
@ -14,7 +14,7 @@
|
||||
{% endwith %}
|
||||
{% for u in user_choices %}
|
||||
<option value='{{ u.id }}'{% if u.id|in_list:query_params.filtering.assigned_to__id__in %} selected='selected'{% endif %}>
|
||||
{{ u.get_username }}{% ifequal u user %} {% trans "(ME)" %}{% endifequal %}
|
||||
{{ u.get_username }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
@ -6,22 +6,22 @@
|
||||
</div>
|
||||
<div class="col col-sm-3">
|
||||
<select id='id_sortx' name='sortx' class="">
|
||||
<option value='created'{% ifequal query_params.sorting "created"%} selected='selected'{% endifequal %}>
|
||||
<option value='created'{% if query_params.sorting == "created"%} selected='selected'{% endif %}>
|
||||
{% trans "Created" %}
|
||||
</option>
|
||||
<option value='title'{% ifequal query_params.sorting "title"%} selected='selected'{% endifequal %}>
|
||||
<option value='title'{% if query_params.sorting == "title"%} selected='selected'{% endif %}>
|
||||
{% trans "Title" %}
|
||||
</option>
|
||||
<option value='queue'{% ifequal query_params.sorting "queue"%} selected='selected'{% endifequal %}>
|
||||
<option value='queue'{% if query_params.sorting == "queue"%} selected='selected'{% endif %}>
|
||||
{% trans "Queue" %}
|
||||
</option>
|
||||
<option value='status'{% ifequal query_params.sorting "status"%} selected='selected'{% endifequal %}>
|
||||
<option value='status'{% if query_params.sorting == "status"%} selected='selected'{% endif %}>
|
||||
{% trans "Status" %}
|
||||
</option>
|
||||
<option value='priority'{% ifequal query_params.sorting "priority"%} selected='selected'{% endifequal %}>
|
||||
<option value='priority'{% if query_params.sorting == "priority"%} selected='selected'{% endif %}>
|
||||
{% trans "Priority" %}
|
||||
</option>
|
||||
<option value='assigned_to'{% ifequal query_params.sorting "assigned_to"%} selected='selected'{% endifequal %}>
|
||||
<option value='assigned_to'{% if query_params.sorting == "assigned_to"%} selected='selected'{% endif %}>
|
||||
{% trans "Owner" %}
|
||||
</option>
|
||||
</select>
|
||||
|
@ -34,7 +34,7 @@
|
||||
{% for q in user_saved_queries_ %}
|
||||
<a class="dropdown-item" href="{% url 'helpdesk:list' %}?saved_query={{ q.id }}">{{ q.title }}
|
||||
{% if q.shared %}
|
||||
(Shared{% ifnotequal user q.user %} by {{ q.user.get_username }}{% endifnotequal %})
|
||||
(Shared{% if user != q.user %} by {{ q.user.get_username }}{% endif %})
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
@ -25,7 +25,7 @@
|
||||
{% for q in user_saved_queries_ %}
|
||||
<a class="dropdown-item small" href="{% url 'helpdesk:list' %}?saved_query={{ q.id }}">{{ q.title }}
|
||||
{% if q.shared %}
|
||||
(Shared{% ifnotequal user q.user %} by {{ q.user.get_username }}{% endifnotequal %})
|
||||
(Shared{% if user != q.user %} by {{ q.user.get_username }}{% endif %})
|
||||
{% endif %}
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
@ -99,34 +99,34 @@
|
||||
<dd class='form_help_text'>{% trans "You can insert ticket and queue details in your message. For more information, see the <a href='../../help/context/'>context help page</a>." %}</dd>
|
||||
|
||||
{% if not ticket.can_be_resolved %}<dd>{% trans "This ticket cannot be resolved or closed until the tickets it depends on are resolved." %}</dd>{% endif %}
|
||||
{% ifequal ticket.status 1 %}
|
||||
{% if ticket.status == 1 %}
|
||||
<input type="hidden" name="new_status" value="{{ ticket.status }}" />
|
||||
{% endifequal %}
|
||||
{% ifequal ticket.status 2 %}
|
||||
{% endif %}
|
||||
{% if ticket.status == 2 %}
|
||||
<dd><div class="form-group">
|
||||
<label for='st_reopened' class='active radio-inline'><input type='radio' name='new_status' value='2' id='st_reopened' checked='checked'>{% trans "Reopened" %} »</label>
|
||||
<label class="radio-inline" for='st_resolved'><input type='radio' name='new_status' value='3' id='st_resolved'{% if not ticket.can_be_resolved %} disabled='disabled'{% endif %}>{% trans "Resolved" %} »</label>
|
||||
<label class="radio-inline" for='st_closed'><input type='radio' name='new_status' value='4' id='st_closed'{% if not ticket.can_be_resolved %} disabled='disabled'{% endif %}>{% trans "Closed" %} »</label>
|
||||
<label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate'>{% trans "Duplicate" %}</label>
|
||||
</div></dd>
|
||||
{% endifequal %}
|
||||
{% ifequal ticket.status 3 %}
|
||||
{% endif %}
|
||||
{% if ticket.status == 3 %}
|
||||
<dd><div class="form-group">
|
||||
<label for='st_reopened' class="radio-inline"><input type='radio' name='new_status' value='2' id='st_reopened'>{% trans "Reopened" %} «</label>
|
||||
<label for='st_resolved' class='active radio-inline'><input type='radio' name='new_status' value='3' id='st_resolved' checked='checked'>{% trans "Resolved" %} »</label>
|
||||
<label class="radio-inline" for='st_closed'><input type='radio' name='new_status' value='4' id='st_closed'>{% trans "Closed" %}</label>
|
||||
</div></dd>
|
||||
{% endifequal %}
|
||||
{% ifequal ticket.status 4 %}
|
||||
{% endif %}
|
||||
{% if ticket.status == 4 %}
|
||||
<dd><div class="form-group"><label for='st_reopened' class="radio-inline"><input type='radio' name='new_status' value='2' id='st_reopened'>{% trans "Reopened" %} «</label>
|
||||
<label class="radio-inline" for='st_closed'><input type='radio' name='new_status' value='4' id='st_closed' checked='checked'>{% trans "Closed" %}</label></div></dd>
|
||||
{% endifequal %}
|
||||
{% ifequal ticket.status 5 %}
|
||||
{% endif %}
|
||||
{% if ticket.status == 5 %}
|
||||
<dd><div class="form-group">
|
||||
<label class="radio-inline" for='st_reopened'><input type='radio' name='new_status' value='2' id='st_reopened'>{% trans "Reopened" %} «</label>
|
||||
<label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate' checked='checked'>{% trans "Duplicate" %}</label>
|
||||
</div></dd>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
|
||||
<input type='hidden' name='public' value='1'>
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
<label for='saved_query'>{% trans "Select Query:" %}</label>
|
||||
<select name='saved_query'>
|
||||
<option value="">--------</option>{% for q in user_saved_queries_ %}
|
||||
<option value="{{ q.id }}"{% ifequal saved_query q %} selected{% endifequal %}>{{ q.title }}</option>{% endfor %}
|
||||
<option value="{{ q.id }}"{% if saved_query==q %} selected{% endif %}>{{ q.title }}</option>{% endfor %}
|
||||
</select>
|
||||
<input class="btn btn-primary" type='submit' value='{% trans "Filter Report" %}'>
|
||||
</form>
|
||||
|
@ -112,39 +112,39 @@
|
||||
|
||||
<dt><label>{% trans "New Status" %}</label></dt>
|
||||
{% if not ticket.can_be_resolved %}<dd>{% trans "This ticket cannot be resolved or closed until the tickets it depends on are resolved." %}</dd>{% endif %}
|
||||
{% ifequal ticket.status 1 %}
|
||||
{% if ticket.status == 1 %}
|
||||
<dd><div class="form-group">
|
||||
<label for='st_open' class='active radio-inline'><input type='radio' name='new_status' value='1' id='st_open' checked='checked'>{% trans "Open" %} »</label>
|
||||
<label for='st_resolved' class="radio-inline"><input type='radio' name='new_status' value='3' id='st_resolved'{% if not ticket.can_be_resolved %} disabled='disabled'{% endif %}>{% trans "Resolved" %} »</label>
|
||||
<label for='st_closed' class="radio-inline"><input type='radio' name='new_status' value='4' id='st_closed'{% if not ticket.can_be_resolved %} disabled='disabled'{% endif %}>{% trans "Closed" %} »</label>
|
||||
<label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate'>{% trans "Duplicate" %}</label>
|
||||
</div></dd>
|
||||
{% endifequal %}
|
||||
{% ifequal ticket.status 2 %}
|
||||
{% endif %}
|
||||
{% if ticket.status == 2 %}
|
||||
<dd><div class="form-group">
|
||||
<label for='st_reopened' class='active radio-inline'><input type='radio' name='new_status' value='2' id='st_reopened' checked='checked'>{% trans "Reopened" %} »</label>
|
||||
<label class="radio-inline" for='st_resolved'><input type='radio' name='new_status' value='3' id='st_resolved'{% if not ticket.can_be_resolved %} disabled='disabled'{% endif %}>{% trans "Resolved" %} »</label>
|
||||
<label class="radio-inline" for='st_closed'><input type='radio' name='new_status' value='4' id='st_closed'{% if not ticket.can_be_resolved %} disabled='disabled'{% endif %}>{% trans "Closed" %} »</label>
|
||||
<label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate'>{% trans "Duplicate" %}</label>
|
||||
</div></dd>
|
||||
{% endifequal %}
|
||||
{% ifequal ticket.status 3 %}
|
||||
{% endif %}
|
||||
{% if ticket.status == 3 %}
|
||||
<dd><div class="form-group">
|
||||
<label for='st_reopened' class="radio-inline"><input type='radio' name='new_status' value='2' id='st_reopened'>{% trans "Reopened" %} «</label>
|
||||
<label for='st_resolved' class='active radio-inline'><input type='radio' name='new_status' value='3' id='st_resolved' checked='checked'>{% trans "Resolved" %} »</label>
|
||||
<label class="radio-inline" for='st_closed'><input type='radio' name='new_status' value='4' id='st_closed'>{% trans "Closed" %}</label>
|
||||
</div></dd>
|
||||
{% endifequal %}
|
||||
{% ifequal ticket.status 4 %}
|
||||
{% endif %}
|
||||
{% if ticket.status == 4 %}
|
||||
<dd><div class="form-group"><label for='st_reopened' class="radio-inline"><input type='radio' name='new_status' value='2' id='st_reopened'>{% trans "Reopened" %} «</label>
|
||||
<label class="radio-inline" for='st_closed'><input type='radio' name='new_status' value='4' id='st_closed' checked='checked'>{% trans "Closed" %}</label></div></dd>
|
||||
{% endifequal %}
|
||||
{% ifequal ticket.status 5 %}
|
||||
{% endif %}
|
||||
{% if ticket.status == 5 %}
|
||||
<dd><div class="form-group">
|
||||
<label class="radio-inline" for='st_reopened'><input type='radio' name='new_status' value='2' id='st_reopened'>{% trans "Reopened" %} «</label>
|
||||
<label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate' checked='checked'>{% trans "Duplicate" %}</label>
|
||||
</div></dd>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
|
||||
{% if helpdesk_settings.HELPDESK_UPDATE_PUBLIC_DEFAULT %}
|
||||
<input type='hidden' name='public' value='1'>
|
||||
@ -176,10 +176,10 @@
|
||||
<dd><input type='text' name='title' value='{{ ticket.title|escape }}' /></dd>
|
||||
|
||||
<dt><label for='id_owner'>{% trans "Owner" %}</label></dt>
|
||||
<dd><select id='id_owner' name='owner'><option value='0'>{% trans "Unassign" %}</option>{% for u in active_users %}<option value='{{ u.id }}' {% ifequal u.id ticket.assigned_to.id %}selected{% endifequal %}>{{ u }}</option>{% endfor %}</select></dd>
|
||||
<dd><select id='id_owner' name='owner'><option value='0'>{% trans "Unassign" %}</option>{% for u in active_users %}{% if u.id == ticket.assigned_to.id %}<option value='{{ u.id }}' selected>{{ u }}</option>{% else %}<option value='{{ u.id }}'>{{ u }}</option>{% endif %}{% endfor %}</select></dd>
|
||||
|
||||
<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>
|
||||
<dd><select id='id_priority' name='priority'>{% for p in priorities %}{% if p.0 == ticket.priority %}<option value='{{ p.0 }}' selected='selected'>{{ p.1 }}</option>{% else %}<option value='{{ p.0 }}'>{{ p.1 }}</option>{% endif %}{% endfor %}</select></dd>
|
||||
|
||||
<dt><label for='id_due_date'>{% trans "Due on" %}</label></dt>
|
||||
<dd>{{ form.due_date }}</dd>
|
||||
|
@ -2,7 +2,7 @@
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.urls import reverse
|
||||
from django.test import override_settings, TestCase
|
||||
from django.utils.encoding import smart_text
|
||||
from django.utils.encoding import smart_str
|
||||
|
||||
from helpdesk import lib, models
|
||||
|
||||
@ -78,7 +78,7 @@ class AttachmentIntegrationTests(TestCase):
|
||||
# Ensure attachment is available with correct content
|
||||
att = models.FollowUpAttachment.objects.get(followup__ticket=response.context['ticket'])
|
||||
with open(os.path.join(MEDIA_DIR, att.file.name)) as file_on_disk:
|
||||
disk_content = smart_text(file_on_disk.read(), 'utf-8')
|
||||
disk_content = smart_str(file_on_disk.read(), 'utf-8')
|
||||
self.assertEqual(disk_content, 'โจ')
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
from django.conf.urls import include, url
|
||||
from django.urls import include, path
|
||||
from django.contrib import admin
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^helpdesk/', include('helpdesk.urls', namespace='helpdesk')),
|
||||
url(r'^admin/', admin.site.urls),
|
||||
path('helpdesk/', include('helpdesk.urls', namespace='helpdesk')),
|
||||
path('admin/', admin.site.urls),
|
||||
]
|
||||
|
108
helpdesk/urls.py
108
helpdesk/urls.py
@ -7,7 +7,7 @@ urls.py - Mapping of URL's to our various views. Note we always used NAMED
|
||||
views for simplicity in linking later on.
|
||||
"""
|
||||
|
||||
from django.conf.urls import url
|
||||
from django.urls import path, re_path
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth import views as auth_views
|
||||
from django.urls import include
|
||||
@ -48,115 +48,123 @@ app_name = 'helpdesk'
|
||||
base64_pattern = r'(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^dashboard/$',
|
||||
path('dashboard/',
|
||||
staff.dashboard,
|
||||
name='dashboard'),
|
||||
|
||||
url(r'^tickets/$',
|
||||
path('tickets/',
|
||||
staff.ticket_list,
|
||||
name='list'),
|
||||
|
||||
url(r'^tickets/update/$',
|
||||
path('tickets/update/',
|
||||
staff.mass_update,
|
||||
name='mass_update'),
|
||||
|
||||
url(r'^tickets/merge$',
|
||||
path('tickets/merge',
|
||||
staff.merge_tickets,
|
||||
name='merge_tickets'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/$',
|
||||
path('tickets/<int:ticket_id>/',
|
||||
staff.view_ticket,
|
||||
name='view'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/followup_edit/(?P<followup_id>[0-9]+)/$',
|
||||
path('tickets/<int:ticket_id>/followup_edit/<int:followup_id>/',
|
||||
staff.followup_edit,
|
||||
name='followup_edit'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/followup_delete/(?P<followup_id>[0-9]+)/$',
|
||||
path('tickets/<int:ticket_id>/followup_delete/<int:followup_id>/',
|
||||
staff.followup_delete,
|
||||
name='followup_delete'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/edit/$',
|
||||
path('tickets/<int:ticket_id>/edit/',
|
||||
staff.edit_ticket,
|
||||
name='edit'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/update/$',
|
||||
path('tickets/<int:ticket_id>/update/',
|
||||
staff.update_ticket,
|
||||
name='update'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/delete/$',
|
||||
path('tickets/<int:ticket_id>/delete/',
|
||||
staff.delete_ticket,
|
||||
name='delete'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/hold/$',
|
||||
path('tickets/<int:ticket_id>/hold/',
|
||||
staff.hold_ticket,
|
||||
name='hold'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/unhold/$',
|
||||
path('tickets/<int:ticket_id>/unhold/',
|
||||
staff.unhold_ticket,
|
||||
name='unhold'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/cc/$',
|
||||
path('tickets/<int:ticket_id>/cc/',
|
||||
staff.ticket_cc,
|
||||
name='ticket_cc'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/cc/add/$',
|
||||
path('tickets/<int:ticket_id>/cc/add/',
|
||||
staff.ticket_cc_add,
|
||||
name='ticket_cc_add'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/cc/delete/(?P<cc_id>[0-9]+)/$',
|
||||
path('tickets/<int:ticket_id>/cc/delete/<int:cc_id>/',
|
||||
staff.ticket_cc_del,
|
||||
name='ticket_cc_del'),
|
||||
|
||||
url(r'^tickets/(?P<ticket_id>[0-9]+)/attachment_delete/(?P<attachment_id>[0-9]+)/$',
|
||||
path('tickets/<int:ticket_id>/dependency/add/',
|
||||
staff.ticket_dependency_add,
|
||||
name='ticket_dependency_add'),
|
||||
|
||||
path('tickets/<int:ticket_id>/dependency/delete/<int:dependency_id>/',
|
||||
staff.ticket_dependency_del,
|
||||
name='ticket_dependency_del'),
|
||||
|
||||
path('tickets/<int:ticket_id>/attachment_delete/<int:attachment_id>/',
|
||||
staff.attachment_del,
|
||||
name='attachment_del'),
|
||||
|
||||
url(r'^raw/(?P<type>\w+)/$',
|
||||
re_path(r'^raw/(?P<type>\w+)/$',
|
||||
staff.raw_details,
|
||||
name='raw'),
|
||||
|
||||
url(r'^rss/$',
|
||||
path('rss/',
|
||||
staff.rss_list,
|
||||
name='rss_index'),
|
||||
|
||||
url(r'^reports/$',
|
||||
path('reports/',
|
||||
staff.report_index,
|
||||
name='report_index'),
|
||||
|
||||
url(r'^reports/(?P<report>\w+)/$',
|
||||
re_path(r'^reports/(?P<report>\w+)/$',
|
||||
staff.run_report,
|
||||
name='run_report'),
|
||||
|
||||
url(r'^save_query/$',
|
||||
path('save_query/',
|
||||
staff.save_query,
|
||||
name='savequery'),
|
||||
|
||||
url(r'^delete_query/(?P<id>[0-9]+)/$',
|
||||
path('delete_query/<int:id>/',
|
||||
staff.delete_saved_query,
|
||||
name='delete_query'),
|
||||
|
||||
url(r'^settings/$',
|
||||
path('settings/',
|
||||
staff.EditUserSettingsView.as_view(),
|
||||
name='user_settings'),
|
||||
|
||||
url(r'^ignore/$',
|
||||
path('ignore/',
|
||||
staff.email_ignore,
|
||||
name='email_ignore'),
|
||||
|
||||
url(r'^ignore/add/$',
|
||||
path('ignore/add/',
|
||||
staff.email_ignore_add,
|
||||
name='email_ignore_add'),
|
||||
|
||||
url(r'^ignore/delete/(?P<id>[0-9]+)/$',
|
||||
path('ignore/delete/<int:id>/',
|
||||
staff.email_ignore_del,
|
||||
name='email_ignore_del'),
|
||||
|
||||
url(r'^datatables_ticket_list/(?P<query>{})$'.format(base64_pattern),
|
||||
re_path(r'^datatables_ticket_list/(?P<query>{})$'.format(base64_pattern),
|
||||
staff.datatables_ticket_list,
|
||||
name="datatables_ticket_list"),
|
||||
|
||||
url(r'^timeline_ticket_list/(?P<query>{})$'.format(base64_pattern),
|
||||
re_path(r'^timeline_ticket_list/(?P<query>{})$'.format(base64_pattern),
|
||||
staff.timeline_ticket_list,
|
||||
name="timeline_ticket_list"),
|
||||
|
||||
@ -174,49 +182,49 @@ if helpdesk_settings.HELPDESK_ENABLE_DEPENDENCIES_ON_TICKET:
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
url(r'^$',
|
||||
path('',
|
||||
protect_view(public.Homepage.as_view()),
|
||||
name='home'),
|
||||
|
||||
url(r'^tickets/submit/$',
|
||||
path('tickets/submit/',
|
||||
public.create_ticket,
|
||||
name='submit'),
|
||||
|
||||
url(r'^tickets/submit_iframe/$',
|
||||
path('tickets/submit_iframe/',
|
||||
public.CreateTicketIframeView.as_view(),
|
||||
name='submit_iframe'),
|
||||
|
||||
url(r'^tickets/success_iframe/$', # Ticket was submitted successfully
|
||||
path('tickets/success_iframe/', # Ticket was submitted successfully
|
||||
public.SuccessIframeView.as_view(),
|
||||
name='success_iframe'),
|
||||
|
||||
url(r'^view/$',
|
||||
path('view/',
|
||||
public.view_ticket,
|
||||
name='public_view'),
|
||||
|
||||
url(r'^change_language/$',
|
||||
path('change_language/',
|
||||
public.change_language,
|
||||
name='public_change_language'),
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
url(r'^rss/user/(?P<user_name>[^/]+)/$',
|
||||
path('rss/user/<str:user_name>/',
|
||||
helpdesk_staff_member_required(feeds.OpenTicketsByUser()),
|
||||
name='rss_user'),
|
||||
|
||||
url(r'^rss/user/(?P<user_name>[^/]+)/(?P<queue_slug>[A-Za-z0-9_-]+)/$',
|
||||
re_path(r'^rss/user/(?P<user_name>[^/]+)/(?P<queue_slug>[A-Za-z0-9_-]+)/$',
|
||||
helpdesk_staff_member_required(feeds.OpenTicketsByUser()),
|
||||
name='rss_user_queue'),
|
||||
|
||||
url(r'^rss/queue/(?P<queue_slug>[A-Za-z0-9_-]+)/$',
|
||||
re_path(r'^rss/queue/(?P<queue_slug>[A-Za-z0-9_-]+)/$',
|
||||
helpdesk_staff_member_required(feeds.OpenTicketsByQueue()),
|
||||
name='rss_queue'),
|
||||
|
||||
url(r'^rss/unassigned/$',
|
||||
path('rss/unassigned/',
|
||||
helpdesk_staff_member_required(feeds.UnassignedTickets()),
|
||||
name='rss_unassigned'),
|
||||
|
||||
url(r'^rss/recent_activity/$',
|
||||
path('rss/recent_activity/',
|
||||
helpdesk_staff_member_required(feeds.RecentFollowUps()),
|
||||
name='rss_activity'),
|
||||
]
|
||||
@ -232,23 +240,23 @@ if helpdesk_settings.HELPDESK_ACTIVATE_API_ENDPOINT:
|
||||
|
||||
|
||||
urlpatterns += [
|
||||
url(r'^login/$',
|
||||
path('login/',
|
||||
login.login,
|
||||
name='login'),
|
||||
|
||||
url(r'^logout/$',
|
||||
path('logout/',
|
||||
auth_views.LogoutView.as_view(
|
||||
template_name='helpdesk/registration/login.html',
|
||||
next_page='../'),
|
||||
name='logout'),
|
||||
|
||||
url(r'^password_change/$',
|
||||
path('password_change/',
|
||||
auth_views.PasswordChangeView.as_view(
|
||||
template_name='helpdesk/registration/change_password.html',
|
||||
success_url='./done'),
|
||||
name='password_change'),
|
||||
|
||||
url(r'^password_change/done$',
|
||||
path('password_change/done',
|
||||
auth_views.PasswordChangeDoneView.as_view(
|
||||
template_name='helpdesk/registration/change_password_done.html',),
|
||||
name='password_change_done'),
|
||||
@ -256,29 +264,29 @@ urlpatterns += [
|
||||
|
||||
if helpdesk_settings.HELPDESK_KB_ENABLED:
|
||||
urlpatterns += [
|
||||
url(r'^kb/$',
|
||||
path('kb/',
|
||||
kb.index,
|
||||
name='kb_index'),
|
||||
|
||||
url(r'^kb/(?P<slug>[A-Za-z0-9_-]+)/$',
|
||||
re_path(r'^kb/(?P<slug>[A-Za-z0-9_-]+)/$',
|
||||
kb.category,
|
||||
name='kb_category'),
|
||||
|
||||
url(r'^kb/(?P<item>[0-9]+)/vote/$',
|
||||
path('kb/<int:item>/vote/',
|
||||
kb.vote,
|
||||
name='kb_vote'),
|
||||
|
||||
url(r'^kb_iframe/(?P<slug>[A-Za-z0-9_-]+)/$',
|
||||
re_path(r'^kb_iframe/(?P<slug>[A-Za-z0-9_-]+)/$',
|
||||
kb.category_iframe,
|
||||
name='kb_category_iframe'),
|
||||
]
|
||||
|
||||
urlpatterns += [
|
||||
url(r'^help/context/$',
|
||||
path('help/context/',
|
||||
TemplateView.as_view(template_name='helpdesk/help_context.html'),
|
||||
name='help_context'),
|
||||
|
||||
url(r'^system_settings/$',
|
||||
path('system_settings/',
|
||||
login_required(DirectTemplateView.as_view(template_name='helpdesk/system_settings.html')),
|
||||
name='system_settings'),
|
||||
]
|
||||
|
@ -11,7 +11,7 @@ from django.contrib.auth import get_user_model
|
||||
from django.contrib.syndication.views import Feed
|
||||
from django.urls import reverse
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from helpdesk.models import Ticket, FollowUp, Queue
|
||||
|
@ -15,8 +15,8 @@ from django.core.exceptions import (
|
||||
from django.urls import reverse
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from django.utils.http import urlquote
|
||||
from django.utils.translation import ugettext as _
|
||||
from urllib.parse import quote
|
||||
from django.utils.translation import gettext as _
|
||||
from django.conf import settings
|
||||
from django.views.decorators.clickjacking import xframe_options_exempt
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
@ -112,7 +112,7 @@ class BaseCreateTicketView(abstract_views.AbstractCreateTicketMixin, FormView):
|
||||
return HttpResponseRedirect('%s?ticket=%s&email=%s&key=%s' % (
|
||||
reverse('helpdesk:public_view'),
|
||||
ticket.ticket_for_url,
|
||||
urlquote(ticket.submitter_email),
|
||||
quote(ticket.submitter_email),
|
||||
ticket.secret_key)
|
||||
)
|
||||
except ValueError:
|
||||
|
@ -19,7 +19,7 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponseRedirect, Http404, HttpResponse, JsonResponse
|
||||
from django.shortcuts import render, get_object_or_404, redirect
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.utils.html import escape
|
||||
from django.utils import timezone
|
||||
from django.views.decorators.csrf import requires_csrf_token
|
||||
|
@ -1,7 +1,6 @@
|
||||
Django>=2.2,<4
|
||||
Django>=2.2
|
||||
django-bootstrap4-form
|
||||
celery
|
||||
django-celery-beat
|
||||
email-reply-parser
|
||||
akismet
|
||||
markdown
|
||||
|
76
setup.py
76
setup.py
@ -8,9 +8,16 @@ version = '0.4.0a1'
|
||||
|
||||
# Provided as an attribute, so you can append to these instead
|
||||
# of replicating them:
|
||||
standard_exclude = ('*.py', '*.pyc', '*$py.class', '*~', '.*', '*.bak')
|
||||
standard_exclude_directories = ('.*', 'CVS', '_darcs', './build',
|
||||
'./dist', 'EGG-INFO', '*.egg-info')
|
||||
standard_exclude = ("*.py", "*.pyc", "*$py.class", "*~", ".*", "*.bak")
|
||||
standard_exclude_directories = (
|
||||
".*",
|
||||
"CVS",
|
||||
"_darcs",
|
||||
"./build",
|
||||
"./dist",
|
||||
"EGG-INFO",
|
||||
"*.egg-info",
|
||||
)
|
||||
|
||||
# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
|
||||
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
@ -18,11 +25,13 @@ standard_exclude_directories = ('.*', 'CVS', '_darcs', './build',
|
||||
# you can't import this from another package, when you don't know if
|
||||
# that package is installed yet.
|
||||
def find_package_data(
|
||||
where='.', package='',
|
||||
where=".",
|
||||
package="",
|
||||
exclude=standard_exclude,
|
||||
exclude_directories=standard_exclude_directories,
|
||||
only_in_packages=True,
|
||||
show_ignored=False):
|
||||
show_ignored=False,
|
||||
):
|
||||
"""
|
||||
Return a dictionary suitable for use in ``package_data``
|
||||
in a distutils ``setup.py`` file.
|
||||
@ -51,7 +60,7 @@ def find_package_data(
|
||||
"""
|
||||
|
||||
out = {}
|
||||
stack = [(convert_path(where), '', package, only_in_packages)]
|
||||
stack = [(convert_path(where), "", package, only_in_packages)]
|
||||
while stack:
|
||||
where, prefix, package, only_in_packages = stack.pop(0)
|
||||
for name in os.listdir(where):
|
||||
@ -59,43 +68,40 @@ def find_package_data(
|
||||
if os.path.isdir(fn):
|
||||
bad_name = False
|
||||
for pattern in exclude_directories:
|
||||
if (fnmatchcase(name, pattern)
|
||||
or fn.lower() == pattern.lower()):
|
||||
if fnmatchcase(name, pattern) or fn.lower() == pattern.lower():
|
||||
bad_name = True
|
||||
if show_ignored:
|
||||
print(
|
||||
"Directory %s ignored by pattern %s" % (fn, pattern),
|
||||
file=sys.stderr
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
break
|
||||
if bad_name:
|
||||
continue
|
||||
if (os.path.isfile(os.path.join(fn, '__init__.py'))
|
||||
and not prefix):
|
||||
if os.path.isfile(os.path.join(fn, "__init__.py")) and not prefix:
|
||||
if not package:
|
||||
new_package = name
|
||||
else:
|
||||
new_package = package + '.' + name
|
||||
stack.append((fn, '', new_package, False))
|
||||
new_package = package + "." + name
|
||||
stack.append((fn, "", new_package, False))
|
||||
else:
|
||||
stack.append((fn, prefix + name + '/', package, only_in_packages))
|
||||
stack.append((fn, prefix + name + "/", package, only_in_packages))
|
||||
elif package or not only_in_packages:
|
||||
# is a file
|
||||
bad_name = False
|
||||
for pattern in exclude:
|
||||
if (fnmatchcase(name, pattern)
|
||||
or fn.lower() == pattern.lower()):
|
||||
if fnmatchcase(name, pattern) or fn.lower() == pattern.lower():
|
||||
bad_name = True
|
||||
if show_ignored:
|
||||
print(
|
||||
"File %s ignored by pattern %s" % (fn, pattern),
|
||||
file=sys.stderr
|
||||
)
|
||||
file=sys.stderr,
|
||||
)
|
||||
break
|
||||
if bad_name:
|
||||
continue
|
||||
out.setdefault(package, []).append(prefix+name)
|
||||
out.setdefault(package, []).append(prefix + name)
|
||||
return out
|
||||
|
||||
|
||||
@ -116,7 +122,7 @@ def get_long_description():
|
||||
|
||||
|
||||
setup(
|
||||
name='django-helpdesk',
|
||||
name="django-helpdesk",
|
||||
version=version,
|
||||
description="Django-powered ticket tracker for your helpdesk",
|
||||
long_description=get_long_description(),
|
||||
@ -128,6 +134,10 @@ setup(
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Framework :: Django",
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"Framework :: Django :: 2.2",
|
||||
>>>>>>> 55c638aeef0b3032eab28bcdc8ee313da9cbf9c0
|
||||
"Framework :: Django :: 3.2",
|
||||
"Environment :: Web Environment",
|
||||
"Operating System :: OS Independent",
|
||||
@ -138,18 +148,26 @@ setup(
|
||||
"Topic :: Office/Business",
|
||||
"Natural Language :: English",
|
||||
],
|
||||
keywords=['django', 'helpdesk', 'django-helpdesk', 'tickets', 'incidents',
|
||||
'cases', 'bugs', 'track', 'support'],
|
||||
author='Ross Poulton',
|
||||
author_email='ross@rossp.org',
|
||||
maintainer='Garret Wassermann',
|
||||
maintainer_email='gwasser@gmail.com',
|
||||
url='https://github.com/django-helpdesk/django-helpdesk',
|
||||
license='BSD',
|
||||
keywords=[
|
||||
"django",
|
||||
"helpdesk",
|
||||
"django-helpdesk",
|
||||
"tickets",
|
||||
"incidents",
|
||||
"cases",
|
||||
"bugs",
|
||||
"track",
|
||||
"support",
|
||||
],
|
||||
author="Ross Poulton",
|
||||
author_email="ross@rossp.org",
|
||||
maintainer="Garret Wassermann",
|
||||
maintainer_email="gwasser@gmail.com",
|
||||
url="https://github.com/django-helpdesk/django-helpdesk",
|
||||
license="BSD",
|
||||
packages=find_packages(),
|
||||
package_data=find_package_data("helpdesk", only_in_packages=False),
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
install_requires=get_requirements(),
|
||||
)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user