2008-08-19 10:50:38 +02:00
"""
2011-01-26 00:08:41 +01:00
django - helpdesk - A Django powered ticket tracker for small enterprise .
2007-12-27 01:29:17 +01:00
2008-02-06 05:36:07 +01:00
( c ) Copyright 2008 Jutda . All Rights Reserved . See LICENSE for details .
2007-12-27 01:29:17 +01:00
2008-08-19 10:50:38 +02:00
forms . py - Definitions of newforms - based forms for creating and maintaining
2008-02-06 05:36:07 +01:00
tickets .
2007-12-27 01:29:17 +01:00
"""
2011-05-03 00:32:15 +02:00
from StringIO import StringIO
2008-08-19 10:50:38 +02:00
2008-08-12 01:24:18 +02:00
from django import forms
2011-11-19 09:34:07 +01:00
from django . forms import extras
2013-11-19 22:24:52 +01:00
from django . core . files . storage import default_storage
2009-01-22 09:08:22 +01:00
from django . conf import settings
2008-05-07 11:04:18 +02:00
from django . utils . translation import ugettext as _
2014-06-18 16:21:37 +02:00
try :
from django . contrib . auth import get_user_model
User = get_user_model ( )
except ImportError :
from django . contrib . auth . models import User
2013-01-23 01:35:18 +01:00
try :
from django . utils import timezone
2013-01-23 01:55:36 +01:00
except ImportError :
2013-01-23 01:35:18 +01:00
from datetime import datetime as timezone
2007-12-27 01:29:17 +01:00
2011-11-08 17:31:05 +01:00
from helpdesk . lib import send_templated_mail , safe_template_context
2011-05-10 11:27:11 +02:00
from helpdesk . models import Ticket , Queue , FollowUp , Attachment , IgnoreEmail , TicketCC , CustomField , TicketCustomFieldValue , TicketDependency
2012-01-17 22:40:44 +01:00
from helpdesk import settings as helpdesk_settings
2008-08-19 10:50:38 +02:00
2014-06-05 01:45:07 +02:00
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
instanceargs [ ' max_length ' ] = field . max_length
elif field . data_type == ' text ' :
fieldclass = forms . CharField
instanceargs [ ' widget ' ] = forms . Textarea
instanceargs [ ' max_length ' ] = field . max_length
elif field . data_type == ' integer ' :
fieldclass = forms . IntegerField
elif field . data_type == ' decimal ' :
fieldclass = forms . DecimalField
instanceargs [ ' decimal_places ' ] = field . decimal_places
instanceargs [ ' max_digits ' ] = field . max_length
elif field . data_type == ' list ' :
fieldclass = forms . ChoiceField
choices = field . choices_as_array
if field . empty_selection_list :
choices . insert ( 0 , ( ' ' , ' --------- ' ) )
instanceargs [ ' choices ' ] = choices
elif field . data_type == ' boolean ' :
fieldclass = forms . BooleanField
elif field . data_type == ' date ' :
fieldclass = forms . DateField
elif field . data_type == ' time ' :
fieldclass = forms . TimeField
elif field . data_type == ' datetime ' :
fieldclass = forms . DateTimeField
elif field . data_type == ' email ' :
fieldclass = forms . EmailField
elif field . data_type == ' url ' :
fieldclass = forms . URLField
elif field . data_type == ' ipaddress ' :
fieldclass = forms . IPAddressField
elif field . data_type == ' slug ' :
fieldclass = forms . SlugField
self . fields [ ' custom_ %s ' % field . name ] = fieldclass ( * * instanceargs )
class EditTicketForm ( CustomFieldMixin , forms . ModelForm ) :
2009-06-03 13:43:46 +02:00
class Meta :
model = Ticket
exclude = ( ' created ' , ' modified ' , ' status ' , ' on_hold ' , ' resolution ' , ' last_escalation ' , ' assigned_to ' )
2011-05-09 23:54:44 +02:00
def __init__ ( self , * args , * * kwargs ) :
"""
Add any custom fields that are defined to the form
"""
super ( EditTicketForm , self ) . __init__ ( * args , * * kwargs )
for field in CustomField . objects . all ( ) :
try :
current_value = TicketCustomFieldValue . objects . get ( ticket = self . instance , field = field )
initial_value = current_value . value
except TicketCustomFieldValue . DoesNotExist :
initial_value = None
instanceargs = {
' label ' : field . label ,
' help_text ' : field . help_text ,
' required ' : field . required ,
' initial ' : initial_value ,
}
2014-06-05 01:45:07 +02:00
self . customfield_to_field ( field , instanceargs )
2011-05-09 23:54:44 +02:00
def save ( self , * args , * * kwargs ) :
for field , value in self . cleaned_data . items ( ) :
if field . startswith ( ' custom_ ' ) :
field_name = field . replace ( ' custom_ ' , ' ' )
customfield = CustomField . objects . get ( name = field_name )
try :
cfv = TicketCustomFieldValue . objects . get ( ticket = self . instance , field = customfield )
except :
cfv = TicketCustomFieldValue ( ticket = self . instance , field = customfield )
cfv . value = value
cfv . save ( )
return super ( EditTicketForm , self ) . save ( * args , * * kwargs )
2009-06-03 13:43:46 +02:00
2011-01-29 07:02:03 +01:00
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 ' , )
2014-06-05 01:45:07 +02:00
class TicketForm ( CustomFieldMixin , forms . Form ) :
2008-08-19 10:50:38 +02:00
queue = forms . ChoiceField (
label = _ ( ' Queue ' ) ,
required = True ,
choices = ( )
)
title = forms . CharField (
max_length = 100 ,
required = True ,
2011-11-28 16:06:36 +01:00
widget = forms . TextInput ( attrs = { ' size ' : ' 60 ' } ) ,
2008-08-19 10:50:38 +02:00
label = _ ( ' Summary of the problem ' ) ,
)
submitter_email = forms . EmailField (
required = False ,
label = _ ( ' Submitter E-Mail Address ' ) ,
2011-11-28 16:06:36 +01:00
widget = forms . TextInput ( attrs = { ' size ' : ' 60 ' } ) ,
2008-08-19 10:50:38 +02:00
help_text = _ ( ' This e-mail address will receive copies of all public '
' updates to this ticket. ' ) ,
)
body = forms . CharField (
2011-11-28 16:06:36 +01:00
widget = forms . Textarea ( attrs = { ' cols ' : 47 , ' rows ' : 15 } ) ,
2008-08-19 10:50:38 +02:00
label = _ ( ' Description of Issue ' ) ,
required = True ,
)
assigned_to = forms . ChoiceField (
choices = ( ) ,
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. ' ) ,
)
priority = forms . ChoiceField (
choices = Ticket . PRIORITY_CHOICES ,
required = False ,
initial = ' 3 ' ,
label = _ ( ' Priority ' ) ,
help_text = _ ( ' Please select a priority carefully. If unsure, leave it '
' as \' 3 \' . ' ) ,
)
2012-01-20 21:48:38 +01:00
due_date = forms . DateTimeField (
widget = extras . SelectDateWidget ,
required = False ,
label = _ ( ' Due on ' ) ,
)
2012-01-21 00:03:07 +01:00
def clean_due_date ( self ) :
data = self . cleaned_data [ ' due_date ' ]
2012-01-21 01:47:41 +01:00
#TODO: add Google calendar update hook
#if not hasattr(self, 'instance') or self.instance.due_date != new_data:
# print "you changed!"
2012-01-21 00:03:07 +01:00
return data
2012-01-20 21:48:38 +01:00
2009-01-22 09:08:22 +01:00
attachment = forms . FileField (
required = False ,
label = _ ( ' Attach File ' ) ,
help_text = _ ( ' You can attach a file such as a document or screenshot to this ticket. ' ) ,
)
2011-02-02 12:22:46 +01:00
def __init__ ( self , * args , * * kwargs ) :
"""
Add any custom fields that are defined to the 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 ,
}
2014-06-05 01:45:07 +02:00
self . customfield_to_field ( field , instanceargs )
2011-02-02 12:22:46 +01:00
2007-12-28 04:29:45 +01:00
def save ( self , user ) :
2007-12-27 01:29:17 +01:00
"""
Writes and returns a Ticket ( ) object
"""
2008-08-19 10:50:38 +02:00
2007-12-27 01:29:17 +01:00
q = Queue . objects . get ( id = int ( self . cleaned_data [ ' queue ' ] ) )
2008-08-19 10:50:38 +02:00
t = Ticket ( title = self . cleaned_data [ ' title ' ] ,
2008-01-15 00:39:43 +01:00
submitter_email = self . cleaned_data [ ' submitter_email ' ] ,
2013-01-23 00:59:12 +01:00
created = timezone . now ( ) ,
2007-12-27 01:29:17 +01:00
status = Ticket . OPEN_STATUS ,
queue = q ,
description = self . cleaned_data [ ' body ' ] ,
2008-01-10 06:06:47 +01:00
priority = self . cleaned_data [ ' priority ' ] ,
2012-01-20 21:48:38 +01:00
due_date = self . cleaned_data [ ' due_date ' ] ,
2007-12-27 01:29:17 +01:00
)
2008-08-19 10:50:38 +02:00
2007-12-27 01:29:17 +01:00
if self . cleaned_data [ ' assigned_to ' ] :
2007-12-28 04:38:39 +01:00
try :
u = User . objects . get ( id = self . cleaned_data [ ' assigned_to ' ] )
t . assigned_to = u
2008-08-18 23:29:31 +02:00
except User . DoesNotExist :
2007-12-28 04:38:39 +01:00
t . assigned_to = None
2007-12-27 01:29:17 +01:00
t . save ( )
2011-02-02 12:22:46 +01:00
for field , value in self . cleaned_data . items ( ) :
if field . startswith ( ' custom_ ' ) :
field_name = field . replace ( ' custom_ ' , ' ' )
customfield = CustomField . objects . get ( name = field_name )
cfv = TicketCustomFieldValue ( ticket = t ,
field = customfield ,
value = value )
cfv . save ( )
2007-12-27 01:29:17 +01:00
2008-01-15 00:39:43 +01:00
f = FollowUp ( ticket = t ,
2008-05-07 11:04:18 +02:00
title = _ ( ' Ticket Opened ' ) ,
2013-01-23 00:59:12 +01:00
date = timezone . now ( ) ,
2008-01-15 00:39:43 +01:00
public = True ,
comment = self . cleaned_data [ ' body ' ] ,
user = user ,
2007-12-28 04:29:45 +01:00
)
if self . cleaned_data [ ' assigned_to ' ] :
2008-08-19 10:50:38 +02:00
f . title = _ ( ' Ticket Opened & Assigned to %(name)s ' ) % {
' name ' : t . get_assigned_to
}
2007-12-28 04:29:45 +01:00
f . save ( )
2009-01-22 09:08:22 +01:00
files = [ ]
if self . cleaned_data [ ' attachment ' ] :
import mimetypes
file = self . cleaned_data [ ' attachment ' ]
filename = file . name . replace ( ' ' , ' _ ' )
a = Attachment (
followup = f ,
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 ( )
if file . size < getattr ( settings , ' MAX_EMAIL_ATTACHMENT_SIZE ' , 512000 ) :
# Only files smaller than 512kb (or as defined in
# settings.MAX_EMAIL_ATTACHMENT_SIZE) are sent via email.
2013-11-19 22:24:52 +01:00
try :
files . append ( a . file . path )
except NotImplementedError :
pass
2008-08-19 10:50:38 +02:00
2011-11-08 17:31:05 +01:00
context = safe_template_context ( t )
context [ ' comment ' ] = f . comment
2009-08-11 11:02:48 +02:00
messages_sent_to = [ ]
2008-01-15 00:39:43 +01:00
if t . submitter_email :
2008-08-19 10:50:38 +02:00
send_templated_mail (
' newticket_submitter ' ,
context ,
recipients = t . submitter_email ,
sender = q . from_address ,
fail_silently = True ,
2009-01-22 09:08:22 +01:00
files = files ,
2008-08-19 10:50:38 +02:00
)
2009-08-11 11:02:48 +02:00
messages_sent_to . append ( t . submitter_email )
2007-12-28 04:29:45 +01:00
2012-10-07 10:46:57 +02:00
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 :
2008-08-19 10:50:38 +02:00
send_templated_mail (
' assigned_owner ' ,
context ,
recipients = t . assigned_to . email ,
sender = q . from_address ,
fail_silently = True ,
2009-01-22 09:08:22 +01:00
files = files ,
2008-08-19 10:50:38 +02:00
)
2009-08-11 11:02:48 +02:00
messages_sent_to . append ( t . assigned_to . email )
2008-01-21 02:02:12 +01:00
2009-08-11 11:02:48 +02:00
if q . new_ticket_cc and q . new_ticket_cc not in messages_sent_to :
2008-08-19 10:50:38 +02:00
send_templated_mail (
' newticket_cc ' ,
context ,
recipients = q . new_ticket_cc ,
sender = q . from_address ,
fail_silently = True ,
2009-01-22 09:08:22 +01:00
files = files ,
2008-08-19 10:50:38 +02:00
)
2009-08-11 11:02:48 +02:00
messages_sent_to . append ( q . new_ticket_cc )
2008-08-19 10:50:38 +02:00
2009-08-11 11:02:48 +02:00
if q . updated_ticket_cc and q . updated_ticket_cc != q . new_ticket_cc and q . updated_ticket_cc not in messages_sent_to :
2008-08-19 10:50:38 +02:00
send_templated_mail (
' newticket_cc ' ,
context ,
recipients = q . updated_ticket_cc ,
sender = q . from_address ,
fail_silently = True ,
2009-01-22 09:08:22 +01:00
files = files ,
2008-08-19 10:50:38 +02:00
)
2008-01-21 02:02:12 +01:00
2007-12-27 01:29:17 +01:00
return t
2008-01-16 01:26:24 +01:00
2008-08-19 10:50:38 +02:00
2014-06-05 01:45:07 +02:00
class PublicTicketForm ( CustomFieldMixin , forms . Form ) :
2008-08-19 10:50:38 +02:00
queue = forms . ChoiceField (
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. ' ) ,
)
priority = forms . ChoiceField (
choices = Ticket . PRIORITY_CHOICES ,
required = True ,
initial = ' 3 ' ,
label = _ ( ' Urgency ' ) ,
help_text = _ ( ' Please select a priority carefully. ' ) ,
)
2012-01-20 21:48:38 +01:00
due_date = forms . DateTimeField (
widget = extras . SelectDateWidget ,
required = False ,
label = _ ( ' Due on ' ) ,
)
2009-01-22 09:08:22 +01:00
attachment = forms . FileField (
required = False ,
label = _ ( ' Attach File ' ) ,
help_text = _ ( ' You can attach a file such as a document or screenshot to this ticket. ' ) ,
)
2011-02-02 12:22:46 +01:00
def __init__ ( self , * args , * * kwargs ) :
"""
Add any custom fields that are defined to the 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 ,
}
2014-06-05 01:45:07 +02:00
self . customfield_to_field ( field , instanceargs )
2011-02-02 12:22:46 +01:00
2008-01-16 01:26:24 +01:00
def save ( self ) :
"""
Writes and returns a Ticket ( ) object
"""
2008-08-19 10:50:38 +02:00
2008-01-16 01:26:24 +01:00
q = Queue . objects . get ( id = int ( self . cleaned_data [ ' queue ' ] ) )
2008-08-19 10:50:38 +02:00
t = Ticket (
title = self . cleaned_data [ ' title ' ] ,
submitter_email = self . cleaned_data [ ' submitter_email ' ] ,
2013-01-23 00:59:12 +01:00
created = timezone . now ( ) ,
2008-08-19 10:50:38 +02:00
status = Ticket . OPEN_STATUS ,
queue = q ,
description = self . cleaned_data [ ' body ' ] ,
priority = self . cleaned_data [ ' priority ' ] ,
2012-01-20 21:48:38 +01:00
due_date = self . cleaned_data [ ' due_date ' ] ,
2008-08-19 10:50:38 +02:00
)
2008-01-16 01:26:24 +01:00
t . save ( )
2011-02-02 12:22:46 +01:00
for field , value in self . cleaned_data . items ( ) :
if field . startswith ( ' custom_ ' ) :
field_name = field . replace ( ' custom_ ' , ' ' )
customfield = CustomField . objects . get ( name = field_name )
cfv = TicketCustomFieldValue ( ticket = t ,
field = customfield ,
value = value )
cfv . save ( )
2008-08-19 10:50:38 +02:00
f = FollowUp (
ticket = t ,
title = _ ( ' Ticket Opened Via Web ' ) ,
2013-01-23 00:59:12 +01:00
date = timezone . now ( ) ,
2008-08-19 10:50:38 +02:00
public = True ,
comment = self . cleaned_data [ ' body ' ] ,
)
2008-01-16 01:26:24 +01:00
f . save ( )
2008-08-19 10:50:38 +02:00
2009-01-22 09:08:22 +01:00
files = [ ]
if self . cleaned_data [ ' attachment ' ] :
import mimetypes
file = self . cleaned_data [ ' attachment ' ]
filename = file . name . replace ( ' ' , ' _ ' )
a = Attachment (
followup = f ,
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 ( )
if file . size < getattr ( settings , ' MAX_EMAIL_ATTACHMENT_SIZE ' , 512000 ) :
# Only files smaller than 512kb (or as defined in
# settings.MAX_EMAIL_ATTACHMENT_SIZE) are sent via email.
files . append ( a . file . path )
2011-11-08 17:31:05 +01:00
context = safe_template_context ( t )
2008-01-16 01:26:24 +01:00
2009-08-11 11:02:48 +02:00
messages_sent_to = [ ]
2008-08-19 10:50:38 +02:00
send_templated_mail (
' newticket_submitter ' ,
context ,
recipients = t . submitter_email ,
sender = q . from_address ,
fail_silently = True ,
2009-01-22 09:08:22 +01:00
files = files ,
2008-08-19 10:50:38 +02:00
)
2009-08-11 11:02:48 +02:00
messages_sent_to . append ( t . submitter_email )
2008-01-16 01:26:24 +01:00
2009-08-11 11:02:48 +02:00
if q . new_ticket_cc and q . new_ticket_cc not in messages_sent_to :
2008-08-19 10:50:38 +02:00
send_templated_mail (
' newticket_cc ' ,
context ,
recipients = q . new_ticket_cc ,
sender = q . from_address ,
fail_silently = True ,
2009-01-22 09:08:22 +01:00
files = files ,
2008-08-19 10:50:38 +02:00
)
2009-08-11 11:02:48 +02:00
messages_sent_to . append ( q . new_ticket_cc )
2008-08-19 10:50:38 +02:00
2009-08-11 11:02:48 +02:00
if q . updated_ticket_cc and q . updated_ticket_cc != q . new_ticket_cc and q . updated_ticket_cc not in messages_sent_to :
2008-08-19 10:50:38 +02:00
send_templated_mail (
' newticket_cc ' ,
context ,
recipients = q . updated_ticket_cc ,
sender = q . from_address ,
fail_silently = True ,
2009-01-22 09:08:22 +01:00
files = files ,
2008-08-19 10:50:38 +02:00
)
2008-01-21 02:02:12 +01:00
2008-01-16 01:26:24 +01:00
return t
2008-08-19 10:50:38 +02:00
2008-09-09 10:32:01 +02:00
class UserSettingsForm ( forms . Form ) :
login_view_ticketlist = forms . BooleanField (
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 ,
)
2008-10-25 00:52:34 +02:00
2009-07-22 10:19:46 +02:00
tickets_per_page = forms . IntegerField (
label = _ ( ' Number of tickets to show per page ' ) ,
help_text = _ ( ' How many tickets do you want to see on the Ticket List page? ' ) ,
required = False ,
min_value = 1 ,
max_value = 1000 ,
)
2009-08-06 10:56:02 +02:00
use_email_as_submitter = forms . BooleanField (
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 ,
)
2008-10-25 00:52:34 +02:00
class EmailIgnoreForm ( forms . ModelForm ) :
class Meta :
model = IgnoreEmail
2009-09-09 10:47:48 +02:00
class TicketCCForm ( forms . ModelForm ) :
2011-02-06 18:49:07 +01:00
def __init__ ( self , * args , * * kwargs ) :
super ( TicketCCForm , self ) . __init__ ( * args , * * kwargs )
2012-01-17 22:40:44 +01:00
if helpdesk_settings . HELPDESK_STAFF_ONLY_TICKET_CC :
users = User . objects . filter ( is_active = True , is_staff = True ) . order_by ( ' username ' )
else :
users = User . objects . filter ( is_active = True ) . order_by ( ' username ' )
self . fields [ ' user ' ] . queryset = users
2009-09-09 10:47:48 +02:00
class Meta :
model = TicketCC
exclude = ( ' ticket ' , )
2011-02-06 18:49:07 +01:00
2011-05-10 11:27:11 +02:00
class TicketDependencyForm ( forms . ModelForm ) :
class Meta :
model = TicketDependency
exclude = ( ' ticket ' , )