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 00000000..fc1ca7ac Binary files /dev/null and b/static/images/alert-hub.png differ diff --git a/static/images/go-logo-long.png b/static/images/go-logo-long.png deleted file mode 100644 index 93b96b59..00000000 Binary files a/static/images/go-logo-long.png and /dev/null differ diff --git a/static/images/open-in-new-tab.png b/static/images/open-in-new-tab.png new file mode 100644 index 00000000..37413c83 Binary files /dev/null and b/static/images/open-in-new-tab.png differ 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 %} +
+ + Activate your account + +
+{% 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 %}