...
 
Commits (77)
......@@ -41,6 +41,16 @@ py37-django-21:
script:
- tox -e py37-django21
py36-django-22:
stage: test
script:
- tox -e py36-django22
py37-django-22:
stage: test
script:
- tox -e py37-django22
coverage:
stage: test
script:
......@@ -57,6 +67,7 @@ pep8:
git-heads:
stage: test
allow_failure: true
script:
- tox -e py36-head
......@@ -66,15 +77,18 @@ django-latest:
script:
- tox -e py36-django-latest
pages:
stage: deploy
dependencies:
- coverage
upstream-client:
stage: test
script:
- mv coverage/ public/
artifacts:
paths:
- public
expire_in: 5 days
- tox -e upstream-client
only:
variables:
- $MAILMANCLIENT_COMMIT_SHA
upstream-django-mailman3:
stage: test
script:
- tox -e upstream-django-mailman3
only:
- master
variables:
- $DJANGO_MAILMAN3_COMMIT_SHA
===================================
Postorius - web ui for GNU Mailman
Postorius - Web UI for GNU Mailman
===================================
.. image:: https://gitlab.com/mailman/postorius/badges/master/build.svg
:target: https://gitlab.com/mailman/postorius/commits/master
......@@ -37,19 +37,18 @@ along with mailman.client. If not, see <http://www.gnu.org/licenses/>.
Requirements
============
Postorius requires Python 3.5 or newer and mailmanclient, the official Python
bindings for GNU Mailman.
Postorius requires Python 3.5+.
The minimum Django version is 1.11.
Postorius needs a running version of GNU Mailman version 3.2
Postorius needs a running version of GNU Mailman version 3.3.0
Installation
============
To install GNU Mailman Suite follow the instructions in the documentation:
http://docs.mailman3.org/
https://docs.mailman3.org/
Acknowledgements
......
#! /usr/bin/env python3
import datetime
import os
import re
import sys
import stat
import datetime
import sys
FSF = 'by the Free Software Foundation, Inc.'
......
......@@ -19,6 +19,7 @@
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
......
......@@ -29,6 +29,10 @@ https://docs.djangoproject.com/en/1.9/ref/settings/
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os
# Compatibility with Bootstrap 3
from django.contrib.messages import constants as messages
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
......@@ -91,6 +95,7 @@ MIDDLEWARE = (
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django_mailman3.middleware.TimezoneMiddleware',
'postorius.middleware.PostoriusMiddleware',
)
......@@ -191,8 +196,6 @@ LOGOUT_URL = 'account_logout'
DEFAULT_FROM_EMAIL = '[email protected]'
# From Address for emails sent to admins
SERVER_EMAIL = '[email protected]'
# Compatibility with Bootstrap 3
from django.contrib.messages import constants as messages
MESSAGE_TAGS = {
messages.ERROR: 'danger'
}
......@@ -288,6 +291,7 @@ LOGGING = {
POSTORIUS_TEMPLATE_BASE_URL = "http://localhost:8000"
try:
from settings_local import *
except ImportError:
......
......@@ -22,6 +22,7 @@ Django test settings for postorius project.
from settings import *
TESTING = True
# Mailman API credentials for testing
......
......@@ -21,8 +21,8 @@ from django.conf.urls import include, url
from django.contrib import admin
from django.http import Http404
from django.urls import reverse_lazy
from django.views.generic import RedirectView
from django.views.defaults import server_error
from django.views.generic import RedirectView
def not_found(request):
......
......@@ -11,6 +11,7 @@ import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
application = get_wsgi_application()
[pytest]
addopts = --tb=short --create-db --reuse-db --ds=test_settings -s
......@@ -17,7 +17,9 @@
import re
import sys
from setuptools import setup, find_packages
from setuptools import find_packages, setup
# Calculate the version number without importing the postorius package.
with open('src/postorius/__init__.py') as fp:
......@@ -51,13 +53,15 @@ setup(
package_dir={'': 'src'},
include_package_data=True,
install_requires=[
'Django>=1.11,<2.2',
'Django>=1.11,<2.3',
'django-mailman3>=1.2.0a1',
'mailmanclient>=3.2.1'
'mailmanclient>=3.2.3a2',
'readme_renderer[md]',
],
tests_require=[
"mock",
"vcrpy",
"beautifulsoup4",
"isort",
],
)
......@@ -17,5 +17,5 @@
# Postorius. If not, see <http://www.gnu.org/licenses/>.
__version__ = '1.2.4'
__version__ = '1.3.0'
default_app_config = 'postorius.apps.PostoriusConfig'
......@@ -20,8 +20,10 @@
Authentication and authorization-related utilities.
"""
from allauth.account.models import EmailAddress
from django.utils import six
from allauth.account.models import EmailAddress
from postorius.models import Domain, List
......
......@@ -18,13 +18,22 @@
import logging
from django.utils import timezone
from postorius import __version__
logger = logging.getLogger(__name__)
# The day of the month which we celebrate as Mailman Day!
MAILMAN_DAY = 1
def postorius(request):
"""Add template variables to context.
"""
return dict(POSTORIUS_VERSION=__version__)
return dict(
POSTORIUS_VERSION=__version__,
# Mailman Day is first of every month.
mailman_day=(timezone.localtime(timezone.now()).day == MAILMAN_DAY),
)
......@@ -13,8 +13,9 @@
# serve to show the default.
import sys
import os
import sys
# add dummy settings environment variable so sphinx can import from Postorius.
os.environ['DJANGO_SETTINGS_MODULE'] = 'postorius.doc.settings'
......
......@@ -40,52 +40,52 @@ And a nginx server section to with it:
::
upstream mailman {
server unix:///run/uwsgi/mailman.sock;
}
server {
listen 80;
# TODO Replace with your domain
server_name lists.example.com;
return 301 https://$server_name$request_uri;
}
## Config for server secured with https
server {
listen 443;
# TODO Replace with your domain
server_name lists.example.com;
ssl on;
# TODO Replace with your crt and key
ssl_certificate /etc/nginx/keys/lists.example.com.crt;
ssl_certificate_key /etc/nginx/keys/lists.example.com.key;
ssl_session_timeout 5m;
ssl_ciphers 'AES128+EECDH:AES128+EDH';
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
charset utf-8;
# max upload size
client_max_body_size 75M; # adjust to taste
location /static {
# TODO Adjust to your static location
alias /srv/django/mailman/public/static;
}
# Finally, send all non-media requests to the Django server.
location / {
uwsgi_pass mailman;
include /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
}
}
upstream mailman {
server unix:///run/uwsgi/mailman.sock;
}
server {
listen 80;
# TODO Replace with your domain
server_name lists.example.com;
return 301 https://$server_name$request_uri;
}
## Config for server secured with https
server {
listen 443;
# TODO Replace with your domain
server_name lists.example.com;
ssl on;
# TODO Replace with your crt and key
ssl_certificate /etc/nginx/keys/lists.example.com.crt;
ssl_certificate_key /etc/nginx/keys/lists.example.com.key;
ssl_session_timeout 5m;
ssl_ciphers 'AES128+EECDH:AES128+EDH';
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
charset utf-8;
# max upload size
client_max_body_size 75M; # adjust to taste
location /static {
# TODO Adjust to your static location
alias /srv/django/mailman/public/static;
}
# Finally, send all non-media requests to the Django server.
location / {
uwsgi_pass mailman;
include /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
}
}
Apache with mod_wsgi
......
......@@ -58,64 +58,57 @@ dash:
# List all currently configured envs:
$ tox -l
py27-django18
py27-django19
# Test Django 1.8 on Python2.7 only:
$ tox -e py27-django18
py35-django111
py35-django20
py35-djangolatest
py36-django111
py36-django20
py36-djangolatest
py37-django111
py37-django20
py37-djangolatest
pep8
# Test Django 2.1 on Python3.7 only:
$ tox -e py37-django21
# Run only tests in ``test_address_activation``:
$ tox -- postorius.tests.test_address_activation
# You get the idea...
$ tox -e py27-django18 -- postorius.tests.test_address_activation
$ tox -e py37-django21 -- postorius.tests.test_address_activation
All test modules reside in the ``postorius/src/postorius/tests``
directory. Please have a look at the existing examples.
Mocking calls to Mailman's REST API
===================================
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
contain pre-recorded HTTP responses.
.. _`vcrpy Documentation`: https://github.com/kevin1024/vcrpy
Calling Mailman's REST API
==========================
If you write new tests, it's advisable to add a separate fixture file
for each test case, so the cached responses don't interfere with other
tests. The cassette files are stored in the
``tests/fixtures/vcr_cassettes`` directory. Check out the existing test
cases for examples.
A lot of Postorius' code involves calls to Mailman's REST API (through the
``mailmanclient`` library). Postorius' test uses `pytest`_ along with
`pytest-django`_ to run tests.
In order to record new API responses for your test case, you need to
first start the mailman core, with the API server listening on port
9001. You can use the ``example_project/mailman.cfg`` file from the
Postorius source.
Postorius uses `pytest fixtures`_ to setup Mailman Core's REST API and is
defined at ``postorius.tests.mailman_api_tests.conftest.mailman_rest_layer``. It
is set to ``autouse=True`` so, all the tests inside the module
``mailman_api_tests`` automatically use it.
.. note::
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:
::
``mailman_rest_layer`` starts up ``incoming`` runner and ``rest`` runner using
``mailman.testing.helpersTestableMaster``. It also removes all the data after
every ``TestCase`` class.
# This will only record the cassette files defined in my_new_test_module:
$ tox -e record -- postorius.tests.my_new_test_module
# This will re-record all cassette files:
$ tox -e record
.. _pytest fixtures: https://docs.pytest.org/en/latest/fixture.html
.. _pytest: https://docs.pytest.org/en/latest/contents.html
.. _pytest-django: https://pytest-django.readthedocs.io/en/latest/
View Auth
=========
View Authorization
==================
Three of Django's default User roles are relvant for Postorius:
Three of Django's default User roles are relevant for Postorius:
- Superuser: Can do everything.
- AnonymousUser: Can view list index and info pages.
......@@ -154,7 +147,7 @@ A quick example:
$ python manage.py mmclient
>>> client
<Client (user:pwd) http://localhost:8001/3.0/>
<Client (user:pwd) http://localhost:8001/3.1/>
>>> print(client.system['mailman_version'])
GNU Mailman 3.0.0b2+ (Here Again)
......
......@@ -18,6 +18,35 @@ You should have received a copy of the GNU Lesser General Public License
along with Postorius. If not, see <http://www.gnu.org/licenses/>.
1.3.0
=====
(2019-09-04)
* Fix a string substitution bug which would cause un-substituted raw string to
be exposed as notification to admin. (Closes #327)
* Add support for ``FILTER_VHOST`` option to filter MalingLists based on
``HOST`` header of incoming request. (Closes #330)
* List Summary page now renders List info as markdown. (Closes #244)
* Moderation action for held message's sender can now be set from held
message's view.(Closes #127)
* Add a 'Ban' button to list of subscription requests to help administrators
against spams. (Closes #339)
* Added support for Django 2.2.
* ``pytest`` will be used to run tests instead of default Django's test runner.
* Remove ``vcrpy`` and use fixtures to start and stop Mailman's REST API to
test against, without having to record tapes to be replayed.
* Corrected display message in 'recieve_list_copy' option in global mailman
preferences of mailman settings. (Closes #351)
* Allow setting a MailingList's Preferred Language. (Closes #303)
* Allow a empty templates as a workaround for missing settings to skip
email decoration. (Closes #331)
* Expose ``digest_volume_frequency``, ``digest_send_periodict`` and
``digests_enabled`` settings for MailingLists.
* Add a badge with count of held messages and pending subscription requests
for moderator approval. (Closes #308)
* Add support to add, view and remove domain owners.
* Allow setting the visibility options for MailingList's member list.
* Make page titles localizable.
1.2.4
=====
(2019-02-09)
......@@ -32,7 +61,7 @@ along with Postorius. If not, see <http://www.gnu.org/licenses/>.
* Expose ``max_num_recipients`` in list settings. (Closes #297)
* Add support for Non-member management in Postorius. (Closes #265)
* `Members` tab in Mailing List settings page is now called `Users`.
* ``Members`` tab in Mailing List settings page is now called ``Users``.
(Closes #309)
* Show pending subscription requests are only pending for Moderator.
(Closes #314)
......@@ -204,7 +233,7 @@ along with Postorius. If not, see <http://www.gnu.org/licenses/>.
* all code now conform to PEP8
* themes: removed obsolete MAILMAN_THEME settings from templates, contexts, file structure; contributed by Richard Wackerbarth (LP: 1043258)
* added access control for list owners and moderators
* added a mailmanclient shell to use as a `manage.py` command (`python manage.py mmclient`)
* added a mailmanclient shell to use as a ``manage.py`` command (``python manage.py mmclient``)
* use "url from future" template tag in all templates. Contributed by Richard Wackerbarth.
* added "new user" form. Contributed by George Chatzisofroniou.
* added user subscription page
......
......@@ -48,8 +48,7 @@ should install Postorius using git:
::
$ pip uninstall mailmanclient
$ pip install git+https://gitlab.com/mailman/mailmanclient.git
$ pip install -U git+https://gitlab.com/mailman/mailmanclient.git
Setup your django project
=========================
......@@ -76,7 +75,7 @@ Third, prepare the database:
$ cd example_project
$ python manage.py migrate
This will create the ``.db file`` (if you ar using SQLite) and will setup all the
This will create the ``postorius.db`` file (if you are using SQLite) and will setup all the
necessary db tables.
To create a superuser which will act as an admin account for Postorius, run the
......
......@@ -17,8 +17,8 @@
# Postorius. If not, see <http://www.gnu.org/licenses/>.
#
from postorius.forms.domain_forms import * # noqa
from postorius.forms.fields import * # noqa
from postorius.forms.member_forms import * # noqa
from postorius.forms.list_forms import * # noqa
from postorius.forms.user_forms import * # noqa
from postorius.forms.domain_forms import * # noqa
from postorius.forms.fields import * # noqa
from postorius.forms.list_forms import * # noqa
from postorius.forms.member_forms import * # noqa
from postorius.forms.user_forms import * # noqa
......@@ -18,11 +18,11 @@
from django import forms
from django.core.validators import validate_email
from django.contrib.sites.models import Site
from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from django.contrib.sites.models import Site
from postorius.forms.fields import SiteModelChoiceField
......@@ -95,3 +95,11 @@ class DomainEditForm(DomainForm):
separte from the DomainForm, so that the mail_host can't be changed.
"""
mail_host = None
class DomainOwnerForm(forms.Form):
"""Form to add a owner for a domain."""
email = forms.EmailField(
label=_("Owner's Email"),
required=True,
)
This diff is collapsed.
......@@ -108,8 +108,8 @@ class UserPreferences(forms.Form):
help_text=_(
'When you are listed explicitly in the To: or Cc: headers of a '
'list message, you can opt to not receive another copy from the '
'mailing list. Select No to receive copies. '
'Select Yes to avoid receiving copies from the mailing list'))
'mailing list. Select Yes to receive copies. '
'Select No to avoid receiving copies from the mailing list'))
class Meta:
......
# -*- coding: utf-8 -*-
# Copyright (C) 2019 by the Free Software Foundation, Inc.
#
# This file is part of Postorius.
#
# Postorius 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.
#
# 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 General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# Postorius. If not, see <http://www.gnu.org/licenses/>.
#
LANGUAGES = (
("ar", "Arabic"),
("ast", "Asturian"),
("ca", "Catalan"),
("cs", "Czech"),
("da", "Danish"),
("de", "German"),
("el", "Greek"),
("es", "Spanish"),
("et", "Estonian"),
("eu", "Euskara"),
("fi", "Finnish"),
("fr", "French"),
("gl", "Galician"),
("he", "Hebrew"),
("hr", "Croatian"),
("hu", "Hungarian"),
("ia", "Interlingua"),
("it", "Italian"),
("ja", "Japanese"),
("ko", "Korean"),
("lt", "Lithuanian"),
("nl", "Dutch"),
("no", "Norwegian"),
("pl", "Polish"),
("pt", "Protuguese"),
("pt_BR", "Protuguese (Brazil)"),
("ro", "Romanian"),
("ru", "Russian"),
("sk", "Slovak"),
("sl", "Slovenian"),
("sr", "Serbian"),
("sv", "Swedish"),
("tr", "Turkish"),
("uk", "Ukrainian"),
("vi", "Vietnamese"),
("zh_CN", "Chinese"),
("zh_TW", "Chinese (Taiwan)"),
("en", "English (USA)"),
)
af
am
an
ar
ast
az
bem
be
bg
bn
bo
br
bs
ca
[email protected]
ce
ckb
co
crh
cs
cv
cy
da
de
el
en_AU
en_CA
en_GB
eo
es
et
eu
fa
fil
fi
fo
fr_CA
fr
frp
fy
ga
gd
gl
gu
he
hi
hr
ht
hu
hy
ia
id
is
it
ja
ka
kk
kl
km
kn
ko
ku
kw
ky
la
lb
lo
lt
lv
mg
mhr
mi
ml
mr
ms
my
nb_NO
nn_NO
ne
nl
nn
oc
os
pa
pl
ps
pt_BR
pt
ro
ru
sa
sc
sd
se
shn
si
sk
sl
sq
sr
sv
sw
szl
ta
te
tg
th
ti
tr
ug
uk
ur
uz
vi
wae
zh_CN
zh_HK
zh_TW
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.