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 content %} {% endblock %} diff --git a/ephios/core/templates/core/mails/base.txt b/ephios/core/templates/core/mails/base.txt new file mode 100644 index 000000000..61d27b678 --- /dev/null +++ b/ephios/core/templates/core/mails/base.txt @@ -0,0 +1,2 @@ +{% block content %}{{ body|wordwrap:80 }}{% endblock %} +{% block footer %}{% endblock %} \ No newline at end of file diff --git a/ephios/core/templates/core/mails/new_account_email.html b/ephios/core/templates/core/mails/new_account_email.html index 430d71f13..8cc02bb61 100644 --- a/ephios/core/templates/core/mails/new_account_email.html +++ b/ephios/core/templates/core/mails/new_account_email.html @@ -1,4 +1,5 @@ {% extends "core/mails/notification.html" %} +{% load settings_extras %} {% load i18n %} {% block content %} @@ -6,8 +7,8 @@ {% translate "You're receiving this email because a new account has been created for you at ephios." %} {% translate "Please go to the following page and choose a new password:" %} {% block reset_link %} - {% trans "Set password" %} + {% url 'password_reset_confirm' uidb64=notification.data.uidb64 token=notification.data.token as reset_location %} + {% trans "Set password" %} {% endblock %} {% translate "Your username is your email address:" %} {{ notification.user.email }} diff --git a/ephios/core/templates/core/mails/new_event.html b/ephios/core/templates/core/mails/new_event.html index c18291692..52997e4d7 100644 --- a/ephios/core/templates/core/mails/new_event.html +++ b/ephios/core/templates/core/mails/new_event.html @@ -1,4 +1,5 @@ {% extends "core/mails/notification.html" %} +{% load settings_extras %} {% load i18n %} {% block content %} @@ -31,7 +32,7 @@

{% endblock %} diff --git a/ephios/core/templates/core/mails/notification.html b/ephios/core/templates/core/mails/notification.html index ef7173aa4..fdb3b3946 100644 --- a/ephios/core/templates/core/mails/notification.html +++ b/ephios/core/templates/core/mails/notification.html @@ -1,4 +1,5 @@ {% extends "core/mails/base.html" %} +{% load settings_extras %} {% load rich_text %} {% load i18n %} @@ -20,8 +21,8 @@ {% block header %}
- - {{ organization_name }} + + {% organization_name %}
{{ block.super }} @@ -49,6 +50,7 @@ {% block footer %} + {% organization_name as organization_name %} {% blocktranslate trimmed %} You are receiving this email as a member of {{ organization_name }}. {% endblocktranslate %} diff --git a/ephios/core/templates/core/mails/notification.txt b/ephios/core/templates/core/mails/notification.txt index d6c5a7358..16cac6209 100644 --- a/ephios/core/templates/core/mails/notification.txt +++ b/ephios/core/templates/core/mails/notification.txt @@ -1,12 +1,19 @@ +{% extends "core/mails/base.txt" %} {% load i18n %} -{{ subject|wordwrap:80 }} +{% load settings_extras %} -{{ body|wordwrap:80 }} +{% block content %} +{{ block.super }} {% for label, url in notification.get_actions %}{{ label }}: {{ url }} {% endfor %} -{% blocktranslate trimmed %} +{% endblock %} + +{% block footer %}{% organization_name as organization_name %}{% blocktranslate trimmed %} You are receiving this email as a member of {{ organization_name }}. {% endblocktranslate %} -{% translate "Click here to view and change your notification settings:" %} -{{ notification_settings_url }} \ No newline at end of file +{% site_url %} + +{% translate "Click here to view and change your notification settings" %}: +{{ notification_settings_url }} +{% endblock %} \ No newline at end of file diff --git a/ephios/core/templates/core/settings/settings_calendar.html b/ephios/core/templates/core/settings/settings_calendar.html index 0df3abdc2..2f04aa3c1 100644 --- a/ephios/core/templates/core/settings/settings_calendar.html +++ b/ephios/core/templates/core/settings/settings_calendar.html @@ -1,4 +1,5 @@ {% extends "core/settings/settings_base.html" %} +{% load settings_extras %} {% load static %} {% load crispy_forms_filters %} {% load i18n %} @@ -9,8 +10,9 @@

{% translate "Calendar" %}

{% translate "You can subscribe to a calendar containing all events that you participate in. Most calendars (e.g. Google Calendar) offer this feature. This allows you to see all events in your personal calendar. To activate this, you need to copy the address of the following link and subscribe to the URL." %}

+ {% url "core:user_event_feed" userprofile.calendar_token as feed_location %}