Sync with namespace and other v0.2 changes

This commit is contained in:
Garret Wassermann
2016-10-29 03:47:15 -04:00
77 changed files with 3848 additions and 3409 deletions

View File

@ -6,6 +6,8 @@ django-helpdesk - A Django powered ticket tracker for small enterprise.
forms.py - Definitions of newforms-based forms for creating and maintaining
tickets.
"""
from django.core.exceptions import ObjectDoesNotExist
try:
from StringIO import StringIO
except ImportError:
@ -13,27 +15,27 @@ except ImportError:
from django import forms
from django.forms import extras
from django.core.files.storage import default_storage
from django.conf import settings
from django.utils.translation import ugettext as _
try:
from django.contrib.auth import get_user_model
User = get_user_model()
except ImportError:
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth import get_user_model
try:
from django.utils import timezone
except ImportError:
from datetime import datetime as timezone
from helpdesk.lib import send_templated_mail, safe_template_context
from helpdesk.models import Ticket, Queue, FollowUp, Attachment, IgnoreEmail, TicketCC, CustomField, TicketCustomFieldValue, TicketDependency
from helpdesk.models import (Ticket, Queue, FollowUp, Attachment, IgnoreEmail, TicketCC,
CustomField, TicketCustomFieldValue, TicketDependency)
from helpdesk import settings as helpdesk_settings
User = get_user_model()
class CustomFieldMixin(object):
"""
Mixin that provides a method to turn CustomFields into an actual field
"""
def customfield_to_field(self, field, instanceargs):
if field.data_type == 'varchar':
fieldclass = forms.CharField
@ -52,7 +54,7 @@ class CustomFieldMixin(object):
fieldclass = forms.ChoiceField
choices = field.choices_as_array
if field.empty_selection_list:
choices.insert(0, ('','---------' ) )
choices.insert(0, ('', '---------'))
instanceargs['choices'] = choices
elif field.data_type == 'boolean':
fieldclass = forms.BooleanField
@ -73,7 +75,9 @@ class CustomFieldMixin(object):
self.fields['custom_%s' % field.name] = fieldclass(**instanceargs)
class EditTicketForm(CustomFieldMixin, forms.ModelForm):
class Meta:
model = Ticket
exclude = ('created', 'modified', 'status', 'on_hold', 'resolution', 'last_escalation', 'assigned_to')
@ -91,15 +95,14 @@ class EditTicketForm(CustomFieldMixin, forms.ModelForm):
except TicketCustomFieldValue.DoesNotExist:
initial_value = None
instanceargs = {
'label': field.label,
'help_text': field.help_text,
'required': field.required,
'initial': initial_value,
}
'label': field.label,
'help_text': field.help_text,
'required': field.required,
'initial': initial_value,
}
self.customfield_to_field(field, instanceargs)
def save(self, *args, **kwargs):
for field, value in self.cleaned_data.items():
@ -108,7 +111,7 @@ class EditTicketForm(CustomFieldMixin, forms.ModelForm):
customfield = CustomField.objects.get(name=field_name)
try:
cfv = TicketCustomFieldValue.objects.get(ticket=self.instance, field=customfield)
except:
except ObjectDoesNotExist:
cfv = TicketCustomFieldValue(ticket=self.instance, field=customfield)
cfv.value = value
cfv.save()
@ -117,42 +120,45 @@ class EditTicketForm(CustomFieldMixin, forms.ModelForm):
class EditFollowUpForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
"Filter not openned tickets here."
super(EditFollowUpForm, self).__init__(*args, **kwargs)
self.fields["ticket"].queryset = Ticket.objects.filter(status__in=(Ticket.OPEN_STATUS, Ticket.REOPENED_STATUS))
class Meta:
model = FollowUp
exclude = ('date', 'user',)
def __init__(self, *args, **kwargs):
"""Filter not openned tickets here."""
super(EditFollowUpForm, self).__init__(*args, **kwargs)
self.fields["ticket"].queryset = Ticket.objects.filter(status__in=(Ticket.OPEN_STATUS, Ticket.REOPENED_STATUS))
class TicketForm(CustomFieldMixin, forms.Form):
queue = forms.ChoiceField(
widget=forms.Select(attrs={'class':'form-control'}),
label=_('Queue'),
required=True,
choices=()
)
)
title = forms.CharField(
max_length=100,
required=True,
widget=forms.TextInput(attrs={'class':'form-control'}),
label=_('Summary of the problem'),
)
)
submitter_email = forms.EmailField(
required=False,
label=_('Submitter E-Mail Address'),
widget=forms.TextInput(attrs={'class':'form-control'}),
help_text=_('This e-mail address will receive copies of all public '
'updates to this ticket.'),
)
'updates to this ticket.'),
)
body = forms.CharField(
widget=forms.Textarea(attrs={'class':'form-control', 'rows': 15}),
label=_('Description of Issue'),
required=True,
)
)
assigned_to = forms.ChoiceField(
widget=forms.Select(attrs={'class':'form-control'}),
@ -160,8 +166,8 @@ class TicketForm(CustomFieldMixin, forms.Form):
required=False,
label=_('Case owner'),
help_text=_('If you select an owner other than yourself, they\'ll be '
'e-mailed details of this ticket immediately.'),
)
'e-mailed details of this ticket immediately.'),
)
priority = forms.ChoiceField(
widget=forms.Select(attrs={'class':'form-control'}),
@ -169,28 +175,27 @@ class TicketForm(CustomFieldMixin, forms.Form):
required=False,
initial='3',
label=_('Priority'),
help_text=_('Please select a priority carefully. If unsure, leave it '
'as \'3\'.'),
)
help_text=_('Please select a priority carefully. If unsure, leave it as \'3\'.'),
)
due_date = forms.DateTimeField(
widget=forms.TextInput(attrs={'class':'form-control'}),
required=False,
label=_('Due on'),
)
)
def clean_due_date(self):
data = self.cleaned_data['due_date']
#TODO: add Google calendar update hook
#if not hasattr(self, 'instance') or self.instance.due_date != new_data:
# print "you changed!"
# TODO: add Google calendar update hook
# if not hasattr(self, 'instance') or self.instance.due_date != new_data:
# print "you changed!"
return data
attachment = forms.FileField(
required=False,
label=_('Attach File'),
help_text=_('You can attach a file such as a document or screenshot to this ticket.'),
)
)
def __init__(self, *args, **kwargs):
"""
@ -199,14 +204,13 @@ class TicketForm(CustomFieldMixin, forms.Form):
super(TicketForm, self).__init__(*args, **kwargs)
for field in CustomField.objects.all():
instanceargs = {
'label': field.label,
'help_text': field.help_text,
'required': field.required,
}
'label': field.label,
'help_text': field.help_text,
'required': field.required,
}
self.customfield_to_field(field, instanceargs)
def save(self, user):
"""
Writes and returns a Ticket() object
@ -214,15 +218,15 @@ class TicketForm(CustomFieldMixin, forms.Form):
q = Queue.objects.get(id=int(self.cleaned_data['queue']))
t = Ticket( title = self.cleaned_data['title'],
submitter_email = self.cleaned_data['submitter_email'],
created = timezone.now(),
status = Ticket.OPEN_STATUS,
queue = q,
description = self.cleaned_data['body'],
priority = self.cleaned_data['priority'],
due_date = self.cleaned_data['due_date'],
)
t = Ticket(title=self.cleaned_data['title'],
submitter_email=self.cleaned_data['submitter_email'],
created=timezone.now(),
status=Ticket.OPEN_STATUS,
queue=q,
description=self.cleaned_data['body'],
priority=self.cleaned_data['priority'],
due_date=self.cleaned_data['due_date'],
)
if self.cleaned_data['assigned_to']:
try:
@ -237,16 +241,16 @@ class TicketForm(CustomFieldMixin, forms.Form):
field_name = field.replace('custom_', '', 1)
customfield = CustomField.objects.get(name=field_name)
cfv = TicketCustomFieldValue(ticket=t,
field=customfield,
value=value)
field=customfield,
value=value)
cfv.save()
f = FollowUp( ticket = t,
title = _('Ticket Opened'),
date = timezone.now(),
public = True,
comment = self.cleaned_data['body'],
user = user,
f = FollowUp(ticket=t,
title=_('Ticket Opened'),
date=timezone.now(),
public=True,
comment=self.cleaned_data['body'],
user=user,
)
if self.cleaned_data['assigned_to']:
f.title = _('Ticket Opened & Assigned to %(name)s') % {
@ -265,7 +269,7 @@ class TicketForm(CustomFieldMixin, forms.Form):
filename=filename,
mime_type=mimetypes.guess_type(filename)[0] or 'application/octet-stream',
size=file.size,
)
)
a.file.save(file.name, file, save=False)
a.save()
@ -290,10 +294,14 @@ class TicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(t.submitter_email)
if t.assigned_to and t.assigned_to != user and t.assigned_to.usersettings.settings.get('email_on_ticket_assign', False) and t.assigned_to.email and t.assigned_to.email not in messages_sent_to:
if t.assigned_to and \
t.assigned_to != user and \
t.assigned_to.usersettings.settings.get('email_on_ticket_assign', False) and \
t.assigned_to.email and \
t.assigned_to.email not in messages_sent_to:
send_templated_mail(
'assigned_owner',
context,
@ -301,7 +309,7 @@ class TicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(t.assigned_to.email)
if q.new_ticket_cc and q.new_ticket_cc not in messages_sent_to:
@ -312,10 +320,12 @@ class TicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(q.new_ticket_cc)
if q.updated_ticket_cc and q.updated_ticket_cc != q.new_ticket_cc and q.updated_ticket_cc not in messages_sent_to:
if q.updated_ticket_cc and \
q.updated_ticket_cc != q.new_ticket_cc and \
q.updated_ticket_cc not in messages_sent_to:
send_templated_mail(
'newticket_cc',
context,
@ -323,7 +333,7 @@ class TicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
return t
@ -333,28 +343,28 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
label=_('Queue'),
required=True,
choices=()
)
)
title = forms.CharField(
max_length=100,
required=True,
widget=forms.TextInput(),
label=_('Summary of your query'),
)
)
submitter_email = forms.EmailField(
required=True,
label=_('Your E-Mail Address'),
help_text=_('We will e-mail you when your ticket is updated.'),
)
)
body = forms.CharField(
widget=forms.Textarea(),
label=_('Description of your issue'),
required=True,
help_text=_('Please be as descriptive as possible, including any '
'details we may need to address your query.'),
)
'details we may need to address your query.'),
)
priority = forms.ChoiceField(
choices=Ticket.PRIORITY_CHOICES,
@ -362,20 +372,20 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
initial='3',
label=_('Urgency'),
help_text=_('Please select a priority carefully.'),
)
)
due_date = forms.DateTimeField(
widget=extras.SelectDateWidget,
required=False,
label=_('Due on'),
)
)
attachment = forms.FileField(
required=False,
label=_('Attach File'),
help_text=_('You can attach a file such as a document or screenshot to this ticket.'),
max_length=1000,
)
)
def __init__(self, *args, **kwargs):
"""
@ -384,10 +394,10 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
super(PublicTicketForm, self).__init__(*args, **kwargs)
for field in CustomField.objects.filter(staff_only=False):
instanceargs = {
'label': field.label,
'help_text': field.help_text,
'required': field.required,
}
'label': field.label,
'help_text': field.help_text,
'required': field.required,
}
self.customfield_to_field(field, instanceargs)
@ -399,15 +409,15 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
q = Queue.objects.get(id=int(self.cleaned_data['queue']))
t = Ticket(
title = self.cleaned_data['title'],
submitter_email = self.cleaned_data['submitter_email'],
created = timezone.now(),
status = Ticket.OPEN_STATUS,
queue = q,
description = self.cleaned_data['body'],
priority = self.cleaned_data['priority'],
due_date = self.cleaned_data['due_date'],
)
title=self.cleaned_data['title'],
submitter_email=self.cleaned_data['submitter_email'],
created=timezone.now(),
status=Ticket.OPEN_STATUS,
queue=q,
description=self.cleaned_data['body'],
priority=self.cleaned_data['priority'],
due_date=self.cleaned_data['due_date'],
)
if q.default_owner and not t.assigned_to:
t.assigned_to = q.default_owner
@ -419,17 +429,17 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
field_name = field.replace('custom_', '', 1)
customfield = CustomField.objects.get(name=field_name)
cfv = TicketCustomFieldValue(ticket=t,
field=customfield,
value=value)
field=customfield,
value=value)
cfv.save()
f = FollowUp(
ticket = t,
title = _('Ticket Opened Via Web'),
date = timezone.now(),
public = True,
comment = self.cleaned_data['body'],
)
ticket=t,
title=_('Ticket Opened Via Web'),
date=timezone.now(),
public=True,
comment=self.cleaned_data['body'],
)
f.save()
@ -443,7 +453,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
filename=filename,
mime_type=mimetypes.guess_type(filename)[0] or 'application/octet-stream',
size=file.size,
)
)
a.file.save(file.name, file, save=False)
a.save()
@ -463,10 +473,13 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(t.submitter_email)
if t.assigned_to and t.assigned_to.usersettings.settings.get('email_on_ticket_assign', False) and t.assigned_to.email and t.assigned_to.email not in messages_sent_to:
if t.assigned_to and \
t.assigned_to.usersettings.settings.get('email_on_ticket_assign', False) and \
t.assigned_to.email and \
t.assigned_to.email not in messages_sent_to:
send_templated_mail(
'assigned_owner',
context,
@ -474,7 +487,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(t.assigned_to.email)
if q.new_ticket_cc and q.new_ticket_cc not in messages_sent_to:
@ -485,10 +498,12 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
messages_sent_to.append(q.new_ticket_cc)
if q.updated_ticket_cc and q.updated_ticket_cc != q.new_ticket_cc and q.updated_ticket_cc not in messages_sent_to:
if q.updated_ticket_cc and \
q.updated_ticket_cc != q.new_ticket_cc and \
q.updated_ticket_cc not in messages_sent_to:
send_templated_mail(
'newticket_cc',
context,
@ -496,7 +511,7 @@ class PublicTicketForm(CustomFieldMixin, forms.Form):
sender=q.from_address,
fail_silently=True,
files=files,
)
)
return t
@ -506,25 +521,19 @@ class UserSettingsForm(forms.Form):
label=_('Show Ticket List on Login?'),
help_text=_('Display the ticket list upon login? Otherwise, the dashboard is shown.'),
required=False,
)
)
email_on_ticket_change = forms.BooleanField(
label=_('E-mail me on ticket change?'),
help_text=_('If you\'re the ticket owner and the ticket is changed via the web by somebody else, do you want to receive an e-mail?'),
required=False,
)
)
email_on_ticket_assign = forms.BooleanField(
label=_('E-mail me when assigned a ticket?'),
help_text=_('If you are assigned a ticket via the web, do you want to receive an e-mail?'),
required=False,
)
email_on_ticket_apichange = forms.BooleanField(
label=_('E-mail me when a ticket is changed via the API?'),
help_text=_('If a ticket is altered by the API, do you want to receive an e-mail?'),
required=False,
)
)
tickets_per_page = forms.ChoiceField(
label=_('Number of tickets to show per page'),
@ -537,15 +546,23 @@ class UserSettingsForm(forms.Form):
label=_('Use my e-mail address when submitting tickets?'),
help_text=_('When you submit a ticket, do you want to automatically use your e-mail address as the submitter address? You can type a different e-mail address when entering the ticket if needed, this option only changes the default.'),
required=False,
)
)
class EmailIgnoreForm(forms.ModelForm):
class Meta:
model = IgnoreEmail
exclude = []
class TicketCCForm(forms.ModelForm):
''' Adds either an email address or helpdesk user as a CC on a Ticket. Used for processing POST requests. '''
class Meta:
model = TicketCC
exclude = ('ticket',)
def __init__(self, *args, **kwargs):
super(TicketCCForm, self).__init__(*args, **kwargs)
if helpdesk_settings.HELPDESK_STAFF_ONLY_TICKET_CC:
@ -553,9 +570,6 @@ class TicketCCForm(forms.ModelForm):
else:
users = User.objects.filter(is_active=True).order_by(User.USERNAME_FIELD)
self.fields['user'].queryset = users
class Meta:
model = TicketCC
exclude = ('ticket',)
class TicketCCUserForm(forms.ModelForm):
''' Adds a helpdesk user as a CC on a Ticket '''
@ -579,6 +593,7 @@ class TicketCCEmailForm(forms.ModelForm):
exclude = ('ticket','user',)
class TicketDependencyForm(forms.ModelForm):
class Meta:
model = TicketDependency
exclude = ('ticket',)