diff --git a/README.rst b/README.rst index 67366f0..1a12722 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ Copyright (C) 1998-2016 by the Free Software Foundation, Inc. The Postorius Django app provides a web user interface to -access GNU Mailman. +access GNU Mailman. Postorius is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as @@ -45,7 +45,7 @@ http://packages.python.org/mailman/ A description how to run Postorius on Django's dev server or using -Apache/mod_wsgi, can be found in the package documentation: +Apache/mod_wsgi, can be found in the package documentation: src/postorius/doc/setup.rst @@ -55,11 +55,10 @@ Many thanks go out to Anna Senarclens de Grancy and Benedict Stein for developing the initial versions of this Django app during the Google Summer of -Code 2010 and 2011. +Code 2010 and 2011. Icons ===== Postorius uses the WPZOOM Developer Icon Set (http://www.wpzoom.com). - diff --git a/ez_setup.py b/ez_setup.py index b74adc0..72a71c5 100644 --- a/ez_setup.py +++ b/ez_setup.py @@ -100,7 +100,7 @@ try: import pkg_resources except ImportError: - return do_download() + return do_download() try: pkg_resources.require("setuptools>="+version); return except pkg_resources.VersionConflict, e: @@ -276,9 +276,3 @@ update_md5(sys.argv[2:]) else: main(sys.argv[1:]) - - - - - - diff --git a/src/postorius/auth/utils.py b/src/postorius/auth/utils.py index 7ba392c..b502e79 100644 --- a/src/postorius/auth/utils.py +++ b/src/postorius/auth/utils.py @@ -24,6 +24,7 @@ from postorius.utils import set_other_emails from postorius.models import List + def user_is_in_list_roster(user, mailing_list, roster): if not user.is_authenticated(): return False @@ -31,7 +32,7 @@ set_other_emails(user) addresses = set([user.email]) | set(user.other_emails) if addresses & set(getattr(mailing_list, roster)): - return True # At least one address is in the roster + return True # At least one address is in the roster return False diff --git a/src/postorius/doc/conf.py b/src/postorius/doc/conf.py index 79a4cc9..68ee085 100644 --- a/src/postorius/doc/conf.py +++ b/src/postorius/doc/conf.py @@ -18,7 +18,7 @@ # add dummy settings environment variable so sphinx can import from Postorius. os.environ['DJANGO_SETTINGS_MODULE'] = 'postorius.doc.settings' -#import the source code directory into Python Path for use with Auto Module +# import the source code directory into Python Path for use with Auto Module APP_ROOT = os.path.dirname(__file__) sys.path.insert(0, os.path.split(APP_ROOT)[0]) @@ -33,7 +33,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -# extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', +#extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', # 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. diff --git a/src/postorius/doc/development.rst b/src/postorius/doc/development.rst index 58ce334..1b332d1 100644 --- a/src/postorius/doc/development.rst +++ b/src/postorius/doc/development.rst @@ -13,16 +13,16 @@ .. _GitLab: https://gitlab.com/mailman/postorius -Changes are not made directly in the project's master branch, but in +Changes are not made directly in the project's master branch, but in feature-related personal branches, which get reviewed and then merged into -the master branch. +the master branch. An ideal workflow would be like this: -1. File a bug to suggest a new feature or report a bug (or just pick one of +1. File a bug to suggest a new feature or report a bug (or just pick one of the existing bugs). 2. Create a new branch with your code changes. -3. Make a "merge request" to get your code reviewed and merged. +3. Make a "merge request" to get your code reviewed and merged. Installing and running the tests @@ -57,7 +57,7 @@ All test modules reside in the ``postorius/src/postorius/tests`` -directory. Please have a look at the existing examples. +directory. Please have a look at the existing examples. Mocking calls to Mailman's REST API @@ -66,7 +66,7 @@ A lot of Postorius' code involves calls to Mailman's REST API (through the mailman.client library). Running these tests against a real instance of Mailman would be bad practice and slow, so ``vcrpy`` *cassettes* are -used instead (see the `vcrpy Documentation`_ for details). These files +used instead (see the `vcrpy Documentation`_ for details). These files contain pre-recorded HTTP responses. .. _`vcrpy Documentation`: https://github.com/kevin1024/vcrpy @@ -83,7 +83,7 @@ Postorius source. .. note:: - Make sure, you use a fresh mailman.db file. + Make sure, you use a fresh mailman.db file. Once the core is running, you can record the new cassette file defined in your test case by running tox with the `record` test env: @@ -105,12 +105,12 @@ - Superuser: Can do everything. - AnonymousUser: Can view list index and info pages. - Authenticated users: Can view list index and info pages. Can (un)subscribe - from lists. + from lists. -Apart from these default roles, there are two others relevant in Postorius: +Apart from these default roles, there are two others relevant in Postorius: - List owners: Can change list settings, moderate messages and delete their - lists. + lists. - List moderators: Can moderate messages. There are a number of decorators to protect views from unauthorized users. @@ -126,8 +126,8 @@ Accessing the Mailman API ========================= -Postorius uses mailman.client to connect to Mailman's REST API. In order to -directly use the client, ``cd`` to your project folder and execute +Postorius uses mailman.client to connect to Mailman's REST API. In order to +directly use the client, ``cd`` to your project folder and execute ``python manage.py mmclient``. This will open a python shell (IPython, if that's available) and provide you with a client object connected to to your local Mailman API server (it uses the credentials from your settings.py). @@ -146,10 +146,9 @@ >>> mailman_dev = client.get_list('mailman-developers@python.org') >>> print mailman_dev settings - {u'description': u'Mailman development', + {u'description': u'Mailman development', u'default_nonmember_action': u'hold', ...} For detailed information how to use mailman.client, check out its documentation_. .. _documentation: https://gitlab.com/mailman/mailmanclient/blob/master/src/mailmanclient/docs/using.rst - diff --git a/src/postorius/doc/index.rst b/src/postorius/doc/index.rst index ffa936e..c4d09bf 100644 --- a/src/postorius/doc/index.rst +++ b/src/postorius/doc/index.rst @@ -12,7 +12,7 @@ .. toctree:: :maxdepth: 1 - + news.rst setup.rst development.rst diff --git a/src/postorius/doc/news.rst b/src/postorius/doc/news.rst index f35f76a..4d5e59d 100644 --- a/src/postorius/doc/news.rst +++ b/src/postorius/doc/news.rst @@ -53,11 +53,11 @@ * Rework of internal testing * Mozilla Persona integration: switch from django-social-auto to django-browserid: Contributed by Abhilash Raj. * Fix manage.py mmclient command for non-IPython shells. Contributed by Ankush Sharma (LP: 1428169). -* Added archiver options: Site-wide enabled archivers can not be enabled +* Added archiver options: Site-wide enabled archivers can not be enabled on a per-list basis through the web UI. * Added functionality to choose or switch subscription addresses. Contributed by Abhilash Raj. -* Added subscription moderation, pre_verification/_confirmation. -* Several style changes. +* Added subscription moderation, pre_verification/_confirmation. +* Several style changes. 1.0 beta 1 -- "Year of the Parrot" diff --git a/src/postorius/doc/settings.py b/src/postorius/doc/settings.py index 3786d5f..1f76bc9 100755 --- a/src/postorius/doc/settings.py +++ b/src/postorius/doc/settings.py @@ -1,4 +1,4 @@ -#-*- coding: utf-8 -*- +# -*- coding: utf-8 -*- # Copyright (C) 1998-2016 by the Free Software Foundation, Inc. # # This file is part of Postorius. diff --git a/src/postorius/doc/setup.rst b/src/postorius/doc/setup.rst index 38b20f8..82c3993 100644 --- a/src/postorius/doc/setup.rst +++ b/src/postorius/doc/setup.rst @@ -114,23 +114,23 @@ These settings need to be added to your Apache VirtualHost: -:: +:: Alias /static /path/to/postorius_standalone/static Order deny,allow Allow from all - + WSGIScriptAlias / /path/to/postorius_standalone/srv/postorius.wsgi Order deny,allow Allow from all - + The first Alias serves the static files (CSS, JS, Images, etc.). The WSGIScriptAlias serves the Django application. The paths need to be changed -depending on which location you downloaded ``postorius_standalone`` to. +depending on which location you downloaded ``postorius_standalone`` to. We're almost ready. But you need to collect the static files from Postorius (which resides somewhere on your pythonpath) to be able to serve them from the @@ -141,4 +141,4 @@ $ python manage.py collectstatic -After reloading the webserver Postorius should be running! +After reloading the webserver Postorius should be running! diff --git a/src/postorius/forms.py b/src/postorius/forms.py index 4eaf396..8b3435c 100644 --- a/src/postorius/forms.py +++ b/src/postorius/forms.py @@ -41,9 +41,6 @@ ) -# Fields - - class ListOfStringsField(forms.Field): widget = forms.widgets.Textarea @@ -65,10 +62,6 @@ return result - -# Forms - - class DomainNew(FieldsetForm): """ @@ -271,7 +264,7 @@ archivers = forms.MultipleChoiceField( widget=forms.CheckboxSelectMultiple, label=_('Active archivers'), - required=False) # May be empty if no archivers are desired. + required=False) # May be empty if no archivers are desired. def __init__(self, *args, **kwargs): super(ArchiveSettingsForm, self).__init__(*args, **kwargs) @@ -399,7 +392,7 @@ choices=((True, _('Yes')), (False, _('No'))), widget=forms.RadioSelect, required=False, - label= _('Include RFC2369 headers'), + label=_('Include RFC2369 headers'), help_text=_( 'Yes is highly recommended. RFC 2369 defines a set of List-* ' 'headers that are normally added to every message sent to the ' @@ -706,7 +699,6 @@ orders.append(order) - class UserPreferences(FieldsetForm): """ diff --git a/src/postorius/management/commands/mmclient.py b/src/postorius/management/commands/mmclient.py index d92104b..5bf48a5 100644 --- a/src/postorius/management/commands/mmclient.py +++ b/src/postorius/management/commands/mmclient.py @@ -21,6 +21,7 @@ from postorius import utils from urllib2 import HTTPError + class Command(BaseCommand): help = """Opens a Python shell with a mailmanclient object named `client`. @@ -30,9 +31,9 @@ foo = client.get_list('foo@example.org') foo.members [] - + A complete list of commands can be found in the mailman.client documentation.""" - + def handle(self, *args, **options): # choose an interpreter console = None diff --git a/src/postorius/templates/postorius/lists/bans.html b/src/postorius/templates/postorius/lists/bans.html index 9d57940..d9d3507 100644 --- a/src/postorius/templates/postorius/lists/bans.html +++ b/src/postorius/templates/postorius/lists/bans.html @@ -61,4 +61,3 @@ {% endwith %} {% endblock %} - diff --git a/src/postorius/templates/postorius/user/address_confirmation_message.txt b/src/postorius/templates/postorius/user/address_confirmation_message.txt index 8166a13..853bb58 100644 --- a/src/postorius/templates/postorius/user/address_confirmation_message.txt +++ b/src/postorius/templates/postorius/user/address_confirmation_message.txt @@ -1,4 +1,4 @@ -Please click the link below to add your email address to your mailing list +Please click the link below to add your email address to your mailing list profile at {{ host_url }}: {{ activation_link }} diff --git a/src/postorius/templatetags/bootstrap_tags.py b/src/postorius/templatetags/bootstrap_tags.py index 827fb16..09c114e 100644 --- a/src/postorius/templatetags/bootstrap_tags.py +++ b/src/postorius/templatetags/bootstrap_tags.py @@ -22,10 +22,12 @@ register = template.Library() + @register.filter(name='add_form_control') def add_form_control(field): return field.as_widget(attrs={'class': 'form-control'}) + @register.filter('fieldtype') def fieldtype(field): return field.field.widget.__class__.__name__ diff --git a/src/postorius/tests/mailman_api_tests/__init__.py b/src/postorius/tests/mailman_api_tests/__init__.py index 8b13789..e69de29 100644 --- a/src/postorius/tests/mailman_api_tests/__init__.py +++ b/src/postorius/tests/mailman_api_tests/__init__.py @@ -1 +0,0 @@ - diff --git a/src/postorius/tests/mailman_api_tests/test_address_activation.py b/src/postorius/tests/mailman_api_tests/test_address_activation.py index f4341bb..7b88944 100644 --- a/src/postorius/tests/mailman_api_tests/test_address_activation.py +++ b/src/postorius/tests/mailman_api_tests/test_address_activation.py @@ -29,7 +29,6 @@ from postorius.tests.utils import ViewTestCase - class TestAddressActivationForm(ViewTestCase): """ Test the activation form. diff --git a/src/postorius/tests/mailman_api_tests/test_list_bans.py b/src/postorius/tests/mailman_api_tests/test_list_bans.py index 2ae37ea..7439002 100644 --- a/src/postorius/tests/mailman_api_tests/test_list_bans.py +++ b/src/postorius/tests/mailman_api_tests/test_list_bans.py @@ -78,7 +78,7 @@ 'Ban email') def test_context_contains_delete_forms(self): - banned = ["banned{}@example.com".format(i) for i in range(1,10)] + banned = ['banned{}@example.com'.format(i) for i in range(1, 10)] for ban in banned: self.m_list.bans.add(ban) response = self.client.get(self.url) diff --git a/src/postorius/tests/mailman_api_tests/test_list_header_matches.py b/src/postorius/tests/mailman_api_tests/test_list_header_matches.py index 1e2975e..197c01e 100644 --- a/src/postorius/tests/mailman_api_tests/test_list_header_matches.py +++ b/src/postorius/tests/mailman_api_tests/test_list_header_matches.py @@ -29,7 +29,6 @@ from postorius.tests.utils import ViewTestCase - class ListHeaderMatchesTest(ViewTestCase): """ Tests for the list settings page. diff --git a/src/postorius/tests/mailman_api_tests/test_list_members.py b/src/postorius/tests/mailman_api_tests/test_list_members.py index 4e45d8a..aef4b65 100644 --- a/src/postorius/tests/mailman_api_tests/test_list_members.py +++ b/src/postorius/tests/mailman_api_tests/test_list_members.py @@ -158,7 +158,6 @@ self.assertHasErrorMessage(response) - class AddModeratorTest(ViewTestCase): """Tests for the list members page. diff --git a/src/postorius/tests/mailman_api_tests/test_list_new.py b/src/postorius/tests/mailman_api_tests/test_list_new.py index 27a7a95..03287a5 100644 --- a/src/postorius/tests/mailman_api_tests/test_list_new.py +++ b/src/postorius/tests/mailman_api_tests/test_list_new.py @@ -31,7 +31,6 @@ from postorius.tests.utils import ViewTestCase - class ListCreationTest(ViewTestCase): """Tests for the new list page.""" diff --git a/src/postorius/tests/mailman_api_tests/test_list_settings.py b/src/postorius/tests/mailman_api_tests/test_list_settings.py index c6204ff..798cf77 100644 --- a/src/postorius/tests/mailman_api_tests/test_list_settings.py +++ b/src/postorius/tests/mailman_api_tests/test_list_settings.py @@ -30,7 +30,6 @@ from postorius.tests.utils import ViewTestCase - class ListSettingsTest(ViewTestCase): """ Tests for the list settings page. diff --git a/src/postorius/tests/mailman_api_tests/test_profile.py b/src/postorius/tests/mailman_api_tests/test_profile.py index 6a760ce..e67d7c2 100644 --- a/src/postorius/tests/mailman_api_tests/test_profile.py +++ b/src/postorius/tests/mailman_api_tests/test_profile.py @@ -92,4 +92,3 @@ 'user_email': self.user.email}, follow=True) self.assertEqual(mock_send_confirmation_link.call_count, 1) self.assertContains(response, 'Currently emails can not be added, please try again later') - diff --git a/src/postorius/tests/mailman_api_tests/test_user.py b/src/postorius/tests/mailman_api_tests/test_user.py index ddc73cb..7a210c7 100644 --- a/src/postorius/tests/mailman_api_tests/test_user.py +++ b/src/postorius/tests/mailman_api_tests/test_user.py @@ -79,10 +79,12 @@ 'receive_own_postings', 'acknowledge_posts', 'hide_address', 'receive_list_copy', ) - # Prepare a Preferences subclass that will check the POST data + # Prepare a Preferences subclass that will check the POST data import mailmanclient._client + class TestingPrefs(mailmanclient._client.Preferences): testcase = self + def save(self): for pref in prefs_with_none: self.testcase.assertNotIn(pref, self._changed_rest_data) diff --git a/src/postorius/tests/test_forms.py b/src/postorius/tests/test_forms.py index 5d99c40..0a14ed7 100644 --- a/src/postorius/tests/test_forms.py +++ b/src/postorius/tests/test_forms.py @@ -31,6 +31,7 @@ }) self.assertTrue(form.is_valid()) + class DomainNewTest(TestCase): def test_form_fields_webhost(self): diff --git a/src/postorius/tests/utils.py b/src/postorius/tests/utils.py index fa3bc2e..55bb0f3 100644 --- a/src/postorius/tests/utils.py +++ b/src/postorius/tests/utils.py @@ -103,7 +103,6 @@ get_flash_messages.__test__ = False - class ViewTestCase(TestCase): use_vcr = True @@ -137,4 +136,3 @@ response = self.client.get(url) self.assertRedirects(response, '{}?next={}'.format(reverse(settings.LOGIN_URL), quote(url))) - diff --git a/src/postorius/utils.py b/src/postorius/utils.py index 53a641a..00571a0 100644 --- a/src/postorius/utils.py +++ b/src/postorius/utils.py @@ -79,7 +79,6 @@ count = property(_get_count) - def paginate(request, collection, count=20, paginator_class=Paginator): # count is the number of items per page paginator = paginator_class(collection, count) diff --git a/src/postorius/views/generic.py b/src/postorius/views/generic.py index abb60ff..4645abc 100644 --- a/src/postorius/views/generic.py +++ b/src/postorius/views/generic.py @@ -17,7 +17,6 @@ # Postorius. If not, see . - from django.views.generic import TemplateView from postorius.models import List, MailmanUser, MailmanApiError, Mailman404Error diff --git a/src/postorius/views/list.py b/src/postorius/views/list.py index a5aa49e..3345ff8 100644 --- a/src/postorius/views/list.py +++ b/src/postorius/views/list.py @@ -180,9 +180,9 @@ """ def get(self, request, list_id): - data = {'list': self.mailing_list, - 'userSubscribed': False, - 'subscribed_address': None} + data = {'list': self.mailing_list, + 'userSubscribed': False, + 'subscribed_address': None} if request.user.is_authenticated(): user_emails = [request.user.email] + request.user.other_emails for address in user_emails: @@ -193,7 +193,7 @@ else: data['userSubscribed'] = True data['subscribed_address'] = address - break # no need to test more addresses + break # no need to test more addresses data['subscribe_form'] = ListSubscribe(user_emails) else: user_emails = None @@ -217,7 +217,7 @@ else: userSubscribed = True old_email = address - break # no need to test more addresses + break # no need to test more addresses if form.is_valid(): email = form.cleaned_data['email'] if old_email == email: @@ -353,6 +353,7 @@ for message_id in message_ids: action(message_id) + @login_required @list_moderator_required def list_moderation(request, list_id): @@ -694,6 +695,7 @@ 'visible_section': visible_section, }) + @login_required @list_owner_required def remove_role(request, list_id=None, role=None, address=None, @@ -838,6 +840,7 @@ # Purge the existing header_matches header_matches.clear() # Add the ones in the form + def form_order(f): # If ORDER is None (new header match), add it last. return f.cleaned_data.get('ORDER') or len(formset.forms) diff --git a/tox.ini b/tox.ini index a673092..30e8c52 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ envlist = py27-django{18,19} [base] -deps = +deps = git+https://gitlab.com/mailman/mailmanclient.git mock vcrpy @@ -25,7 +25,7 @@ deps = {[base]deps} Django==1.8 -setenv = +setenv = PYTHONPATH = {toxinidir} POSTORIUS_VCR_RECORD_MODE = all commands = @@ -33,14 +33,14 @@ # These are used for local development and expect mailman.client to be -# sitting in a directory next to this one. +# sitting in a directory next to this one. [testenv:dev] usedevelop = True basepython = python2.7 deps = {[base]deps} Django==1.8 -setenv = +setenv = PYTHONPATH = {toxinidir} commands = # Install mailman.client from local repo instead of from pypi @@ -53,7 +53,7 @@ deps = {[base]deps} Django==1.8 -setenv = +setenv = PYTHONPATH = {toxinidir} POSTORIUS_VCR_RECORD_MODE = all commands =