forked from ISTI-ansible-roles/ansible-role-mailman
See #616. Broken hostname in the mailman footer.
This commit is contained in:
parent
e773af529a
commit
0773fef2ac
23
README.md
23
README.md
|
|
@ -91,6 +91,29 @@ Disable this option before upgrading HyperKitty, django-mailman3, or
|
|||
mailman-hyperkitty, then review whether the compatibility patches are still
|
||||
needed with the new versions.
|
||||
|
||||
The legacy Mailman 3.3.1 decorator is also patched so the optional
|
||||
`${hyperkitty_url}` footer placeholder expands to an empty value when the
|
||||
list's archive policy is `never` or its HyperKitty archiver is disabled. The
|
||||
site-level generic footer can therefore include a permalink only for messages
|
||||
that have an active archive. A custom per-list footer still overrides this
|
||||
site default.
|
||||
|
||||
HyperKitty builds `List-Archive`, `Archived-At`, and `${hyperkitty_url}` from
|
||||
the Django Site associated with each mail domain. On installations with one
|
||||
canonical web frontend, manage that Site explicitly:
|
||||
|
||||
```yaml
|
||||
mailman_manage_postorius_site: true
|
||||
mailman_postorius_site_id: 2
|
||||
mailman_postorius_site_domain: 'mailman.example.org'
|
||||
mailman_postorius_site_name: 'Example Mailman'
|
||||
```
|
||||
|
||||
The helper refuses a domain already assigned to another Django Site and only
|
||||
updates the configured Site ID. Existing django-mailman3 mail-domain mappings
|
||||
remain attached to that Site. Preview the database change with
|
||||
`sudo -u mailman /usr/local/sbin/mailman-configure-django-site --check`.
|
||||
|
||||
## uWSGI availability safeguards
|
||||
|
||||
The Mailman web application runs with multiple uWSGI worker processes. A
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ mailman_weekly_verified_restart_start_timeout: 120
|
|||
# Compatibility fixes for the legacy HyperKitty stack. These are deliberately
|
||||
# opt-in and pinned: a package upgrade must be reviewed before patching sources.
|
||||
mailman_enable_legacy_hyperkitty_compatibility_patches: false
|
||||
mailman_legacy_core_version: '3.3.1'
|
||||
mailman_legacy_hyperkitty_version: '1.3.3'
|
||||
mailman_legacy_django_mailman3_version: '1.3.4'
|
||||
mailman_legacy_mailman_hyperkitty_version: '1.1.0'
|
||||
|
|
@ -219,6 +220,12 @@ mailman_postorius_uwsgi_reload_on_rss: 512
|
|||
mailman_postorius_uwsgi_plugins: 'systemd_logger,python36'
|
||||
# 1 is the predefined one, that must be deleted
|
||||
mailman_postorius_site_id: 2
|
||||
# Optionally keep the Django Site used by django-mailman3 aligned with the
|
||||
# canonical web frontend. HyperKitty uses this value in archive URLs.
|
||||
mailman_manage_postorius_site: false
|
||||
mailman_postorius_site_domain: 'localhost'
|
||||
mailman_postorius_site_name: 'Mailman'
|
||||
mailman_postorius_site_config_script: '/usr/local/sbin/mailman-configure-django-site'
|
||||
mailman_postorius_allowed_hosts:
|
||||
- 'localhost'
|
||||
- '{{ ansible_fqdn }}'
|
||||
|
|
|
|||
|
|
@ -38,10 +38,34 @@
|
|||
args:
|
||||
creates: '{{ mailman_postorius_dir }}/static/admin/js/actions.js'
|
||||
|
||||
- name: Install the canonical Django Site configuration helper
|
||||
ansible.builtin.template:
|
||||
src: mailman-configure-django-site.py.j2
|
||||
dest: '{{ mailman_postorius_site_config_script }}'
|
||||
owner: root
|
||||
group: '{{ mailman_user }}'
|
||||
mode: '0750'
|
||||
when: mailman_manage_postorius_site | bool
|
||||
tags: [ 'mailman_postorius_site' ]
|
||||
|
||||
- name: Configure the canonical Django Site used by archive links
|
||||
become: true
|
||||
become_user: '{{ mailman_user }}'
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- '{{ mailman_postorius_site_config_script }}'
|
||||
register: mailman_postorius_site_config_result
|
||||
changed_when: "'CHANGED:' in mailman_postorius_site_config_result.stdout"
|
||||
when:
|
||||
- mailman_manage_postorius_site | bool
|
||||
- not ansible_check_mode
|
||||
tags: [ 'mailman_postorius_site' ]
|
||||
|
||||
- name: Install the hyperkitty configuration file
|
||||
template: src=mailman-hyperkitty.cfg.j2 dest={{ mailman_conf_dir }}/mailman-hyperkitty.cfg owner=root group={{ mailman_user }} mode=0440
|
||||
when: mailman_use_hyperkitty_archiver | bool
|
||||
register: mailman_hyperkitty_install
|
||||
notify: Restart mailman
|
||||
|
||||
tags: [ 'mailman', 'postorius', 'hyperkitty', 'mailman_conf' ]
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
#!{{ mailman_bindir }}/python3
|
||||
|
||||
"""Configure the canonical Django Site used by Mailman web applications."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
PROJECT_DIR = {{ mailman_postorius_dir | to_json }}
|
||||
SITE_ID = {{ mailman_postorius_site_id | int }}
|
||||
SITE_DOMAIN = {{ mailman_postorius_site_domain | to_json }}
|
||||
SITE_NAME = {{ mailman_postorius_site_name | to_json }}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
'--check', action='store_true',
|
||||
help='report the required change without updating the database')
|
||||
arguments = parser.parse_args()
|
||||
|
||||
os.chdir(PROJECT_DIR)
|
||||
sys.path.insert(0, PROJECT_DIR)
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
|
||||
|
||||
import django
|
||||
django.setup()
|
||||
|
||||
from django.contrib.sites.models import Site
|
||||
from django.db import transaction
|
||||
|
||||
if (not SITE_DOMAIN or
|
||||
any(character in SITE_DOMAIN for character in '/: \t\r\n')):
|
||||
raise RuntimeError(
|
||||
'site domain must be a hostname without scheme or path: {!r}'.format(
|
||||
SITE_DOMAIN))
|
||||
|
||||
with transaction.atomic():
|
||||
conflicting = Site.objects.select_for_update().filter(
|
||||
domain=SITE_DOMAIN).exclude(pk=SITE_ID).first()
|
||||
if conflicting is not None:
|
||||
raise RuntimeError(
|
||||
'site domain {!r} is already assigned to Site {}'.format(
|
||||
SITE_DOMAIN, conflicting.pk))
|
||||
|
||||
site = Site.objects.select_for_update().get(pk=SITE_ID)
|
||||
changes = []
|
||||
if site.domain != SITE_DOMAIN:
|
||||
changes.append('domain {!r} -> {!r}'.format(site.domain, SITE_DOMAIN))
|
||||
site.domain = SITE_DOMAIN
|
||||
if site.name != SITE_NAME:
|
||||
changes.append('name {!r} -> {!r}'.format(site.name, SITE_NAME))
|
||||
site.name = SITE_NAME
|
||||
if changes:
|
||||
if arguments.check:
|
||||
print('WOULD CHANGE: Site {}: {}'.format(
|
||||
SITE_ID, ', '.join(changes)))
|
||||
else:
|
||||
site.save(update_fields=('domain', 'name'))
|
||||
print('CHANGED: Site {}: {}'.format(
|
||||
SITE_ID, ', '.join(changes)))
|
||||
else:
|
||||
print('OK: Site {} already uses {} ({})'.format(
|
||||
SITE_ID, SITE_DOMAIN, SITE_NAME))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except Exception as error:
|
||||
print('ERROR: {}'.format(error), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
# better if it is not.
|
||||
# However, if your Mailman installation is accessed via HTTPS, the URL needs
|
||||
# to match your SSL certificate (e.g. https://lists.example.com/hyperkitty).
|
||||
base_url: {{ mailman_site_url }}/hyperkitty/
|
||||
base_url: {{ mailman_site_url | regex_replace('/+$', '') }}/hyperkitty/
|
||||
|
||||
# Shared API key, must be the identical to the value in HyperKitty's
|
||||
# settings.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import pkg_resources
|
|||
|
||||
|
||||
EXPECTED_VERSIONS = {
|
||||
'mailman': {{ mailman_legacy_core_version | to_json }},
|
||||
'HyperKitty': {{ mailman_legacy_hyperkitty_version | to_json }},
|
||||
'django-mailman3': {{ mailman_legacy_django_mailman3_version | to_json }},
|
||||
'mailman-hyperkitty': {{ mailman_legacy_mailman_hyperkitty_version | to_json }},
|
||||
|
|
@ -157,6 +158,21 @@ MAILMAN_HYPERKITTY_SEND_NEW = ''' message_text = msg.as_bytes()
|
|||
except (MessageError, KeyError, UnicodeEncodeError) as error:
|
||||
'''
|
||||
|
||||
MAILMAN_DECORATE_IMPORT_OLD = (
|
||||
"from mailman.interfaces.handler import IHandler\n"
|
||||
)
|
||||
MAILMAN_DECORATE_IMPORT_NEW = (
|
||||
"from mailman.interfaces.archiver import ArchivePolicy\n"
|
||||
"from mailman.interfaces.handler import IHandler\n"
|
||||
)
|
||||
MAILMAN_DECORATE_DATA_OLD = " d = {}\n"
|
||||
MAILMAN_DECORATE_DATA_NEW = " d = {'hyperkitty_url': ''}\n"
|
||||
MAILMAN_DECORATE_ARCHIVER_OLD = " if archiver.is_enabled:\n"
|
||||
MAILMAN_DECORATE_ARCHIVER_NEW = (
|
||||
" if (archiver.is_enabled and\n"
|
||||
" mlist.archive_policy is not ArchivePolicy.never):\n"
|
||||
)
|
||||
|
||||
HYPERKITTY_TASKS_WITH_MAILING_LIST = (
|
||||
'_rebuild_mailinglist_cache_recent',
|
||||
'_rebuild_mailinglist_cache_for_month',
|
||||
|
|
@ -270,6 +286,33 @@ def patch_mailman_hyperkitty(source):
|
|||
'mailman-hyperkitty byte delivery'), True
|
||||
|
||||
|
||||
def patch_mailman_decorate(source):
|
||||
"""Make the HyperKitty footer placeholder safely optional."""
|
||||
updated = source
|
||||
changed = False
|
||||
|
||||
if MAILMAN_DECORATE_IMPORT_NEW not in updated:
|
||||
updated = replace_once(
|
||||
updated, MAILMAN_DECORATE_IMPORT_OLD, MAILMAN_DECORATE_IMPORT_NEW,
|
||||
'Mailman decorate ArchivePolicy import')
|
||||
changed = True
|
||||
|
||||
replacements = (
|
||||
(MAILMAN_DECORATE_DATA_OLD, MAILMAN_DECORATE_DATA_NEW,
|
||||
'Mailman decorate optional HyperKitty placeholder'),
|
||||
(MAILMAN_DECORATE_ARCHIVER_OLD, MAILMAN_DECORATE_ARCHIVER_NEW,
|
||||
'Mailman decorate archive-policy check'),
|
||||
)
|
||||
for old, new, description in replacements:
|
||||
if new in updated:
|
||||
if old in updated:
|
||||
raise RuntimeError('{} is ambiguous'.format(description))
|
||||
continue
|
||||
updated = replace_once(updated, old, new, description)
|
||||
changed = True
|
||||
return updated, changed
|
||||
|
||||
|
||||
def patch_hyperkitty_tasks(source):
|
||||
"""Backport the upstream stale-list guards to all affected tasks."""
|
||||
updated = source
|
||||
|
|
@ -351,6 +394,7 @@ def quarantine_catalog(path):
|
|||
|
||||
|
||||
def main():
|
||||
mailman = distribution('mailman')
|
||||
hyperkitty = distribution('HyperKitty')
|
||||
django_mailman3 = distribution('django-mailman3')
|
||||
mailman_hyperkitty = distribution('mailman-hyperkitty')
|
||||
|
|
@ -361,18 +405,23 @@ def main():
|
|||
django_mailman3.location, 'django_mailman3', 'lib', 'scrub.py')
|
||||
mailman_hyperkitty_path = os.path.join(
|
||||
mailman_hyperkitty.location, 'mailman_hyperkitty', '__init__.py')
|
||||
mailman_decorate_path = os.path.join(
|
||||
mailman.location, 'mailman', 'handlers', 'decorate.py')
|
||||
|
||||
incoming_source, incoming_changed = patch_hyperkitty(load_source(incoming_path))
|
||||
tasks_source, tasks_changed = patch_hyperkitty_tasks(load_source(tasks_path))
|
||||
scrub_source, scrub_changed = patch_scrubber(load_source(scrub_path))
|
||||
mailman_hyperkitty_source, mailman_hyperkitty_changed = (
|
||||
patch_mailman_hyperkitty(load_source(mailman_hyperkitty_path)))
|
||||
mailman_decorate_source, mailman_decorate_changed = (
|
||||
patch_mailman_decorate(load_source(mailman_decorate_path)))
|
||||
|
||||
# Compile all transformed files before replacing any one of them.
|
||||
compile(incoming_source, incoming_path, 'exec')
|
||||
compile(tasks_source, tasks_path, 'exec')
|
||||
compile(scrub_source, scrub_path, 'exec')
|
||||
compile(mailman_hyperkitty_source, mailman_hyperkitty_path, 'exec')
|
||||
compile(mailman_decorate_source, mailman_decorate_path, 'exec')
|
||||
|
||||
try:
|
||||
import django_extensions
|
||||
|
|
@ -393,13 +442,17 @@ def main():
|
|||
if mailman_hyperkitty_changed:
|
||||
atomic_write(mailman_hyperkitty_path, mailman_hyperkitty_source)
|
||||
print('CHANGED: patched {}'.format(mailman_hyperkitty_path))
|
||||
if mailman_decorate_changed:
|
||||
atomic_write(mailman_decorate_path, mailman_decorate_source)
|
||||
print('CHANGED: patched {}'.format(mailman_decorate_path))
|
||||
for catalog_path, error in catalogs:
|
||||
destination = quarantine_catalog(catalog_path)
|
||||
print('CHANGED: quarantined {} as {} ({})'.format(
|
||||
catalog_path, destination, error))
|
||||
|
||||
if (not incoming_changed and not tasks_changed and not scrub_changed and
|
||||
not mailman_hyperkitty_changed and not catalogs):
|
||||
not mailman_hyperkitty_changed and not mailman_decorate_changed and
|
||||
not catalogs):
|
||||
print('OK: compatibility fixes are already applied')
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@
|
|||
$display_name mailing list
|
||||
$listname
|
||||
{{ mailman_site_url }}postorius/lists/$list_id/
|
||||
${hyperkitty_url}
|
||||
|
|
|
|||
|
|
@ -2,3 +2,4 @@
|
|||
$display_name mailing list
|
||||
$listname
|
||||
{{ mailman_site_url }}postorius/lists/$list_id/
|
||||
${hyperkitty_url}
|
||||
|
|
|
|||
Loading…
Reference in New Issue