diff --git a/setup.py b/setup.py index 0ae9bdf..8dcca73 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ packages=find_packages('src'), package_dir={'': 'src'}, include_package_data=True, - install_requires=['django>=1.5', + install_requires=['django>=1.5, <1.8', 'django-browserid', 'mailmanclient'] ) diff --git a/src/postorius/models.py b/src/postorius/models.py index c21147b..295cd24 100644 --- a/src/postorius/models.py +++ b/src/postorius/models.py @@ -26,6 +26,7 @@ from datetime import datetime, timedelta from django.conf import settings from django.contrib.auth.models import User +from django.core.exceptions import ImproperlyConfigured from django.core.urlresolvers import reverse from django.core.mail import send_mail from django.db import models @@ -227,7 +228,7 @@ def __unicode__(self): return u'Address Confirmation Profile for {0}'.format(self.email) - + @property def is_expired(self): """ @@ -261,13 +262,13 @@ The following settings are recognized: >>> EMAIL_CONFIRMATION_TEMPLATE = 'postorius/address_confirmation_message.txt' - >>> EMAIL_CONFIRMATION_FROM = 'Confirmation needed' - >>> EMAIL_CONFIRMATION_SUBJECT = 'postmaster@list.org' + >>> EMAIL_CONFIRMATION_FROM = 'postmaster@list.org' + >>> EMAIL_CONFIRMATION_SUBJECT = 'Confirmation needed' :param request: The HTTP request object. - :type request: HTTPRequest + :type request: HTTPRequest :param template_context: The context used when rendering the template. - Falls back to host url and activation link. + Falls back to host url and activation link. :type template_context: django.template.Context """ # create the host url and the activation link need for the template @@ -276,7 +277,7 @@ url = reverse('address_activation_link', kwargs={'activation_key': self.activation_key}) activation_link = '{0}{1}'.format(host_url, url) - # Detect the right template path, either from the param, + # Detect the right template path, either from the param, # the setting or the default if not template_path: template_path = getattr(settings, @@ -289,7 +290,18 @@ {'activation_link': activation_link, 'host_url': host_url}) email_subject = getattr( settings, 'EMAIL_CONFIRMATION_SUBJECT', u'Confirmation needed') + try: + sender_address = getattr(settings, 'EMAIL_CONFIRMATION_FROM') + except AttributeError: + # settings.EMAIL_CONFIRMATION_FROM is not defined, fallback + # settings.DEFAULT_EMAIL_FROM as mentioned in the django + # docs. If that also fails, raise a `ImproperlyConfigured` Error. + try: + sender_address = getattr(settings, 'DEFAULT_FROM_EMAIL') + except AttributeError: + raise ImproperlyConfigured + send_mail(email_subject, get_template(template_path).render(template_context), - getattr(settings, 'EMAIL_CONFIRMATION_FROM'), + sender_address, [self.email]) diff --git a/src/postorius/templates/postorius/base.html b/src/postorius/templates/postorius/base.html index 308231b..063e08b 100644 --- a/src/postorius/templates/postorius/base.html +++ b/src/postorius/templates/postorius/base.html @@ -3,7 +3,7 @@ - + Mailman/Postorius @@ -13,7 +13,6 @@ - diff --git a/src/postorius/templates/postorius/lists/memberoptions.html b/src/postorius/templates/postorius/lists/memberoptions.html index 766fc89..ffaa700 100644 --- a/src/postorius/templates/postorius/lists/memberoptions.html +++ b/src/postorius/templates/postorius/lists/memberoptions.html @@ -4,7 +4,7 @@ {% load nav_helpers %} {% block main %} - {% list_nav '' '' %} + {% list_nav '' 'Member Options' %} {% if not user.is_superuser or not user.is_list_owner %}
diff --git a/src/postorius/templates/postorius/lists/summary.html b/src/postorius/templates/postorius/lists/summary.html index f6b0fec..99fc33a 100644 --- a/src/postorius/templates/postorius/lists/summary.html +++ b/src/postorius/templates/postorius/lists/summary.html @@ -6,7 +6,7 @@ {% block body_class %}list_summary{% endblock %} {% block main %} - {% if user.is_superuser %} + {% if user.is_superuser or user.is_list_owner or user.is_list_moderator %} {% include 'postorius/menu/list_nav.html' %} {% endif %}

{{list.display_name}}

diff --git a/src/postorius/templates/postorius/menu/list_nav.html b/src/postorius/templates/postorius/menu/list_nav.html index 18b0af3..4429e17 100644 --- a/src/postorius/templates/postorius/menu/list_nav.html +++ b/src/postorius/templates/postorius/menu/list_nav.html @@ -15,7 +15,7 @@ {% if user.is_superuser or user.is_list_owner %}
  • {% trans "Settings" %}
  • {% endif %} - {% if user.is_superuser or user.is_list_moderator %} + {% if user.is_superuser or user.is_list_owner %}
  • {% trans "Mass Subscribe" %}
  • {% endif %} {% if user.is_superuser or user.is_list_owner %} diff --git a/src/postorius/templates/postorius/user_address_activation_sent.html b/src/postorius/templates/postorius/user_address_activation_sent.html index 72cb84f..a369f77 100644 --- a/src/postorius/templates/postorius/user_address_activation_sent.html +++ b/src/postorius/templates/postorius/user_address_activation_sent.html @@ -7,4 +7,3 @@

    {% trans 'Email address activation sent' %}

    {% trans "A confirmation link has been sent to the email address you submitted. Please check your email account and click on the confirmation link to add this address for your account." %}

    {% endblock main %} - diff --git a/src/postorius/urls.py b/src/postorius/urls.py index e7ada3e..2dfde4d 100644 --- a/src/postorius/urls.py +++ b/src/postorius/urls.py @@ -42,7 +42,7 @@ ListSummaryView.as_view( ), name='list_summary'), url(r'^subscribe$', - ListSubsribeView.as_view( + ListSubscribeView.as_view( ), name='list_subscribe'), url(r'^unsubscribe/(?P[^/]+)$', ListUnsubscribeView.as_view( @@ -51,7 +51,7 @@ 'list_subscriptions', name='list_subscriptions'), url(r'^mass_subscribe/$', - ListMassSubsribeView.as_view( + ListMassSubscribeView.as_view( ), name='mass_subscribe'), url(r'^delete$', 'list_delete', name='list_delete'), @@ -86,7 +86,8 @@ 'postorius.views', (r'^$', 'list_index'), # /account/ - url(r'^accounts/login/$', login_view, {"template_name": "postorius/login.html"}, name='user_login'), + url(r'^accounts/login/$', login_view, + {"template_name": "postorius/login.html"}, name='user_login'), url(r'^accounts/logout/$', 'user_logout', name='user_logout'), url(r'^accounts/profile/$', 'user_profile', name='user_profile'), url(r'^tasks/$', 'user_tasks', name='user_tasks'), diff --git a/src/postorius/views/list.py b/src/postorius/views/list.py index e94652d..ee6e038 100644 --- a/src/postorius/views/list.py +++ b/src/postorius/views/list.py @@ -24,6 +24,8 @@ from django.core.urlresolvers import reverse from django.shortcuts import render_to_response, redirect from django.template import RequestContext +from django.core.validators import validate_email +from django.core.exceptions import ValidationError from django.utils.decorators import method_decorator from django.utils.translation import gettext as _ from urllib2 import HTTPError @@ -188,7 +190,7 @@ context_instance=RequestContext(request)) -class ListSubsribeView(MailingListView): +class ListSubscribeView(MailingListView): """Subscribe a mailing list.""" @@ -231,7 +233,7 @@ return redirect('list_summary', self.mailing_list.list_id) -class ListMassSubsribeView(MailingListView): +class ListMassSubscribeView(MailingListView): """Mass subscription.""" @@ -249,25 +251,22 @@ else: emails = request.POST["emails"].splitlines() for email in emails: - parts = email.split('@') - if len(parts) != 2 or '.' not in parts[1]: + try: + validate_email(email) + self.mailing_list.subscribe(address=email) + messages.success(request, + 'The address %s has been subscribed to %s.' % + (email, self.mailing_list.fqdn_listname)) + except MailmanApiError: + return utils.render_api_error(request) + except HTTPError, e: + messages.error(request, e) + except ValidationError: messages.error(request, 'The email address %s is not valid.' % email) - else: - try: - self.mailing_list.subscribe(address=email) - messages.success( - request, - 'The address %s has been subscribed to %s.' % - (email, self.mailing_list.fqdn_listname)) - except MailmanApiError: - return utils.render_api_error(request) - except HTTPError, e: - messages.error(request, e) return redirect('mass_subscribe', self.mailing_list.list_id) - def _get_choosable_domains(request): try: domains = Domain.objects.all() @@ -474,7 +473,8 @@ context_instance=RequestContext(request)) -@list_owner_required + +@list_moderator_required def list_held_messages(request, list_id): """Shows a list of held messages. """ @@ -487,7 +487,7 @@ context_instance=RequestContext(request)) -@list_owner_required +@list_moderator_required def accept_held_message(request, list_id, msg_id): """Accepts a held message. """ @@ -503,7 +503,7 @@ return redirect('list_held_messages', the_list.list_id) -@list_owner_required +@list_moderator_required def discard_held_message(request, list_id, msg_id): """Accepts a held message. """ @@ -519,7 +519,7 @@ return redirect('list_held_messages', the_list.list_id) -@list_owner_required +@list_moderator_required def defer_held_message(request, list_id, msg_id): """Accepts a held message. """ @@ -535,7 +535,7 @@ return redirect('list_held_messages', the_list.list_id) -@list_owner_required +@list_moderator_required def reject_held_message(request, list_id, msg_id): """Accepts a held message. """ @@ -685,11 +685,11 @@ archivers = m_list.archivers if len(to_activate) > 0: messages.success(request, - _('New archivers activated for this list: ' + _('You activated new archivers for this list: ' '{0}'.format(', '.join(to_activate)))) if len(to_disable) > 0: messages.success(request, - _('Archivers disabled for this list: ' + _('You disabled the following archivers: ' '{0}'.format(', '.join(to_disable)))) enabled = [key for key in archivers.keys() if archivers[key] is True] initial = {'archivers': enabled} diff --git a/tox.ini b/tox.ini index 02d17c9..c7f021a 100644 --- a/tox.ini +++ b/tox.ini @@ -23,4 +23,5 @@ PYTHONPATH = {toxinidir} POSTORIUS_VCR_RECORD_MODE = all commands = + pip install -e ../mailman.client django-admin.py test --settings=testing.test_settings {posargs:postorius}