From 3f8fc2cd689d6819a45f2ba7bbd1ff93c8b77265 Mon Sep 17 00:00:00 2001 From: Ross Poulton Date: Tue, 9 Sep 2008 08:32:01 +0000 Subject: [PATCH] * Added force_insert and force_update parameters to model save() overrides (as per Django rev 8670) * Added 'UserSettings' model to provide a user profile system independent of existing Django user profiles, for two reasons: 1) Avoids users having to update settings.py and 2) Allows jutda-helpdesk to integrate with websites who already use a User Profile * Settings added in this revision allow a user to control e-mail alerts, and to determine whether they see the dashboard or ticket list at login. * New 'Settings' link in page footer for signed-in users * Logout now takes you to the Helpdesk homepage * Fixed file attachment bug in management/commands/get_email.py which seemed to have been un-done (fixes issue # 4. Jutda-helpdesk is now compatible with Django 1.0! --- README | 4 -- forms.py | 27 ++++++++- management/commands/get_email.py | 19 +++--- models.py | 86 ++++++++++++++++++++++++--- templates/helpdesk/base.html | 2 +- templates/helpdesk/user_settings.html | 22 +++++++ urls.py | 5 ++ views/api.py | 4 +- views/public.py | 5 +- views/staff.py | 33 +++++++--- 10 files changed, 173 insertions(+), 34 deletions(-) create mode 100644 templates/helpdesk/user_settings.html 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 %} +

User Settings

+ +

Use the following options to change the way your helpdesk system works for you. These settings do not impact any other user.

{% endblocktrans %} + +
+
+
{% for field in form %} +
+
{{ field }}
+ {% if field.errors %}
{{ field.errors }}
{% endif %} + {% if field.help_text %}
{{ field.help_text }}
{% endif %} + {% endfor %}
+
+ +
+ +{% 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[0-9]+)/$', 'delete_saved_query', name='helpdesk_delete_query'), + + url(r'^settings/$', + 'user_settings', + name='helpdesk_user_settings'), ) urlpatterns += patterns('helpdesk.views.public', @@ -97,6 +101,7 @@ urlpatterns += patterns('', url(r'^logout/$', 'django.contrib.auth.views.logout', + {'next_page': '../'}, name='logout'), ) diff --git a/views/api.py b/views/api.py index a5ea1f71..9374f572 100644 --- a/views/api.py +++ b/views/api.py @@ -215,7 +215,7 @@ class API: fail_silently=True, ) - if ticket.assigned_to and self.request.user != ticket.assigned_to: + if ticket.assigned_to and self.request.user != ticket.assigned_to and getattr(ticket.assigned_to.usersettings.settings, 'email_on_ticket_apichange', False): send_templated_mail( 'updated_owner', context, @@ -276,7 +276,7 @@ class API: fail_silently=True, ) - if ticket.assigned_to and self.request.user != ticket.assigned_to: + if ticket.assigned_to and self.request.user != ticket.assigned_to and getattr(ticket.assigned_to.usersettings.settings, 'email_on_ticket_apichange', False): send_templated_mail( 'resolved_resolved', context, diff --git a/views/public.py b/views/public.py index 21ae23b2..1c059540 100644 --- a/views/public.py +++ b/views/public.py @@ -22,7 +22,10 @@ from helpdesk.models import Ticket, Queue def homepage(request): if request.user.is_authenticated(): - return HttpResponseRedirect(reverse('helpdesk_dashboard')) + if getattr(request.user.usersettings.settings, 'login_view_ticketlist', False): + return HttpResponseRedirect(reverse('helpdesk_list')) + else: + return HttpResponseRedirect(reverse('helpdesk_dashboard')) if request.method == 'POST': form = PublicTicketForm(request.POST) diff --git a/views/staff.py b/views/staff.py index 2b0acfa0..72cb40d9 100644 --- a/views/staff.py +++ b/views/staff.py @@ -20,7 +20,7 @@ from django.shortcuts import render_to_response, get_object_or_404 from django.template import loader, Context, RequestContext from django.utils.translation import ugettext as _ -from helpdesk.forms import TicketForm +from helpdesk.forms import TicketForm, UserSettingsForm from helpdesk.lib import send_templated_mail, line_chart, bar_chart, query_to_dict, apply_query, safe_template_context from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch @@ -234,13 +234,14 @@ def update_ticket(request, ticket_id): else: template_staff = 'updated_owner' - send_templated_mail( - template_staff, - context, - recipients=ticket.assigned_to.email, - sender=ticket.queue.from_address, - fail_silently=True, - ) + if (not reassigned or ( reassigned and getattr(ticket.assigned_to.usersettings.settings, 'email_on_ticket_assign', False))) or (not reassigned and getattr(ticket.assigned_to.usersettings.settings, 'email_on_ticket_change', False)): + send_templated_mail( + template_staff, + context, + recipients=ticket.assigned_to.email, + sender=ticket.queue.from_address, + fail_silently=True, + ) if ticket.queue.updated_ticket_cc: if reassigned: @@ -636,3 +637,19 @@ def delete_saved_query(request, id): 'query': query, })) delete_saved_query = login_required(delete_saved_query) + +def user_settings(request): + s = request.user.usersettings + if request.POST: + form = UserSettingsForm(request.POST) + if form.is_valid(): + s.settings = form.cleaned_data + s.save() + else: + form = UserSettingsForm(s.settings) + + return render_to_response('helpdesk/user_settings.html', + RequestContext(request, { + 'form': form, + })) +user_settings = login_required(user_settings)