diff --git a/README b/README index 7b98cfe8..6dc775ba 100644 --- a/README +++ b/README @@ -31,10 +31,6 @@ LICENSE.JQUERY and LICENSE.NICEDIT for their respective license terms. 3. An existing WORKING Django project with database etc. If you cannot log into the Admin, you won't get this product working. -4. pygooglechart (needs minor mods to @staticmethod calls for python 2.3) - http://pygooglechart.slowchop.com/ - - ######################### 3. Installation ######################### diff --git a/forms.py b/forms.py index 7b1b5867..be8cfc77 100644 --- a/forms.py +++ b/forms.py @@ -112,7 +112,7 @@ class TicketForm(forms.Form): fail_silently=True, ) - if t.assigned_to and t.assigned_to != user: + if t.assigned_to and t.assigned_to != user and getattr(t.assigned_to.usersettings.settings, 'email_on_ticket_assign', False): send_templated_mail( 'assigned_owner', context, @@ -240,3 +240,28 @@ class PublicTicketForm(forms.Form): return t + +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, + ) diff --git a/management/commands/get_email.py b/management/commands/get_email.py index 940a476b..29d82564 100644 --- a/management/commands/get_email.py +++ b/management/commands/get_email.py @@ -245,15 +245,16 @@ def ticket_from_message(message, queue): for file in files: filename = file['filename'].replace(' ', '_') - a = Attachment( - followup=f, - filename=filename, - mime_type=file['type'], - size=len(file['content']), - ) - a.file.save(file['filename'], ContentFile(file['content'])) - a.save() - print " - %s" % file['filename'] + if file['content']: + a = Attachment( + followup=f, + filename=filename, + mime_type=file['type'], + size=len(file['content']), + ) + a.file.save(file['filename'], ContentFile(file['content'])) + a.save() + print " - %s" % file['filename'] if __name__ == '__main__': diff --git a/models.py b/models.py index aefed79d..475047bc 100644 --- a/models.py +++ b/models.py @@ -182,7 +182,7 @@ class Queue(models.Model): return u'%s <%s>' % (self.title, self.email_address) from_address = property(_from_address) - def save(self): + def save(self, force_insert=False, force_update=False): if self.email_box_type == 'imap' and not self.email_box_imap_folder: self.email_box_imap_folder = 'INBOX' @@ -195,7 +195,7 @@ class Queue(models.Model): self.email_box_port = 995 elif self.email_box_type == 'pop3' and not self.email_box_ssl: self.email_box_port = 110 - super(Queue, self).save() + super(Queue, self).save(force_insert, force_update) class Ticket(models.Model): @@ -402,7 +402,7 @@ class Ticket(models.Model): return ('helpdesk_view', [str(self.id)]) get_absolute_url = models.permalink(get_absolute_url) - def save(self): + def save(self, force_insert=False, force_update=False): if not self.id: # This is a new ticket as no ID yet exists. self.created = datetime.now() @@ -412,7 +412,7 @@ class Ticket(models.Model): self.modified = datetime.now() - super(Ticket, self).save() + super(Ticket, self).save(force_insert, force_update) class FollowUpManager(models.Manager): @@ -488,12 +488,12 @@ class FollowUp(models.Model): def get_absolute_url(self): return u"%s#followup%s" % (self.ticket.get_absolute_url(), self.id) - def save(self): + def save(self, force_insert=False, force_update=False): t = self.ticket t.modified = datetime.now() self.date = datetime.now() t.save() - super(FollowUp, self).save() + super(FollowUp, self).save(force_insert, force_update) class TicketChange(models.Model): @@ -796,9 +796,9 @@ class KBItem(models.Model): 'changed.'), ) - def save(self): + def save(self, force_insert=False, force_update=False): self.last_updated = datetime.now() - return super(KBItem, self).save() + return super(KBItem, self).save(force_insert, force_update) def _score(self): if self.votes > 0: @@ -855,3 +855,73 @@ class SavedSearch(models.Model): return u'%s (*)' % self.title else: return u'%s' % self.title + +class UserSettings(models.Model): + """ + A bunch of user-specific settings that we want to be able to define, such + as notification preferences and other things that should probably be + configurable. + + We should always refer to user.usersettings.settings['setting_name']. + """ + + user = models.OneToOneField(User) + + settings_pickled = models.TextField( + _('Settings Dictionary'), + help_text=_('This is a base64-encoded representation of a pickled Python dictionary. Do not change this field via the admin.'), + blank=True, + null=True, + ) + + def _set_settings(self, data): + # data should always be a Python dictionary. + import cPickle, base64 + self.settings_pickled = base64.urlsafe_b64encode(cPickle.dumps(data)) + + def _get_settings(self): + # return a python dictionary representing the pickled data. + import cPickle, base64 + try: + return cPickle.loads(base64.urlsafe_b64decode(str(self.settings_pickled))) + except cPickle.UnpicklingError: + return {} + + settings = property(_get_settings, _set_settings) + + def __unicode__(self): + return u'Preferences for %s' % self.user + + class Meta: + verbose_name = 'User Settings' + verbose_name_plural = 'User Settings' + + +def create_usersettings(sender, created_models=[], instance=None, created=False, **kwargs): + """ + Helper function to create UserSettings instances as + required, eg when we first create the UserSettings database + table via 'syncdb' or when we save a new user. + + If we end up with users with no UserSettings, then we get horrible + 'DoesNotExist: UserSettings matching query does not exist.' errors. + """ + if sender == User and created: + # This is a new user, so lets create their settings entry. + s = UserSettings(user=instance) + s.save() + elif UserSettings in created_models: + # We just created the UserSettings model, lets create a UserSettings + # entry for each existing user. This will only happen once (at install + # time, or at upgrade) when the UserSettings model doesn't already + # exist. + for u in User.objects.all(): + try: + s = UserSettings.objects.get(user=u) + except UserSettings.DoesNotExist: + s = UserSettings(user=u) + s.save() + +models.signals.post_syncdb.connect(create_usersettings) +models.signals.post_save.connect(create_usersettings, sender=User) + diff --git a/templates/helpdesk/base.html b/templates/helpdesk/base.html index 23e2ef43..f45a45a2 100644 --- a/templates/helpdesk/base.html +++ b/templates/helpdesk/base.html @@ -28,7 +28,7 @@ {% block helpdesk_body %}{% endblock %}
{% include "helpdesk/debug.html" %} diff --git a/templates/helpdesk/user_settings.html b/templates/helpdesk/user_settings.html new file mode 100644 index 00000000..f737bd73 --- /dev/null +++ b/templates/helpdesk/user_settings.html @@ -0,0 +1,22 @@ +{% extends "helpdesk/base.html" %}{% load i18n %} + +{% block helpdesk_title %}{% trans "Change User Settings" %}{% endblock %} + +{% block helpdesk_body %}{% blocktrans %} +Use the following options to change the way your helpdesk system works for you. These settings do not impact any other user.
{% endblocktrans %} + + + +{% endblock %} diff --git a/urls.py b/urls.py index 18020799..4fb802f1 100644 --- a/urls.py +++ b/urls.py @@ -69,6 +69,10 @@ urlpatterns = patterns('helpdesk.views.staff', url(r'^delete_query/(?P