diff --git a/ephios/api/views/events.py b/ephios/api/views/events.py index 36cb9cd26..afa4fc48d 100644 --- a/ephios/api/views/events.py +++ b/ephios/api/views/events.py @@ -1,7 +1,4 @@ -from urllib.parse import urljoin - import django_filters -from django.conf import settings from django.db.models import Max, Min, Prefetch from oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope from rest_framework import filters, serializers, viewsets @@ -9,6 +6,7 @@ from rest_framework_guardian import filters as guardian_filters from ephios.core.models import Event, EventType, Shift +from ephios.core.templatetags.settings_extras import make_absolute class SignupStatsSerializer(serializers.Serializer): @@ -53,7 +51,7 @@ class EventSerializer(serializers.ModelSerializer): frontend_url = serializers.SerializerMethodField() def get_frontend_url(self, obj): - return urljoin(settings.GET_SITE_URL(), obj.get_absolute_url()) + return make_absolute(obj.get_absolute_url()) class Meta: model = Event diff --git a/ephios/core/context.py b/ephios/core/context.py index 90a518ac0..84ee559c6 100644 --- a/ephios/core/context.py +++ b/ephios/core/context.py @@ -21,6 +21,5 @@ def ephios_base_context(request): "footer": footer, "LANGUAGE_CODE": get_language(), "ephios_version": settings.EPHIOS_VERSION, - "SITE_URL": settings.GET_SITE_URL(), "PWA_APP_ICONS": settings.PWA_APP_ICONS, } diff --git a/ephios/core/plugins.py b/ephios/core/plugins.py index 3e0fa058b..3140b8046 100644 --- a/ephios/core/plugins.py +++ b/ephios/core/plugins.py @@ -48,7 +48,7 @@ def is_receiver_path_enabled(searchpath): relies in a module that is either an enabled plugin or considered ephios core. Uses a cache that gets reset when enabled plugins preference changes. """ - enabled_paths = settings.EPHIOS_CORE_MODULES + [ + enabled_paths = settings.EPHIOS_APP_MODULES + [ plugin.module for plugin in get_enabled_plugins() ] # Not using `startwith`, as we don't want to match "ephios_foobar" against "ephios_foo" diff --git a/ephios/core/services/mail/cid.py b/ephios/core/services/mail/cid.py index 88c7a3de3..3df4eab28 100644 --- a/ephios/core/services/mail/cid.py +++ b/ephios/core/services/mail/cid.py @@ -2,13 +2,14 @@ import os import re from email.mime.image import MIMEImage -from urllib.parse import urljoin, urlparse +from urllib.parse import urlparse import requests from bs4 import BeautifulSoup -from django.conf import settings from django.core.mail import EmailMultiAlternatives, SafeMIMEMultipart +from ephios.core.templatetags.settings_extras import make_absolute + logger = logging.getLogger(__name__) @@ -94,7 +95,7 @@ def convert_image_to_cid(image_src, cid_id, verify_ssl=True): else: # replaced normalize_image_url with these two lines if "://" not in image_src: - image_src = urljoin(settings.GET_SITE_URL(), image_src) + image_src = make_absolute(image_src) path = urlparse(image_src).path guess_subtype = os.path.splitext(path)[1][1:] response = requests.get(image_src, verify=verify_ssl) diff --git a/ephios/core/services/mail/send.py b/ephios/core/services/mail/send.py index 577edb60c..fe8d9057c 100644 --- a/ephios/core/services/mail/send.py +++ b/ephios/core/services/mail/send.py @@ -1,8 +1,10 @@ import logging +from typing import List, Optional from css_inline import css_inline from django.conf import settings from django.core.mail import SafeMIMEMultipart, SafeMIMEText +from django.template.loader import render_to_string from ephios.core.services.mail.cid import ( CustomEmail, @@ -14,13 +16,14 @@ def send_mail( - to, - subject, - plaintext, + to: List[str], + subject: str, + plaintext: str, html=None, from_email=None, - cc=None, - bcc=None, + cc: Optional[List[str]] = None, + bcc: Optional[List[str]] = None, + attachments: Optional[list] = None, is_autogenerated=True, ): headers = {} @@ -41,9 +44,46 @@ def send_mail( if html: html_part = prepare_html_part(html) email.attach_alternative(html_part, "multipart/related") + for attachment in attachments or []: + email.attach( + attachment["filename"], + attachment["content"], + attachment["mimetype"], + ) email.send() +def send_mail_template( + to: List[str], + subject: str, + plaintext: str, + html_template_name="core/mails/base.html", + additional_html_context=None, + from_email=None, + cc: Optional[List[str]] = None, + bcc: Optional[List[str]] = None, + attachments: Optional[list] = None, + is_autogenerated=True, +): + context = { + "subject": subject, + "body": plaintext, + **(additional_html_context or {}), + } + html = render_to_string(html_template_name, context) + send_mail( + to=to, + subject=subject, + plaintext=plaintext, + html=html, + from_email=from_email, + cc=cc, + bcc=bcc, + attachments=attachments, + is_autogenerated=is_autogenerated, + ) + + def prepare_plaintext(plaintext): """ Prepare the given plaintext for inclusion in the email. diff --git a/ephios/core/services/notifications/types.py b/ephios/core/services/notifications/types.py index 2ba32f8ca..593b4f498 100644 --- a/ephios/core/services/notifications/types.py +++ b/ephios/core/services/notifications/types.py @@ -1,7 +1,5 @@ from typing import List -from urllib.parse import urljoin -from django.conf import settings from django.contrib.auth.tokens import default_token_generator from django.template.loader import render_to_string from django.urls import reverse @@ -9,13 +7,13 @@ from django.utils.formats import date_format from django.utils.http import urlsafe_base64_encode from django.utils.translation import gettext_lazy as _ -from dynamic_preferences.registries import global_preferences_registry from guardian.shortcuts import get_users_with_perms from ephios.core.models import AbstractParticipation, Event, LocalParticipation, UserProfile from ephios.core.models.users import Consequence, Notification from ephios.core.signals import register_notification_types from ephios.core.signup.participants import LocalUserParticipant +from ephios.core.templatetags.settings_extras import make_absolute def installed_notification_types(): @@ -70,13 +68,7 @@ def get_render_context(cls, notification): "subject": cls.get_subject(notification), "body": cls.get_body(notification), "notification": notification, - "notification_settings_url": urljoin( - settings.GET_SITE_URL(), reverse("core:settings_notifications") - ), - "organization_name": global_preferences_registry.manager().get( - "general__organization_name" - ), - "SITE_URL": settings.GET_SITE_URL(), + "notification_settings_url": make_absolute(reverse("core:settings_notifications")), } @classmethod @@ -114,7 +106,7 @@ def get_actions(cls, notification): @classmethod def _get_personal_data_url(cls, notification): - return urljoin(settings.GET_SITE_URL(), reverse("core:settings_personal_data")) + return make_absolute(reverse("core:settings_personal_data")) class NewProfileNotification(AbstractNotificationHandler): @@ -156,7 +148,7 @@ def _get_reset_url(cls, notification): "token": notification.data["token"], }, ) - return urljoin(settings.GET_SITE_URL(), reset_link) + return make_absolute(reset_link) class NewEventNotification(AbstractNotificationHandler): @@ -201,7 +193,7 @@ def get_render_context(cls, notification): @classmethod def get_actions(cls, notification): event = Event.objects.get(pk=notification.data.get("event_id")) - return [(str(_("View event")), urljoin(settings.GET_SITE_URL(), event.get_absolute_url()))] + return [(str(_("View event")), make_absolute(event.get_absolute_url()))] class ParticipationMixin: @@ -210,7 +202,7 @@ def get_actions(cls, notification): shift = AbstractParticipation.objects.get( id=notification.data.get("participation_id") ).shift - return [(str(_("View event")), urljoin(settings.GET_SITE_URL(), shift.get_absolute_url()))] + return [(str(_("View event")), make_absolute(shift.get_absolute_url()))] @classmethod def send(cls, participation: AbstractParticipation, **additional_data): @@ -324,8 +316,7 @@ def send(cls, participation: AbstractParticipation, **additional_data): slug=cls.slug, user=user, data={ - "disposition_url": urljoin( - settings.GET_SITE_URL(), + "disposition_url": make_absolute( reverse( "core:shift_disposition", kwargs={"pk": participation.shift.pk} ), @@ -346,7 +337,7 @@ def get_actions(cls, notification): return [ ( str(_("View event")), - urljoin(settings.GET_SITE_URL(), participation.shift.get_absolute_url()), + make_absolute(participation.shift.get_absolute_url()), ), (str(_("Disposition")), notification.data.get("disposition_url")), ] @@ -469,7 +460,7 @@ def get_body(cls, notification): @classmethod def get_actions(cls, notification): event = Event.objects.get(pk=notification.data.get("event_id")) - return [(str(_("View event")), urljoin(settings.GET_SITE_URL(), event.get_absolute_url()))] + return [(str(_("View event")), make_absolute(event.get_absolute_url()))] class CustomEventParticipantNotification(AbstractNotificationHandler): diff --git a/ephios/core/services/password_reset.py b/ephios/core/services/password_reset.py index 823e2a669..8490867c2 100644 --- a/ephios/core/services/password_reset.py +++ b/ephios/core/services/password_reset.py @@ -25,7 +25,6 @@ def password_changed(self, password, user): { "subject": _("Your ephios password has been changed"), "body": text_content, - "SITE_URL": settings.GET_SITE_URL(), }, ) message = EmailMultiAlternatives( diff --git a/ephios/core/templates/core/account_updated_email.html b/ephios/core/templates/core/account_updated_email.html deleted file mode 100644 index 48beede8b..000000000 --- a/ephios/core/templates/core/account_updated_email.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "email_base.html" %} -{% load i18n %} -{% block content %} - {% trans "You're receiving this email because your account at ephios has been updated." %} - {% trans "You can see the changes in your profile:" %} {{ SITE_URL }}{{ url }} - {% trans "Your username is your email address:" %} {{ email }} -{% endblock %} \ No newline at end of file diff --git a/ephios/core/templates/core/mails/base.html b/ephios/core/templates/core/mails/base.html index a5c4f79db..d11b80169 100644 --- a/ephios/core/templates/core/mails/base.html +++ b/ephios/core/templates/core/mails/base.html @@ -1,3 +1,4 @@ +{% load settings_extras %} {% load rich_text %} {% load i18n %} @@ -119,7 +120,7 @@
|
-
+
|
{% block header %}
- {{ subject }}+{{ subject|default_if_none:"" }}{% endblock %} |
| - {{ body|rich_text }} + {{ body|default_if_none:""|rich_text }} |
| {% translate "View event" %} + href="{{ event.get_absolute_url|make_absolute }}">{% translate "View event" %} |