From f95c142dba6160f1229581811dc115f724664511 Mon Sep 17 00:00:00 2001 From: Joost Rijneveld Date: Sun, 31 Jul 2016 12:31:10 +0200 Subject: [PATCH 1/3] Create models for mailinglists --- website/mailinglists/__init__.py | 0 website/mailinglists/admin.py | 16 ++++++ website/mailinglists/apps.py | 5 ++ .../mailinglists/migrations/0001_initial.py | 51 +++++++++++++++++++ website/mailinglists/migrations/__init__.py | 0 website/mailinglists/models.py | 37 ++++++++++++++ website/mailinglists/views.py | 7 +++ 7 files changed, 116 insertions(+) create mode 100644 website/mailinglists/__init__.py create mode 100644 website/mailinglists/admin.py create mode 100644 website/mailinglists/apps.py create mode 100644 website/mailinglists/migrations/0001_initial.py create mode 100644 website/mailinglists/migrations/__init__.py create mode 100644 website/mailinglists/models.py create mode 100644 website/mailinglists/views.py diff --git a/website/mailinglists/__init__.py b/website/mailinglists/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/website/mailinglists/admin.py b/website/mailinglists/admin.py new file mode 100644 index 00000000..5d634bed --- /dev/null +++ b/website/mailinglists/admin.py @@ -0,0 +1,16 @@ +from django.contrib import admin + +from .models import MailingList, VerbatimAddress, ListAlias + + +class VerbatimAddressInline(admin.TabularInline): + model = VerbatimAddress + + +class ListAliasInline(admin.TabularInline): + model = ListAlias + + +@admin.register(MailingList) +class GeneralMeetingAdmin(admin.ModelAdmin): + inlines = (VerbatimAddressInline, ListAliasInline) diff --git a/website/mailinglists/apps.py b/website/mailinglists/apps.py new file mode 100644 index 00000000..5521e67d --- /dev/null +++ b/website/mailinglists/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class MailinglistsConfig(AppConfig): + name = 'mailinglists' diff --git a/website/mailinglists/migrations/0001_initial.py b/website/mailinglists/migrations/0001_initial.py new file mode 100644 index 00000000..c3759dbe --- /dev/null +++ b/website/mailinglists/migrations/0001_initial.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10b1 on 2016-07-31 10:23 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('committees', '0004_auto_20160727_2253'), + ('members', '0003_merge_20160727_2333'), + ] + + operations = [ + migrations.CreateModel( + name='ListAlias', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('alias', models.EmailField(max_length=254)), + ], + ), + migrations.CreateModel( + name='MailingList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.EmailField(max_length=254)), + ('prefix', models.CharField(max_length=200)), + ('archived', models.BooleanField(default=True)), + ('moderated', models.BooleanField(default=False)), + ('committees', models.ManyToManyField(blank=True, to='committees.Committee')), + ('members', models.ManyToManyField(blank=True, to='members.Member')), + ], + ), + migrations.CreateModel( + name='VerbatimAddress', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('address', models.EmailField(max_length=254)), + ('mailinglist', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='addresses', to='mailinglists.MailingList')), + ], + ), + migrations.AddField( + model_name='listalias', + name='mailinglist', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='aliasses', to='mailinglists.MailingList'), + ), + ] diff --git a/website/mailinglists/migrations/__init__.py b/website/mailinglists/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/website/mailinglists/models.py b/website/mailinglists/models.py new file mode 100644 index 00000000..fa61bccf --- /dev/null +++ b/website/mailinglists/models.py @@ -0,0 +1,37 @@ +from django.db import models + +from members.models import Member +from committees.models import Committee + + +class MailingList(models.Model): + name = models.EmailField() + prefix = models.CharField(max_length=200) + archived = models.BooleanField(default=True) + moderated = models.BooleanField(default=False) + members = models.ManyToManyField(Member, blank=True) + committees = models.ManyToManyField(Committee, blank=True) + + def all_addresses(self): + for member in self.members.all(): + yield member.email + + for committee in self.committees.all().prefetch_related("members"): + for member in committee.members.all(): + yield member.email + + for address in self.addresses.all(): + yield address + + def __str__(self): + return self.name + + +class VerbatimAddress(models.Model): + address = models.EmailField() + mailinglist = models.ForeignKey(MailingList, related_name='addresses') + + +class ListAlias(models.Model): + alias = models.EmailField() + mailinglist = models.ForeignKey(MailingList, related_name='aliasses') diff --git a/website/mailinglists/views.py b/website/mailinglists/views.py new file mode 100644 index 00000000..ca544c0e --- /dev/null +++ b/website/mailinglists/views.py @@ -0,0 +1,7 @@ + + +# Consider replacing this completely; +# - either by a cronjob Python script that queries the database directly +# - or by a __save__ handler that updates mailman when MailingList changes +def API(request): + pass -- GitLab From 215afc5306a3dbfcbeb5553b195ef36b6e19ed1f Mon Sep 17 00:00:00 2001 From: Joost Rijneveld Date: Fri, 5 Aug 2016 22:44:00 +0200 Subject: [PATCH 2/3] Make mailinglist API functionally equivalent This should probably be replaced again soon by something nicer, but this is functionally equivalent to what we had. --- .../migrations/0002_auto_20160805_1101.py | 25 +++++++++ .../migrations/0003_auto_20160805_1108.py | 26 ++++++++++ .../migrations/0004_auto_20160805_1113.py | 26 ++++++++++ .../migrations/0005_auto_20160805_2245.py | 26 ++++++++++ website/mailinglists/models.py | 23 +++++++-- .../templates/mailinglists/custom_list.txt | 3 ++ .../templates/mailinglists/list_data.txt | 7 +++ .../templates/mailinglists/lists.txt | 3 ++ website/mailinglists/urls.py | 7 +++ website/mailinglists/views.py | 51 ++++++++++++++++++- website/members/models.py | 6 +++ website/thaliawebsite/settings/settings.py | 1 + website/thaliawebsite/urls.py | 1 + 13 files changed, 199 insertions(+), 6 deletions(-) create mode 100644 website/mailinglists/migrations/0002_auto_20160805_1101.py create mode 100644 website/mailinglists/migrations/0003_auto_20160805_1108.py create mode 100644 website/mailinglists/migrations/0004_auto_20160805_1113.py create mode 100644 website/mailinglists/migrations/0005_auto_20160805_2245.py create mode 100644 website/mailinglists/templates/mailinglists/custom_list.txt create mode 100644 website/mailinglists/templates/mailinglists/list_data.txt create mode 100644 website/mailinglists/templates/mailinglists/lists.txt create mode 100644 website/mailinglists/urls.py diff --git a/website/mailinglists/migrations/0002_auto_20160805_1101.py b/website/mailinglists/migrations/0002_auto_20160805_1101.py new file mode 100644 index 00000000..c98f43ab --- /dev/null +++ b/website/mailinglists/migrations/0002_auto_20160805_1101.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10 on 2016-08-05 09:01 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mailinglists', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='listalias', + name='alias', + field=models.CharField(max_length=100), + ), + migrations.AlterField( + model_name='mailinglist', + name='name', + field=models.CharField(max_length=100), + ), + ] diff --git a/website/mailinglists/migrations/0003_auto_20160805_1108.py b/website/mailinglists/migrations/0003_auto_20160805_1108.py new file mode 100644 index 00000000..f7c466fd --- /dev/null +++ b/website/mailinglists/migrations/0003_auto_20160805_1108.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10 on 2016-08-05 09:08 +from __future__ import unicode_literals + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mailinglists', '0002_auto_20160805_1101'), + ] + + operations = [ + migrations.AlterField( + model_name='listalias', + name='alias', + field=models.CharField(max_length=100, validators=[django.core.validators.RegexValidator(message='Enter a [a-zA-Z0-9] name', regex='[a-zA-Z0-9]+')]), + ), + migrations.AlterField( + model_name='mailinglist', + name='name', + field=models.CharField(max_length=100, validators=[django.core.validators.RegexValidator(message='Enter a [a-zA-Z0-9] name', regex='[a-zA-Z0-9]+')]), + ), + ] diff --git a/website/mailinglists/migrations/0004_auto_20160805_1113.py b/website/mailinglists/migrations/0004_auto_20160805_1113.py new file mode 100644 index 00000000..1bff7e8e --- /dev/null +++ b/website/mailinglists/migrations/0004_auto_20160805_1113.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10 on 2016-08-05 09:13 +from __future__ import unicode_literals + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mailinglists', '0003_auto_20160805_1108'), + ] + + operations = [ + migrations.AlterField( + model_name='listalias', + name='alias', + field=models.CharField(max_length=100, validators=[django.core.validators.RegexValidator(message='Enter a [a-zA-Z0-9] name', regex='^[a-zA-Z0-9]+$')]), + ), + migrations.AlterField( + model_name='mailinglist', + name='name', + field=models.CharField(max_length=100, validators=[django.core.validators.RegexValidator(message='Enter a [a-zA-Z0-9] name', regex='^[a-zA-Z0-9]+$')]), + ), + ] diff --git a/website/mailinglists/migrations/0005_auto_20160805_2245.py b/website/mailinglists/migrations/0005_auto_20160805_2245.py new file mode 100644 index 00000000..6c3d8536 --- /dev/null +++ b/website/mailinglists/migrations/0005_auto_20160805_2245.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10rc1 on 2016-08-05 20:45 +from __future__ import unicode_literals + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('mailinglists', '0004_auto_20160805_1113'), + ] + + operations = [ + migrations.AlterField( + model_name='listalias', + name='alias', + field=models.CharField(max_length=100, validators=[django.core.validators.RegexValidator(message='Enter a simpler name', regex='^[a-zA-Z0-9]+$')]), + ), + migrations.AlterField( + model_name='mailinglist', + name='name', + field=models.CharField(max_length=100, validators=[django.core.validators.RegexValidator(message='Enter a simpler name', regex='^[a-zA-Z0-9]+$')]), + ), + ] diff --git a/website/mailinglists/models.py b/website/mailinglists/models.py index fa61bccf..93af4d0b 100644 --- a/website/mailinglists/models.py +++ b/website/mailinglists/models.py @@ -1,11 +1,18 @@ from django.db import models +from django.core import validators +from django.utils.translation import ugettext_lazy as _ from members.models import Member from committees.models import Committee class MailingList(models.Model): - name = models.EmailField() + name = models.CharField(max_length=100, + validators=[validators.RegexValidator( + regex=r'^[a-zA-Z0-9]+$', + message=_('Enter a simpler name')) + ], + ) prefix = models.CharField(max_length=200) archived = models.BooleanField(default=True) moderated = models.BooleanField(default=False) @@ -14,11 +21,11 @@ class MailingList(models.Model): def all_addresses(self): for member in self.members.all(): - yield member.email + yield member.user.email for committee in self.committees.all().prefetch_related("members"): for member in committee.members.all(): - yield member.email + yield member.user.email for address in self.addresses.all(): yield address @@ -31,7 +38,15 @@ class VerbatimAddress(models.Model): address = models.EmailField() mailinglist = models.ForeignKey(MailingList, related_name='addresses') + def __str__(self): + return self.address + class ListAlias(models.Model): - alias = models.EmailField() + alias = models.CharField(max_length=100, + validators=[validators.RegexValidator( + regex=r'^[a-zA-Z0-9]+$', + message=_('Enter a simpler name')) + ], + ) mailinglist = models.ForeignKey(MailingList, related_name='aliasses') diff --git a/website/mailinglists/templates/mailinglists/custom_list.txt b/website/mailinglists/templates/mailinglists/custom_list.txt new file mode 100644 index 00000000..e7f257e6 --- /dev/null +++ b/website/mailinglists/templates/mailinglists/custom_list.txt @@ -0,0 +1,3 @@ +START +{% for member in members %}{{ member.user.email }} +{% endfor %}END diff --git a/website/mailinglists/templates/mailinglists/list_data.txt b/website/mailinglists/templates/mailinglists/list_data.txt new file mode 100644 index 00000000..ce7c2f28 --- /dev/null +++ b/website/mailinglists/templates/mailinglists/list_data.txt @@ -0,0 +1,7 @@ +START +{{ list.name }} +{{ list.prefix }} +{{ list.archived|yesno:"1,0" }} +{{ list.moderated|yesno:"1,0" }} +{% for address in list.all_addresses %}{{ address }} +{% endfor %}END diff --git a/website/mailinglists/templates/mailinglists/lists.txt b/website/mailinglists/templates/mailinglists/lists.txt new file mode 100644 index 00000000..76262704 --- /dev/null +++ b/website/mailinglists/templates/mailinglists/lists.txt @@ -0,0 +1,3 @@ +START +{% for list in lists %}{{ list.pk }} +{% endfor %}END diff --git a/website/mailinglists/urls.py b/website/mailinglists/urls.py new file mode 100644 index 00000000..b12db577 --- /dev/null +++ b/website/mailinglists/urls.py @@ -0,0 +1,7 @@ +from django.conf.urls import include, url + +from . import views + +urlpatterns = [ + url('^', views.index, name='index'), +] diff --git a/website/mailinglists/views.py b/website/mailinglists/views.py index ca544c0e..ac989afa 100644 --- a/website/mailinglists/views.py +++ b/website/mailinglists/views.py @@ -1,7 +1,54 @@ +from django.core.exceptions import PermissionDenied +from django.shortcuts import render, get_object_or_404 +from django.http import Http404 +from .models import MailingList +from members.models import Member +from committees.models import CommitteeMembership + +import hashlib # Consider replacing this completely; # - either by a cronjob Python script that queries the database directly # - or by a __save__ handler that updates mailman when MailingList changes -def API(request): - pass +# - or at least by a more nicely separated API with less GET variables +# see issue #29 + + +def index(request): + if 'apikey' not in request.GET: + raise PermissionDenied + apihash = hashlib.sha1(request.GET['apikey'].encode('utf-8')).hexdigest() + if apihash != 'cb004452d9c80e295bebfc778871b3b082d70ad8': + raise PermissionDenied + if 'lists' in request.GET: + context = {'lists': MailingList.objects.all()} + return render(request, 'mailinglists/lists.txt', context, + content_type='text/plain') + elif 'list' in request.GET: + mailinglist = get_object_or_404(MailingList, + pk=int(request.GET['list'])) + return render(request, 'mailinglists/list_data.txt', + {'list': mailinglist}, content_type='text/plain') + elif 'membership_type' in request.GET: + membership_type = {"Current Members": "member", + "Benefactor": "supporter", + "Honorary Member": "honorary" + }[request.GET['membership_type']] + members = Member.all_with_membership(membership_type, 'user') + elif 'custom' in request.GET: + if request.GET['custom'] == 'commissievoorzitters': + memberships = (CommitteeMembership.active_memberships + .filter(chair=True) + .prefetch_related('member__user')) + members = [x.member for x in memberships] + # The intern is always included; we mock an object with a .email + members += [{'user': {'email': 'intern@thalia.nu'}}] + elif request.GET['custom'] == 'optin': + members = (Member.objects.filter(receive_optin=True) + .prefetch_related('user')) + try: + return render(request, 'mailinglists/custom_list.txt', + {'members': members}, content_type='text/plain') + except NameError: + raise Http404("Insufficient arguments") diff --git a/website/members/models.py b/website/members/models.py index f3f56c81..0f0b88d7 100644 --- a/website/members/models.py +++ b/website/members/models.py @@ -76,6 +76,12 @@ class Member(models.Model): is_active.boolean = True is_active.short_description = _('Is this user currently active') + @classmethod + def all_with_membership(cls, membership_type, prefetch=None): + return [x for x in cls.objects.all().prefetch_related(prefetch) + if x.current_membership + and x.current_membership.type == membership_type] + # ---- Address information ----- address_street = models.CharField( diff --git a/website/thaliawebsite/settings/settings.py b/website/thaliawebsite/settings/settings.py index 99d13e11..acef7519 100644 --- a/website/thaliawebsite/settings/settings.py +++ b/website/thaliawebsite/settings/settings.py @@ -48,6 +48,7 @@ INSTALLED_APPS = [ 'committees', 'photos', 'utils', + 'mailinglists', ] MIDDLEWARE = [ diff --git a/website/thaliawebsite/urls.py b/website/thaliawebsite/urls.py index 37b3fe30..c191c0cb 100644 --- a/website/thaliawebsite/urls.py +++ b/website/thaliawebsite/urls.py @@ -27,6 +27,7 @@ import members urlpatterns = [ url(r'^$', TemplateView.as_view(template_name='index.html'), name='index'), url(r'^admin/', admin.site.urls), + url(r'^mailinglists/', include('mailinglists.urls', namespace='mailinglists')), url(r'^members/', include('members.urls', namespace='members')), url(r'^nyi$', TemplateView.as_view(template_name='status/nyi.html'), name='#'), url(r'^association/', include([ -- GitLab From c916a51f68c8665e5afc59b7155dcabbf911d627 Mon Sep 17 00:00:00 2001 From: Joost Rijneveld Date: Fri, 5 Aug 2016 22:51:36 +0200 Subject: [PATCH 3/3] Cascade Foreignkeys to prevent breaking in 2.0 --- website/mailinglists/models.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/website/mailinglists/models.py b/website/mailinglists/models.py index 93af4d0b..f22118c7 100644 --- a/website/mailinglists/models.py +++ b/website/mailinglists/models.py @@ -36,7 +36,9 @@ class MailingList(models.Model): class VerbatimAddress(models.Model): address = models.EmailField() - mailinglist = models.ForeignKey(MailingList, related_name='addresses') + mailinglist = models.ForeignKey(MailingList, + on_delete=models.CASCADE, + related_name='addresses') def __str__(self): return self.address @@ -49,4 +51,6 @@ class ListAlias(models.Model): message=_('Enter a simpler name')) ], ) - mailinglist = models.ForeignKey(MailingList, related_name='aliasses') + mailinglist = models.ForeignKey(MailingList, + on_delete=models.CASCADE, + related_name='aliasses') -- GitLab