Commit 8b2c8789 authored by Abhilash Raj's avatar Abhilash Raj

List Style Selection

parent c20609c6
......@@ -49,7 +49,7 @@ setup(
install_requires=[
'Django>=1.11',
'django-mailman3>=1.2.0a1',
'mailmanclient>=3.1.2a1'
'mailmanclient>=3.2.0b1'
],
tests_require=[
"mock",
......
......@@ -23,6 +23,12 @@ along with Postorius. If not, see <http://www.gnu.org/licenses/>.
(XXXX-XX-XX)
* Postorius now runs only on Python 3.4+ and supports Django 1.8 and 1.11+
* Added the ability to set and edit ``alias_domain`` to the ``domains`` forms.
* List Create form now allows selecting the ``style``. A ``style`` is how a new
mailing list is configured.
* Minimum supported Mailman Core version is now 3.2.0. This is because the
``styles`` attribute for MailingList resource is exposed in 3.2, which
contains all the default ``styles`` supported by Core and their human readable
description.
1.1.2
=====
......
......@@ -61,11 +61,12 @@ class ListNew(forms.Form):
choices=(
(True, _("Advertise this list in list index")),
(False, _("Hide this list in list index"))))
list_style = forms.ChoiceField()
description = forms.CharField(
label=_('Description'),
required=False)
def __init__(self, domain_choices, *args, **kwargs):
def __init__(self, domain_choices, style_choices, *args, **kwargs):
super(ListNew, self).__init__(*args, **kwargs)
self.fields["mail_host"] = forms.ChoiceField(
widget=forms.Select(),
......@@ -73,7 +74,14 @@ class ListNew(forms.Form):
required=True,
choices=domain_choices,
error_messages={'required': _("Choose an existing Domain."),
'invalid': "Choose a valid Mail Host"})
'invalid': _("Choose a valid Mail Host")})
self.fields["list_style"] = forms.ChoiceField(
widget=forms.Select(),
label=_('List Style'),
required=True,
choices=style_choices,
error_messages={'required': _("Choose a List Style."),
'invalid': _("Choose a valid List Style.")})
if len(domain_choices) < 2:
self.fields["mail_host"].help_text = _(
"Site admin has not created any domains")
......@@ -104,6 +112,7 @@ class ListNew(forms.Form):
layout = [["List Details",
"listname",
"mail_host",
"list_style",
"list_owner",
"description",
"advertised"], ]
......
......@@ -199,3 +199,9 @@ class Member(MailmanRestModel):
"""Member model class.
"""
objects = MailmanRestManager('member', 'members')
class Style(MailmanRestModel):
"""
"""
objects = MailmanRestManager(None, 'styles')
......@@ -42,6 +42,21 @@ interactions:
content-length: ['194']
content-type: [application/json; charset=UTF-8]
status: {code: 200, message: OK}
- request:
body: null
headers:
accept-encoding: ['gzip, deflate']
method: GET
uri: http://localhost:9001/3.0/lists/styles
response:
body: {string: '{"default": "legacy-default", "http_etag": "\"21a4b8b38716167b458ed2d8e88579233938274b\"",
"style_names": ["legacy-announce", "legacy-default"], "styles": [{"description":
"Announce only mailing list style.", "name": "legacy-announce"}, {"description":
"Ordinary discussion mailing list style.", "name": "legacy-default"}]}'}
headers:
content-length: ['323']
content-type: [application/json; charset=UTF-8]
status: {code: 200, message: OK}
- request:
body: null
headers:
......@@ -76,38 +91,10 @@ interactions:
method: GET
uri: http://localhost:9001/3.0/users
response:
body: {string: '{"entries": [{"created_on": "2005-08-01T07:49:23", "display_name":
"None", "http_etag": "\"be0b24af66b14f066f01a9739bfc41dea2681275\"", "is_server_owner":
false, "self_link": "http://localhost:9001/3.0/users/108", "user_id": 108},
{"created_on": "2005-08-01T07:49:23", "display_name": "None", "http_etag":
"\"5424e86150dfc1bfdc17389ca84dd3e6a1c3e649\"", "is_server_owner": false,
"self_link": "http://localhost:9001/3.0/users/109", "user_id": 109}], "http_etag":
"\"7a4fe6ed13fbe5dedff8f6a61ef10fbe92f776fc\"", "start": 0, "total_size":
2}'}
body: {string: '{"http_etag": "\"32223434a0f3af4cdc4673d1fbc5bac1f6d98fd3\"",
"start": 0, "total_size": 0}'}
headers:
content-length: ['539']
content-length: ['90']
content-type: [application/json; charset=UTF-8]
status: {code: 200, message: OK}
- request:
body: null
headers:
accept-encoding: ['gzip, deflate']
method: DELETE
uri: http://localhost:9001/3.0/users/108
response:
body: {string: ''}
headers:
Content-Length: ['0']
status: {code: 204, message: No Content}
- request:
body: null
headers:
accept-encoding: ['gzip, deflate']
method: DELETE
uri: http://localhost:9001/3.0/users/109
response:
body: {string: ''}
headers:
Content-Length: ['0']
status: {code: 204, message: No Content}
version: 1
......@@ -42,6 +42,21 @@ interactions:
content-length: ['194']
content-type: [application/json; charset=UTF-8]
status: {code: 200, message: OK}
- request:
body: null
headers:
accept-encoding: ['gzip, deflate']
method: GET
uri: http://localhost:9001/3.0/lists/styles
response:
body: {string: '{"default": "legacy-default", "http_etag": "\"21a4b8b38716167b458ed2d8e88579233938274b\"",
"style_names": ["legacy-announce", "legacy-default"], "styles": [{"description":
"Announce only mailing list style.", "name": "legacy-announce"}, {"description":
"Ordinary discussion mailing list style.", "name": "legacy-default"}]}'}
headers:
content-length: ['323']
content-type: [application/json; charset=UTF-8]
status: {code: 200, message: OK}
- request:
body: null
headers:
......@@ -69,7 +84,7 @@ interactions:
content-type: [application/json; charset=UTF-8]
status: {code: 200, message: OK}
- request:
body: fqdn_listname=a_new_list%40example.com
body: fqdn_listname=a_new_list%40example.com&style_name=legacy-default
headers:
accept-encoding: ['gzip, deflate']
content-type: [application/x-www-form-urlencoded]
......@@ -99,7 +114,7 @@ interactions:
content-type: [application/json; charset=UTF-8]
status: {code: 200, message: OK}
- request:
body: list_id=a_new_list.example.com&role=owner&subscriber=owner%40example.com
body: display_name=None&list_id=a_new_list.example.com&role=owner&subscriber=owner%40example.com
headers:
accept-encoding: ['gzip, deflate']
content-type: [application/x-www-form-urlencoded]
......@@ -110,7 +125,7 @@ interactions:
headers:
content-length: ['0']
content-type: [application/json; charset=UTF-8]
location: ['http://localhost:9001/3.0/members/110']
location: ['http://localhost:9001/3.0/members/1']
status: {code: 201, message: Created}
- request:
body: null
......@@ -184,13 +199,13 @@ interactions:
uri: http://localhost:9001/3.0/lists/a_new_list.example.com/roster/owner
response:
body: {string: '{"entries": [{"address": "http://localhost:9001/3.0/addresses/owner@example.com",
"delivery_mode": "regular", "email": "owner@example.com", "http_etag": "\"bee8426cb1ceca1d3748dae8d056fbc8e253625a\"",
"list_id": "a_new_list.example.com", "member_id": 110, "moderation_action":
"accept", "role": "owner", "self_link": "http://localhost:9001/3.0/members/110",
"user": "http://localhost:9001/3.0/users/110"}], "http_etag": "\"6925f9f397017bca14160c3b7c4e8b89d3e24974\"",
"delivery_mode": "regular", "email": "owner@example.com", "http_etag": "\"4bed237af62a873178c56fb29b71ccb29bda9780\"",
"list_id": "a_new_list.example.com", "member_id": 1, "moderation_action":
"accept", "role": "owner", "self_link": "http://localhost:9001/3.0/members/1",
"user": "http://localhost:9001/3.0/users/1"}], "http_etag": "\"59b824311de2b496ca560c165da8b238a2e724e1\"",
"start": 0, "total_size": 1}'}
headers:
content-length: ['496']
content-length: ['490']
content-type: [application/json; charset=UTF-8]
status: {code: 200, message: OK}
- request:
......@@ -227,13 +242,13 @@ interactions:
method: GET
uri: http://localhost:9001/3.0/users
response:
body: {string: '{"entries": [{"created_on": "2005-08-01T07:49:23", "http_etag":
"\"c71a9946b482d4d63b30b77bd28ea4b022d52b08\"", "is_server_owner": false,
"self_link": "http://localhost:9001/3.0/users/110", "user_id": 110}], "http_etag":
"\"6168dfeb6607ec361b07f08a3dbd332a2be90222\"", "start": 0, "total_size":
body: {string: '{"entries": [{"created_on": "2005-08-01T07:49:23", "display_name":
"None", "http_etag": "\"255d01f1281de196c8b6b707f4fafeab70a58e4d\"", "is_server_owner":
false, "self_link": "http://localhost:9001/3.0/users/1", "user_id": 1}], "http_etag":
"\"e388248057c301c1da1f1295e4532030227f2222\"", "start": 0, "total_size":
1}'}
headers:
content-length: ['297']
content-length: ['317']
content-type: [application/json; charset=UTF-8]
status: {code: 200, message: OK}
- request:
......@@ -241,7 +256,7 @@ interactions:
headers:
accept-encoding: ['gzip, deflate']
method: DELETE
uri: http://localhost:9001/3.0/users/110
uri: http://localhost:9001/3.0/users/1
response:
body: {string: ''}
headers:
......
......@@ -18,10 +18,7 @@
from allauth.account.models import EmailAddress
from django.contrib.auth.models import User
try:
from django.core.urlresolvers import reverse
except ImportError:
from django.urls import reverse
from django.urls import reverse
from postorius.tests.utils import ViewTestCase
......@@ -50,9 +47,11 @@ class ListCreationTest(ViewTestCase):
'mail_host': 'example.com',
'list_owner': 'owner@example.com',
'advertised': 'True',
'list_style': 'legacy-default',
'description': 'A new list.'}
response = self.client.post(reverse('list_new'), post_data)
self.assertHasSuccessMessage(response)
self.assertEqual(response.status_code, 302)
# self.assertHasSuccessMessage(response)
a_new_list = self.mm_client.get_list('a_new_list@example.com')
self.assertEqual(a_new_list.fqdn_listname, 'a_new_list@example.com')
self.assertEqual(a_new_list.owners, ['owner@example.com'])
......@@ -63,6 +62,7 @@ class ListCreationTest(ViewTestCase):
'mail_host': 'example.com',
'list_owner': 'owner@example.com',
'advertised': 'True',
'list_style': 'legacy-default',
'description': 'A new list.'}
response = self.client.post(reverse('list_new'), post_data)
self.assertEqual(response.status_code, 200)
......
......@@ -113,31 +113,42 @@ class TestChangeSubscription(TestCase):
class TestListNew(TestCase):
def test_form_fields_list(self):
form = ListNew([
("mailman.most-desirable.org", "mailman.most-desirable.org")],
{'listname': 'xyz',
'mail_host': 'mailman.most-desirable.org',
'list_owner': 'contact@mailman.most-desirable.org',
'advertised': 'True',
'description': 'The Most Desirable organization', })
domain_choices = [
("mailman.most-desirable.org", "mailman.most-desirable.org")]
style_choices = [
("legacy-default", 'Ordinary discussion mailing list style.'),
("legacy-announce", 'Announce only mailing list style.')]
form = ListNew(domain_choices, style_choices,
{'listname': 'xyz',
'mail_host': 'mailman.most-desirable.org',
'list_owner': 'contact@mailman.most-desirable.org',
'advertised': 'True',
'list_style': 'legacy-default',
'description': 'The Most Desirable organization'})
self.assertTrue(form.is_valid(), form.errors)
def test_form_fields_list_invalid(self):
form = ListNew([
("mailman.most-desirable.org", "mailman.most-desirable.org")],
{'listname': 'xy#z',
'mail_host': 'mailman.most-desirable.org',
'list_owner': 'mailman.most-desirable.org',
'advertised': 'abcd',
'description': 'The Most Desirable organization', })
domain_choices = [
("mailman.most-desirable.org", "mailman.most-desirable.org")]
style_choices = [
("legacy-default", 'Ordinary discussion mailing list style.'),
("legacy-announce", 'Announce only mailing list style.')]
form = ListNew(domain_choices, style_choices,
{'listname': 'xy#z',
'mail_host': 'mailman.most-desirable.org',
'list_owner': 'mailman.most-desirable.org',
'advertised': 'abcd',
'list_style': 'defg',
'description': 'The Most Desirable organization'})
self.assertFalse(form.is_valid())
# Test that all invalid fields are actually checked.
for field in ('list_owner', 'advertised'):
self.assertTrue(field in form.errors)
self.assertTrue('Enter a valid email address.' in
form.errors['list_owner'])
self.assertTrue('Select a valid choice. abcd is not one of the available choices.' # noqa
in form.errors['advertised'])
self.assertTrue(
'Select a valid choice. abcd is not one of the available choices.'
in form.errors['advertised'])
def test_form_without_domain_choices(self):
form = ListNew([],
......@@ -152,13 +163,17 @@ class TestListNew(TestCase):
'Site admin has not created any domains')
def test_listname_validation(self):
form = ListNew([
("mailman.most-desirable.org", "mailman.most-desirable.org")],
{'listname': 'xy@z',
'mail_host': 'mailman.most-desirable.org',
'list_owner': 'mailman.most-desirable.org',
'advertised': 'abcd',
'description': 'The Most Desirable organization', })
domain_choices = [
("mailman.most-desirable.org", "mailman.most-desirable.org")]
style_choices = [
("legacy-default", 'Ordinary discussion mailing list style.'),
("legacy-announce", 'Announce only mailing list style.')]
form = ListNew(domain_choices, style_choices,
{'listname': 'xy@z',
'mail_host': 'mailman.most-desirable.org',
'list_owner': 'mailman.most-desirable.org',
'advertised': 'abcd',
'description': 'The Most Desirable organization', })
self.assertFalse(form.is_valid())
self.assertTrue('listname' in form.errors)
self.assertTrue('Please enter a valid listname' in
......@@ -176,21 +191,32 @@ class TestListNew(TestCase):
'description': 'The Most Desirable organization', })
self.assertFalse(form.is_valid())
self.assertTrue('listname' in form.errors)
self.assertEqual('Please enter a valid listname, "@" is not allowed in listname', # noqa
form.errors['listname'])
self.assertEqual(
'Please enter a valid listname, "@" is not allowed in listname',
form.errors['listname'])
def test_form_fields_order(self):
form = ListNew([
("mailman.most-desirable.org", "mailman.most-desirable.org")],
{'listname': 'xyz',
'mail_host': 'mailman.most-desirable.org',
'list_owner': 'mailman@most-desirable.org',
'advertised': 'True',
'description': 'The Most Desirable organization', })
domain_choices = [
("mailman.most-desirable.org", "mailman.most-desirable.org")]
style_choices = [
("legacy-default", 'Ordinary discussion mailing list style.'),
("legacy-announce", 'Announce only mailing list style.')]
form = ListNew(domain_choices, style_choices,
{'listname': 'xyz',
'mail_host': 'mailman.most-desirable.org',
'list_owner': 'mailman@most-desirable.org',
'list_style': 'legacy-default',
'advertised': 'True',
'description': 'The Most Desirable organization', })
self.assertTrue(form.is_valid())
# The order of the fields should remain exactly like this.
self.assertEqual(list(form.fields),
['listname', 'mail_host', 'list_owner', 'advertised', 'description']) # noqa
['listname',
'mail_host',
'list_owner',
'advertised',
'list_style',
'description'])
class TestListIdentityForm(TestCase):
......
......@@ -43,7 +43,7 @@ from postorius.forms import (
ListIdentityForm, ListMassSubscription, ListMassRemoval, ListAddBanForm,
ListHeaderMatchForm, ListHeaderMatchFormset, MemberModeration,
DMARCMitigationsForm, ListAnonymousSubscribe)
from postorius.models import Domain, List, Mailman404Error
from postorius.models import Domain, List, Mailman404Error, Style
from postorius.auth.decorators import (
list_owner_required, list_moderator_required, superuser_required)
from postorius.views.generic import MailingListView
......@@ -527,6 +527,19 @@ def _get_choosable_domains(request):
return [(d.mail_host, d.mail_host) for d in domains]
def _get_choosable_styles(request):
styles = Style.objects.all()
options = [(style['name'], style['description'])
for style in styles['styles']]
# Reorder to put the default at the beginning
for style_option in options:
if style_option[0] == styles['default']:
options.remove(style_option)
options.insert(0, style_option)
break
return options
@login_required
@superuser_required
def list_new(request, template='postorius/lists/new.html'):
......@@ -542,8 +555,9 @@ def list_new(request, template='postorius/lists/new.html'):
mailing_list = None
choosable_domains = [('', _('Choose a Domain'))]
choosable_domains += _get_choosable_domains(request)
choosable_styles = _get_choosable_styles(request)
if request.method == 'POST':
form = ListNew(choosable_domains, request.POST)
form = ListNew(choosable_domains, choosable_styles, request.POST)
if form.is_valid():
# grab domain
domain = Domain.objects.get_or_404(
......@@ -551,7 +565,8 @@ def list_new(request, template='postorius/lists/new.html'):
# creating the list
try:
mailing_list = domain.create_list(
form.cleaned_data['listname'])
form.cleaned_data['listname'],
style_name=form.cleaned_data['list_style'])
mailing_list.add_owner(form.cleaned_data['list_owner'])
list_settings = mailing_list.settings
if form.cleaned_data['description']:
......@@ -569,7 +584,7 @@ def list_new(request, template='postorius/lists/new.html'):
else:
messages.error(request, _('Please check the errors below'))
else:
form = ListNew(choosable_domains, initial={
form = ListNew(choosable_domains, choosable_styles, initial={
'list_owner': request.user.email,
'advertised': True,
})
......
......@@ -2,6 +2,7 @@
envlist = py{35,36}-django{111,20,latest},pep8
[testenv]
basepython = python3
usedevelop = True
deps =
mock
......@@ -23,7 +24,6 @@ setenv =
[testenv:coverage]
basepython = python3
deps =
mock
vcrpy
......@@ -37,7 +37,6 @@ commands =
[testenv:pep8]
basepython = python3
deps =
flake8
Django>1.11,<1.12
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment