From 15b834f573b34b43a8bc68608c433366b2492cdc Mon Sep 17 00:00:00 2001 From: Patrick Samson Date: Sat, 14 Sep 2013 14:12:18 +0200 Subject: [PATCH] Fixed issue #35, the app can work without the sites framework --- docs/notification.rst | 3 ++- postman/admin.py | 3 ++- postman/api.py | 12 ++++++++++-- postman/forms.py | 3 ++- postman/models.py | 6 +++--- postman/tests.py | 30 ++++++++++++++++++++++++------ postman/utils.py | 12 +++++------- postman/views.py | 2 ++ 8 files changed, 50 insertions(+), 21 deletions(-) diff --git a/docs/notification.rst b/docs/notification.rst index 76f68d3..84e752c 100644 --- a/docs/notification.rst +++ b/docs/notification.rst @@ -17,7 +17,8 @@ An email is sent, using these templates: The available context variables are: -* ``site``: the Site instance +* ``site``: the *Site* instance if the "sites" framework is installed, otherwise a *RequestSite* instance + with a fallback to *None* in the case of the API * ``object``: the Message instance * ``action``: 'rejection' or 'acceptance' diff --git a/postman/admin.py b/postman/admin.py index aaf2f38..3e1c954 100644 --- a/postman/admin.py +++ b/postman/admin.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from django import forms from django.contrib import admin +from django.contrib.sites.models import get_current_site from django.db import transaction from django.utils.translation import ugettext, ugettext_lazy as _ @@ -134,7 +135,7 @@ class MessageAdmin(admin.ModelAdmin): obj.clean_for_visitor() super(MessageAdmin, self).save_model(request, obj, form, change) obj.update_parent(form.initial_status) - obj.notify_users(form.initial_status, is_auto_moderated=False) + obj.notify_users(form.initial_status, get_current_site(request), is_auto_moderated=False) class PendingMessageAdminForm(forms.ModelForm): diff --git a/postman/api.py b/postman/api.py index f9f53f5..ad337b1 100644 --- a/postman/api.py +++ b/postman/api.py @@ -18,6 +18,7 @@ for e in events: """ from __future__ import unicode_literals +from django.contrib.sites.models import Site try: from django.utils.timezone import now # Django 1.4 aware datetimes except ImportError: @@ -26,6 +27,12 @@ except ImportError: from postman.models import Message, STATUS_PENDING, STATUS_ACCEPTED + +def _get_site(): + # do not require the sites framework to be installed ; and no request object is available here + return Site.objects.get_current() if Site._meta.installed else None + + def pm_broadcast(sender, recipients, subject, body='', skip_notification=False): """ Broadcast a message to multiple Users. @@ -47,7 +54,8 @@ def pm_broadcast(sender, recipients, subject, body='', skip_notification=False): message.pk = None message.save() if not skip_notification: - message.notify_users(STATUS_PENDING) + message.notify_users(STATUS_PENDING, _get_site()) + def pm_write(sender, recipient, subject, body='', skip_notification=False, auto_archive=False, auto_delete=False, auto_moderators=None): @@ -77,4 +85,4 @@ def pm_write(sender, recipient, subject, body='', skip_notification=False, message.sender_deleted_at = now() message.save() if not skip_notification: - message.notify_users(initial_status) + message.notify_users(initial_status, _get_site()) diff --git a/postman/forms.py b/postman/forms.py index b0ca4d1..126b26a 100644 --- a/postman/forms.py +++ b/postman/forms.py @@ -48,6 +48,7 @@ class BaseWriteForm(forms.ModelForm): user_filter = kwargs.pop('user_filter', None) max = kwargs.pop('max', None) channel = kwargs.pop('channel', None) + self.site = kwargs.pop('site', None) super(BaseWriteForm, self).__init__(*args, **kwargs) self.instance.sender = sender if (sender and sender.is_authenticated()) else None @@ -141,7 +142,7 @@ class BaseWriteForm(forms.ModelForm): if self.instance.is_rejected(): is_successful = False self.instance.update_parent(initial_status) - self.instance.notify_users(initial_status) + self.instance.notify_users(initial_status, self.site) # some resets for next reuse if not isinstance(r, get_user_model()): self.instance.email = '' diff --git a/postman/models.py b/postman/models.py index 5162767..1cb34c0 100644 --- a/postman/models.py +++ b/postman/models.py @@ -419,16 +419,16 @@ class Message(models.Model): parent.replied_at = None parent.save() - def notify_users(self, initial_status, is_auto_moderated=True): + def notify_users(self, initial_status, site, is_auto_moderated=True): """Notify the rejection (to sender) or the acceptance (to recipient) of the message.""" if initial_status == STATUS_PENDING: if self.is_rejected(): # Bypass: for an online user, no need to notify when rejection is immediate. # Only useful for a visitor as an archive copy of the message, otherwise lost. if not (self.sender_id and is_auto_moderated): - (notify_user if self.sender_id else email_visitor)(self, 'rejection') + (notify_user if self.sender_id else email_visitor)(self, 'rejection', site) elif self.is_accepted(): - (notify_user if self.recipient_id else email_visitor)(self, 'acceptance') + (notify_user if self.recipient_id else email_visitor)(self, 'acceptance', site) def get_dates(self): """Get some dates to restore later.""" diff --git a/postman/tests.py b/postman/tests.py index 6adb666..fad8cc9 100644 --- a/postman/tests.py +++ b/postman/tests.py @@ -7,7 +7,7 @@ Test suite. base.html 404.html -To have a fast test session, you can set a minimal configuration as: +To have a fast test session, set a minimal configuration as: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. @@ -22,11 +22,11 @@ INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', - 'django.contrib.sites', + # 'django.contrib.sites', # is optional 'django.contrib.admin', - # 'pagination', # or use the mock - # 'ajax_select', # is an option - # 'notification', # is an option + # 'pagination', # or use the mock + # 'ajax_select', # is an option + # 'notification', # is an option 'postman', ) @@ -44,6 +44,7 @@ try: except ImportError: from postman.future_1_5 import get_user_model from django.contrib.auth.models import AnonymousUser +from django.contrib.sites.models import Site from django.core import mail from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse, clear_url_caches, get_resolver, get_urlconf @@ -429,6 +430,23 @@ class ViewTest(BaseTest): self.check_status(Message.objects.get(), status=STATUS_REJECTED, recipient_deleted_at=True, moderation_date=True, moderation_reason="some reason") + def test_write_notification(self): + "Test the fallback for the site name in the generation of a notification, when the django.contrib.sites app is not installed." + settings.POSTMAN_AUTO_MODERATE_AS = True # will generate an acceptance notification + url = reverse('postman_write') + data = {'subject': 's', 'body': 'b', 'recipients': self.user2.get_username()} + self.assertTrue(self.client.login(username='foo', password='pass')) + response = self.client.post(url, data, HTTP_REFERER=url) + self.assertRedirects(response, url) + self.check_status(Message.objects.get(), status=STATUS_ACCEPTED, moderation_date=True) + self.assertEqual(len(mail.outbox), 1) + # can't use get_current_site(response.request) because response.request is not an HttpRequest and doesn't have a get_host attribute + if Site._meta.installed: + sitename = Site.objects.get_current().name + else: + sitename = "testserver" # the SERVER_NAME environment variable is not accessible here + self.assertTrue(sitename in mail.outbox[0].subject) + def test_reply_authentication(self): "Test permission and what template & form are used." template = "postman/reply.html" @@ -1303,7 +1321,7 @@ class MessageTest(BaseTest): def check_notification(self, m, mail_number, email=None, is_auto_moderated=True, notice_label=None): "Check number of mails, recipient, and notice creation." - m.notify_users(STATUS_PENDING, is_auto_moderated) + m.notify_users(STATUS_PENDING, Site.objects.get_current() if Site._meta.installed else None, is_auto_moderated) self.assertEqual(len(mail.outbox), mail_number) if mail_number: self.assertEqual(mail.outbox[0].to, [email]) diff --git a/postman/utils.py b/postman/utils.py index f7d5d7c..64dbacf 100644 --- a/postman/utils.py +++ b/postman/utils.py @@ -4,7 +4,6 @@ import sys from textwrap import TextWrapper from django.conf import settings -from django.contrib.sites.models import Site from django.template.loader import render_to_string from django.utils.encoding import force_unicode from django.utils.translation import ugettext, ugettext_lazy as _ @@ -65,9 +64,8 @@ def format_subject(subject): return subject if re.match(pattern, subject, re.IGNORECASE) else str.format(subject=subject) -def email(subject_template, message_template, recipient_list, object, action=None): +def email(subject_template, message_template, recipient_list, object, action, site): """Compose and send an email.""" - site = Site.objects.get_current() ctx_dict = {'site': site, 'object': object, 'action': action} subject = render_to_string(subject_template, ctx_dict) # Email subject *must not* contain newlines @@ -77,12 +75,12 @@ def email(subject_template, message_template, recipient_list, object, action=Non send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, recipient_list, fail_silently=True) -def email_visitor(object, action): +def email_visitor(object, action, site): """Email a visitor.""" - email('postman/email_visitor_subject.txt', 'postman/email_visitor.txt', [object.email], object, action) + email('postman/email_visitor_subject.txt', 'postman/email_visitor.txt', [object.email], object, action, site) -def notify_user(object, action): +def notify_user(object, action, site): """Notify a user.""" if action == 'rejection': user = object.sender @@ -98,4 +96,4 @@ def notify_user(object, action): notification.send(users=[user], label=label, extra_context={'pm_message': object, 'pm_action': action}) else: if not DISABLE_USER_EMAILING and user.email and user.is_active: - email('postman/email_user_subject.txt', 'postman/email_user.txt', [user.email], object, action) + email('postman/email_user_subject.txt', 'postman/email_user.txt', [user.email], object, action, site) diff --git a/postman/views.py b/postman/views.py index 2dceef5..614f172 100644 --- a/postman/views.py +++ b/postman/views.py @@ -8,6 +8,7 @@ try: from django.contrib.auth import get_user_model # Django 1.5 except ImportError: from postman.future_1_5 import get_user_model +from django.contrib.sites.models import get_current_site from django.core.urlresolvers import reverse from django.db.models import Q from django.http import Http404 @@ -163,6 +164,7 @@ class ComposeMixin(object): 'user_filter': self.user_filter, 'exchange_filter': self.exchange_filter, 'max': self.max, + 'site': get_current_site(self.request), }) return kwargs -- 2.39.5