Merge #1009 Django 4 support into unstable

This commit is contained in:
Garret Wassermann 2022-04-23 02:24:17 -04:00
commit 224f1cb83e
27 changed files with 180 additions and 154 deletions

View File

@ -13,7 +13,7 @@ Including another URLconf
1. Import the include() function: from django.conf.urls import url, include 1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 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.contrib import admin
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static 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/ # https://docs.djangoproject.com/en/1.10/howto/static-files/
urlpatterns = [ urlpatterns = [
url(r'^admin/', admin.site.urls), path('admin/', admin.site.urls),
url(r'^', include('helpdesk.urls', namespace='helpdesk')), path('', include('helpdesk.urls', namespace='helpdesk')),
url(r'^api/auth/', include('rest_framework.urls', namespace='rest_framework')) path('api/auth/', include('rest_framework.urls', namespace='rest_framework'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@ -1,7 +1,7 @@
from django.contrib import admin from django.contrib import admin
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import gettext_lazy as _
from helpdesk.models import Queue, Ticket, FollowUp, PreSetReply from helpdesk.models import Queue, Ticket, FollowUp, PreSetReply, KBCategory
from helpdesk.models import EscalationExclusion, EmailTemplate from helpdesk.models import EscalationExclusion, EmailTemplate, KBItem
from helpdesk.models import TicketChange, KBIAttachment, FollowUpAttachment, IgnoreEmail from helpdesk.models import TicketChange, KBIAttachment, FollowUpAttachment, IgnoreEmail
from helpdesk.models import CustomField from helpdesk.models import CustomField
from helpdesk import settings as helpdesk_settings from helpdesk import settings as helpdesk_settings

View File

@ -27,7 +27,7 @@ from django.core.exceptions import ValidationError
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.db.models import Q from django.db.models import Q
from django.utils import encoding, timezone 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 email_reply_parser import EmailReplyParser
from helpdesk import settings from helpdesk import settings
@ -129,7 +129,7 @@ def pop3_sync(q, logger, server):
if type(raw_content[0]) is bytes: if type(raw_content[0]) is bytes:
full_message = "\n".join([elm.decode('utf-8') for elm in raw_content]) full_message = "\n".join([elm.decode('utf-8') for elm in raw_content])
else: 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) ticket = object_from_message(message=full_message, queue=q, logger=logger)
if ticket: if ticket:
@ -175,7 +175,7 @@ def imap_sync(q, logger, server):
for num in msgnums: for num in msgnums:
logger.info("Processing message %s" % num) logger.info("Processing message %s" % num)
status, data = server.fetch(num, '(RFC822)') 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: try:
ticket = object_from_message(message=full_message, queue=q, logger=logger) ticket = object_from_message(message=full_message, queue=q, logger=logger)
except TypeError: except TypeError:
@ -268,7 +268,7 @@ def process_queue(q, logger):
for i, m in enumerate(mail, 1): for i, m in enumerate(mail, 1):
logger.info("Processing message %d" % i) logger.info("Processing message %d" % i)
with open(m, 'r') as f: 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) ticket = object_from_message(message=full_message, queue=q, logger=logger)
if ticket: if ticket:
logger.info("Successfully processed message %d, ticket/comment created.", i) 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") logger.debug("Discovered plain text MIME part")
else: else:
try: try:
email_body = encoding.smart_text(part.get_payload(decode=True)) email_body = encoding.smart_str(part.get_payload(decode=True))
except UnicodeDecodeError: 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: if not body and not full_body:
# no text has been parsed so far - try such deep parsing for some messages # no text has been parsed so far - try such deep parsing for some messages

View File

@ -12,7 +12,7 @@ from datetime import datetime, date, time
from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django import forms from django import forms
from django.conf import settings 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.contrib.auth import get_user_model
from django.utils import timezone from django.utils import timezone

View File

@ -11,7 +11,7 @@ import mimetypes
from datetime import datetime, date, time from datetime import datetime, date, time
from django.conf import settings 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 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(): if ak.verify_key():
ak_data = { ak_data = {
'user_ip': request.META.get('REMOTE_ADDR', '127.0.0.1'), 'user_ip': request.META.get('REMOTE_ADDR', '127.0.0.1'),
'user_agent': request.META.get('HTTP_USER_AGENT', ''), 'user_agent': request.headers.get('User-Agent', ''),
'referrer': request.META.get('HTTP_REFERER', ''), 'referrer': request.headers.get('Referer', ''),
'comment_type': 'comment', 'comment_type': 'comment',
'comment_author': '', 'comment_author': '',
} }
return ak.comment_check(smart_text(text), data=ak_data) return ak.comment_check(smart_str(text), data=ak_data)
return False return False
@ -135,8 +135,9 @@ def process_attachments(followup, attached_files):
for attached in attached_files: for attached in attached_files:
if attached.size: if attached.size:
filename = smart_text(attached.name) filename = smart_str(attached.name)
att = followup.followupattachment_set.create( att = FollowUpAttachment(
followup=followup,
file=attached, file=attached,
filename=filename, filename=filename,
mime_type=attached.content_type or mime_type=attached.content_type or

View File

@ -19,7 +19,7 @@ from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.db.utils import IntegrityError 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 from helpdesk.models import Queue

View File

@ -8,7 +8,7 @@ create_usersettings.py - Easy way to create helpdesk-specific settings for
users who don't yet have them. 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.core.management.base import BaseCommand
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model

View File

@ -15,7 +15,7 @@ import sys
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.db.models import Q 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 django.utils import timezone
from helpdesk.models import Queue, Ticket, FollowUp, EscalationExclusion, TicketChange from helpdesk.models import Queue, Ticket, FollowUp, EscalationExclusion, TicketChange

View File

@ -2,7 +2,7 @@
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import migrations from django.db import migrations
from django.db.utils import IntegrityError 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): def create_and_assign_permissions(apps, schema_editor):

View File

@ -14,7 +14,7 @@ from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
from django.utils import timezone 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 from io import StringIO
import re import re
import os import os
@ -1031,11 +1031,11 @@ class TicketChange(models.Model):
def __str__(self): def __str__(self):
out = '%s ' % self.field out = '%s ' % self.field
if not self.new_value: if not self.new_value:
out += ugettext('removed') out += gettext('removed')
elif not self.old_value: elif not self.old_value:
out += ugettext('set to %s') % self.new_value out += gettext('set to %s') % self.new_value
else: 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, 'old_value': self.old_value,
'new_value': self.new_value 'new_value': self.new_value
} }

View File

@ -1,7 +1,7 @@
from django.db.models import Q from django.db.models import Q
from django.urls import reverse from django.urls import reverse
from django.utils.html import escape 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 b64encode
from base64 import b64decode from base64 import b64decode

View File

@ -3,9 +3,9 @@
<h2>Queries</h2> <h2>Queries</h2>
<p> <p>
{{ sql_queries|length }} Quer{{ sql_queries|pluralize:"y,ies" }} {{ 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>) (<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> </p>
<table id="debugQueryTable" style="display: none;"> <table id="debugQueryTable" style="display: none;">
<col width="1"></col> <col width="1"></col>

View File

@ -14,7 +14,7 @@
{% endwith %} {% endwith %}
{% for u in user_choices %} {% for u in user_choices %}
<option value='{{ u.id }}'{% if u.id|in_list:query_params.filtering.assigned_to__id__in %} selected='selected'{% endif %}> <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> </option>
{% endfor %} {% endfor %}
</select> </select>

View File

@ -6,22 +6,22 @@
</div> </div>
<div class="col col-sm-3"> <div class="col col-sm-3">
<select id='id_sortx' name='sortx' class=""> <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" %} {% trans "Created" %}
</option> </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" %} {% trans "Title" %}
</option> </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" %} {% trans "Queue" %}
</option> </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" %} {% trans "Status" %}
</option> </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" %} {% trans "Priority" %}
</option> </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" %} {% trans "Owner" %}
</option> </option>
</select> </select>

View File

@ -34,7 +34,7 @@
{% for q in user_saved_queries_ %} {% for q in user_saved_queries_ %}
<a class="dropdown-item" href="{% url 'helpdesk:list' %}?saved_query={{ q.id }}">{{ q.title }} <a class="dropdown-item" href="{% url 'helpdesk:list' %}?saved_query={{ q.id }}">{{ q.title }}
{% if q.shared %} {% 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 %} {% endif %}
</a> </a>
{% endfor %} {% endfor %}

View File

@ -25,7 +25,7 @@
{% for q in user_saved_queries_ %} {% for q in user_saved_queries_ %}
<a class="dropdown-item small" href="{% url 'helpdesk:list' %}?saved_query={{ q.id }}">{{ q.title }} <a class="dropdown-item small" href="{% url 'helpdesk:list' %}?saved_query={{ q.id }}">{{ q.title }}
{% if q.shared %} {% 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 %} {% endif %}
</a> </a>
{% endfor %} {% endfor %}

View File

@ -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> <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 %} {% 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 }}" /> <input type="hidden" name="new_status" value="{{ ticket.status }}" />
{% endifequal %} {% endif %}
{% ifequal ticket.status 2 %} {% if ticket.status == 2 %}
<dd><div class="form-group"> <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" %} &raquo;</label> <label for='st_reopened' class='active radio-inline'><input type='radio' name='new_status' value='2' id='st_reopened' checked='checked'>{% trans "Reopened" %} &raquo;</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" %} &raquo;</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" %} &raquo;</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" %} &raquo;</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" %} &raquo;</label>
<label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate'>{% trans "Duplicate" %}</label> <label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate'>{% trans "Duplicate" %}</label>
</div></dd> </div></dd>
{% endifequal %} {% endif %}
{% ifequal ticket.status 3 %} {% if ticket.status == 3 %}
<dd><div class="form-group"> <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" %} &laquo;</label> <label for='st_reopened' class="radio-inline"><input type='radio' name='new_status' value='2' id='st_reopened'>{% trans "Reopened" %} &laquo;</label>
<label for='st_resolved' class='active radio-inline'><input type='radio' name='new_status' value='3' id='st_resolved' checked='checked'>{% trans "Resolved" %} &raquo;</label> <label for='st_resolved' class='active radio-inline'><input type='radio' name='new_status' value='3' id='st_resolved' checked='checked'>{% trans "Resolved" %} &raquo;</label>
<label class="radio-inline" for='st_closed'><input type='radio' name='new_status' value='4' id='st_closed'>{% trans "Closed" %}</label> <label class="radio-inline" for='st_closed'><input type='radio' name='new_status' value='4' id='st_closed'>{% trans "Closed" %}</label>
</div></dd> </div></dd>
{% endifequal %} {% endif %}
{% ifequal ticket.status 4 %} {% 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" %} &laquo;</label> <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" %} &laquo;</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> <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 %} {% endif %}
{% ifequal ticket.status 5 %} {% if ticket.status == 5 %}
<dd><div class="form-group"> <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" %} &laquo;</label> <label class="radio-inline" for='st_reopened'><input type='radio' name='new_status' value='2' id='st_reopened'>{% trans "Reopened" %} &laquo;</label>
<label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate' checked='checked'>{% trans "Duplicate" %}</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> </div></dd>
{% endifequal %} {% endif %}
<input type='hidden' name='public' value='1'> <input type='hidden' name='public' value='1'>

View File

@ -29,7 +29,7 @@
<label for='saved_query'>{% trans "Select Query:" %}</label> <label for='saved_query'>{% trans "Select Query:" %}</label>
<select name='saved_query'> <select name='saved_query'>
<option value="">--------</option>{% for q in user_saved_queries_ %} <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> </select>
<input class="btn btn-primary" type='submit' value='{% trans "Filter Report" %}'> <input class="btn btn-primary" type='submit' value='{% trans "Filter Report" %}'>
</form> </form>

View File

@ -112,39 +112,39 @@
<dt><label>{% trans "New Status" %}</label></dt> <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 %} {% 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"> <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" %} &raquo;</label> <label for='st_open' class='active radio-inline'><input type='radio' name='new_status' value='1' id='st_open' checked='checked'>{% trans "Open" %} &raquo;</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" %} &raquo;</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" %} &raquo;</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" %} &raquo;</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" %} &raquo;</label>
<label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate'>{% trans "Duplicate" %}</label> <label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate'>{% trans "Duplicate" %}</label>
</div></dd> </div></dd>
{% endifequal %} {% endif %}
{% ifequal ticket.status 2 %} {% if ticket.status == 2 %}
<dd><div class="form-group"> <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" %} &raquo;</label> <label for='st_reopened' class='active radio-inline'><input type='radio' name='new_status' value='2' id='st_reopened' checked='checked'>{% trans "Reopened" %} &raquo;</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" %} &raquo;</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" %} &raquo;</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" %} &raquo;</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" %} &raquo;</label>
<label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate'>{% trans "Duplicate" %}</label> <label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate'>{% trans "Duplicate" %}</label>
</div></dd> </div></dd>
{% endifequal %} {% endif %}
{% ifequal ticket.status 3 %} {% if ticket.status == 3 %}
<dd><div class="form-group"> <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" %} &laquo;</label> <label for='st_reopened' class="radio-inline"><input type='radio' name='new_status' value='2' id='st_reopened'>{% trans "Reopened" %} &laquo;</label>
<label for='st_resolved' class='active radio-inline'><input type='radio' name='new_status' value='3' id='st_resolved' checked='checked'>{% trans "Resolved" %} &raquo;</label> <label for='st_resolved' class='active radio-inline'><input type='radio' name='new_status' value='3' id='st_resolved' checked='checked'>{% trans "Resolved" %} &raquo;</label>
<label class="radio-inline" for='st_closed'><input type='radio' name='new_status' value='4' id='st_closed'>{% trans "Closed" %}</label> <label class="radio-inline" for='st_closed'><input type='radio' name='new_status' value='4' id='st_closed'>{% trans "Closed" %}</label>
</div></dd> </div></dd>
{% endifequal %} {% endif %}
{% ifequal ticket.status 4 %} {% 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" %} &laquo;</label> <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" %} &laquo;</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> <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 %} {% endif %}
{% ifequal ticket.status 5 %} {% if ticket.status == 5 %}
<dd><div class="form-group"> <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" %} &laquo;</label> <label class="radio-inline" for='st_reopened'><input type='radio' name='new_status' value='2' id='st_reopened'>{% trans "Reopened" %} &laquo;</label>
<label class="radio-inline" for='st_duplicate'><input type='radio' name='new_status' value='5' id='st_duplicate' checked='checked'>{% trans "Duplicate" %}</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> </div></dd>
{% endifequal %} {% endif %}
{% if helpdesk_settings.HELPDESK_UPDATE_PUBLIC_DEFAULT %} {% if helpdesk_settings.HELPDESK_UPDATE_PUBLIC_DEFAULT %}
<input type='hidden' name='public' value='1'> <input type='hidden' name='public' value='1'>
@ -176,10 +176,10 @@
<dd><input type='text' name='title' value='{{ ticket.title|escape }}' /></dd> <dd><input type='text' name='title' value='{{ ticket.title|escape }}' /></dd>
<dt><label for='id_owner'>{% trans "Owner" %}</label></dt> <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> <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> <dt><label for='id_due_date'>{% trans "Due on" %}</label></dt>
<dd>{{ form.due_date }}</dd> <dd>{{ form.due_date }}</dd>

View File

@ -2,7 +2,7 @@
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.urls import reverse from django.urls import reverse
from django.test import override_settings, TestCase 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 from helpdesk import lib, models
@ -78,7 +78,7 @@ class AttachmentIntegrationTests(TestCase):
# Ensure attachment is available with correct content # Ensure attachment is available with correct content
att = models.FollowUpAttachment.objects.get(followup__ticket=response.context['ticket']) att = models.FollowUpAttachment.objects.get(followup__ticket=response.context['ticket'])
with open(os.path.join(MEDIA_DIR, att.file.name)) as file_on_disk: 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, 'โจ') self.assertEqual(disk_content, 'โจ')

View File

@ -1,7 +1,7 @@
from django.conf.urls import include, url from django.urls import include, path
from django.contrib import admin from django.contrib import admin
urlpatterns = [ urlpatterns = [
url(r'^helpdesk/', include('helpdesk.urls', namespace='helpdesk')), path('helpdesk/', include('helpdesk.urls', namespace='helpdesk')),
url(r'^admin/', admin.site.urls), path('admin/', admin.site.urls),
] ]

View File

@ -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. 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.decorators import login_required
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
from django.urls import include 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}=)?$' base64_pattern = r'(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$'
urlpatterns = [ urlpatterns = [
url(r'^dashboard/$', path('dashboard/',
staff.dashboard, staff.dashboard,
name='dashboard'), name='dashboard'),
url(r'^tickets/$', path('tickets/',
staff.ticket_list, staff.ticket_list,
name='list'), name='list'),
url(r'^tickets/update/$', path('tickets/update/',
staff.mass_update, staff.mass_update,
name='mass_update'), name='mass_update'),
url(r'^tickets/merge$', path('tickets/merge',
staff.merge_tickets, staff.merge_tickets,
name='merge_tickets'), name='merge_tickets'),
url(r'^tickets/(?P<ticket_id>[0-9]+)/$', path('tickets/<int:ticket_id>/',
staff.view_ticket, staff.view_ticket,
name='view'), 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, staff.followup_edit,
name='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, staff.followup_delete,
name='followup_delete'), name='followup_delete'),
url(r'^tickets/(?P<ticket_id>[0-9]+)/edit/$', path('tickets/<int:ticket_id>/edit/',
staff.edit_ticket, staff.edit_ticket,
name='edit'), name='edit'),
url(r'^tickets/(?P<ticket_id>[0-9]+)/update/$', path('tickets/<int:ticket_id>/update/',
staff.update_ticket, staff.update_ticket,
name='update'), name='update'),
url(r'^tickets/(?P<ticket_id>[0-9]+)/delete/$', path('tickets/<int:ticket_id>/delete/',
staff.delete_ticket, staff.delete_ticket,
name='delete'), name='delete'),
url(r'^tickets/(?P<ticket_id>[0-9]+)/hold/$', path('tickets/<int:ticket_id>/hold/',
staff.hold_ticket, staff.hold_ticket,
name='hold'), name='hold'),
url(r'^tickets/(?P<ticket_id>[0-9]+)/unhold/$', path('tickets/<int:ticket_id>/unhold/',
staff.unhold_ticket, staff.unhold_ticket,
name='unhold'), name='unhold'),
url(r'^tickets/(?P<ticket_id>[0-9]+)/cc/$', path('tickets/<int:ticket_id>/cc/',
staff.ticket_cc, staff.ticket_cc,
name='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, staff.ticket_cc_add,
name='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, staff.ticket_cc_del,
name='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, staff.attachment_del,
name='attachment_del'), name='attachment_del'),
url(r'^raw/(?P<type>\w+)/$', re_path(r'^raw/(?P<type>\w+)/$',
staff.raw_details, staff.raw_details,
name='raw'), name='raw'),
url(r'^rss/$', path('rss/',
staff.rss_list, staff.rss_list,
name='rss_index'), name='rss_index'),
url(r'^reports/$', path('reports/',
staff.report_index, staff.report_index,
name='report_index'), name='report_index'),
url(r'^reports/(?P<report>\w+)/$', re_path(r'^reports/(?P<report>\w+)/$',
staff.run_report, staff.run_report,
name='run_report'), name='run_report'),
url(r'^save_query/$', path('save_query/',
staff.save_query, staff.save_query,
name='savequery'), name='savequery'),
url(r'^delete_query/(?P<id>[0-9]+)/$', path('delete_query/<int:id>/',
staff.delete_saved_query, staff.delete_saved_query,
name='delete_query'), name='delete_query'),
url(r'^settings/$', path('settings/',
staff.EditUserSettingsView.as_view(), staff.EditUserSettingsView.as_view(),
name='user_settings'), name='user_settings'),
url(r'^ignore/$', path('ignore/',
staff.email_ignore, staff.email_ignore,
name='email_ignore'), name='email_ignore'),
url(r'^ignore/add/$', path('ignore/add/',
staff.email_ignore_add, staff.email_ignore_add,
name='email_ignore_add'), name='email_ignore_add'),
url(r'^ignore/delete/(?P<id>[0-9]+)/$', path('ignore/delete/<int:id>/',
staff.email_ignore_del, staff.email_ignore_del,
name='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, staff.datatables_ticket_list,
name="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, staff.timeline_ticket_list,
name="timeline_ticket_list"), name="timeline_ticket_list"),
@ -174,49 +182,49 @@ if helpdesk_settings.HELPDESK_ENABLE_DEPENDENCIES_ON_TICKET:
] ]
urlpatterns += [ urlpatterns += [
url(r'^$', path('',
protect_view(public.Homepage.as_view()), protect_view(public.Homepage.as_view()),
name='home'), name='home'),
url(r'^tickets/submit/$', path('tickets/submit/',
public.create_ticket, public.create_ticket,
name='submit'), name='submit'),
url(r'^tickets/submit_iframe/$', path('tickets/submit_iframe/',
public.CreateTicketIframeView.as_view(), public.CreateTicketIframeView.as_view(),
name='submit_iframe'), name='submit_iframe'),
url(r'^tickets/success_iframe/$', # Ticket was submitted successfully path('tickets/success_iframe/', # Ticket was submitted successfully
public.SuccessIframeView.as_view(), public.SuccessIframeView.as_view(),
name='success_iframe'), name='success_iframe'),
url(r'^view/$', path('view/',
public.view_ticket, public.view_ticket,
name='public_view'), name='public_view'),
url(r'^change_language/$', path('change_language/',
public.change_language, public.change_language,
name='public_change_language'), name='public_change_language'),
] ]
urlpatterns += [ urlpatterns += [
url(r'^rss/user/(?P<user_name>[^/]+)/$', path('rss/user/<str:user_name>/',
helpdesk_staff_member_required(feeds.OpenTicketsByUser()), helpdesk_staff_member_required(feeds.OpenTicketsByUser()),
name='rss_user'), 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()), helpdesk_staff_member_required(feeds.OpenTicketsByUser()),
name='rss_user_queue'), 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()), helpdesk_staff_member_required(feeds.OpenTicketsByQueue()),
name='rss_queue'), name='rss_queue'),
url(r'^rss/unassigned/$', path('rss/unassigned/',
helpdesk_staff_member_required(feeds.UnassignedTickets()), helpdesk_staff_member_required(feeds.UnassignedTickets()),
name='rss_unassigned'), name='rss_unassigned'),
url(r'^rss/recent_activity/$', path('rss/recent_activity/',
helpdesk_staff_member_required(feeds.RecentFollowUps()), helpdesk_staff_member_required(feeds.RecentFollowUps()),
name='rss_activity'), name='rss_activity'),
] ]
@ -232,23 +240,23 @@ if helpdesk_settings.HELPDESK_ACTIVATE_API_ENDPOINT:
urlpatterns += [ urlpatterns += [
url(r'^login/$', path('login/',
login.login, login.login,
name='login'), name='login'),
url(r'^logout/$', path('logout/',
auth_views.LogoutView.as_view( auth_views.LogoutView.as_view(
template_name='helpdesk/registration/login.html', template_name='helpdesk/registration/login.html',
next_page='../'), next_page='../'),
name='logout'), name='logout'),
url(r'^password_change/$', path('password_change/',
auth_views.PasswordChangeView.as_view( auth_views.PasswordChangeView.as_view(
template_name='helpdesk/registration/change_password.html', template_name='helpdesk/registration/change_password.html',
success_url='./done'), success_url='./done'),
name='password_change'), name='password_change'),
url(r'^password_change/done$', path('password_change/done',
auth_views.PasswordChangeDoneView.as_view( auth_views.PasswordChangeDoneView.as_view(
template_name='helpdesk/registration/change_password_done.html',), template_name='helpdesk/registration/change_password_done.html',),
name='password_change_done'), name='password_change_done'),
@ -256,29 +264,29 @@ urlpatterns += [
if helpdesk_settings.HELPDESK_KB_ENABLED: if helpdesk_settings.HELPDESK_KB_ENABLED:
urlpatterns += [ urlpatterns += [
url(r'^kb/$', path('kb/',
kb.index, kb.index,
name='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, kb.category,
name='kb_category'), name='kb_category'),
url(r'^kb/(?P<item>[0-9]+)/vote/$', path('kb/<int:item>/vote/',
kb.vote, kb.vote,
name='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, kb.category_iframe,
name='kb_category_iframe'), name='kb_category_iframe'),
] ]
urlpatterns += [ urlpatterns += [
url(r'^help/context/$', path('help/context/',
TemplateView.as_view(template_name='helpdesk/help_context.html'), TemplateView.as_view(template_name='helpdesk/help_context.html'),
name='help_context'), name='help_context'),
url(r'^system_settings/$', path('system_settings/',
login_required(DirectTemplateView.as_view(template_name='helpdesk/system_settings.html')), login_required(DirectTemplateView.as_view(template_name='helpdesk/system_settings.html')),
name='system_settings'), name='system_settings'),
] ]

View File

@ -11,7 +11,7 @@ from django.contrib.auth import get_user_model
from django.contrib.syndication.views import Feed from django.contrib.syndication.views import Feed
from django.urls import reverse from django.urls import reverse
from django.db.models import Q 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 django.shortcuts import get_object_or_404
from helpdesk.models import Ticket, FollowUp, Queue from helpdesk.models import Ticket, FollowUp, Queue

View File

@ -15,8 +15,8 @@ from django.core.exceptions import (
from django.urls import reverse from django.urls import reverse
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import render from django.shortcuts import render
from django.utils.http import urlquote from urllib.parse import quote
from django.utils.translation import ugettext as _ from django.utils.translation import gettext as _
from django.conf import settings from django.conf import settings
from django.views.decorators.clickjacking import xframe_options_exempt from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.decorators.csrf import csrf_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' % ( return HttpResponseRedirect('%s?ticket=%s&email=%s&key=%s' % (
reverse('helpdesk:public_view'), reverse('helpdesk:public_view'),
ticket.ticket_for_url, ticket.ticket_for_url,
urlquote(ticket.submitter_email), quote(ticket.submitter_email),
ticket.secret_key) ticket.secret_key)
) )
except ValueError: except ValueError:

View File

@ -19,7 +19,7 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models import Q from django.db.models import Q
from django.http import HttpResponseRedirect, Http404, HttpResponse, JsonResponse from django.http import HttpResponseRedirect, Http404, HttpResponse, JsonResponse
from django.shortcuts import render, get_object_or_404, redirect 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.html import escape
from django.utils import timezone from django.utils import timezone
from django.views.decorators.csrf import requires_csrf_token from django.views.decorators.csrf import requires_csrf_token

View File

@ -1,7 +1,6 @@
Django>=2.2,<4 Django>=2.2
django-bootstrap4-form django-bootstrap4-form
celery celery
django-celery-beat
email-reply-parser email-reply-parser
akismet akismet
markdown markdown

View File

@ -8,9 +8,16 @@ version = '0.4.0a1'
# Provided as an attribute, so you can append to these instead # Provided as an attribute, so you can append to these instead
# of replicating them: # of replicating them:
standard_exclude = ('*.py', '*.pyc', '*$py.class', '*~', '.*', '*.bak') standard_exclude = ("*.py", "*.pyc", "*$py.class", "*~", ".*", "*.bak")
standard_exclude_directories = ('.*', 'CVS', '_darcs', './build', standard_exclude_directories = (
'./dist', 'EGG-INFO', '*.egg-info') ".*",
"CVS",
"_darcs",
"./build",
"./dist",
"EGG-INFO",
"*.egg-info",
)
# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) # (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 # 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 # you can't import this from another package, when you don't know if
# that package is installed yet. # that package is installed yet.
def find_package_data( def find_package_data(
where='.', package='', where=".",
package="",
exclude=standard_exclude, exclude=standard_exclude,
exclude_directories=standard_exclude_directories, exclude_directories=standard_exclude_directories,
only_in_packages=True, only_in_packages=True,
show_ignored=False): show_ignored=False,
):
""" """
Return a dictionary suitable for use in ``package_data`` Return a dictionary suitable for use in ``package_data``
in a distutils ``setup.py`` file. in a distutils ``setup.py`` file.
@ -51,7 +60,7 @@ def find_package_data(
""" """
out = {} out = {}
stack = [(convert_path(where), '', package, only_in_packages)] stack = [(convert_path(where), "", package, only_in_packages)]
while stack: while stack:
where, prefix, package, only_in_packages = stack.pop(0) where, prefix, package, only_in_packages = stack.pop(0)
for name in os.listdir(where): for name in os.listdir(where):
@ -59,38 +68,35 @@ def find_package_data(
if os.path.isdir(fn): if os.path.isdir(fn):
bad_name = False bad_name = False
for pattern in exclude_directories: for pattern in exclude_directories:
if (fnmatchcase(name, pattern) if fnmatchcase(name, pattern) or fn.lower() == pattern.lower():
or fn.lower() == pattern.lower()):
bad_name = True bad_name = True
if show_ignored: if show_ignored:
print( print(
"Directory %s ignored by pattern %s" % (fn, pattern), "Directory %s ignored by pattern %s" % (fn, pattern),
file=sys.stderr file=sys.stderr,
) )
break break
if bad_name: if bad_name:
continue continue
if (os.path.isfile(os.path.join(fn, '__init__.py')) if os.path.isfile(os.path.join(fn, "__init__.py")) and not prefix:
and not prefix):
if not package: if not package:
new_package = name new_package = name
else: else:
new_package = package + '.' + name new_package = package + "." + name
stack.append((fn, '', new_package, False)) stack.append((fn, "", new_package, False))
else: 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: elif package or not only_in_packages:
# is a file # is a file
bad_name = False bad_name = False
for pattern in exclude: for pattern in exclude:
if (fnmatchcase(name, pattern) if fnmatchcase(name, pattern) or fn.lower() == pattern.lower():
or fn.lower() == pattern.lower()):
bad_name = True bad_name = True
if show_ignored: if show_ignored:
print( print(
"File %s ignored by pattern %s" % (fn, pattern), "File %s ignored by pattern %s" % (fn, pattern),
file=sys.stderr file=sys.stderr,
) )
break break
if bad_name: if bad_name:
@ -116,7 +122,7 @@ def get_long_description():
setup( setup(
name='django-helpdesk', name="django-helpdesk",
version=version, version=version,
description="Django-powered ticket tracker for your helpdesk", description="Django-powered ticket tracker for your helpdesk",
long_description=get_long_description(), long_description=get_long_description(),
@ -128,6 +134,10 @@ setup(
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Framework :: Django", "Framework :: Django",
<<<<<<< HEAD
=======
"Framework :: Django :: 2.2",
>>>>>>> 55c638aeef0b3032eab28bcdc8ee313da9cbf9c0
"Framework :: Django :: 3.2", "Framework :: Django :: 3.2",
"Environment :: Web Environment", "Environment :: Web Environment",
"Operating System :: OS Independent", "Operating System :: OS Independent",
@ -138,18 +148,26 @@ setup(
"Topic :: Office/Business", "Topic :: Office/Business",
"Natural Language :: English", "Natural Language :: English",
], ],
keywords=['django', 'helpdesk', 'django-helpdesk', 'tickets', 'incidents', keywords=[
'cases', 'bugs', 'track', 'support'], "django",
author='Ross Poulton', "helpdesk",
author_email='ross@rossp.org', "django-helpdesk",
maintainer='Garret Wassermann', "tickets",
maintainer_email='gwasser@gmail.com', "incidents",
url='https://github.com/django-helpdesk/django-helpdesk', "cases",
license='BSD', "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(), packages=find_packages(),
package_data=find_package_data("helpdesk", only_in_packages=False), package_data=find_package_data("helpdesk", only_in_packages=False),
include_package_data=True, include_package_data=True,
zip_safe=False, zip_safe=False,
install_requires=get_requirements(), install_requires=get_requirements(),
) )