Merge branch 'master' into feature-public-ticket-defaults

This commit is contained in:
Garret Wassermann 2018-01-10 13:18:29 -05:00 committed by GitHub
commit ed0596e430
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 88 additions and 63 deletions

View File

@ -194,6 +194,8 @@ USE_TZ = True
# https://docs.djangoproject.com/en/1.11/howto/static-files/ # https://docs.djangoproject.com/en/1.11/howto/static-files/
STATIC_URL = '/static/' STATIC_URL = '/static/'
# static root needs to be defined in order to use collectstatic
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
# MEDIA_ROOT is where media uploads are stored. # MEDIA_ROOT is where media uploads are stored.
# We set this to a directory to host file attachments created # We set this to a directory to host file attachments created

View File

@ -13,7 +13,7 @@ project_root = os.path.dirname(here)
NAME = 'django-helpdesk-demodesk' NAME = 'django-helpdesk-demodesk'
DESCRIPTION = 'A demo Django project using django-helpdesk' DESCRIPTION = 'A demo Django project using django-helpdesk'
README = open(os.path.join(here, 'README.rst')).read() README = open(os.path.join(here, 'README.rst')).read()
VERSION = '0.2.3' VERSION = '0.2.6'
#VERSION = open(os.path.join(project_root, 'VERSION')).read().strip() #VERSION = open(os.path.join(project_root, 'VERSION')).read().strip()
AUTHOR = 'django-helpdesk team' AUTHOR = 'django-helpdesk team'
URL = 'https://github.com/django-helpdesk/django-helpdesk' URL = 'https://github.com/django-helpdesk/django-helpdesk'

View File

@ -1,6 +1,6 @@
from functools import wraps from functools import wraps
from django.core.urlresolvers import reverse from django.urls import reverse
from django.http import HttpResponseRedirect, Http404 from django.http import HttpResponseRedirect, Http404
from django.utils.decorators import available_attrs from django.utils.decorators import available_attrs
@ -14,9 +14,9 @@ def protect_view(view_func):
""" """
@wraps(view_func, assigned=available_attrs(view_func)) @wraps(view_func, assigned=available_attrs(view_func))
def _wrapped_view(request, *args, **kwargs): def _wrapped_view(request, *args, **kwargs):
if not request.user.is_authenticated() and helpdesk_settings.HELPDESK_REDIRECT_TO_LOGIN_BY_DEFAULT: if not request.user.is_authenticated and helpdesk_settings.HELPDESK_REDIRECT_TO_LOGIN_BY_DEFAULT:
return HttpResponseRedirect(reverse('helpdesk:login')) return HttpResponseRedirect(reverse('helpdesk:login'))
elif not request.user.is_authenticated() and helpdesk_settings.HELPDESK_ANON_ACCESS_RAISES_404: elif not request.user.is_authenticated and helpdesk_settings.HELPDESK_ANON_ACCESS_RAISES_404:
raise Http404 raise Http404
return view_func(request, *args, **kwargs) return view_func(request, *args, **kwargs)

View File

@ -11,7 +11,7 @@ forms.py - Definitions of newforms-based forms for creating and maintaining
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.utils.six import StringIO from django.utils.six import StringIO
from django import forms from django import forms
from django.forms import extras from django.forms import widgets
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model

View File

@ -374,7 +374,10 @@ def ticket_from_message(message, queue, logger):
non_b64_err = TypeError non_b64_err = TypeError
try: try:
logger.debug("Try to base64 decode the attachment payload") logger.debug("Try to base64 decode the attachment payload")
if six.PY2:
payloadToWrite = base64.decodestring(payload) payloadToWrite = base64.decodestring(payload)
else:
payloadToWrite = base64.decodebytes(payload)
except non_b64_err: except non_b64_err:
logger.debug("Payload was not base64 encoded, using raw bytes") logger.debug("Payload was not base64 encoded, using raw bytes")
payloadToWrite = payload payloadToWrite = payload

View File

@ -140,7 +140,7 @@ class Migration(migrations.Migration):
('votes', models.IntegerField(verbose_name='Votes', default=0, help_text='Total number of votes cast for this item')), ('votes', models.IntegerField(verbose_name='Votes', default=0, help_text='Total number of votes cast for this item')),
('recommendations', models.IntegerField(verbose_name='Positive Votes', default=0, help_text='Number of votes for this item which were POSITIVE.')), ('recommendations', models.IntegerField(verbose_name='Positive Votes', default=0, help_text='Number of votes for this item which were POSITIVE.')),
('last_updated', models.DateTimeField(verbose_name='Last Updated', blank=True, help_text='The date on which this question was most recently changed.')), ('last_updated', models.DateTimeField(verbose_name='Last Updated', blank=True, help_text='The date on which this question was most recently changed.')),
('category', models.ForeignKey(verbose_name='Category', to='helpdesk.KBCategory')), ('category', models.ForeignKey(verbose_name='Category', to='helpdesk.KBCategory', on_delete=models.CASCADE)),
], ],
options={ options={
'verbose_name_plural': 'Knowledge base items', 'verbose_name_plural': 'Knowledge base items',
@ -203,7 +203,7 @@ class Migration(migrations.Migration):
('title', models.CharField(verbose_name='Query Name', help_text='User-provided name for this query', max_length=100)), ('title', models.CharField(verbose_name='Query Name', help_text='User-provided name for this query', max_length=100)),
('shared', models.BooleanField(verbose_name='Shared With Other Users?', default=False, help_text='Should other users see this query?')), ('shared', models.BooleanField(verbose_name='Shared With Other Users?', default=False, help_text='Should other users see this query?')),
('query', models.TextField(verbose_name='Search Query', help_text='Pickled query object. Be wary changing this.')), ('query', models.TextField(verbose_name='Search Query', help_text='Pickled query object. Be wary changing this.')),
('user', models.ForeignKey(verbose_name='User', to=settings.AUTH_USER_MODEL)), ('user', models.ForeignKey(verbose_name='User', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
], ],
options={ options={
'verbose_name_plural': 'Saved searches', 'verbose_name_plural': 'Saved searches',
@ -226,8 +226,8 @@ class Migration(migrations.Migration):
('priority', models.IntegerField(verbose_name='Priority', help_text='1 = Highest Priority, 5 = Low Priority', blank=3, default=3, choices=[(1, '1. Critical'), (2, '2. High'), (3, '3. Normal'), (4, '4. Low'), (5, '5. Very Low')])), ('priority', models.IntegerField(verbose_name='Priority', help_text='1 = Highest Priority, 5 = Low Priority', blank=3, default=3, choices=[(1, '1. Critical'), (2, '2. High'), (3, '3. Normal'), (4, '4. Low'), (5, '5. Very Low')])),
('due_date', models.DateTimeField(null=True, verbose_name='Due on', blank=True)), ('due_date', models.DateTimeField(null=True, verbose_name='Due on', blank=True)),
('last_escalation', models.DateTimeField(editable=False, null=True, blank=True, help_text='The date this ticket was last escalated - updated automatically by management/commands/escalate_tickets.py.')), ('last_escalation', models.DateTimeField(editable=False, null=True, blank=True, help_text='The date this ticket was last escalated - updated automatically by management/commands/escalate_tickets.py.')),
('assigned_to', models.ForeignKey(null=True, verbose_name='Assigned to', blank=True, related_name='assigned_to', to=settings.AUTH_USER_MODEL)), ('assigned_to', models.ForeignKey(null=True, verbose_name='Assigned to', blank=True, related_name='assigned_to', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
('queue', models.ForeignKey(verbose_name='Queue', to='helpdesk.Queue')), ('queue', models.ForeignKey(verbose_name='Queue', to='helpdesk.Queue', on_delete=models.CASCADE)),
], ],
options={ options={
'verbose_name_plural': 'Tickets', 'verbose_name_plural': 'Tickets',
@ -244,8 +244,8 @@ class Migration(migrations.Migration):
('email', models.EmailField(null=True, verbose_name='E-Mail Address', help_text='For non-user followers, enter their e-mail address', blank=True, max_length=75)), ('email', models.EmailField(null=True, verbose_name='E-Mail Address', help_text='For non-user followers, enter their e-mail address', blank=True, max_length=75)),
('can_view', models.BooleanField(verbose_name='Can View Ticket?', default=False, help_text='Can this CC login to view the ticket details?')), ('can_view', models.BooleanField(verbose_name='Can View Ticket?', default=False, help_text='Can this CC login to view the ticket details?')),
('can_update', models.BooleanField(verbose_name='Can Update Ticket?', default=False, help_text='Can this CC login and update the ticket?')), ('can_update', models.BooleanField(verbose_name='Can Update Ticket?', default=False, help_text='Can this CC login and update the ticket?')),
('ticket', models.ForeignKey(verbose_name='Ticket', to='helpdesk.Ticket')), ('ticket', models.ForeignKey(verbose_name='Ticket', to='helpdesk.Ticket', on_delete=models.CASCADE)),
('user', models.ForeignKey(null=True, verbose_name='User', blank=True, to=settings.AUTH_USER_MODEL, help_text='User who wishes to receive updates for this ticket.')), ('user', models.ForeignKey(null=True, verbose_name='User', blank=True, to=settings.AUTH_USER_MODEL, help_text='User who wishes to receive updates for this ticket.', on_delete=models.CASCADE)),
], ],
options={ options={
}, },
@ -258,7 +258,7 @@ class Migration(migrations.Migration):
('field', models.CharField(verbose_name='Field', max_length=100)), ('field', models.CharField(verbose_name='Field', max_length=100)),
('old_value', models.TextField(null=True, verbose_name='Old Value', blank=True)), ('old_value', models.TextField(null=True, verbose_name='Old Value', blank=True)),
('new_value', models.TextField(null=True, verbose_name='New Value', blank=True)), ('new_value', models.TextField(null=True, verbose_name='New Value', blank=True)),
('followup', models.ForeignKey(verbose_name='Follow-up', to='helpdesk.FollowUp')), ('followup', models.ForeignKey(verbose_name='Follow-up', to='helpdesk.FollowUp', on_delete=models.CASCADE)),
], ],
options={ options={
'verbose_name_plural': 'Ticket changes', 'verbose_name_plural': 'Ticket changes',
@ -271,8 +271,8 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
('value', models.TextField(null=True, blank=True)), ('value', models.TextField(null=True, blank=True)),
('field', models.ForeignKey(verbose_name='Field', to='helpdesk.CustomField')), ('field', models.ForeignKey(verbose_name='Field', to='helpdesk.CustomField', on_delete=models.CASCADE)),
('ticket', models.ForeignKey(verbose_name='Ticket', to='helpdesk.Ticket')), ('ticket', models.ForeignKey(verbose_name='Ticket', to='helpdesk.Ticket', on_delete=models.CASCADE)),
], ],
options={ options={
'verbose_name_plural': 'Ticket custom field values', 'verbose_name_plural': 'Ticket custom field values',
@ -284,8 +284,8 @@ class Migration(migrations.Migration):
name='TicketDependency', name='TicketDependency',
fields=[ fields=[
('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
('depends_on', models.ForeignKey(related_name='depends_on', verbose_name='Depends On Ticket', to='helpdesk.Ticket')), ('depends_on', models.ForeignKey(related_name='depends_on', verbose_name='Depends On Ticket', to='helpdesk.Ticket', on_delete=models.CASCADE)),
('ticket', models.ForeignKey(related_name='ticketdependency', verbose_name='Ticket', to='helpdesk.Ticket')), ('ticket', models.ForeignKey(related_name='ticketdependency', verbose_name='Ticket', to='helpdesk.Ticket', on_delete=models.CASCADE)),
], ],
options={ options={
'verbose_name_plural': 'Ticket dependencies', 'verbose_name_plural': 'Ticket dependencies',
@ -298,7 +298,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)),
('settings_pickled', models.TextField(null=True, verbose_name='Settings Dictionary', blank=True, help_text='This is a base64-encoded representation of a pickled Python dictionary. Do not change this field via the admin.')), ('settings_pickled', models.TextField(null=True, verbose_name='Settings Dictionary', blank=True, help_text='This is a base64-encoded representation of a pickled Python dictionary. Do not change this field via the admin.')),
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)), ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
], ],
options={ options={
'verbose_name_plural': 'User Settings', 'verbose_name_plural': 'User Settings',
@ -325,13 +325,13 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='followup', model_name='followup',
name='ticket', name='ticket',
field=models.ForeignKey(verbose_name='Ticket', to='helpdesk.Ticket'), field=models.ForeignKey(verbose_name='Ticket', to='helpdesk.Ticket', on_delete=models.CASCADE),
preserve_default=True, preserve_default=True,
), ),
migrations.AddField( migrations.AddField(
model_name='followup', model_name='followup',
name='user', name='user',
field=models.ForeignKey(null=True, verbose_name='User', blank=True, to=settings.AUTH_USER_MODEL), field=models.ForeignKey(null=True, verbose_name='User', blank=True, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
preserve_default=True, preserve_default=True,
), ),
migrations.AddField( migrations.AddField(
@ -343,7 +343,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='attachment', model_name='attachment',
name='followup', name='followup',
field=models.ForeignKey(verbose_name='Follow-up', to='helpdesk.FollowUp'), field=models.ForeignKey(verbose_name='Follow-up', to='helpdesk.FollowUp', on_delete=models.CASCADE),
preserve_default=True, preserve_default=True,
), ),
] ]

View File

@ -18,7 +18,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('queues', models.ManyToManyField(to='helpdesk.Queue', verbose_name='Authorized Queues')), ('queues', models.ManyToManyField(to='helpdesk.Queue', verbose_name='Authorized Queues')),
('user', models.OneToOneField(verbose_name='User', to=settings.AUTH_USER_MODEL)), ('user', models.OneToOneField(verbose_name='User', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
], ],
options={ options={
'verbose_name': 'Queue Membership', 'verbose_name': 'Queue Membership',

View File

@ -15,6 +15,8 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='usersettings', model_name='usersettings',
name='user', name='user',
field=models.OneToOneField(to=settings.AUTH_USER_MODEL, related_name='usersettings_helpdesk'), field=models.OneToOneField(to=settings.AUTH_USER_MODEL,
related_name='usersettings_helpdesk',
on_delete=models.CASCADE),
), ),
] ]

View File

@ -255,11 +255,11 @@ class Queue(models.Model):
default_owner = models.ForeignKey( default_owner = models.ForeignKey(
settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name='default_owner', related_name='default_owner',
blank=True, blank=True,
null=True, null=True,
verbose_name=_('Default owner'), verbose_name=_('Default owner'),
on_delete=models.SET_NULL,
) )
def __str__(self): def __str__(self):
@ -387,6 +387,7 @@ class Ticket(models.Model):
queue = models.ForeignKey( queue = models.ForeignKey(
Queue, Queue,
on_delete=models.CASCADE,
verbose_name=_('Queue'), verbose_name=_('Queue'),
) )
@ -412,6 +413,7 @@ class Ticket(models.Model):
assigned_to = models.ForeignKey( assigned_to = models.ForeignKey(
settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='assigned_to', related_name='assigned_to',
blank=True, blank=True,
null=True, null=True,
@ -526,7 +528,7 @@ class Ticket(models.Model):
""" """
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse from django.urls import reverse
try: try:
site = Site.objects.get_current() site = Site.objects.get_current()
except ImproperlyConfigured: except ImproperlyConfigured:
@ -546,7 +548,7 @@ class Ticket(models.Model):
""" """
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse from django.urls import reverse
try: try:
site = Site.objects.get_current() site = Site.objects.get_current()
except ImproperlyConfigured: except ImproperlyConfigured:
@ -579,8 +581,8 @@ class Ticket(models.Model):
return '%s %s' % (self.id, self.title) return '%s %s' % (self.id, self.title)
def get_absolute_url(self): def get_absolute_url(self):
return 'helpdesk:view', (self.id,) from django.urls import reverse
get_absolute_url = models.permalink(get_absolute_url) return reverse('helpdesk:view', args=(self.id,))
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.id: if not self.id:
@ -628,6 +630,7 @@ class FollowUp(models.Model):
ticket = models.ForeignKey( ticket = models.ForeignKey(
Ticket, Ticket,
on_delete=models.CASCADE,
verbose_name=_('Ticket'), verbose_name=_('Ticket'),
) )
@ -659,6 +662,7 @@ class FollowUp(models.Model):
user = models.ForeignKey( user = models.ForeignKey(
settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
blank=True, blank=True,
null=True, null=True,
verbose_name=_('User'), verbose_name=_('User'),
@ -701,6 +705,7 @@ class TicketChange(models.Model):
followup = models.ForeignKey( followup = models.ForeignKey(
FollowUp, FollowUp,
on_delete=models.CASCADE,
verbose_name=_('Follow-up'), verbose_name=_('Follow-up'),
) )
@ -763,6 +768,7 @@ class Attachment(models.Model):
followup = models.ForeignKey( followup = models.ForeignKey(
FollowUp, FollowUp,
on_delete=models.CASCADE,
verbose_name=_('Follow-up'), verbose_name=_('Follow-up'),
) )
@ -964,8 +970,8 @@ class KBCategory(models.Model):
verbose_name_plural = _('Knowledge base categories') verbose_name_plural = _('Knowledge base categories')
def get_absolute_url(self): def get_absolute_url(self):
return 'helpdesk:kb_category', (), {'slug': self.slug} from django.urls import reverse
get_absolute_url = models.permalink(get_absolute_url) return reverse('helpdesk:kb_category', kwargs={'slug': self.slug})
@python_2_unicode_compatible @python_2_unicode_compatible
@ -976,6 +982,7 @@ class KBItem(models.Model):
""" """
category = models.ForeignKey( category = models.ForeignKey(
KBCategory, KBCategory,
on_delete=models.CASCADE,
verbose_name=_('Category'), verbose_name=_('Category'),
) )
@ -1031,8 +1038,8 @@ class KBItem(models.Model):
verbose_name_plural = _('Knowledge base items') verbose_name_plural = _('Knowledge base items')
def get_absolute_url(self): def get_absolute_url(self):
return 'helpdesk:kb_item', (self.id,) from django.urls import reverse
get_absolute_url = models.permalink(get_absolute_url) return reverse('helpdesk:kb_item', args=(self.id,))
@python_2_unicode_compatible @python_2_unicode_compatible
@ -1049,6 +1056,7 @@ class SavedSearch(models.Model):
""" """
user = models.ForeignKey( user = models.ForeignKey(
settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
verbose_name=_('User'), verbose_name=_('User'),
) )
@ -1093,6 +1101,7 @@ class UserSettings(models.Model):
user = models.OneToOneField( user = models.OneToOneField(
settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="usersettings_helpdesk") related_name="usersettings_helpdesk")
settings_pickled = models.TextField( settings_pickled = models.TextField(
@ -1251,11 +1260,13 @@ class TicketCC(models.Model):
ticket = models.ForeignKey( ticket = models.ForeignKey(
Ticket, Ticket,
on_delete=models.CASCADE,
verbose_name=_('Ticket'), verbose_name=_('Ticket'),
) )
user = models.ForeignKey( user = models.ForeignKey(
settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
blank=True, blank=True,
null=True, null=True,
help_text=_('User who wishes to receive updates for this ticket.'), help_text=_('User who wishes to receive updates for this ticket.'),
@ -1425,11 +1436,13 @@ class CustomField(models.Model):
class TicketCustomFieldValue(models.Model): class TicketCustomFieldValue(models.Model):
ticket = models.ForeignKey( ticket = models.ForeignKey(
Ticket, Ticket,
on_delete=models.CASCADE,
verbose_name=_('Ticket'), verbose_name=_('Ticket'),
) )
field = models.ForeignKey( field = models.ForeignKey(
CustomField, CustomField,
on_delete=models.CASCADE,
verbose_name=_('Field'), verbose_name=_('Field'),
) )
@ -1458,12 +1471,14 @@ class TicketDependency(models.Model):
ticket = models.ForeignKey( ticket = models.ForeignKey(
Ticket, Ticket,
on_delete=models.CASCADE,
verbose_name=_('Ticket'), verbose_name=_('Ticket'),
related_name='ticketdependency', related_name='ticketdependency',
) )
depends_on = models.ForeignKey( depends_on = models.ForeignKey(
Ticket, Ticket,
on_delete=models.CASCADE,
verbose_name=_('Depends On Ticket'), verbose_name=_('Depends On Ticket'),
related_name='depends_on', related_name='depends_on',
) )

View File

@ -1,7 +1,6 @@
/*! jQuery UI - v1.12.1 - 2016-09-14 /*! jQuery UI - v1.12.1 - 2016-09-14
* http://jqueryui.com * http://jqueryui.com
* Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css * Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css
* To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6
* Copyright jQuery Foundation and other contributors; Licensed MIT */ * Copyright jQuery Foundation and other contributors; Licensed MIT */
/* Layout helpers /* Layout helpers

View File

@ -8,7 +8,6 @@
* *
* http://api.jqueryui.com/category/theming/ * http://api.jqueryui.com/category/theming/
* *
* To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6
*/ */

View File

@ -14,7 +14,7 @@ templatetags/ticket_to_link.py - Used in ticket comments to allow wiki-style
import re import re
from django import template from django import template
from django.core.urlresolvers import reverse from django.urls import reverse
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from helpdesk.models import Ticket from helpdesk.models import Ticket

View File

@ -20,7 +20,11 @@ def get_staff_user(username='helpdesk.staff', password='password'):
def reload_urlconf(urlconf=None): def reload_urlconf(urlconf=None):
from imp import reload # python 3 needs this import. from django.utils import six
if six.PY2:
from imp import reload
else:
from importlib import reload
if urlconf is None: if urlconf is None:
from django.conf import settings from django.conf import settings
@ -33,7 +37,7 @@ def reload_urlconf(urlconf=None):
if urlconf in sys.modules: if urlconf in sys.modules:
reload(sys.modules[urlconf]) reload(sys.modules[urlconf])
from django.core.urlresolvers import clear_url_caches from django.urls import clear_url_caches
clear_url_caches() clear_url_caches()

View File

@ -6,7 +6,7 @@ import shutil
from tempfile import gettempdir from tempfile import gettempdir
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.urlresolvers 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_text

View File

@ -92,7 +92,7 @@ class GetEmailParametricTemplate(object):
if self.socks: if self.socks:
from socks import ProxyConnectionError from socks import ProxyConnectionError
with self.assertRaisesRegexp(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)): with self.assertRaisesRegex(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
call_command('get_email') call_command('get_email')
else: else:
@ -165,7 +165,7 @@ class GetEmailParametricTemplate(object):
if self.socks: if self.socks:
from socks import ProxyConnectionError from socks import ProxyConnectionError
with self.assertRaisesRegexp(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)): with self.assertRaisesRegex(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
call_command('get_email') call_command('get_email')
else: else:
@ -278,7 +278,7 @@ class GetEmailParametricTemplate(object):
if self.socks: if self.socks:
from socks import ProxyConnectionError from socks import ProxyConnectionError
with self.assertRaisesRegexp(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)): with self.assertRaisesRegex(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
call_command('get_email') call_command('get_email')
else: else:
@ -525,7 +525,7 @@ a9eiiQ+3V1v+7wWHXCzq
if self.socks: if self.socks:
from socks import ProxyConnectionError from socks import ProxyConnectionError
with self.assertRaisesRegexp(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)): with self.assertRaisesRegex(ProxyConnectionError, '%s:%s' % (unrouted_socks_server, unused_port)):
call_command('get_email') call_command('get_email')
else: else:

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.core.urlresolvers import reverse from django.urls import reverse
from django.test import TestCase from django.test import TestCase
from helpdesk.tests.helpers import get_staff_user, reload_urlconf from helpdesk.tests.helpers import get_staff_user, reload_urlconf
@ -23,7 +23,7 @@ class TestKBDisabled(TestCase):
def test_navigation(self): def test_navigation(self):
"""Test proper rendering of navigation.html by accessing the dashboard""" """Test proper rendering of navigation.html by accessing the dashboard"""
from django.core.urlresolvers import NoReverseMatch from django.urls import NoReverseMatch
self.client.login(username=get_staff_user().get_username(), password='password') self.client.login(username=get_staff_user().get_username(), password='password')
self.assertRaises(NoReverseMatch, reverse, 'helpdesk:kb_index') self.assertRaises(NoReverseMatch, reverse, 'helpdesk:kb_index')

View File

@ -1,6 +1,6 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.core.urlresolvers import reverse from django.urls import reverse
from django.test import TestCase from django.test import TestCase
from django.test.client import Client from django.test.client import Client

View File

@ -1,7 +1,7 @@
from helpdesk.models import Queue, Ticket from helpdesk.models import Queue, Ticket
from django.test import TestCase from django.test import TestCase
from django.test.client import Client from django.test.client import Client
from django.core.urlresolvers import reverse from django.urls import reverse
class PublicActionsTestCase(TestCase): class PublicActionsTestCase(TestCase):

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.core.urlresolvers import reverse from django.urls import reverse
from django.test import TestCase from django.test import TestCase
from helpdesk.models import Queue from helpdesk.models import Queue
from helpdesk.tests.helpers import get_staff_user from helpdesk.tests.helpers import get_staff_user

View File

@ -1,6 +1,6 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core import mail from django.core import mail
from django.core.urlresolvers import reverse from django.urls import reverse
from django.test import TestCase from django.test import TestCase
from django.test.client import Client from django.test.client import Client
from helpdesk.models import CustomField, Queue, Ticket from helpdesk.models import CustomField, Queue, Ticket

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.core.urlresolvers import reverse from django.urls import reverse
from django.test import TestCase from django.test import TestCase
from helpdesk.models import Ticket, Queue from helpdesk.models import Ticket, Queue

View File

@ -2,7 +2,7 @@ from helpdesk.models import Queue, CustomField, Ticket
from django.test import TestCase from django.test import TestCase
from django.core import mail from django.core import mail
from django.test.client import Client from django.test.client import Client
from django.core.urlresolvers import reverse from django.urls import reverse
try: # python 3 try: # python 3
from urllib.parse import urlparse from urllib.parse import urlparse

View File

@ -1,6 +1,6 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.core import mail from django.core import mail
from django.core.urlresolvers import reverse from django.urls import reverse
from django.test import TestCase from django.test import TestCase
from django.test.client import Client from django.test.client import Client
from helpdesk.models import CustomField, Queue, Ticket from helpdesk.models import CustomField, Queue, Ticket

View File

@ -9,7 +9,7 @@ views/feeds.py - A handful of staff-only RSS feeds to provide ticket details
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.syndication.views import Feed from django.contrib.syndication.views import Feed
from django.core.urlresolvers 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 ugettext as _
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404

View File

@ -24,7 +24,7 @@ from helpdesk.models import Ticket, Queue, UserSettings, KBCategory
@protect_view @protect_view
def homepage(request): def homepage(request):
if request.user.is_staff or \ if request.user.is_staff or \
(request.user.is_authenticated() and (request.user.is_authenticated and
helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE): helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE):
try: try:
if request.user.usersettings_helpdesk.settings.get('login_view_ticketlist', False): if request.user.usersettings_helpdesk.settings.get('login_view_ticketlist', False):
@ -75,7 +75,7 @@ def homepage(request):
if queue: if queue:
initial_data['queue'] = queue.id initial_data['queue'] = queue.id
if request.user.is_authenticated() and request.user.email: if request.user.is_authenticated and request.user.email:
initial_data['submitter_email'] = request.user.email initial_data['submitter_email'] = request.user.email
form = PublicTicketForm(initial=initial_data) form = PublicTicketForm(initial=initial_data)

View File

@ -12,7 +12,7 @@ from datetime import datetime, timedelta
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import user_passes_test from django.contrib.auth.decorators import user_passes_test
from django.core.urlresolvers import reverse from django.urls import reverse
from django.core.exceptions import ValidationError, PermissionDenied from django.core.exceptions import ValidationError, PermissionDenied
from django.db import connection from django.db import connection
from django.db.models import Q from django.db.models import Q
@ -46,14 +46,14 @@ User = get_user_model()
if helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE: if helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE:
# treat 'normal' users like 'staff' # treat 'normal' users like 'staff'
staff_member_required = user_passes_test( staff_member_required = user_passes_test(
lambda u: u.is_authenticated() and u.is_active) lambda u: u.is_authenticated and u.is_active)
else: else:
staff_member_required = user_passes_test( staff_member_required = user_passes_test(
lambda u: u.is_authenticated() and u.is_active and u.is_staff) lambda u: u.is_authenticated and u.is_active and u.is_staff)
superuser_required = user_passes_test( superuser_required = user_passes_test(
lambda u: u.is_authenticated() and u.is_active and u.is_superuser) lambda u: u.is_authenticated and u.is_active and u.is_superuser)
def _get_user_queues(user): def _get_user_queues(user):
@ -387,7 +387,7 @@ def subscribe_staff_member_to_ticket(ticket, user):
def update_ticket(request, ticket_id, public=False): def update_ticket(request, ticket_id, public=False):
if not (public or ( if not (public or (
request.user.is_authenticated() and request.user.is_authenticated and
request.user.is_active and ( request.user.is_active and (
request.user.is_staff or request.user.is_staff or
helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE))): helpdesk_settings.HELPDESK_ALLOW_NON_STAFF_TICKET_UPDATE))):
@ -644,7 +644,7 @@ def update_ticket(request, ticket_id, public=False):
ticket.save() ticket.save()
# auto subscribe user if enabled # auto subscribe user if enabled
if helpdesk_settings.HELPDESK_AUTO_SUBSCRIBE_ON_TICKET_RESPONSE and request.user.is_authenticated(): if helpdesk_settings.HELPDESK_AUTO_SUBSCRIBE_ON_TICKET_RESPONSE and request.user.is_authenticated:
ticketcc_string, SHOW_SUBSCRIBE = return_ticketccstring_and_show_subscribe(request.user, ticket) ticketcc_string, SHOW_SUBSCRIBE = return_ticketccstring_and_show_subscribe(request.user, ticket)
if SHOW_SUBSCRIBE: if SHOW_SUBSCRIBE:
subscribe_staff_member_to_ticket(ticket, request.user) subscribe_staff_member_to_ticket(ticket, request.user)

View File

@ -29,7 +29,8 @@ class QuickDjangoTest(object):
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'bootstrapform', 'bootstrapform',
) )
MIDDLEWARE_CLASSES = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
@ -78,7 +79,7 @@ class QuickDjangoTest(object):
} }
}, },
INSTALLED_APPS=self.INSTALLED_APPS + self.apps, INSTALLED_APPS=self.INSTALLED_APPS + self.apps,
MIDDLEWARE_CLASSES=self.MIDDLEWARE_CLASSES, MIDDLEWARE=self.MIDDLEWARE,
ROOT_URLCONF='helpdesk.tests.urls', ROOT_URLCONF='helpdesk.tests.urls',
STATIC_URL='/static/', STATIC_URL='/static/',
TEMPLATES=self.TEMPLATES TEMPLATES=self.TEMPLATES

View File

@ -6,7 +6,7 @@ from distutils.util import convert_path
from fnmatch import fnmatchcase from fnmatch import fnmatchcase
from setuptools import setup, find_packages from setuptools import setup, find_packages
version = '0.2.4' version = '0.2.6'
# 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: