From b1e8a61265f2c02ea7a8c8455abc8ff8674dd1bb Mon Sep 17 00:00:00 2001 From: Barsha Thakuri <47474980+barshathakuri@users.noreply.github.com> Date: Wed, 4 Dec 2024 21:12:07 +0545 Subject: [PATCH] Feature/subscription improvements (#105) * Update subscriptions email template - Add helper script to create fake alerts - Remove legacy templates - Add helper dev views to update templates * Fix email templates * Fix email raw text templates - Truncate alert admins to 40 chars * Add error catch - Update TODOs - Upgrade pyright --------- Co-authored-by: thenav56 --- .pre-commit-config.yaml | 2 +- .../management/commands/create_fake_alerts.py | 128 +++++++++++++ apps/subscription/emails.py | 107 +++++++---- apps/subscription/views.py | 36 ---- apps/user/emails.py | 36 ++-- apps/user/templates/email_reset.html | 24 --- apps/user/templates/email_verification.html | 15 -- apps/user/templates/new_email_verify.html | 24 --- apps/user/templates/password_reset.html | 24 --- main/permalinks.py | 8 + main/settings.py | 8 +- main/urls.py | 12 +- main/views.py | 67 +++++++ static/images/alert-hub.png | Bin 0 -> 27779 bytes static/images/go-logo-long.png | Bin 21246 -> 0 bytes static/images/open-in-new-tab.png | Bin 0 -> 4040 bytes templates/emails/base.html | 175 +++++++++++++++--- templates/emails/base.txt | 19 +- templates/emails/subscription/body.html | 121 ++++++++++-- templates/emails/subscription/body.txt | 15 ++ templates/emails/user/activation/body.html | 47 +++-- templates/emails/user/activation/body.txt | 9 +- .../emails/user/password_changed/body.html | 98 +++++----- .../emails/user/password_changed/body.txt | 20 +- .../emails/user/password_reset/body.html | 85 +++++---- templates/emails/user/password_reset/body.txt | 29 +-- 26 files changed, 735 insertions(+), 374 deletions(-) create mode 100644 apps/cap_feed/management/commands/create_fake_alerts.py delete mode 100644 apps/subscription/views.py delete mode 100644 apps/user/templates/email_reset.html delete mode 100644 apps/user/templates/email_verification.html delete mode 100644 apps/user/templates/new_email_verify.html delete mode 100644 apps/user/templates/password_reset.html create mode 100644 main/views.py create mode 100644 static/images/alert-hub.png delete mode 100644 static/images/go-logo-long.png create mode 100644 static/images/open-in-new-tab.png diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0b370c7d..6ea02128 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,6 +31,6 @@ repos: - id: flake8 - repo: https://github.com/RobertCraigie/pyright-python - rev: v1.1.389 + rev: v1.1.390 hooks: - id: pyright diff --git a/apps/cap_feed/management/commands/create_fake_alerts.py b/apps/cap_feed/management/commands/create_fake_alerts.py new file mode 100644 index 00000000..132dd22a --- /dev/null +++ b/apps/cap_feed/management/commands/create_fake_alerts.py @@ -0,0 +1,128 @@ +import factory +from django.conf import settings +from django.core.management.base import BaseCommand +from django.utils import timezone + +from apps.cap_feed.factories import ( + Admin1Factory, + AlertFactory, + AlertInfoFactory, + CountryFactory, + FeedFactory, + RegionFactory, +) +from apps.cap_feed.models import Admin1, AlertInfo, Country, Feed, Region +from apps.subscription.factories import UserAlertSubscriptionFactory +from apps.subscription.models import UserAlertSubscription +from apps.subscription.tasks import process_pending_subscription_alerts +from apps.user.models import User + +SUBSCRIPTION_FILTERSET = [ + ( + "Sub-1", + dict( + filter_alert_urgencies=[AlertInfo.Urgency.IMMEDIATE], + filter_alert_severities=[], + filter_alert_certainties=[], + filter_alert_categories=[], + ), + ), + ( + "Sub-1", + dict( + filter_alert_urgencies=[AlertInfo.Urgency.IMMEDIATE, AlertInfo.Urgency.EXPECTED], + filter_alert_severities=[AlertInfo.Severity.MODERATE], + filter_alert_certainties=[], + filter_alert_categories=[], + ), + ), + ( + "Sub-3", + dict( + filter_alert_urgencies=[AlertInfo.Urgency.EXPECTED], + filter_alert_severities=[AlertInfo.Severity.MODERATE], + filter_alert_certainties=[AlertInfo.Certainty.LIKELY], + filter_alert_categories=[], + ), + ), + # Everything + ( + "All", + dict( + filter_alert_urgencies=[], + filter_alert_severities=[], + filter_alert_certainties=[], + filter_alert_categories=[], + ), + ), +] + + +class Command(BaseCommand): + def add_arguments(self, parser): + parser.add_argument('--user-email', dest='user_email', required=True) + + def handle(self, *_, **kwargs): + if not settings.ALLOW_FAKE_DATA: + self.stdout.write( + self.style.WARNING( + "Add ALLOW_FAKE_DATA=true and DJANGO_DEBUG=true to environment variable to allow fake data generation" + ) + ) + return + + user_email = kwargs['user_email'] + user = User.objects.get(email=user_email) + + FAKE_REGION_NAME = "[Fake] Asia" + FAKE_COUNTRY_NAME = "[Fake] Nepal" + FAKE_ADMIN1_NAME = "[Fake] Bagmati" + FAKE_FEED_URL = "https://fake-feed.com/123" + + if (r_asia := Region.objects.filter(name=FAKE_REGION_NAME).first()) is None: + r_asia = RegionFactory.create(name=FAKE_REGION_NAME) + if (c_nepal := Country.objects.filter(name=FAKE_COUNTRY_NAME).first()) is None: + c_nepal = CountryFactory.create(name=FAKE_COUNTRY_NAME, region=r_asia) + if (ad1_bagmati := Admin1.objects.filter(name=FAKE_ADMIN1_NAME).first()) is None: + ad1_bagmati = Admin1Factory.create(name=FAKE_ADMIN1_NAME, country=c_nepal) + + if (feed1 := Feed.objects.filter(url=FAKE_FEED_URL).first()) is None: + feed1 = FeedFactory.create(url=FAKE_FEED_URL, country=c_nepal, polling_interval=False) + + random_id = timezone.now().isoformat() + alert_list = AlertFactory.create_batch( + 50, + url=factory.Sequence(lambda n: f"https://alert-{random_id}-{n}.com/test"), + feed=feed1, + country=c_nepal, + admin1s=[ad1_bagmati], + sent=timezone.now().date(), + ) + + alert_info_iterator = dict( + category=factory.Iterator(AlertInfo.Category.choices, getter=lambda c: c[0]), + urgency=factory.Iterator(AlertInfo.Urgency.choices, getter=lambda c: c[0]), + severity=factory.Iterator(AlertInfo.Severity.choices, getter=lambda c: c[0]), + certainty=factory.Iterator(AlertInfo.Certainty.choices, getter=lambda c: c[0]), + ) + for alert in alert_list: + AlertInfoFactory.create_batch( + 2, + event=factory.Sequence(lambda n: f"Event-{n}"), + alert=alert, + **alert_info_iterator, + ) + + # Delete all existing fake subscriptions for this user + UserAlertSubscription.objects.filter(user=user, name__startswith="[Fake]").delete() + for name, filters in SUBSCRIPTION_FILTERSET: + UserAlertSubscriptionFactory.create( + name=f"[Fake] {name}", + email_frequency=UserAlertSubscription.EmailFrequency.DAILY, + user=user, + filter_alert_country=c_nepal, + filter_alert_admin1s=[ad1_bagmati.pk], + **filters, + ) + # Tag new alerts to subscriptions + process_pending_subscription_alerts() diff --git a/apps/subscription/emails.py b/apps/subscription/emails.py index ce71941e..b9b66d58 100644 --- a/apps/subscription/emails.py +++ b/apps/subscription/emails.py @@ -1,5 +1,7 @@ +import logging from datetime import timedelta +from django.conf import settings from django.db import models from django.utils import timezone from django.utils.encoding import force_bytes @@ -9,8 +11,11 @@ from apps.user.models import EmailNotificationType, User from main.permalinks import Permalink from main.tokens import TokenManager +from utils.common import logger_log_extra from utils.emails import send_email +logger = logging.getLogger(__name__) + def generate_unsubscribe_user_alert_subscription_url(subscription: UserAlertSubscription) -> str: uid = urlsafe_base64_encode(force_bytes(subscription.pk)) @@ -22,7 +27,7 @@ def generate_unsubscribe_user_alert_subscription_url(subscription: UserAlertSubs def generate_user_alert_subscription_email_context( user: User, email_frequency: UserAlertSubscription.EmailFrequency, -) -> tuple[dict, models.QuerySet[UserAlertSubscription]]: +) -> tuple[bool, dict, models.QuerySet[UserAlertSubscription]]: # NOTE: Number of subscription is static and less than UserAlertSubscription.LIMIT_PER_USER subscription_qs = UserAlertSubscription.objects.filter(user=user, email_frequency=email_frequency) @@ -31,62 +36,90 @@ def generate_user_alert_subscription_email_context( elif email_frequency == UserAlertSubscription.EmailFrequency.WEEKLY: from_datetime_threshold = timezone.now() - timedelta(days=7) elif email_frequency == UserAlertSubscription.EmailFrequency.MONTHLY: - # TODO: Calculate days instead of using 30 days + # TODO: Calculate month days instead of using 30 days from_datetime_threshold = timezone.now() - timedelta(days=30) - subscription_data = [ - { - 'subscription': subscription, - 'unsubscribe_url': generate_unsubscribe_user_alert_subscription_url(subscription), - 'latest_alerts': [ - subscription_alert.alert - # NOTE: N+1 query, but N < 10 for now - # TODO: Index/partition alert__sent column? - for subscription_alert in ( - SubscriptionAlert.objects.select_related('alert') - .filter( - subscription=subscription, - alert__sent__gte=from_datetime_threshold, - ) - .order_by('-alert__sent')[:5] - ) - ], + def _alert_data(alert): + # TODO: Fix N+1 for alert.infos.first() and alert.admin1s + info = alert.infos.first() + return { + "url": Permalink.alert_detail(alert.pk), + "name": info and info.event or f"Alert #{alert.pk}", + "urgency": info and info.urgency or '-', + "severity": info and info.severity or '-', + "certainty": info and info.certainty or '-', + "admins": ",".join(list(alert.admin1s.values_list("name", flat=True))) or '-', } - for subscription in subscription_qs - ] + + subscription_data = [] + for subscription in subscription_qs.iterator(): + latest_alerts = [ + _alert_data(subscription_alert.alert) + # NOTE: N+1 query, but N < 10 for now + # TODO: Index/partition alert__sent column? + for subscription_alert in ( + SubscriptionAlert.objects.select_related('alert') + .filter( + subscription=subscription, + alert__sent__gte=from_datetime_threshold, + ) + .order_by('-alert__sent')[:5] + ) + ] + if latest_alerts: + subscription_data.append( + { + 'subscription': subscription, + 'url': Permalink.subscription_detail(subscription.pk), + 'unsubscribe_url': generate_unsubscribe_user_alert_subscription_url(subscription), + 'latest_alerts': latest_alerts, + } + ) context = { - 'subscriptions': subscription_data, + 'subscriptions_data': subscription_data, } - return context, subscription_qs + return len(context["subscriptions_data"]) > 0, context, subscription_qs def send_user_alert_subscription_email(user: User, email_frequency: UserAlertSubscription.EmailFrequency): - context, subscription_qs = generate_user_alert_subscription_email_context(user, email_frequency) + have_data, context, subscription_qs = generate_user_alert_subscription_email_context(user, email_frequency) sent_at = timezone.now() - send_email( - user=user, - email_type=EmailNotificationType.ALERT_SUBSCRIPTIONS, - subject="Daily Alerts", # TODO: Is this fine? - email_html_template='emails/subscription/body.html', - email_text_template='emails/subscription/body.txt', - context=context, - ) + if have_data: + send_email( + user=user, + email_type=EmailNotificationType.ALERT_SUBSCRIPTIONS, + subject=f"{settings.EMAIL_SUBJECT_PREFIX} {email_frequency.label}", + email_html_template='emails/subscription/body.html', + email_text_template='emails/subscription/body.txt', + context=context, + ) # Post action subscription_qs.update(email_last_sent_at=sent_at) def send_user_alert_subscriptions_email(email_frequency: UserAlertSubscription.EmailFrequency): - # TODO: Send in parallel if email service supports it + # TODO: Send in parallel if email service supports it? users_qs = User.objects.filter( id__in=UserAlertSubscription.objects.filter(email_frequency=email_frequency).values('user'), ) - # TODO: Handle failure for user in users_qs.iterator(): - # TODO: Trigger this as cronjob - # TODO: Pass timezone.now for ref time - send_user_alert_subscription_email(user, email_frequency) + # TODO: Trigger this as cronjob? + # TODO: Pass timezone.now for ref time? + try: + send_user_alert_subscription_email(user, email_frequency) + except Exception: + logger.error( + "Subscription: Failed to send email to user", + exc_info=True, + extra=logger_log_extra( + { + 'user_id': user.pk, + 'email_frequency': email_frequency, + } + ), + ) diff --git a/apps/subscription/views.py b/apps/subscription/views.py deleted file mode 100644 index 163873c9..00000000 --- a/apps/subscription/views.py +++ /dev/null @@ -1,36 +0,0 @@ -from django.contrib.auth.decorators import login_required -from django.http import HttpResponse -from django.template import loader - -from .emails import generate_user_alert_subscription_email_context -from .models import UserAlertSubscription - -USER_ALERT_SUBSCRIPTION_EMAIL_PREVIEW_MESSAGE = """ - To use email_frequency in GET params, Please specify integer values. Default is Daily
- Use this for reference

- """ + '
'.join( - [f"{frequency.label}: {frequency.value}" for frequency in UserAlertSubscription.EmailFrequency] -) - - -@login_required -def user_alert_subscription_email_preview(request): - try: - email_frequency = int( - request.GET.get( - "email_frequency", - UserAlertSubscription.EmailFrequency.DAILY, - ) - ) - if email_frequency not in UserAlertSubscription.EmailFrequency: - return HttpResponse(USER_ALERT_SUBSCRIPTION_EMAIL_PREVIEW_MESSAGE) - email_frequency = UserAlertSubscription.EmailFrequency(email_frequency) - except ValueError: - return HttpResponse(USER_ALERT_SUBSCRIPTION_EMAIL_PREVIEW_MESSAGE) - - context, _ = generate_user_alert_subscription_email_context( - request.user, - email_frequency, - ) - template = loader.get_template("emails/subscription/body.html") - return HttpResponse(template.render(context, request)) diff --git a/apps/user/emails.py b/apps/user/emails.py index e01b1f03..f6750f1a 100644 --- a/apps/user/emails.py +++ b/apps/user/emails.py @@ -1,4 +1,4 @@ -from django.utils import timezone +from django.conf import settings from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode @@ -17,14 +17,14 @@ def send_account_activation(user: User): uid = urlsafe_base64_encode(force_bytes(user.pk)) token = TokenManager.account_activation_token_generator.make_token(user) context = { - 'activation_url': Permalink.user_activation(uid, token), + "activation_url": Permalink.user_activation(uid, token), } send_email( user=user, email_type=EmailNotificationType.ACCOUNT_ACTIVATION, - subject="Account Activation", - email_html_template='emails/user/activation/body.html', - email_text_template='emails/user/activation/body.txt', + subject=f"{settings.EMAIL_SUBJECT_PREFIX} Account Activation", + email_html_template="emails/user/activation/body.html", + email_text_template="emails/user/activation/body.txt", context=context, ) @@ -41,17 +41,16 @@ def send_password_reset( uid = urlsafe_base64_encode(force_bytes(user.pk)) token = TokenManager.password_reset_token_generator.make_token(user) context = { - 'time': timezone.now(), - 'location': client_ip, - 'device': device_type, - 'password_reset_url': Permalink.user_password_reset(uid, token), + "location": client_ip, + "device": device_type, + "password_reset_url": Permalink.user_password_reset(uid, token), } send_email( user=user, email_type=EmailNotificationType.PASSWORD_RESET, - subject="Alert Hub: Password Reset", - email_html_template='emails/user/password_reset/body.html', - email_text_template='emails/user/password_reset/body.txt', + subject=f"{settings.EMAIL_SUBJECT_PREFIX} Password Reset", + email_html_template="emails/user/password_reset/body.html", + email_text_template="emails/user/password_reset/body.txt", context=context, ) return uid, token @@ -59,16 +58,15 @@ def send_password_reset( def send_password_changed_notification(user, client_ip, device_type): context = { - 'time': timezone.now(), - 'location': client_ip, - 'device': device_type, - 'frontend_forgot_password': Permalink.FORGOT_PASSWORD, + "location": client_ip, + "device": device_type, + "frontend_forgot_password": Permalink.FORGOT_PASSWORD, } send_email( user=user, email_type=EmailNotificationType.PASSWORD_CHANGED, - subject='Alert Hub: Password Changed', - email_html_template='emails/user/password_changed/body.html', - email_text_template='emails/user/password_changed/body.txt', + subject=f"{settings.EMAIL_SUBJECT_PREFIX} Password Changed", + email_html_template="emails/user/password_changed/body.html", + email_text_template="emails/user/password_changed/body.txt", context=context, ) diff --git a/apps/user/templates/email_reset.html b/apps/user/templates/email_reset.html deleted file mode 100644 index b238c5c7..00000000 --- a/apps/user/templates/email_reset.html +++ /dev/null @@ -1,24 +0,0 @@ - - - -

Hello {{ user.email }},

- -

-Here is your verification code to change your email: -

- -

-{{ reset_token }} -

- -

-If you did not request this email, you can ignore it. -

- -

-Best,
-IFRC Alert hub -

- - - diff --git a/apps/user/templates/email_verification.html b/apps/user/templates/email_verification.html deleted file mode 100644 index eb913a47..00000000 --- a/apps/user/templates/email_verification.html +++ /dev/null @@ -1,15 +0,0 @@ - - - -

Confirm your email address.

- -

-Welcome to IFRC Alert Hub. Use this verification code to complete sign up. -

- -

- Verification Code: {{ verification_token }} -

- - - diff --git a/apps/user/templates/new_email_verify.html b/apps/user/templates/new_email_verify.html deleted file mode 100644 index f96e0c04..00000000 --- a/apps/user/templates/new_email_verify.html +++ /dev/null @@ -1,24 +0,0 @@ - - - -

Hello {{ user.email }},

- -

-Here is your verification code to verify your new email address: -

- -

-{{ verification_token }} -

- -

-If you did not request this email, you can ignore it. -

- -

-Best,
-IFRC Alert hub -

- - - diff --git a/apps/user/templates/password_reset.html b/apps/user/templates/password_reset.html deleted file mode 100644 index 4ded19ce..00000000 --- a/apps/user/templates/password_reset.html +++ /dev/null @@ -1,24 +0,0 @@ - - - -

Hello {{ user.email }},

- -

-You requested a password reset. Here is your verification code to set a new password: -

- -

-{{ reset_token }} -

- -

-If you did not request this, please ignore this email. -

- -

-Best,
-IFRC Alert hub -

- - - diff --git a/main/permalinks.py b/main/permalinks.py index 36f6251e..53621122 100644 --- a/main/permalinks.py +++ b/main/permalinks.py @@ -14,6 +14,14 @@ def user_password_reset(cls, uid: str, token: str): def user_activation(cls, uid: str, token: str): return f'{cls.BASE_URL}/user-activation/{uid}/{token}' + @classmethod + def subscription_detail(cls, id: int): + return f'{cls.BASE_URL}/subscription-detail/{id}' + + @classmethod + def alert_detail(cls, id: int): + return f'{cls.BASE_URL}/alert-detail/{id}' + @classmethod def unsubscribe_user_alert_subscription(cls, uid: str, token: str): return f'{cls.BASE_URL}/unsubscribe-user-alert-subscription/{uid}/{token}' diff --git a/main/settings.py b/main/settings.py index b9e14c92..1251c317 100644 --- a/main/settings.py +++ b/main/settings.py @@ -55,6 +55,7 @@ TEST_CACHE_REDIS_URL=(str, None), # redis://redis:6379/11 # Email EMAIL_HOST=str, + EMAIL_SUBJECT_PREFIX=(str, 'Alert Hub:'), EMAIL_USE_TLS=(bool, True), EMAIL_PORT=(int, 587), EMAIL_HOST_USER=str, @@ -89,6 +90,7 @@ UPTIME_WORKER_HEARTBEAT=(str, None), HCAPTCHA_SITEKEY=str, HCAPTCHA_SECRET=str, + ALLOW_FAKE_DATA=(bool, False), ) # SECURITY WARNING: keep the secret key used in production secret! @@ -105,7 +107,7 @@ APP_FRONTEND_HOST = env('APP_FRONTEND_HOST') DJANGO_APP_TYPE = env('DJANGO_APP_TYPE') -DJANGO_APP_ENVIRONMENT = env('DJANGO_APP_ENVIRONMENT') +DJANGO_APP_ENVIRONMENT = env('DJANGO_APP_ENVIRONMENT').upper() # Application definition @@ -407,6 +409,7 @@ # Email - SMTP Settings EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_SUBJECT_PREFIX = env('EMAIL_SUBJECT_PREFIX') EMAIL_USE_TLS = env('EMAIL_USE_TLS') EMAIL_HOST = env('EMAIL_HOST') EMAIL_PORT = env('EMAIL_PORT') @@ -517,3 +520,6 @@ def log_render_extra_context(record): PREMAILER_OPTIONS = dict( disable_validation=not DEBUG, # Disable validation in production ) + +# Misc +ALLOW_FAKE_DATA = env("ALLOW_FAKE_DATA") diff --git a/main/urls.py b/main/urls.py index 88cde0e0..330771a6 100644 --- a/main/urls.py +++ b/main/urls.py @@ -20,9 +20,14 @@ from django.urls import include, path, re_path from django.views.decorators.csrf import csrf_exempt -from apps.subscription.views import user_alert_subscription_email_preview from main.graphql.schema import CustomAsyncGraphQLView from main.graphql.schema import schema as graphql_schema +from main.views import ( + password_changed_email_preview, + password_reset_email_preview, + user_activation_email_preview, + user_alert_subscription_email_preview, +) urlpatterns = [ path('admin/', admin.site.urls, name='admin'), @@ -48,7 +53,10 @@ 'graphiql/', csrf_exempt(CustomAsyncGraphQLView.as_view(schema=graphql_schema)), ), - re_path(r'^dev/user-alert-subscription-email/preview/$', user_alert_subscription_email_preview), + re_path(r'^dev/email-preview/user-alert-subscription/$', user_alert_subscription_email_preview), + re_path(r'^dev/email-preview/password-reset/$', password_reset_email_preview), + re_path(r'^dev/email-preview/password-changed/$', password_changed_email_preview), + re_path(r'^dev/email-preview/user-activation/$', user_activation_email_preview), ] ) diff --git a/main/views.py b/main/views.py new file mode 100644 index 00000000..ca05fec5 --- /dev/null +++ b/main/views.py @@ -0,0 +1,67 @@ +from django.contrib.auth.decorators import login_required +from django.http import HttpResponse +from django.template import loader + +from apps.subscription.emails import generate_user_alert_subscription_email_context +from apps.subscription.models import UserAlertSubscription +from main.permalinks import Permalink + +USER_ALERT_SUBSCRIPTION_EMAIL_PREVIEW_MESSAGE = """ + To use email_frequency in GET params, Please specify integer values. Default is Daily
+ Use this for reference

+ """ + '
'.join( + [f"{frequency.label}: {frequency.value}" for frequency in UserAlertSubscription.EmailFrequency] +) + + +@login_required +def user_alert_subscription_email_preview(request): + try: + email_frequency = int( + request.GET.get( + "email_frequency", + UserAlertSubscription.EmailFrequency.DAILY, + ) + ) + if email_frequency not in UserAlertSubscription.EmailFrequency: + return HttpResponse(USER_ALERT_SUBSCRIPTION_EMAIL_PREVIEW_MESSAGE) + email_frequency = UserAlertSubscription.EmailFrequency(email_frequency) + except ValueError: + return HttpResponse(USER_ALERT_SUBSCRIPTION_EMAIL_PREVIEW_MESSAGE) + + have_data, context, _ = generate_user_alert_subscription_email_context( + request.user, + email_frequency, + ) + if have_data: + template = loader.get_template("emails/subscription/body.html") + return HttpResponse(template.render(context, request)) + return HttpResponse("Nothing to display.. Try tagging alerts to subscriptions") + + +def password_reset_email_preview(request): + context = { + "location": "192.168.00.00", + "device": "FakeOS", + "password_reset_url": Permalink.user_password_reset("fake-uid", "fake-token"), + } + template = loader.get_template("emails/user/password_reset/body.html") + return HttpResponse(template.render(context, request)) + + +def password_changed_email_preview(request): + context = { + "location": "192.168.00.00", + "device": "FakeOS", + "frontend_forgot_password": Permalink.FORGOT_PASSWORD, + } + template = loader.get_template("emails/user/password_changed/body.html") + return HttpResponse(template.render(context, request)) + + +def user_activation_email_preview(request): + context = { + "activation_url": Permalink.user_activation("fake-uid", "fake-token"), + } + template = loader.get_template("emails/user/activation/body.html") + return HttpResponse(template.render(context, request)) diff --git a/static/images/alert-hub.png b/static/images/alert-hub.png new file mode 100644 index 0000000000000000000000000000000000000000..fc1ca7ac475736fa2855cc11c03e6fd072cc07fb GIT binary patch literal 27779 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 z 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 HSVsO21pI~% literal 0 HcmV?d00001 diff --git a/static/images/go-logo-long.png b/static/images/go-logo-long.png deleted file mode 100644 index 93b96b590b6db4dbea9412cec72669abc8d1a2b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21246 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

ASj9x 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 diff --git a/static/images/open-in-new-tab.png b/static/images/open-in-new-tab.png new file mode 100644 index 0000000000000000000000000000000000000000..37413c83fee4e0629560fd1b499f21b6f1addb06 GIT binary patch literal 4040 zcmeH~>049R7RJ{}0))vhNC_e)2&fE&Fj!R71RMYdV%35clsMv0g({PxB$!$pf?@&3 zNN~hk5V5FLu+8BCqf`_SQBf#(j{{Z&LDUGz$z8F}eeR#oFE?NIe)HS!+B|&W47LW`m5;H?)@H( z2O1!MYcxOVMg4%qObeF-RxXP}a=DX!xx{Gl=6Y_Pxz$YRa5XY(>ZX-e&m&k#?IxM* z9dDa>E$u12Lz9U*H_v-ds|_(JB_(b+)j#?9vB@Mh_{f|OQJMQPmN@3w^{w?>mH1(( zo$h$kUAxrtqGP#fwx24dFHj%MF7m+pgFbs|pYHR8S9B z-78+S$yT^B7fS1@o~o9-k@{EFcLWZCVmB<3%R9E~&O9}f)U!z0U?5$VpO|n= z=&u7sYm{JD$Pmx)q&vwFDWnN-dpV5UvpPYaM?Fl4(SKxsmn%dIT2*aRwk)BCmkko< z6mBw)?bn6Zr1!2p=ZKE8NV{Q~!Jqnr{#bFL%cLlpAqmxM!()E8fOCgok*v?G$blh= z(jQU(-5aKFru9dpAaY}o)`Jb7RJE<)dMI-GlbIx6p?YUl6xsuERQCz>t||g@Y$2vA zQJ>Mpkwu%!yp`8|#S9qX0lrNggCFgLIOSY&M4hp`-`o|(H7(u=*NRuB;ewXHSMj5D4D!W5I7ATY1k+g;j+*tWNVfd$D!kE``sQ)_ofRP=x3l zj?kQH$VF(P5KglPa7)lFHX}f=T%$^{MY&41cr3~=lt#Kj*UCaUTBDk4q|0=z?kE{3 zk;bmpwVD!8K0~A0fHZcwu5}Db6O>4uSL>XZC_^=>i#8~i>zwRS5-4%-dZcbBXSQn8 zdk>p_gCo}ev{9Eh|Y%y z(HU2*Z4MwH$($0uMuFYyfTXltIcpwWlo#GoNhA&e6P z-0`QAaM?jOv^CohT2Y*L2<;U@pP2wHfpqx<0v4ik6AR{jNpWr?EKmqbjnJPipM(&d z0)*BSrv{hkpb%Qk1~`?VTb9lM?s$sx+7)C?0d)CPlpZKOP@**xr31w|W~2hTd?HF* z?=ht3an>yic;rQKo*+GsgQuXhK#4R5>2{RkC=SI(=}!wrp=6^R4YJFDv;ZesrSH5g|H#5IUzaB~cpHvHZOBxQ!`dd6PYmaSGwP3A*nGgU;=6gU*>6-eWhAt#^{H z$pa``sXMWR0ax?;vags!XRtze3HM#ST<3}za6Lii${|4MPv`ChPzc@Kfj^PrOf-jx zdhIk^`cDCL?lF-4pb+vg%PGzP3*49MveK|wrptPVjYOBV7t7CGE%T5q7AJ}mHv>ec z6v8CTTk8OtoQqJSWxm78WkYf5XJbWF2+P?B1vGh*1q;!6+Xm+OQyd?JXAoKt;DFAb z5u$S%LR*Ssg<(AuLII|t13DX7piuUiJqQJImy=Yo1Z6(TNFdoL+fgd|%)T;Gu2ixB zyQf<`>d?54rx^I!N;_~k#F*5)M_&-eq|Vx4XH06|qpt{KQfK+w z7?YaEwM2>3-`1GaJgzfgOzJFu9E{t}#k^Q=r1ns5iZX-%W*{xHW`Wf$E%UMs2yhGT zPX`jWdLCN6187kW3rb0Z7{MLk93brwqSF$gB@28Io=4|cigO8_BNf8N5d;JaXc2CE zTn$1yNWw?=5;}2z8Zjpvxl%qBp_ka;auc1Td@LJyT$o|6P`DAIbsFBSS&)0M&n&JSQO*lP<58?;QHD46$Kvu`FZ68C-_L-v=;s3`TGvW(%F zOC(n1xof5{@L+?``)v+ToKiUyd7htvuha)Kpivn;8h7BeJFJ=i#ugtotk6Lv57_t! zjA6sk=vda@=XZxNMmJB-t1a3*V~^UdA!`J9tciEW? z8)Q$IaJYUF|Ez|0t^i~n%647PlqXau0YEEnGarHj~-JIkFO{zzTQ^!@W$653V6lryu=3Cca^)=y1#T4 z!M!6cC1=&z**pkYGT9O~eXsL|lgUzZ*yQ=V;eaw5YFu#itnN8(*9M4rsgv1^+_^8c zG0;}&AZE%Irly&Q2H|j2arlj!p%Ad;fuVc92@@9Vi4BFd z`AJQVjLv#9UTxY^bF18%^XmTY7%|j7c?o-B!{Cm0`^US64jbrA?i&Yd9(Ql-O{+fQ zo8Ei3daNOKR<{Y_ZJwp7MLMPNiLYFd@pD6+=%+$2(y~m2-Rs*QRK0)yU)eQEELBDszccA!z@;<7A$7{XLwooBPp7>%#7;Fy+8HvW(#7mP z%z~EwXNHzG;%eBd%4|Z^vhjGbfgAT%bke_KF6=wY%S;~n(7j*EgZsta`}CUdqdUA9 z_^bF-0xqFSy<(Fea8+fKCr#c)rwAO0av83!a1Y3ACpjA&<4rc(gC*X8@M?ExG#qRU zC&PJvW{xT}het>0jM&u)w4wWoVbeAXiAjQL*slJu7pzPll~(v^clT@-5BW7@Ky;(w zM#j+5NGU;lb>?;92Vq8zVO<-+y;WWPtYqBt+aSsLBRJbHew%IS9I#r`pO#lwcfP;& z0FdQ_v9$isB`kU>Q~6t)PT4-Xx2vl~Ed6xWkT3c;hH-tOjl@JR&#Z{2-{Vd-4Ee3K zG9Njskhu2#C{6oysMyi(dTXM~NTPJH)FGpBNXPsj+k zx>Tt1s7y>vqh7yC-a5QA2ObrL4CR?s7$~XFnRF@M<`Toz(vyY*f%F|)h!q(B%SHWr zsZx`0X0Q0v@S7}=S4GIlX5Y%*1hsIeQ?9++LccJV*>=f0wG(=J@~>t*=GhN?ZF4cW zYC-wKiC=-&{rDEu!3oaqk8ey(=}=$p4Wi>xmv4O?J$bzLpZuqabHC9_=ZsfxQH(mX Q&A5`t8Bx>A!X!KX1Hm`WWB>pF literal 0 HcmV?d00001 diff --git a/templates/emails/base.html b/templates/emails/base.html index 93ee4522..34e042d1 100644 --- a/templates/emails/base.html +++ b/templates/emails/base.html @@ -1,47 +1,162 @@ {% load premailer %} {% load custom_tags %} -{% load static %} {% premailer %} - - + + + + + + + + + + + {% block head %}{% endblock %} - -

- -
- - - {% load i18n %}{% autoescape off %} - - + +
+
- {% block title %}{% endblock %} - - -
+ + + -
+ + + + + + +
+ image description +
+
-
+ + +
+

+ {% block title %}{% endblock %} +

+
+
+ {% block title_description %}{% endblock %} +
+ + + + -
- {% block body %}{% endblock %} -
-

Thank you for using Alert Hub

-

The Alert Hub team

-
+ {% block body %}{% endblock %} + +
+ + + + + + + + +
+
+ Disclaimer! +
+
+ The IFRC Alert Hub publishes only official alerts issued by recognised government alerting + agencies. Alerts are not issued or published by the IFRC or its member Red Cross and Red + Crescent National Societies. The IFRC takes no responsibility for the information contained + in the alerts, which is solely the responsibility of the issuing agency. The IFRC makes + every effort to identify broken government alert feeds and to highlight this information for + users of the Alert Hub. However, the IFRC does not maintain 24/7 monitoring of the status of + the government feeds and it is the responsibility of Alert Hub users to identify redundant + sources of hazard alert information. +
+
+
+ If you need any help, don’t hesitate to reach out to us at
im@ifrc.org
+
- {% endautoescape %}
- + {% endpremailer %} diff --git a/templates/emails/base.txt b/templates/emails/base.txt index 86447e42..41afdb70 100644 --- a/templates/emails/base.txt +++ b/templates/emails/base.txt @@ -1,13 +1,16 @@ {% block head %}{% endblock %} - {% block title %}{% endblock %} - +{% block title_description %}{% endblock %} {% block body %}{% endblock %} -Thank you for using Alert Hub -The Alert Hub team +Disclaimer! +The IFRC Alert Hub publishes only official alerts issued by recognised government alerting +agencies. Alerts are not issued or published by the IFRC or its member Red Cross and Red +Crescent National Societies. The IFRC takes no responsibility for the information contained +in the alerts, which is solely the responsibility of the issuing agency. The IFRC makes +every effort to identify broken government alert feeds and to highlight this information for +users of the Alert Hub. However, the IFRC does not maintain 24/7 monitoring of the status of +the government feeds and it is the responsibility of Alert Hub users to identify redundant +sources of hazard alert information. -{% if email_type in unsubscribe_email_types %} -Would you prefer to not receive these kinds of emails anymore? -Use this link to unsubscribe "{{ protocol }}://{{ domain }}{% url 'unsubscribe_email' uidb64=unsubscribe_email_id token=unsubscribe_email_token email_type=email_type %}" -{% endif %} +If you need any help, don’t hesitate to reach out to us at im@ifrc.org diff --git a/templates/emails/subscription/body.html b/templates/emails/subscription/body.html index 0f00f6ba..a013faf9 100644 --- a/templates/emails/subscription/body.html +++ b/templates/emails/subscription/body.html @@ -1,26 +1,107 @@ - - - - -

-{% for subscription in subscriptions %} -Subscription: {{ subscription.name }} -
-Latest alerts: - {% for alert in subscription.latest_alerts %} - - {{ alert.url }} -- {{ alert.sent }} -
+{% extends "emails/base.html" %} +{% load custom_tags %} +{% block title %} +You got new
Alerts! +{% endblock %} + +{% block title_description %} +

We have some new alerts that you are subscribed to.

+{% endblock %} + +{% block body %} + +{% for subscription_data in subscriptions_data %} + +
+ + +

+ {{ subscription_data.subscription.name }} | {{ subscription_data.subscription.filter_alert_country.name }} +

+ + + Open Subscription + + + + Unsubscribe + +
+ + + + + + + + + + + + + + {% for alert_data in subscription_data.latest_alerts %} + + + + + + + + {% endfor %} -
+
+
+ Alert Name +
+
+
+ Urgency +
+
+
+ Severity +
+
+
+ Certainty +
+
+
+ Admin1 +
+
+ + {{ alert_data.name }} + + +
+ {{ alert_data.urgency }} +
+
+
+ {{ alert_data.severity }} +
+
+
+ {{ alert_data.certainty }} +
+
+
+ {{ alert_data.admins|truncatechars:40 }} +
+
-Unsubscribe: {{ subscription.unsubscribe_url }} {% endfor %} -

- -

-

- - +{% endblock %} diff --git a/templates/emails/subscription/body.txt b/templates/emails/subscription/body.txt index e69de29b..dee71a81 100644 --- a/templates/emails/subscription/body.txt +++ b/templates/emails/subscription/body.txt @@ -0,0 +1,15 @@ +{% extends "emails/base.txt" %} +{% block title %}You got new Alerts!{% endblock %} +{% block title_description %}We have some new alerts that you are subscribed to. {% endblock %} +{% block body %}{% for subscription_data in subscriptions_data %} +Subscription: {{ subscription_data.subscription.name }} | {{ subscription_data.subscription.filter_alert_country.name }} +URL: {{ subscription_data.url }} +Unsubscribe: {{ subscription_data.unsubscribe_url }} +Alerts: {% for alert_data in subscription_data.latest_alerts %} + Alert Name: {{ alert_data.name }} + Alert Url: {{ alert_data.url }} + Alert Urgency: {{ alert_data.urgency }} + Alert Severity: {{ alert_data.severity }} + Alert certainty: {{ alert_data.certainty }} + Alert Admins: {{ alert_data.admins|truncatechars:40}} +{% endfor %}{% endfor %}{% endblock %} diff --git a/templates/emails/user/activation/body.html b/templates/emails/user/activation/body.html index 27ce8f0c..0b02ed5e 100644 --- a/templates/emails/user/activation/body.html +++ b/templates/emails/user/activation/body.html @@ -1,15 +1,40 @@ {% extends "emails/base.html" %} -{% block body %} -
-

- Please click on the link below to activate your account: -

- - Activate your account - -
+{% block head %} + +{% endblock %} +{% block title %} +IFRC Alert Hub account activation {% endblock %} + +{% block title_description %} +

Please open the link below to activate your account:

+{% endblock %} + +{% block body %} + +{% endblock %} \ No newline at end of file diff --git a/templates/emails/user/activation/body.txt b/templates/emails/user/activation/body.txt index 0b03d557..6aee60b1 100644 --- a/templates/emails/user/activation/body.txt +++ b/templates/emails/user/activation/body.txt @@ -1,8 +1,5 @@ {% extends "emails/base.txt" %} -{% block body %} - -Please open the link below to activate your account: -"{{ activation_url }}" - -{% endblock %} +{% block title %}IFRC Alert Hub account activation{% endblock %} +{% block body %}Please open the link below to activate your account: +Activate your account: {{ activation_url }}{% endblock%} diff --git a/templates/emails/user/password_changed/body.html b/templates/emails/user/password_changed/body.html index c3a6b120..5a6d31a5 100644 --- a/templates/emails/user/password_changed/body.html +++ b/templates/emails/user/password_changed/body.html @@ -1,64 +1,58 @@ {% extends "emails/base.html" %} {% block head %} - + .actions-container { + margin: 23px; + } + {% endblock %} {% block title %} - Your password has been changed successfully. +Hi there {{ user.first_name|default:"User" }}, +Your password has been changed successfully. {% endblock %} {% block body %} -

- Hi there {{ user.first_name }}, -
- Your password has been changed successfully. +{% if location or device %} + + + + + + + + + +
Location{{location}}
Device{{device}}
+{% endif %} +

+

+ If you are aware of this change, you can disregard this email. + If this was not triggered by you, please reset your password.

- - - - - - - - - - - - - -
Time{{time}}
Location{{location}}
Device{{device}}
-
-

- If you are aware of this change, you can disregard this email. - If this was not triggered by you, please reset your password. -

- - Reset Password - -
+ + Reset your password + +
{% endblock%} diff --git a/templates/emails/user/password_changed/body.txt b/templates/emails/user/password_changed/body.txt index af419754..054dcbcf 100644 --- a/templates/emails/user/password_changed/body.txt +++ b/templates/emails/user/password_changed/body.txt @@ -1,15 +1,11 @@ {% extends "emails/base.txt" %} -{% block title %} - Your password has been changed successfully. -{% endblock %} -{% block body %} - Hi there {{ user.first_name }}, - Your password has been changed successfully. - Time: {{time}} - Location: {{location}} - Device: {{device}} - If you are aware of this change, you can disregard this email. - If this was not triggered by you, please reset your password. - Reset Password: {{ frontend_forgot_password }} +{% block title %}Hi there {{ user.first_name|default:"User" }}, +Your password has been changed successfully.{% endblock %} +{% block body %}{% if location or device %} +Location: {{location}} +Device: {{device}}{% endif %} +If you are aware of this change, you can disregard this email. +If this was not triggered by you, please reset your password. +Reset Password: {{ frontend_forgot_password }} {% endblock%} diff --git a/templates/emails/user/password_reset/body.html b/templates/emails/user/password_reset/body.html index 96edf752..992765e3 100644 --- a/templates/emails/user/password_reset/body.html +++ b/templates/emails/user/password_reset/body.html @@ -1,45 +1,66 @@ {% extends "emails/base.html" %} {% block head %} - + {% endblock %} + {% block title %} - Reset Password +IFRC Alert Hub password reset {% endblock %} + +{% block title_description %} +

It seems you've forgotten your password.

+{% endblock %} + + {% block body %} -

- It seems you've forgotten your password. +

+ +

+ If you didn't request to reset your password, you may simply ignore this email.

-
-

- If you didn't request to reset your password, you may simply ignore this email. -

- - Reset your password - -
+ {% if location or device %} -

More detail on password reset trigger

- - - - - - - - +

+ More detail on password reset trigger +

+
Time{{time}}
Location{{location}}
+ + + - - - + + +
Location{{location}}
Device{{device}}
Device{{device}}
{% endif %} -{% endblock %} +
+ +{% endblock %} \ No newline at end of file diff --git a/templates/emails/user/password_reset/body.txt b/templates/emails/user/password_reset/body.txt index d24d1ace..32599b8a 100644 --- a/templates/emails/user/password_reset/body.txt +++ b/templates/emails/user/password_reset/body.txt @@ -1,22 +1,11 @@ -{% block title %} -Reset Password -{% endblock %} - +{% block title %}Reset Password{% endblock %} {% block body %} - {% if welcome %} - Before you can start using your account, you'll first need to create a password. - You can do this by clicking on the link below: - Set your password - {% else %} - It seems you've forgotten your password. - If you didn't request to reset your password, you may simply ignore this email. - Reset your password - {% endif %} - {{ password_reset_url }} - {% if location or device %} - More detail on password reset trigger - Time: {{time}} - Location: {{location}} - Device: {{device}} - {% endif %} +It seems you've forgotten your password. +If you didn't request to reset your password, you may simply ignore this email. +Reset your password: {{ password_reset_url }} +{% if location or device %} +More detail on password reset trigger + Location: {{location}} + Device: {{device}} +{% endif %} {% endblock %}