From ee66a04cb280e958a3a646b41fdf8d8e56c56345 Mon Sep 17 00:00:00 2001 From: SimolZimol <70102430+SimolZimol@users.noreply.github.com> Date: Mon, 13 Apr 2026 19:00:46 +0200 Subject: [PATCH] modified: web/blueprints/group_admin.py modified: web/blueprints/site_admin.py modified: web/mailer.py --- web/blueprints/group_admin.py | 28 +++++++------ web/blueprints/site_admin.py | 74 +++++++++++++++++------------------ web/mailer.py | 73 +++++++++++++++++++++++++++++++++- 3 files changed, 124 insertions(+), 51 deletions(-) diff --git a/web/blueprints/group_admin.py b/web/blueprints/group_admin.py index f290926..40b7414 100644 --- a/web/blueprints/group_admin.py +++ b/web/blueprints/group_admin.py @@ -7,7 +7,7 @@ from datetime import datetime, timedelta from functools import wraps from flask import Blueprint, render_template, request, redirect, url_for, session, flash from config import Config -from mailer import send_mail +from mailer import send_mail, build_invite_email import panel_db as db from roles import GROUP_MANAGEMENT_ROLES, GROUP_ROLE_OPTIONS, GROUP_ROLE_SET, OWNER_ONLY_ROLES, role_label @@ -150,14 +150,15 @@ def member_invite(): if mail_settings: subject = f"Invitation to join {session.get('group_name', 'your group')}" - text_body = ( - f"Hello {username},\n\n" - f"You have been invited to join the group '{session.get('group_name', 'your group')}' on MCLogger as {role_label(role)}.\n" - f"Open this link to create your account:\n\n{invite_url}\n\n" - f"This invite expires in {Config.INVITE_EXPIRY_HOURS} hours.\n" + text_body, html_body = build_invite_email( + username=username, + invite_url=invite_url, + expiry_text=f"in {Config.INVITE_EXPIRY_HOURS} hours", + group_name=session.get("group_name", "your group"), + role_name=role_label(role), ) try: - send_mail(mail_settings, email, subject, text_body) + send_mail(mail_settings, email, subject, text_body, html_body=html_body) if invite: db.mark_group_invite_sent(invite["id"], group_id) flash(f"Invitation email sent to '{email}'.", "success") @@ -193,14 +194,15 @@ def resend_invite(invite_id): invite_url = url_for("auth.accept_invite", token=invite["token"], _external=True) subject = f"Invitation to join {session.get('group_name', 'your group')}" - text_body = ( - f"Hello {invite['invited_username']},\n\n" - f"You have been invited to join the group '{session.get('group_name', 'your group')}' on MCLogger as {role_label(invite['role'])}.\n" - f"Open this link to create your account:\n\n{invite_url}\n\n" - f"This invite expires on {invite['expires_at']}.\n" + text_body, html_body = build_invite_email( + username=invite["invited_username"], + invite_url=invite_url, + expiry_text=f"on {invite['expires_at']}", + group_name=session.get("group_name", "your group"), + role_name=role_label(invite["role"]), ) try: - send_mail(mail_settings, invite["invited_email"], subject, text_body) + send_mail(mail_settings, invite["invited_email"], subject, text_body, html_body=html_body) db.mark_group_invite_sent(invite_id, group_id) flash("Invitation email resent.", "success") except Exception: diff --git a/web/blueprints/site_admin.py b/web/blueprints/site_admin.py index a3c81d9..166ba76 100644 --- a/web/blueprints/site_admin.py +++ b/web/blueprints/site_admin.py @@ -6,7 +6,7 @@ from functools import wraps from datetime import datetime, timedelta from flask import Blueprint, render_template, request, redirect, url_for, session, flash from config import Config -from mailer import send_mail +from mailer import send_mail, build_invite_email import panel_db as db from roles import GROUP_MANAGEMENT_ROLES, GROUP_ROLE_OPTIONS, GROUP_ROLE_SET, role_label @@ -283,14 +283,15 @@ def group_member_invite(group_id): if mail_settings: subject = f"Invitation to join {group['name']}" - text_body = ( - f"Hello {username},\n\n" - f"You have been invited to join the group '{group['name']}' on MCLogger as {role_label(role)}.\n" - f"Open this link to create your account:\n\n{invite_url}\n\n" - f"This invite expires in {Config.INVITE_EXPIRY_HOURS} hours.\n" + text_body, html_body = build_invite_email( + username=username, + invite_url=invite_url, + expiry_text=f"in {Config.INVITE_EXPIRY_HOURS} hours", + group_name=group["name"], + role_name=role_label(role), ) try: - send_mail(mail_settings, email, subject, text_body) + send_mail(mail_settings, email, subject, text_body, html_body=html_body) if invite: db.mark_group_invite_sent(invite["id"], group_id) flash(f"Invitation email sent to '{email}'.", "success") @@ -330,14 +331,15 @@ def group_invite_resend(group_id, invite_id): return redirect(url_for("site_admin.group_members", group_id=group_id)) invite_url = url_for("auth.accept_invite", token=invite["token"], _external=True) subject = f"Invitation to join {group['name']}" - text_body = ( - f"Hello {invite['invited_username']},\n\n" - f"You have been invited to join the group '{group['name']}' on MCLogger as {role_label(invite['role'])}.\n" - f"Open this link to create your account:\n\n{invite_url}\n\n" - f"This invite expires on {invite['expires_at']}.\n" + text_body, html_body = build_invite_email( + username=invite["invited_username"], + invite_url=invite_url, + expiry_text=f"on {invite['expires_at']}", + group_name=group["name"], + role_name=role_label(invite["role"]), ) try: - send_mail(mail_settings, invite["invited_email"], subject, text_body) + send_mail(mail_settings, invite["invited_email"], subject, text_body, html_body=html_body) db.mark_group_invite_sent(invite_id, group_id) flash("Invitation email resent.", "success") except Exception: @@ -399,23 +401,22 @@ def user_new(): if group_id: group = db.get_group_by_id(group_id) subject = f"Invitation to join {group['name']}" - body = ( - f"Hello {username},\n\n" - f"You have been invited to join the group '{group['name']}' on MCLogger" - f" as {role_label(effective_role)}.\n" - f"Open this link to create your account:\n\n{invite_url}\n\n" - f"This invite expires in {Config.INVITE_EXPIRY_HOURS} hours.\n" + body, html_body = build_invite_email( + username=username, + invite_url=invite_url, + expiry_text=f"in {Config.INVITE_EXPIRY_HOURS} hours", + group_name=group["name"], + role_name=role_label(effective_role), ) else: subject = "You have been invited to MCLogger" - body = ( - f"Hello {username},\n\n" - f"You have been invited to create an account on MCLogger.\n" - f"Open this link to create your account:\n\n{invite_url}\n\n" - f"This invite expires in {Config.INVITE_EXPIRY_HOURS} hours.\n" + body, html_body = build_invite_email( + username=username, + invite_url=invite_url, + expiry_text=f"in {Config.INVITE_EXPIRY_HOURS} hours", ) try: - send_mail(mail_settings, email, subject, body) + send_mail(mail_settings, email, subject, body, html_body=html_body) invite = db.get_invite_by_token(token) if invite: db.mark_invite_sent_global(invite["id"]) @@ -463,23 +464,22 @@ def user_invite_resend(invite_id): if invite["group_id"]: group = db.get_group_by_id(invite["group_id"]) subject = f"Invitation to join {group['name']}" - body = ( - f"Hello {invite['invited_username']},\n\n" - f"You have been invited to join the group '{group['name']}' on MCLogger" - f" as {role_label(invite['role'])}.\n" - f"Open this link to create your account:\n\n{invite_url}\n\n" - f"This invite expires on {invite['expires_at']}.\n" + body, html_body = build_invite_email( + username=invite["invited_username"], + invite_url=invite_url, + expiry_text=f"on {invite['expires_at']}", + group_name=group["name"], + role_name=role_label(invite["role"]), ) else: subject = "You have been invited to MCLogger" - body = ( - f"Hello {invite['invited_username']},\n\n" - f"You have been invited to create an account on MCLogger.\n" - f"Open this link to create your account:\n\n{invite_url}\n\n" - f"This invite expires on {invite['expires_at']}.\n" + body, html_body = build_invite_email( + username=invite["invited_username"], + invite_url=invite_url, + expiry_text=f"on {invite['expires_at']}", ) try: - send_mail(mail_settings, invite["invited_email"], subject, body) + send_mail(mail_settings, invite["invited_email"], subject, body, html_body=html_body) db.mark_invite_sent_global(invite_id) flash("Invitation email resent.", "success") except Exception: diff --git a/web/mailer.py b/web/mailer.py index 7ec7447..8a91dcd 100644 --- a/web/mailer.py +++ b/web/mailer.py @@ -1,4 +1,5 @@ import smtplib +from html import escape from email.message import EmailMessage from config import Config @@ -10,13 +11,83 @@ def build_from_header(from_email: str, from_name: str | None = None) -> str: return from_email +def build_invite_email( + username: str, + invite_url: str, + expiry_text: str, + group_name: str | None = None, + role_name: str | None = None, +) -> tuple[str, str]: + safe_user = escape(username) + safe_url = escape(invite_url) + safe_expiry = escape(expiry_text) + safe_group = escape(group_name) if group_name else None + safe_role = escape(role_name) if role_name else None -def send_mail(settings: dict, recipient: str, subject: str, text_body: str): + if safe_group: + role_part = f" as {safe_role}" if safe_role else "" + intro_html = ( + f"You have been invited to join the group {safe_group} " + f"on MCLogger{role_part}." + ) + role_text = f" as {role_name}" if role_name else "" + intro_text = f"You have been invited to join the group '{group_name}' on MCLogger{role_text}." + else: + intro_html = "You have been invited to create an account on MCLogger." + intro_text = "You have been invited to create an account on MCLogger." + + text_body = ( + f"Hello {username},\n\n" + f"{intro_text}\n" + f"Open this link to create your account:\n\n{invite_url}\n\n" + f"This invite expires {expiry_text}.\n" + ) + + html_body = f""" + + +
+| + + | +