diff --git a/NEWS.rst b/NEWS.rst
index 355e535..e0dcca8 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -2,16 +2,16 @@
postorius - web ui for GNU Mailman
==================================
-Copyright (C) 1998-2010 by the Free Software Foundation, Inc.
+Copyright (C) 1998-2012 by the Free Software Foundation, Inc.
-The ``postorius`` Django app provides a web user interface to
+The postorius Django app provides a web user interface to
access GNU Mailman.
-``postorius`` is free software: you can redistribute it and/or
+postorius is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, version 3 of the License.
-``postorius`` is distributed in the hope that it will be useful,
+postorius is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
General Public License for more details.
@@ -24,9 +24,9 @@
===========================
(2012-03-23)
-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.
+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.
* add/remove/edit mailing lists
* edit list settings
@@ -39,4 +39,5 @@
* accept/discard/reject/defer messages
* Implementation of Django Messages contributed by Benedict Stein (LP: #920084)
* Dependency check in setup.py contributed by Daniel Mizyrycki
-* Proper processing of acceptable aliases in list settings form contributed by Daniel Mizyrycki
+* Proper processing of acceptable aliases in list settings form contributed by
+ Daniel Mizyrycki
diff --git a/README.rst b/README.rst
index 7bc5313..74950a8 100644
--- a/README.rst
+++ b/README.rst
@@ -1,17 +1,17 @@
===================================
-mailmanweb - web ui for GNU Mailman
+postorius - web ui for GNU Mailman
===================================
-Copyright (C) 1998-2010 by the Free Software Foundation, Inc.
+Copyright (C) 1998-2012 by the Free Software Foundation, Inc.
-The ``mailmanweb`` Django app provides a web user interface to
+The postorius Django app provides a web user interface to
access GNU Mailman.
-``mailmanweb`` is free software: you can redistribute it and/or
+postorius is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, version 3 of the License.
-``mailmanweb`` is distributed in the hope that it will be useful,
+postorius is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
General Public License for more details.
@@ -23,16 +23,33 @@
Requirements
============
-``mailmanweb`` requires Python 2.6 or newer and ``mailman.client``,
-the official Python bindings for GNU Mailman.
+postorius requires Python 2.6 or newer and mailman.client,
+the official Python bindings for GNU Mailman, it also requires
+django-social-auth.
+The minimum Django version is 1.3.
+postorius needs a running version of GNU Mailman version 3.
+
+
+Installation
+============
+
+To install GNU Mailman follow the instructions in the documentation:
+http://packages.python.org/mailman/
+
+A description how to run postorius on Django's dev server, can be found in
+the GNU Mailman wiki:
+http://wiki.list.org/display/DEV/A+5+minute+guide+to+get+the+Mailman+web+UI+running
+
+You can also find a guide how to run postorius using the Apache web server
+on the Mailman wiki.
Acknowledgements
================
-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.
+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.
Icons
diff --git a/dev_setup/manage.py b/dev_setup/manage.py
index daae3a9..73da5fa 100755
--- a/dev_setup/manage.py
+++ b/dev_setup/manage.py
@@ -1,6 +1,6 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-# Copyright (C) 1998-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2012 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
diff --git a/dev_setup/settings.py b/dev_setup/settings.py
index e1168f7..eeb9c23 100644
--- a/dev_setup/settings.py
+++ b/dev_setup/settings.py
@@ -1,5 +1,5 @@
#-*- coding: utf-8 -*-
-# Copyright (C) 1998-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2012 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
diff --git a/dev_setup/urls.py b/dev_setup/urls.py
index a1a5e9f..4c9370e 100644
--- a/dev_setup/urls.py
+++ b/dev_setup/urls.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright (C) 1998-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2012 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
diff --git a/src/postorius/auth/restbackend.py b/src/postorius/auth/restbackend.py
deleted file mode 100644
index 7f6c20b..0000000
--- a/src/postorius/auth/restbackend.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (C) 1998-2010 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free
-# Software Foundation, either version 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# GNU Mailman. If not, see .
-
-from django.contrib.auth.models import User, check_password
-
-class RESTBackend:
- """
- Authenticate against the settings the REST Middleware
- checking permissions ...
-
- Development uses hardcoded users atm.
-
- """
-
- supports_object_permissions = False
- supports_anonymous_user = False
- supports_inactive_user = False
-
- def authenticate(self, **credentials):
- """
- This authenticate function will check with the REST Middleware
- wheteher the user exists and did provide a valid password.
-
- DEV: TODO - needs Middleware connection
- """
- # make_password is used to create sha1 strings
- valid_users = {"james@example.com": "james", #workaround until middleware exists
- "katie@example.com": "katie",
- "kevin@example.com": "kevin"}
- login_valid = credentials["username"] in valid_users.keys()
- try:
- pwd_valid = (credentials["password"] == valid_users[credentials["username"]])
- except KeyError:
- pwd_valid = False
- if login_valid and pwd_valid:
- try:
- user = User.objects.get(username=credentials["username"])
- except User.DoesNotExist:
- # Create a new user. Note that we can set password
- # to anything, because it won't be checked; the password
- # from settings.py will.
- user = User(username=credentials["username"], password='doesnt matter')
- user.is_staff = False
- user.is_superuser = False
- user.save()
- return user
- return None
-
- def get_user(self, user_id):
- try:
- return User.objects.get(pk=user_id)
- except User.DoesNotExist:
- return None
-
- def has_perm(self, user_obj, perm):
- if perm == "server_admin":
- if user_obj.username == "james@example.com":
- return True
- else:
- return False
- elif perm == "perm": #Test Fallback
- pass
- else:
- raise Exception(perm+" Permisson unknown")
diff --git a/src/postorius/fieldset_forms.py b/src/postorius/fieldset_forms.py
index 775ef3a..61b1b6b 100644
--- a/src/postorius/fieldset_forms.py
+++ b/src/postorius/fieldset_forms.py
@@ -62,7 +62,7 @@
"""
# Create the divs in each fieldset by calling create_divs.
return u'
for each field."""
diff --git a/src/postorius/forms.py b/src/postorius/forms.py
index 6913c07..199f20d 100644
--- a/src/postorius/forms.py
+++ b/src/postorius/forms.py
@@ -48,8 +48,10 @@
)
def clean_mail_host(self):
mail_host = self.cleaned_data['mail_host']
- try: validate_email('mail@' + mail_host)
- except: raise forms.ValidationError(_("Please enter a valid Mail Host (mail.example.net)"))
+ try:
+ validate_email('mail@' + mail_host)
+ except:
+ raise forms.ValidationError(_("Enter a valid Mail Host"))
return mail_host
def clean_web_host(self):
@@ -57,7 +59,7 @@
try:
validate_email('mail@' + web_host)
except:
- raise forms.ValidationError(_("Please enter a valid Web Host (example.net)"))
+ raise forms.ValidationError(_("Please enter a valid Web Host"))
return web_host
class Meta:
@@ -68,7 +70,10 @@
the list should be the wished name of the fieldset, the following
the fields that should be included in the fieldset.
"""
- layout = [["Please enter Details","mail_host", "web_host", "description",]]
+ layout = [["Please enter Details",
+ "mail_host",
+ "web_host",
+ "description",]]
class ListNew(FieldsetForm):
@@ -121,7 +126,7 @@
try:
validate_email(self.cleaned_data['listname']+'@example.net')
except:
- raise forms.ValidationError(_("Please enter a valid listname (my-list-1)"))
+ raise forms.ValidationError(_("Please enter a valid listname"))
return self.cleaned_data['listname']
class Meta:
@@ -132,7 +137,12 @@
the list should be the wished name of the fieldset, the following
the fields that should be included in the fieldset.
"""
- layout = [["List Details", "listname", "mail_host", "list_owner", "description", "advertised"],]
+ layout = [["List Details",
+ "listname",
+ "mail_host",
+ "list_owner",
+ "description",
+ "advertised"],]
class ListSubscribe(FieldsetForm):
"""Form fields to join an existing list.
@@ -141,7 +151,8 @@
widget = forms.HiddenInput(),
error_messages = {'required': _('Please enter an email address.'),
'invalid': _('Please enter a valid email address.')})
- display_name = forms.CharField(label=_('Your name (optional)'), required=False)
+ display_name = forms.CharField(label=_('Your name (optional)'),
+ required=False)
class ListUnsubscribe(FieldsetForm):
"""Form fields to leave an existing list.
@@ -185,7 +196,7 @@
autorespond_choices = (
("none", _("No automatic response")),
("respond_and_discard", _("Respond and discard message")),
- ("respond_and_continue", _("Respond and continue processing the message")),
+ ("respond_and_continue", _("Respond and continue processing")),
)
autorespond_owner = forms.ChoiceField(
choices = autorespond_choices,
@@ -217,7 +228,7 @@
widget = forms.Textarea(),
required = False,
)
- autoresponse_grace_period = forms.CharField(#TODO - either different type or different Validator !
+ autoresponse_grace_period = forms.CharField(
label = _('Autoresponse grace period'),
)
bounces_address = forms.EmailField(
@@ -715,9 +726,9 @@
section_descriptions = {
"List Identity":_("Basic identity settings for the list"),
"Automatic Responses":_("All options for Autoreply"),
- "Alter Messages":_("Settings that modify messages to be sent to members"),
+ "Alter Messages":_("Settings that modify member messages"),
"Digest": _("Digest-related options"),
- "Message Acceptance": _("Options related to when messages are accepted"),
+ "Message Acceptance": _("Options related to accepting messages"),
}
def clean_acceptable_aliases(self):
data = self.cleaned_data['acceptable_aliases']
@@ -748,7 +759,7 @@
pass #empty form
def truncate(self):
"""
- truncates the form to have only those fields which are in self.layout
+ truncates the form to have only those fields which are in self.layout
"""
#delete form.fields which are not in the layout
used_options=[]
@@ -946,10 +957,10 @@
"""
super(UserSettings, self).__init__(*args, **kwargs)
self.fields['address'] = forms.ChoiceField(choices=(address_choices),
- widget = forms.Select(),
- error_messages = {'required': _("Please choose an address."),},
- required = True,
- label = _('Default email address'),)
+ widget = forms.Select(),
+ error_messages = {'required': _("Please choose an address."),},
+ required = True,
+ label = _('Default email address'),)
id = forms.IntegerField( # this should probably not be
# changeable...
diff --git a/src/postorius/tests/__init__.py b/src/postorius/tests/__init__.py
index 9722531..5aaf63c 100644
--- a/src/postorius/tests/__init__.py
+++ b/src/postorius/tests/__init__.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright (C) 1998-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 1998-2012 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
diff --git a/src/postorius/tests/old_tests.txt b/src/postorius/tests/old_tests.txt
deleted file mode 100644
index aa91a50..0000000
--- a/src/postorius/tests/old_tests.txt
+++ /dev/null
@@ -1,107 +0,0 @@
-Change the List Settings
-========================
-
-Try to update the settings. Here we must provide all the settings
-on the page to be allowed to update it.
-
- >>> response = c.post('/settings/new_list%40mail.example.com/',
- ... {'send_welcome_msg': True,
- ... 'advertised': True,
- ... u'list_name': u'new_list',
- ... 'unsubscribe_policy': 9,
- ... 'autorespond_owner': 9,
- ... 'default_member_moderation': True,
- ... 'scrub_nondigest': True,
- ... 'subscribe_auto_approval': 'Subscribe auto approval lorem ipsum dolor sit',
- ... u'fqdn_listname': u'new_list@example.com',
- ... 'gateway_to_news': True,
- ... 'encode_ascii_prefixes': True,
- ... 'generic_nonmember_action': 9,
- ... 'autoresponse_grace_period': 'Auto response grace period lorem ipsum dolor sit',
- ... 'autoresponse_owner_text': 'Auto response owner text lorem ipsum dolor sit',
- ... 'digest_is_default': True,
- ... 'bounce_info_stale_after': 'Bounce info stale after lorem ipsum dolor sit',
- ... 'welcome_msg': 'Welcome message lorem ipsum dolor sit',
- ... 'topics_enabled': True,
- ... 'digest_size_threshold': 9,
- ... 'header_matches': 'Header matches lorem ipsum dolor sit',
- ... u'real_name': u'New_list',
- ... u'host_name': u'example.com',
- ... 'reject_these_nonmembers': 'Reject these non members lorem ipsum dolor sit',
- ... 'collapse_alternatives': True,
- ... 'linked_newsgroup': 'Linked newsgroup lorem ipsum dolor sit',
- ... 'send_reminders': True,
- ... 'hold_these_nonmembers': 'Hold these non members lorem ipsum dolor sit',
- ... 'digest_header': 'Digest header lorem ipsum dolor sit',
- ... 'archive_private': True,
- ... 'bounce_matching_headers': 'Bounce matching headers lorem ipsum dolor sit',
- ... 'bounce_score_threshold': 9,
- ... 'nondigestable': True,
- ... u'http_etag': u'"008c561be0aeaf134fea95066e5a7509a79e4842"',
- ... 'bounce_notify_owner_on_removal': True,
- ... 'autoresponse_request_text': 'Auto response request text lorem ipsum dolor sit',
- ... 'personalize': 'Personalize lorem ipsum dolor sit',
- ... 'max_num_recipients': 9,
- ... 'post_id': 9,
- ... 'send_goodbye_msg': True,
- ... 'max_days_to_hold': 9,
- ... 'pipeline': 'Pipeline lorem ipsum dolor sit',
- ... 'start_chain': 'Start chain lorem ipsum dolor sit',
- ... 'preferred_language': 'Preferred language lorem ipsum dolor sit',
- ... 'autorespond_requests': 9,
- ... 'msg_header': 'Message header lorem ipsum dolor sit',
- ... 'max_message_size': 9,
- ... 'bounce_you_are_disabled_warnings': 9,
- ... 'private_roster': True,
- ... 'require_explicit_destination': True,
- ... 'gateway_to_mail': True,
- ... 'digest_send_periodic': True,
- ... 'digestable': True,
- ... 'member_moderation_notice': 'Member moderation notice lorem ipsum dolor sit',
- ... 'bounce_you_are_disabled_warnings_interval': 'Bounce you are disabled warnings lorem ipsum dolor sit',
- ... u'self_link': u'http://localhost:8001/3.0/lists/new_list@example.com',
- ... 'digest_footer': 'Digest footer lorem ipsum dolor sit',
- ... 'discard_these_nonmembers': 'Discard these non members lorem ipsum dolor sit',
- ... 'respond_to_post_requests': True,
- ... 'mime_is_default_digest': True,
- ... 'subject_prefix': 'Subject prefix lorem ipsum dolor sit',
- ... 'convert_html_to_plaintext': True,
- ... 'autorespond_postings': 9,
- ... 'msg_footer': 'Message footer lorem ipsum dolor sit',
- ... 'info': 'Info lorem ipsum dolor sit',
- ... 'reply_goes_to_list': 'Reply goes to list lorem ipsum dolor sit',
- ... 'obscure_addresses': True,
- ... 'include_list_post_header': True,
- ... 'news_moderation': 'News moderation lorem ipsum dolor sit',
- ... 'topics': 'Topics (BLOB format) lorem ipsum dolor sit',
- ... 'bounce_notify_owner_on_disable': True,
- ... 'goodbye_msg': 'Goodbye message lorem ipsum dolor sit',
- ... 'topics_bodylines_limit': 9,
- ... 'id': 9,
- ... 'filter_content': True,
- ... 'emergency': True,
- ... 'member_moderation_action': True,
- ... 'archive': True,
- ... 'nonmember_rejection_notice': 'Non member rejection notice lorem ipsum dolor sit',
- ... 'list_id': 'Some list ID lorem ipsum dolor sit',
- ... 'first_strip_reply_to': True,
- ... 'nntp_host': 'Nntp host lorem ipsum dolor sit',
- ... 'news_prefix_subject_too': True,
- ... 'bounce_processing': True,
- ... 'description': 'Description lorem ipsum dolor sit',
- ... 'reply_to_address': 'some_reply_to_address@lorem.ipsum',
- ... 'moderator_password': 'Moderator password lorem ipsum dolor sit',
- ... 'digest_volume_frequency': 'Digest volume frequency lorem ipsum dolor sit',
- ... 'include_rfc2369_headers': True,
- ... 'forward_auto_discards': True,
- ... 'ban_list': 'Ban list lorem ipsum dolor sit',
- ... 'new_member_options': 9,
- ... 'subscribe_policy': 9,
- ... 'bounce_unrecognized_goes_to_list_owner': True,
- ... 'autoresponse_postings_text': 'Auto response postings text lorem ipsum dolor sit'})
-
-If the post was successful, a positive response should appear in
-the HTML content.
-
- >>> print "The list has been updated." in response.content
- True
diff --git a/src/postorius/tests/test_to_check.txt b/src/postorius/tests/test_to_check.txt
deleted file mode 100644
index cfa0884..0000000
--- a/src/postorius/tests/test_to_check.txt
+++ /dev/null
@@ -1,58 +0,0 @@
-Change the User Settings #TODO → LP:820827
-========================
-
-Now let's check out the user settings. Start by accessing the user
-settings page. The user settings also requires the user to be logged
-in. We'll call the page and log in as the Katie.
-
- >>> response = c.post('/user_settings/katie%40example.com/',
- ... {"addr": "katie@example.com",
- ... "psw": "katie"})
-
-Let's check that we ended up on the right page.
-
- >>> print "User Settings" in response.content
- True
-
-The settings page contains two tabs - one for the general user settings
-valid for all lists and a specific membership page with links to all
-lists the user is subscribed to. On the latter the user can change the
-settings for each list.
-We'll start by changing some of the user settings. We'll set the real
-name to Katie and the default email address to 'jack@example.com'.
-
- >>> response = c.post('/user_settings/katie%40example.com/',
- ... {'real_name': 'Katie',
- ... 'address': u'jack@example.com'})
-
-If we now check the content of the page that was loaded, we should get
-a confirmation that everything went well.
-
- >>> print "The user settings have been updated." in response.content
- True
-
-
-#MEMBERSHIP SETTINGS part2 #TODO - → LP:820827
-We want to make sure we don't hide our address when posting to the
-list, so we change this option and save the form.
-
- >>> response = c.post('/membership_settings/katie%40example.com/?list=test-one@example.com',
- ... {"hide_address": False})
-
-Now we just need to make sure the saving went well. We do this by
-checking the content of the page that was loaded.
-
- >>> print "The membership settings have been updated." in response.content
- True
-
-We feel done with the user and memebership settings so let's log out
-before we continue.
-
- >>> response = c.get('/lists/logout/',)
-
-Again, if the request was successful we should end up on the list info
-page. Make sure that we got redirected there.
-
- >>> print "All mailing lists" in response.content
- True
-"""
diff --git a/src/postorius/tests/tests.py b/src/postorius/tests/tests.py
index 2118b4a..a0cbd58 100644
--- a/src/postorius/tests/tests.py
+++ b/src/postorius/tests/tests.py
@@ -30,7 +30,9 @@
You need to stop all Mailman3 instances before running the tests
Modules needed
- As we can't make sure that you're running the same language as we did we made sure that each test below is executed using the exact same translation mechanism as we use to Display you Status Messages and other GUI Texts.
+ As we can't make sure that you're running the same language as we did we
+ made sure that each test below is executed using the exact same translation
+ mechanism as we use to Display you Status Messages and other GUI Texts.
Import Translation Module to check success messages
>>> from django.utils.translation import gettext as _
@@ -57,132 +59,28 @@
Login Required
==================================================
-As described within the installation instructions we *already* started using authentification. The easiest way testing it is that we simply load a page which is restricted to some users only.
+As described within the installation instructions we *already* started using
+authentification. The easiest way testing it is that we simply load a page
+which is restricted to some users only.
This was done using Django's @login_required Decorator in front of the View.
-One of the pages which requires a Login is the Domain Administration, if we can load the page without a redirect to the Login page, you're either already logged in or something went wrong.
+One of the pages which requires a Login is the Domain Administration, if
+we can load the page without a redirect to the Login page, you're either
+already logged in or something went wrong.
>>> response = c.get('/domains/')
>>> print type(response) == HttpResponseRedirect
True
-Login of a User
-===============
-
-We've decided to write our own Authentification Backend to use with Django.
-This will handle all @login_required .authenticate() .login() requests.
-
-As we do not have the Authenticating Part which connects Both Mailman and the WebUI we had to hardcode usernames and permissions into the file (auth/restbackend.py)
-For more information what we're planning to implement here take a look at the Acknowledgements.
-
- .. note::
- If you're planning to expand this feel free to use this wonderful resource:
- https://docs.djangoproject.com/en/dev/topics/auth/
-
-Once the new middleware is in place we will need to create a user first. At the moment the user is automaticly created upon success of the login procedure.
-
- >>> #c.... adduser() #TODO add user
-
-Users will have to use the Login form which is located at (/accounts/login/) in order to authenticate themself. The Login / Logout button is linked in the bottom left corner of each page as well.
-
-After each successful login users should be redirected either to the site which they requested before - stored in a GET Value named next - or get the List index. Only if they've used a faulty login they should stay on the Login Page to try again.
-
- >>> response = c.post('/accounts/login/',
- ... {"user": "james@example.com",
- ... "password": "james"})
-
- >>> print type(response) == HttpResponseRedirect
- True
-
-Unfortuneatly the Test Client requires to use the Login directly because it does handle each request seperately. For this reason we have to use the following part in the Tests only to authenticate a user.
-Each successful Login will return True and write the users object into the request context, which allows simple checks whether there is a user logged in and what his name is.
-
- >>> c.login(username='katie@example.com', password='katie')
- True
-
-Permissions
-===========
-
-Our own Auth Backend allows the use of Djangos own Permission Decorator which is
-
-.. code-block:: python
-
- @permission_required(NAME_OF_PERMISSION)
-
-At the moment we've installed this for Domain Administration,
-
- .. note::
- Please take a look at the ackownledgement to see what is working in this part
-
-Get the Domains page and get redirected because Katie who is logged in doesn't have the Permission
-
- >>> response = c.get('/domains/')
- >>> print type(response) == HttpResponseRedirect
- True
-
-Logout Katie who isn't a Domain-Owner and Login James who should be allowed to view this page
-
- >>> c.logout() #katie
- >>> c.login(username='james@example.com', password='james')
- True
-
-Check that the Page now loads correctly
-
- >>> response = c.get('/domains/')
- >>> response.status_code
- 200
-
-
=====
Pages
=====
-
-Create a New Domain
-===================
-
-Domain Administration is called by opening the URL mentioned below. Prequirements like Authorisation and Permissions have been covered before.
-Now we do check that the response really does have the correct heading.
-
- >>> response = c.get('/domains/')
- >>> print "Domain Index" in response.content
- True
-
-On this page there should be a button which allows to create a new Domain.
-If you're running Mailman for the first time you need to create a Domain before creating Mailinglists. That's only because each List is Part of a Domain and could not be created without it's reference.
-
- >>> '
New Domain' in response.content
- True
-
-For sure the page allowing the creation of a new Domain should open correclty as well
- >>> response = c.get('/domains/new/')
- >>> response.status_code
- 200
- >>> print "Add a new Domain" in response.content #TODO - change heading
- True
-
-Each Domain has two main Data Parts, most obvious for a mailinglist we do need a mail_host that's the part behind the @ when getting an email. In addition we offer you this WebUI for configuration, some may have multiple URLs they can use to access the same installation of mailman. For this reason each Mailinglist gets it's own web_host as well - which doesn't need to be unique.
-
-Testing the Site we do now submit the form we've loaded earlier by sending all necessary data in a POST request. The new Domain will be called mail.example.com and available via it's web_host example.com.
-
- .. note::
- If you do want to use web_host filtering in your webUI you need to remember adding the URL to your /etc/hosts - at least for development
-
- >>> response = c.post('/domains/new/',
- ... {"mail_host": "mail.example.com",
- ... "web_host": "example.com",
- ... "description": "doctest testing domain"})
- >>> response = c.get('/domains/')
-
-Then we check that everything went well.
- >>> response.status_code
- 200
- >>> print "doctest testing domain" in response.content
- True
-
Create a New List
=================
-After creating a Domain you should be able to create new Lists. The Button for doing so is shown on the List index Page which should offer a list of all available (adverrtised) lists.
+After creating a Domain you should be able to create new Lists. The Button for
+doing so is shown on the List index Page which should offer a list of all
+available (adverrtised) lists.
>>> response = c.get('/lists/')
>>> response.status_code
@@ -190,7 +88,8 @@
>>> "Lists
on" in response.content
True
-The new List creation form is opened by clicking on the Button mentioned above or accessing the page directly
+The new List creation form is opened by clicking on the Button mentioned above
+or accessing the page directly
>>> response = c.get('/lists/new/')
>>> response.status_code
@@ -198,8 +97,11 @@
>>> print "Create a new List on" in response.content
True
-Creating a new List we do need to specify at least the below mentioned items. Those were entered using some nice GUI Forms which do only show up available Values or offer you to choose a name which will be checked during validation.
-We're now submitting the form using a POST request and get redirected to the List Index Page
+Creating a new List we do need to specify at least the below mentioned items.
+Those were entered using some nice GUI Forms which do only show up available
+Values or offer you to choose a name which will be checked during validation.
+We're now submitting the form using a POST request and get redirected to
+the List Index Page
>>> response = c.post('/lists/new/',
... {"listname": "new_list1",
@@ -211,7 +113,10 @@
>>> print type(response) == HttpResponseRedirect
True
-As List index is an overview of all advertised Lists and we've choosen to do so we should now see our new List within the overview. HTTP_HOST is added as META Data for the request because we do only want to see Domains which belong to the example.com web_host
+As List index is an overview of all advertised Lists and we've choosen to
+do so we should now see our new List within the overview. HTTP_HOST is added
+as META Data for the request because we do only want to see Domains which
+belong to the example.com web_host
>>> response = c.get('/lists/',HTTP_HOST='example.com')
>>> response.status_code
@@ -222,7 +127,10 @@
List Summary
============
-List summary is a dashboard for each List. It does have Links to the most useful functions which are only related to that Domain. These include the Values mentioned below. _(function) is used to Translate these to you local language.
+List summary is a dashboard for each List. It does have Links to the most
+useful functions which are only related to that Domain. These include the
+Values mentioned below. _(function) is used to Translate these to you local
+language.
>>> response = c.get('/lists/new_list1%40mail.example.com/',)
>>> response.status_code
@@ -239,7 +147,8 @@
Subscriptions
=============
-The Subscriptions form is found on the below URL. Last part of the Url is one of [None,'subscribe','unsubscribe']
+The Subscriptions form is found on the below URL. Last part of the Url is one
+of [None,'subscribe','unsubscribe']
>>> url = '/subscriptions/new_list1%40mail.example.com/subscribe'
>>> response = c.get(url)
@@ -266,7 +175,8 @@
>>> print (_('Subscribed')+' katie@example.com') in response.content
True
-The logged in user (james@example.com) can now modify his own membership using a button which is displayed in list_summary.
+The logged in user (james@example.com) can now modify his own membership
+using a button which is displayed in list_summary.
>>> response = c.get('/lists/new_list1%40mail.example.com/')
>>> "mm_membership" in response.content
@@ -284,7 +194,9 @@
Mass Subscribe Users (within settings)
======================================
-Another page related to Mass Subscriptions will be available to List Owners as well. This page will allow adding a couple of users to one lists at the same time.
+Another page related to Mass Subscriptions will be available to List Owners
+as well. This page will allow adding a couple of users to one lists at the
+same time.
>>> url = '/subscriptions/new_list1%40mail.example.com/mass_subscribe/'
>>> response = c.get(url)
@@ -308,25 +220,6 @@
>>> print _("The mass subscription was successful.") in response.content
True
-Change the Memebership Settings
-===============================
-
-Now let's go to the membership settings page. Once we go there we
-should get a list of all the available lists.
-
- >>> response = c.get('/membership_settings/new_list1%40mail.example.com/')
- >>> print "Membership Settings" in response.content
- True
-
-Select the list 'new_list1@example.com'.
-
- >>> response = c.get('/membership_settings/new_list1%40mail.example.com/')
- >>> print ("Membership Settings" in response.content) and ("for new_list1@mail.example.com" in response.content)
- True
-
-.. note::
- This page relies on the Middleware connecting the Django Project with Mailman - see acknowledgements
-
Delete the List
===============
diff --git a/src/postorius/urls.py b/src/postorius/urls.py
index b121bf9..9076120 100644
--- a/src/postorius/urls.py
+++ b/src/postorius/urls.py
@@ -31,7 +31,9 @@
url(r'^accounts/membership/(?:(?P[^/]+)/)?$',
'membership_settings', kwargs={"tab": "membership"},
name='membership_settings'),
- url(r'^accounts/mailmansettings/$', 'user_mailmansettings', name='user_mailmansettings'),
+ url(r'^accounts/mailmansettings/$',
+ 'user_mailmansettings',
+ name='user_mailmansettings'),
# /settings/
url(r'^settings/$', 'site_settings', name="site_settings"),
url(r'^settings/domains/$', 'domain_index', name='domain_index'),
@@ -53,20 +55,25 @@
'mass_subscribe', name='mass_subscribe'),
url(r'^lists/(?P[^/]+)/delete$', 'list_delete',
name='list_delete'),
- url(r'^lists/(?P[^/]+)/held_messages/(?P[^/]+)/accept$', 'accept_held_message',
+ url(r'^lists/(?P[^/]+)/held_messages/(?P[^/]+)/accept$',
+ 'accept_held_message',
name='accept_held_message'),
- url(r'^lists/(?P[^/]+)/held_messages/(?P[^/]+)/discard$', 'discard_held_message',
+ url(r'^lists/(?P[^/]+)/held_messages/(?P[^/]+)/discard$',
+ 'discard_held_message',
name='discard_held_message'),
- url(r'^lists/(?P[^/]+)/held_messages/(?P[^/]+)/defer$', 'defer_held_message',
+ url(r'^lists/(?P[^/]+)/held_messages/(?P[^/]+)/defer$',
+ 'defer_held_message',
name='defer_held_message'),
- url(r'^lists/(?P[^/]+)/held_messages/(?P[^/]+)/reject$', 'reject_held_message',
+ url(r'^lists/(?P[^/]+)/held_messages/(?P[^/]+)/reject$',
+ 'reject_held_message',
name='reject_held_message'),
- url(r'^lists/(?P[^/]+)/held_messages$', 'list_held_messages',
+ url(r'^lists/(?P[^/]+)/held_messages$',
+ 'list_held_messages',
name='list_held_messages'),
url(r'^user_settings/$', 'user_settings', kwargs={"tab": "user"},
name='user_settings'),
url(r'^lists/(?P[^/]+)/settings/(?P[^/]+)?(?:/(?P.*))?$',
- 'list_settings', name='list_settings'),
+ 'list_settings', name='list_settings'),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/src/postorius/views.py b/src/postorius/views.py
index 310979c..7f639a4 100644
--- a/src/postorius/views.py
+++ b/src/postorius/views.py
@@ -26,7 +26,8 @@
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import logout, authenticate, login
-from django.contrib.auth.decorators import (login_required, permission_required,
+from django.contrib.auth.decorators import (login_required,
+ permission_required,
user_passes_test)
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.models import User
@@ -58,7 +59,8 @@
existing_domains = Domain.objects.all()
except MailmanApiError:
return utils.render_api_error(request)
- return render_to_response('postorius/domain_index.html', {'domains':existing_domains,},
+ return render_to_response('postorius/domain_index.html',
+ {'domains':existing_domains,},
context_instance=RequestContext(request))
@login_required
@@ -111,17 +113,21 @@
form = ListNew(choosable_domains, request.POST)
if form.is_valid():
#grab domain
- domain = Domain.objects.get_or_404(mail_host=form.cleaned_data['mail_host'])
+ domain = Domain.objects.get_or_404(
+ mail_host=form.cleaned_data['mail_host'])
#creating the list
try:
- mailing_list = domain.create_list(form.cleaned_data['listname'])
+ mailing_list = domain.create_list(
+ form.cleaned_data['listname'])
list_settings = mailing_list.settings
list_settings["description"] = form.cleaned_data['description']
- list_settings["owner_address"] = form.cleaned_data['list_owner']
+ list_settings["owner_address"] = \
+ form.cleaned_data['list_owner']
list_settings["advertised"] = form.cleaned_data['advertised']
list_settings.save()
messages.success(request, _("List created"))
- return redirect("list_summary",fqdn_listname=mailing_list.fqdn_listname)
+ return redirect("list_summary",
+ fqdn_listname=mailing_list.fqdn_listname)
#TODO catch correct Error class:
except HTTPError, e:
messages.error(request,e)
@@ -138,7 +144,8 @@
choosable_domains = [("",_("Choose a Domain"))]
for domain in domains:
choosable_domains.append((domain.mail_host,domain.mail_host))
- form = ListNew(choosable_domains,initial={'list_owner': request.user.email})
+ form = ListNew(choosable_domains,
+ initial={'list_owner': request.user.email})
return render_to_response(template, {'form': form},
context_instance=RequestContext(request))
@@ -164,7 +171,8 @@
context_instance=RequestContext(request))
@user_passes_test(lambda u: u.is_superuser)
-def list_metrics(request,fqdn_listname=None,option=None,template='postorius/lists/metrics.html'):
+def list_metrics(request,fqdn_listname=None,option=None,
+ template='postorius/lists/metrics.html'):
"""
PUBLIC
an entry page for each list which displays additional (non-editable)
@@ -220,7 +228,8 @@
email = request.POST.get('email')
real_name = request.POST.get('real_name')
the_list.subscribe(email, real_name)
- messages.success(request,_('You are now subscribed to %s.' % the_list.fqdn_listname))
+ messages.success(request,
+ _('You are subscribed to %s.' % the_list.fqdn_listname))
return redirect('list_summary', the_list.fqdn_listname)
else:
logger.debug(form)
@@ -252,8 +261,9 @@
return redirect('list_summary', the_list.fqdn_listname)
@login_required
-def list_subscriptions(request, option=None, fqdn_listname=None, user_email = None,
- template = 'postorius/lists/subscriptions.html', *args, **kwargs):#TODO **only kwargs ?
+def list_subscriptions(request, option=None, fqdn_listname=None,
+ user_email=None,
+ template='postorius/lists/subscriptions.html', *args, **kwargs):
"""
Display the information there is available for a list. This
function also enables subscribing or unsubscribing a user to a
@@ -285,17 +295,22 @@
# the form was valid so try to subscribe the user
try:
email = form.cleaned_data['email']
- response = the_list.subscribe(address=email,real_name=form.cleaned_data.get('real_name', ''))
+ response = the_list.subscribe(address=email,
+ real_name=form.cleaned_data.get('real_name', ''))
return render_to_response('postorius/lists/summary.html',
- {'list': the_list,
- 'option':option,
- 'message':_("Subscribed ")+ email },context_instance=RequestContext(request))
+ {'list': the_list,
+ 'option':option,
+ 'message':_("Subscribed ") + email },
+ context_instance=RequestContext(request))
except HTTPError, e:
return render_to_response('postorius/errors/generic.html',
- {'error':e}, context_instance=RequestContext(request))
+ {'error':e},
+ context_instance=RequestContext(request))
else: #invalid subscribe form
form_subscribe = form
- form_unsubscribe = ListUnsubscribe(initial = {'fqdn_listname': fqdn_listname, 'name' : 'unsubscribe'})
+ form_unsubscribe = ListUnsubscribe(
+ initial={'fqdn_listname': fqdn_listname,
+ 'name': 'unsubscribe'})
elif request.POST.get('name', '') == "unsubscribe":
form = ListUnsubscribe(request.POST)
if form.is_valid():
@@ -304,32 +319,40 @@
email = form.cleaned_data["email"]
response = the_list.unsubscribe(address=email)
return render_to_response('postorius/lists/summary.html',
- {'list': the_list, 'message':_("Unsubscribed ")+ email },context_instance=RequestContext(request))
+ {'list': the_list,
+ 'message':_("Unsubscribed ") + email },
+ context_instance=RequestContext(request))
except ValueError, e:
- return render_to_response('postorius/errors/generic.html',
- {'error':e}, context_instance=RequestContext(request))
+ return render_to_response('postorius/errors/generic.html',
+ {'error': e},
+ context_instance=RequestContext(request))
else:#invalid unsubscribe form
- form_subscribe = ListSubscribe(initial = {'fqdn_listname': fqdn_listname,
- 'option':option,
- 'name' : 'subscribe'})
+ form_subscribe = ListSubscribe(
+ initial={'fqdn_listname': fqdn_listname,
+ 'option':option,
+ 'name' : 'subscribe'})
form_unsubscribe = ListUnsubscribe(request.POST)
else:
# the request was a GET request so set the two forms to empty
# forms
if option=="subscribe" or not option:
- form_subscribe = ListSubscribe(initial = {'fqdn_listname': fqdn_listname, 'email':request.user.username,
- 'name' : 'subscribe'})
+ form_subscribe = ListSubscribe(
+ initial={'fqdn_listname': fqdn_listname,
+ 'email': request.user.username,
+ 'name' : 'subscribe'})
if option=="unsubscribe" or not option:
- form_unsubscribe = ListUnsubscribe(initial = {'fqdn_listname': fqdn_listname, 'email':request.user.username,
- 'name' : 'unsubscribe'})
- the_list = List.objects.get_or_404(fqdn_listname=fqdn_listname)#TODO
- return render_to_response(template, {'form_subscribe': form_subscribe,
- 'form_unsubscribe': form_unsubscribe,
- 'message':message,
- 'error':error,
- 'list': the_list,
- }
- ,context_instance=RequestContext(request))
+ form_unsubscribe = ListUnsubscribe(
+ initial={'fqdn_listname': fqdn_listname,
+ 'email':request.user.username,
+ 'name' : 'unsubscribe'})
+ the_list = List.objects.get_or_404(fqdn_listname=fqdn_listname)
+ return render_to_response(template,
+ {'form_subscribe': form_subscribe,
+ 'form_unsubscribe': form_unsubscribe,
+ 'message':message,
+ 'error':error,
+ 'list': the_list,},
+ context_instance=RequestContext(request))
@login_required
@user_passes_test(lambda u: u.is_superuser)
@@ -346,7 +369,8 @@
lists = List.objects.all()
return redirect("list_index")
else:
- submit_url = reverse('list_delete',kwargs={'fqdn_listname':fqdn_listname})
+ submit_url = reverse('list_delete',
+ kwargs={'fqdn_listname': fqdn_listname})
cancel_url = reverse('list_index',)
return render_to_response('postorius/confirm_dialog.html',
{'submit_url': submit_url,
@@ -428,7 +452,8 @@
@login_required
@user_passes_test(lambda u: u.is_superuser)
-def list_settings(request, fqdn_listname=None, visible_section=None, visible_option=None,
+def list_settings(request, fqdn_listname=None, visible_section=None,
+ visible_option=None,
template='postorius/lists/settings.html'):
"""
View and edit the settings of a list.
@@ -437,7 +462,8 @@
Use //
to show only parts of the settings
- is optional / is used to differ in between section and option might result in using //option
+ is optional / is used to differ in between section and option might
+ result in using //option
"""
message = ""
logger.debug(visible_section)
@@ -452,7 +478,8 @@
temp = ListSettings('','')
for section in temp.layout:
try:
- form_sections.append((section[0],temp.section_descriptions[section[0]]))
+ form_sections.append((section[0],
+ temp.section_descriptions[section[0]]))
except KeyError, e:
error=e
del temp
@@ -483,14 +510,14 @@
#recreate the form using the settings
form = ListSettings(visible_section,visible_option,data=used_settings)
form.truncate()
- return render_to_response(template, {'form': form,
- 'form_sections': form_sections,
- 'message': message,
- 'list': the_list,
- 'visible_option':visible_option,
- 'visible_section':visible_section,
- }
- ,context_instance=RequestContext(request))
+ return render_to_response(template,
+ {'form': form,
+ 'form_sections': form_sections,
+ 'message': message,
+ 'list': the_list,
+ 'visible_option':visible_option,
+ 'visible_section':visible_section,},
+ context_instance=RequestContext(request))
@login_required
@user_passes_test(lambda u: u.is_superuser)
@@ -516,13 +543,14 @@
for email in emails:
# very simple test if email address is valid
parts = email.split('@')
- if len(parts) == 2 and '.' in parts[1]: #TODO - move check to clean method of the form - see example in django docs
+ if len(parts) == 2 and '.' in parts[1]:
try:
the_list.subscribe(address=email, real_name="")
message = "The mass subscription was successful."
- except Exception, e: #TODO find right exception and catch only this one
- return render_to_response('postorius/errors/generic.html',
- {'error': str(e)})
+ except Exception, e:
+ return render_to_response(
+ 'postorius/errors/generic.html',
+ {'error': str(e)})
else:
# At least one email address wasn't valid so
@@ -535,10 +563,11 @@
# A request to view the page was send so return the form to
# mass subscribe users.
form = ListMassSubscription()
- return render_to_response(template, {'form': form,
- 'message': message,
- 'list': the_list}
- ,context_instance=RequestContext(request))
+ return render_to_response(template,
+ {'form': form,
+ 'message': message,
+ 'list': the_list},
+ context_instance=RequestContext(request))
@login_required
def user_mailmansettings(request):
@@ -576,13 +605,14 @@
membership_lists = []
try:
- c = Client('%s/3.0' % settings.REST_SERVER, settings.API_USER, settings.API_PASS)
+ c = Client('%s/3.0' % settings.REST_SERVER, settings.API_USER,
+ settings.API_PASS)
if tab == "membership":
if fqdn_listname:
the_list = List.objects.get(fqdn_listname=fqdn_listname)
user_object = the_list.get_member(member)
else:
- message = ("Using a workaround to replace missing Client functionality → LP:820827")
+ message = ("")
for mlist in List.objects.all():
try:
mlist.get_member(member)
@@ -592,20 +622,20 @@
else:
# address_choices for the 'address' field must be a list of
# tuples of length 2
- raise Exception("WORK in PROGRRESS needs REST Auth Middleware! - TODO")
+ raise Exception("")
address_choices = [[addr, addr] for addr in user_object.address]
except AttributeError, e:
return render_to_response('postorius/errors/generic.html',
- {'error': str(e)+"REST API not found / Offline"},
- context_instance=RequestContext(request))
+ {'error': str(e)+"REST API not found / Offline"},
+ context_instance=RequestContext(request))
except ValueError, e:
return render_to_response('postorius/errors/generic.html',
{'error': e},
context_instance=RequestContext(request))
except HTTPError,e :
return render_to_response('postorius/errors/generic.html',
- {'error': _("List ")+fqdn_listname+_(" does not exist")},
- context_instance=RequestContext(request))
+ {'error': _("List ")+fqdn_listname+_(" does not exist")},
+ context_instance=RequestContext(request))
#-----------------------------------------------------------------
if request.method == 'POST':
# The form enables both user and member settings. As a result
@@ -661,13 +691,14 @@
data ={}#Todo https://bugs.launchpad.net/mailman/+bug/821438
form = UserSettings(data)
- return render_to_response(template, {'form': form,
- 'tab': tab,
- 'list': the_list,
- 'membership_lists': membership_lists,
- 'message': message,
- 'member': member}
- ,context_instance=RequestContext(request))
+ return render_to_response(template,
+ {'form': form,
+ 'tab': tab,
+ 'list': the_list,
+ 'membership_lists': membership_lists,
+ 'message': message,
+ 'member': member},
+ context_instance=RequestContext(request))
def user_logout(request):
logout(request)