diff --git a/selfserv/settings.example.py b/selfserv/settings.example.py index 417de37..f02d9e3 100644 --- a/selfserv/settings.example.py +++ b/selfserv/settings.example.py @@ -25,6 +25,7 @@ DEBUG = True ALLOWED_HOSTS = [] +ORG_NAME = 'Society' PROMO_LOGO_URL = 'https://placehold.it/2000x500' PROMO_LOGO_LINK = 'https://example.com' PROMO_GROUPS_MANDATORY = ['All Years'] @@ -121,6 +122,7 @@ AUTHENTICATION_BACKENDS = ( ) LOGIN_URL = 'login' LOGIN_REDIRECT_URL = 'index' + SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = None # FIXME SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = None # FIXME #SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {'hd': 'monash.edu'} # To restrict to a particular domain @@ -128,6 +130,11 @@ SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = None # FIXME GOOGLE_API_KEY = None # FIXME GOOGLE_CALENDAR_ID = None # FIXME +AWS_KEY_ID = None # FIXME +AWS_SECRET = None # FIXME +AWS_REGION = 'us-east-1' +AWS_SENDER_EMAIL = 'postmaster@example.com' # FIXME + RATELIMIT_KEY = 'ip' # https://django-ratelimit.readthedocs.io/en/stable/keys.html#common-keys e.g. 'header:CF-Connecting-IP' SOCIAL_AUTH_PIPELINE = ( diff --git a/ssmembership/jinja2/ssmembership/email/base.html b/ssmembership/jinja2/ssmembership/email/base.html new file mode 120000 index 0000000..b594f4c --- /dev/null +++ b/ssmembership/jinja2/ssmembership/email/base.html @@ -0,0 +1 @@ +../../../../sspromotions/jinja2/sspromotions/email/base.html \ No newline at end of file diff --git a/ssmembership/jinja2/ssmembership/email/import.html b/ssmembership/jinja2/ssmembership/email/import.html new file mode 100644 index 0000000..63463e8 --- /dev/null +++ b/ssmembership/jinja2/ssmembership/email/import.html @@ -0,0 +1,59 @@ +{% extends 'ssmembership/email/base.html' %} + +{# + Society Self-Service + Copyright © 2018-2019 Yingtong Li (RunasSudo) + + Design by SendWithUs (Apache 2.0 licence) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +#} + +{% block content %} + + + + + + + +
+
Membership renewal
+

Dear {{ name }},

+

From 2019, {{ import('django.conf').settings.ORG_NAME }} is required by law to review its membership annually. You can renew your membership for free by clicking the button below or visiting {{ baseurl }}{{ url('mimport_index') }}. The process is very quick and should take less than a minute.

+

By making sure your details are up to date, you'll also be able to receive personalised weekly emails with relevant news and events from around the Monash Medicine community.

+ +
+

If you do not renew your membership by 20 March 2019, your membership will expire, and you will not be able to buy tickets to {{ import('django.conf').settings.ORG_NAME }} events at member prices or run for election within {{ import('django.conf').settings.ORG_NAME }} without paying a membership fee. Please make sure to renew your membership by 20 March 2019 to avoid this.

+

If you do not want to renew your membership, or you are no longer a Monash medical student, simply ignore this email.

+

If you encounter any issues renewing your membership, or have any other questions, please contact the Secretary, Yingtong Li, at {{ import('django.conf').settings.AWS_SENDER_EMAIL }}.

+

Please note that emails are being sent in stages. If other students have not received this email, please let them know that this is normal, and they should receive their email within 7 days. Otherwise, contact {{ import('django.conf').settings.AWS_SENDER_EMAIL }}.

+
+ + + + + + + + + +
+   +
+ + +{% endblock content %} diff --git a/ssmembership/jinja2/ssmembership/email/import.txt b/ssmembership/jinja2/ssmembership/email/import.txt new file mode 100644 index 0000000..235e8ef --- /dev/null +++ b/ssmembership/jinja2/ssmembership/email/import.txt @@ -0,0 +1,35 @@ +{# + Society Self-Service + Copyright © 2018-2019 Yingtong Li (RunasSudo) + + Design by SendWithUs (Apache 2.0 licence) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +#} +Dear {{ name }}, + +From 2019, {{ import('django.conf').settings.ORG_NAME }} is required by law to review its membership annually. You can renew your membership for free by going to the link below or visiting {{ baseurl }}{{ url('mimport_index') }}. The process is very quick and should take less than a minute. + +By making sure your details are up to date, you'll also be able to receive personalised weekly emails with relevant news and events from around the Monash Medicine community. + +Renew membership for free: +{{ baseurl }}{{ renew_url }} + +If you do not renew your membership by **20 March 2019**, your membership will expire, and you will not be able to buy tickets to {{ import('django.conf').settings.ORG_NAME }} events at member prices or run for election within {{ import('django.conf').settings.ORG_NAME }} without paying a membership fee. Please make sure to renew your membership by 20 March 2019 to avoid this. + +If you do not want to renew your membership, or you are no longer a Monash medical student, simply ignore this email. + +If you encounter any issues renewing your membership, or have any other questions, please contact the Secretary, Yingtong Li, at {{ import('django.conf').settings.AWS_SENDER_EMAIL }}. + +Please note that emails are being sent in stages. If other students have not received this email, please let them know that this is normal, and they should receive their email within 7 days. Otherwise, contact {{ import('django.conf').settings.AWS_SENDER_EMAIL }}. diff --git a/ssmembership/jinja2/ssmembership/import/review.html b/ssmembership/jinja2/ssmembership/import/review.html index 3c692e1..da88908 100644 --- a/ssmembership/jinja2/ssmembership/import/review.html +++ b/ssmembership/jinja2/ssmembership/import/review.html @@ -69,13 +69,13 @@
-

Bulletin subscriptions

+

MUMUS Mail

@@ -91,7 +91,7 @@
{% endif %} {% endfor %} -

The MUMUS Bulletin is now personalised. Choose the groups that you would like to see first in your weekly email bulletin.

+

MUMUS Mail is personalised for you. Choose the groups that you would like to see first in each edition of MUMUS Mail.

diff --git a/ssmembership/management/commands/sendimportemail.py b/ssmembership/management/commands/sendimportemail.py new file mode 100644 index 0000000..3f794fa --- /dev/null +++ b/ssmembership/management/commands/sendimportemail.py @@ -0,0 +1,107 @@ +# Society Self-Service +# Copyright © 2018-2019 Yingtong Li (RunasSudo) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import boto3 +from botocore.exceptions import ClientError +import premailer + +from django.core.management.base import BaseCommand, CommandError + +from django.conf import settings +from django.template import loader +from django.urls import reverse + +from ssmembership.mimport import get_members + +import hmac +import logging +import time +import urllib.parse + +class Command(BaseCommand): + help = 'Send emails for membership import (renewal)' + + def add_arguments(self, parser): + parser.add_argument('ids', nargs='*', type=int, help='Members with ID numbers equal to these values will be emailed (default all)') + parser.add_argument('--render', action='store_true', help='Render to stdout instead of sending emails') + + def handle(self, *args, **options): + template_html = loader.get_template('ssmembership/email/import.html') + template_txt = loader.get_template('ssmembership/email/import.txt') + + members = get_members() + + if len(options['ids']) > 0: + members = [member for member in members if member[0] in options['ids']] + else: + raise Exception('Must provide IDs') + + client = boto3.client('ses', aws_access_key_id=settings.AWS_KEY_ID, aws_secret_access_key=settings.AWS_SECRET, region_name=settings.AWS_REGION) + + def send_mail(**kwargs): + for i in range(0, 10): + try: + client.send_email(**kwargs) + return + except ClientError as e: + if e['Error']['Code'] == 'Throttling' and e['Error']['Message'] == 'Maximum sending rate exceeded.': + wait_time = max(10*(2**i), 5000) + self.stdout.write(self.style.NOTICE('Reached maximum sending rate, waiting {} ms'.format(wait_time))) + time.sleep(wait_time/1000) + else: + raise e + raise Exception('Reached maximum number of retries') + + for member in members: + #_id, student_id, email, first_name, last_name, year, is_msa, phone, date + sig = hmac.new(settings.SECRET_KEY_MEMBERSIG.encode('utf-8'), member[2].encode('utf-8'), 'sha256').hexdigest() + renew_url = reverse('mimport_signed') + '?' + urllib.parse.urlencode({'email': member[2], 'sig': sig}) + + template_args = { + 'name': member[3].strip() + ' ' + member[4].strip(), + 'renew_url': renew_url, + 'baseurl': 'https://' + settings.ALLOWED_HOSTS[0] + } + + content_html = premailer.Premailer(template_html.render(template_args), cssutils_logging_level=logging.ERROR).transform() + content_txt = template_txt.render(template_args) + + if options['render']: + self.stdout.write(content_html) + else: + self.stdout.write('Emailing {} at {}'.format(member[0], member[2])) + send_mail( + Destination={ + 'ToAddresses': [member[2]], + }, + Message={ + 'Body': { + 'Html': { + 'Charset': 'utf-8', + 'Data': content_html, + }, + 'Text': { + 'Charset': 'utf-8', + 'Data': content_txt, + }, + }, + 'Subject': { + 'Charset': 'utf-8', + 'Data': settings.ORG_NAME + ' membership renewal', + }, + }, + Source='{} <{}>'.format(settings.ORG_NAME, settings.AWS_SENDER_EMAIL), + ) diff --git a/ssmembership/mimport.py b/ssmembership/mimport.py index 15981da..c08def1 100644 --- a/ssmembership/mimport.py +++ b/ssmembership/mimport.py @@ -22,6 +22,16 @@ from . import models import datetime +def get_members(): + conn = sqlite3.connect('file:members.db?mode=ro', uri=True) + cur = conn.cursor() + + cur.execute('SELECT * FROM members') + result = cur.fetchall() + conn.close() + + return result + def by_email(email): conn = sqlite3.connect('file:members.db?mode=ro', uri=True) cur = conn.cursor()