mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-09-16 23:38:36 +00:00
feat: setting email template content (#15974)
* feat: setting email template content * perf: tempale list * perf: custom template render to string * perf: content serialize valid * perf: Custom msg template base class * perf: Template content reset * perf: Update templates config * perf: Remove useless code --------- Co-authored-by: wangruidong <940853815@qq.com>
This commit is contained in:
@@ -5,12 +5,23 @@ from accounts.models import Account
|
||||
from acls.models import LoginACL, LoginAssetACL
|
||||
from assets.models import Asset
|
||||
from audits.models import UserLoginLog
|
||||
from common.views.template import custom_render_to_string
|
||||
from notifications.notifications import UserMessage
|
||||
from users.models import User
|
||||
|
||||
|
||||
class UserLoginReminderMsg(UserMessage):
|
||||
subject = _('User login reminder')
|
||||
template_name = 'acls/user_login_reminder.html'
|
||||
contexts = [
|
||||
{"name": "city", "label": _('Login city'), "default": "北京"},
|
||||
{"name": "username", "label": _('User'), "default": "zhangsan"},
|
||||
{"name": "ip", "label": "IP", "default": "8.8.8.8"},
|
||||
{"name": "recipient_name", "label": '接收人名称', "default": "zhangsan"},
|
||||
{"name": "recipient_username", "label": '接收人用户名', "default": "张三"},
|
||||
{"name": "user_agent", "label": _('User agent'), "default": "Mozilla/5.0"},
|
||||
{"name": "acl_name", "label": _('ACL name'), "default": "login acl"},
|
||||
]
|
||||
|
||||
def __init__(self, user, user_log: UserLoginLog, acl: LoginACL):
|
||||
self.user_log = user_log
|
||||
@@ -23,11 +34,12 @@ class UserLoginReminderMsg(UserMessage):
|
||||
'ip': user_log.ip,
|
||||
'city': user_log.city,
|
||||
'username': user_log.username,
|
||||
'recipient': self.user,
|
||||
'acl_name': self.acl_name,
|
||||
'recipient_name': self.user.name,
|
||||
'recipient_username': self.user.username,
|
||||
'user_agent': user_log.user_agent,
|
||||
}
|
||||
message = render_to_string('acls/user_login_reminder.html', context)
|
||||
message = custom_render_to_string(self.template_name, context)
|
||||
|
||||
return {
|
||||
'subject': str(self.subject),
|
||||
@@ -43,6 +55,18 @@ class UserLoginReminderMsg(UserMessage):
|
||||
|
||||
class AssetLoginReminderMsg(UserMessage):
|
||||
subject = _('User login alert for asset')
|
||||
template_name = 'acls/asset_login_reminder.html'
|
||||
contexts = [
|
||||
{"name": "city", "label": _('Login city'), "default": "北京"},
|
||||
{"name": "username", "label": _('User'), "default": "zhangsan"},
|
||||
{"name": "name", "label": _('Name'), "default": "zhangsan"},
|
||||
{"name": "asset", "label": _('Asset'), "default": "dev server"},
|
||||
{"name": "recipient_name", "label": '接收人名称', "default": "zhangsan"},
|
||||
{"name": "recipient_username", "label": '接收人用户名', "default": "张三"},
|
||||
{"name": "account", "label": _('Account Input username'), "default": "root"},
|
||||
{"name": "account_name", "label": _('Account name'), "default": "root"},
|
||||
{"name": "acl_name", "label": _('ACL name'), "default": "login acl"},
|
||||
]
|
||||
|
||||
def __init__(
|
||||
self, user, asset: Asset, login_user: User,
|
||||
@@ -51,6 +75,7 @@ class AssetLoginReminderMsg(UserMessage):
|
||||
):
|
||||
self.ip = ip
|
||||
self.asset = asset
|
||||
self.login_user = login_user
|
||||
self.account = account
|
||||
self.acl_name = str(acl)
|
||||
self.login_user = login_user
|
||||
@@ -60,7 +85,8 @@ class AssetLoginReminderMsg(UserMessage):
|
||||
def get_html_msg(self) -> dict:
|
||||
context = {
|
||||
'ip': self.ip,
|
||||
'recipient': self.user,
|
||||
'recipient_name': self.user.name,
|
||||
'recipient_username': self.user.username,
|
||||
'username': self.login_user.username,
|
||||
'name': self.login_user.name,
|
||||
'asset': str(self.asset),
|
||||
@@ -68,7 +94,7 @@ class AssetLoginReminderMsg(UserMessage):
|
||||
'account_name': self.account.name,
|
||||
'acl_name': self.acl_name,
|
||||
}
|
||||
message = render_to_string('acls/asset_login_reminder.html', context)
|
||||
message = custom_render_to_string(self.template_name, context)
|
||||
|
||||
return {
|
||||
'subject': str(self.subject),
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{% load i18n %}
|
||||
|
||||
<h3>{% trans 'Dear' %}: {{ recipient.name }}[{{ recipient.username }}]</h3>
|
||||
<h3>{% trans 'Dear' %}: {{ recipient_name }}[{{ recipient_username }}]</h3>
|
||||
<hr>
|
||||
<p>{% trans 'We would like to inform you that a user has recently logged:' %}<p>
|
||||
<p><strong>{% trans 'User details' %}:</strong></p>
|
||||
|
@@ -1,14 +1,24 @@
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from common.utils import get_logger
|
||||
from common.utils.timezone import local_now_display
|
||||
from common.views.template import custom_render_to_string
|
||||
from notifications.notifications import UserMessage
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
class DifferentCityLoginMessage(UserMessage):
|
||||
subject = _('Different city login reminder')
|
||||
template_name = 'authentication/_msg_different_city.html'
|
||||
contexts = [
|
||||
{"name": "city", "label": _('Login city'), "default": "北京"},
|
||||
{"name": "username", "label": _('User'), "default": "zhangsan"},
|
||||
{"name": "name", "label": _('Name'), "default": "zhangsan"},
|
||||
{"name": "ip", "label": "IP", "default": "8.8.8.8"},
|
||||
{"name": "time", "label": _('Login Date'), "default": "2025-01-01 12:00:00"},
|
||||
]
|
||||
|
||||
def __init__(self, user, ip, city):
|
||||
self.ip = ip
|
||||
self.city = city
|
||||
@@ -16,18 +26,16 @@ class DifferentCityLoginMessage(UserMessage):
|
||||
|
||||
def get_html_msg(self) -> dict:
|
||||
now = local_now_display()
|
||||
subject = _('Different city login reminder')
|
||||
context = dict(
|
||||
subject=subject,
|
||||
name=self.user.name,
|
||||
username=self.user.username,
|
||||
ip=self.ip,
|
||||
time=now,
|
||||
city=self.city,
|
||||
)
|
||||
message = render_to_string('authentication/_msg_different_city.html', context)
|
||||
message = custom_render_to_string(self.template_name, context)
|
||||
return {
|
||||
'subject': subject,
|
||||
'subject': self.subject,
|
||||
'message': message
|
||||
}
|
||||
|
||||
@@ -41,6 +49,16 @@ class DifferentCityLoginMessage(UserMessage):
|
||||
|
||||
|
||||
class OAuthBindMessage(UserMessage):
|
||||
subject = _('OAuth binding reminder')
|
||||
template_name = 'authentication/_msg_oauth_bind.html'
|
||||
contexts = [
|
||||
{"name": "username", "label": _('User'), "default": "zhangsan"},
|
||||
{"name": "name", "label": _('Name'), "default": "zhangsan"},
|
||||
{"name": "ip", "label": "IP", "default": "8.8.8.8"},
|
||||
{"name": "oauth_name", "label": _('OAuth name'), "default": "WeCom"},
|
||||
{"name": "oauth_id", "label": _('OAuth ID'), "default": "000001"},
|
||||
]
|
||||
|
||||
def __init__(self, user, ip, oauth_name, oauth_id):
|
||||
super().__init__(user)
|
||||
self.ip = ip
|
||||
@@ -51,7 +69,6 @@ class OAuthBindMessage(UserMessage):
|
||||
now = local_now_display()
|
||||
subject = self.oauth_name + ' ' + _('binding reminder')
|
||||
context = dict(
|
||||
subject=subject,
|
||||
name=self.user.name,
|
||||
username=self.user.username,
|
||||
ip=self.ip,
|
||||
@@ -59,7 +76,7 @@ class OAuthBindMessage(UserMessage):
|
||||
oauth_name=self.oauth_name,
|
||||
oauth_id=self.oauth_id
|
||||
)
|
||||
message = render_to_string('authentication/_msg_oauth_bind.html', context)
|
||||
message = custom_render_to_string(self.template_name, context)
|
||||
return {
|
||||
'subject': subject,
|
||||
'message': message
|
||||
|
@@ -6,12 +6,12 @@
|
||||
{% trans 'Please click the link below to reset your password, if not your request, concern your account security' %}
|
||||
<br>
|
||||
<br>
|
||||
<a href="{{ rest_password_url }}?token={{ rest_password_token}}" class='showLink' target="_blank">
|
||||
<a href="{{ rest_password_url }}?token={{ rest_password_token }}" class='showLink' target="_blank">
|
||||
{% trans 'Click here reset password' %}
|
||||
</a>
|
||||
</p>
|
||||
<br>
|
||||
<p>
|
||||
{% trans 'This link is valid for 1 hour. After it expires' %}
|
||||
<a href="{{ forget_password_url }}?email={{ user.email }}">{% trans 'request new one' %}</a>
|
||||
<a href="{{ forget_password_url }}?email={{ email }}">{% trans 'request new one' %}</a>
|
||||
</p>
|
||||
|
43
apps/common/views/template.py
Normal file
43
apps/common/views/template.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.template import Context
|
||||
from django.template import Engine, TemplateSyntaxError
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils._os import safe_join
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def safe_render_to_string(template_name, context=None, request=None, using=None):
|
||||
with open(template_name, encoding="utf-8") as f:
|
||||
template_code = f.read()
|
||||
safe_engine = Engine(
|
||||
debug=False,
|
||||
libraries={}, # 禁用自定义 tag 库
|
||||
builtins=[], # 不自动加载内置标签
|
||||
)
|
||||
try:
|
||||
template = safe_engine.from_string(template_code)
|
||||
except TemplateSyntaxError as e:
|
||||
logger.error(e)
|
||||
return template_code
|
||||
return template.render(Context(context or {}))
|
||||
|
||||
|
||||
def _get_data_template_path(template_name: str):
|
||||
# 保存到 data/template/<原路径>.html
|
||||
# 例如 template_name users/_msg_x.html -> data/template/users/_msg_x.html
|
||||
rel_path = template_name.replace('/', os.sep)
|
||||
return safe_join(settings.DATA_DIR, 'template', rel_path)
|
||||
|
||||
|
||||
def custom_render_to_string(template_name, context=None, request=None, using=None):
|
||||
# 如果自定的义模板存在,则使用自定义模板,否则使用系统模板
|
||||
custom_template = _get_data_template_path(template_name)
|
||||
if os.path.exists(custom_template):
|
||||
template = safe_render_to_string(custom_template, context=context, request=request, using=using)
|
||||
else:
|
||||
template = render_to_string(template_name, context=context, request=request, using=using)
|
||||
return template
|
@@ -1,20 +1,28 @@
|
||||
import os
|
||||
|
||||
from django.template.loader import render_to_string
|
||||
from rest_framework import status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.mixins import ListModelMixin, UpdateModelMixin, RetrieveModelMixin
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from common.api import JMSGenericViewSet
|
||||
from common.permissions import IsValidUser
|
||||
from common.permissions import OnlySuperUser, IsValidUser
|
||||
from common.views.template import _get_data_template_path
|
||||
from notifications.backends import BACKEND
|
||||
from notifications.models import SystemMsgSubscription, UserMsgSubscription
|
||||
from notifications.notifications import CustomMsgTemplateBase
|
||||
from notifications.notifications import system_msgs
|
||||
from notifications.serializers import (
|
||||
SystemMsgSubscriptionSerializer, SystemMsgSubscriptionByCategorySerializer,
|
||||
UserMsgSubscriptionSerializer,
|
||||
)
|
||||
from notifications.serializers import TemplateEditSerializer
|
||||
|
||||
__all__ = (
|
||||
'BackendListView', 'SystemMsgSubscriptionViewSet',
|
||||
'UserMsgSubscriptionViewSet', 'get_all_test_messages'
|
||||
'UserMsgSubscriptionViewSet', 'get_all_test_messages', 'TemplateViewSet',
|
||||
)
|
||||
|
||||
|
||||
@@ -130,3 +138,72 @@ def get_all_test_messages(request):
|
||||
<hr />
|
||||
""").format(msg_cls.__name__, msg_text)
|
||||
return HttpResponse(html_data + text_data)
|
||||
|
||||
|
||||
class TemplateViewSet(JMSGenericViewSet):
|
||||
permission_classes = [OnlySuperUser]
|
||||
|
||||
def list(self, request):
|
||||
result = []
|
||||
metas = [cls.as_dict() for cls in CustomMsgTemplateBase._registry]
|
||||
for meta in metas:
|
||||
item = {
|
||||
'template_name': meta['template_name'],
|
||||
'subject': meta.get('subject', ''),
|
||||
'contexts': meta.get('contexts', []),
|
||||
'content': None,
|
||||
'content_error': None,
|
||||
'source': None,
|
||||
}
|
||||
|
||||
data_path = _get_data_template_path(meta['template_name'])
|
||||
try:
|
||||
if os.path.exists(data_path):
|
||||
with open(data_path, 'r', encoding='utf-8') as f:
|
||||
item['content'] = f.read()
|
||||
item['source'] = 'data'
|
||||
else:
|
||||
ctx = {x.get('name'): x.get('default') for x in item['contexts']}
|
||||
try:
|
||||
rendered = render_to_string(meta['template_name'], ctx)
|
||||
item['content'] = rendered
|
||||
item['source'] = 'original'
|
||||
except Exception as e:
|
||||
item['content_error'] = str(e)
|
||||
item['source'] = 'original_error'
|
||||
except Exception as e:
|
||||
item['content_error'] = str(e)
|
||||
result.append(item)
|
||||
|
||||
return Response(result)
|
||||
|
||||
@action(detail=False, methods=['patch'], url_path='edit', name='edit')
|
||||
def edit(self, request):
|
||||
"""保存前端编辑的模板内容到 data/template/<template_name> 目录"""
|
||||
|
||||
serializer = TemplateEditSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
template_name = serializer.validated_data['EMAIL_TEMPLATE_NAME']
|
||||
content = serializer.validated_data['EMAIL_TEMPLATE_CONTENT']
|
||||
|
||||
data_path = _get_data_template_path(template_name)
|
||||
data_dir = os.path.dirname(data_path)
|
||||
try:
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
with open(data_path, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
except Exception as e:
|
||||
return Response({'ok': False, 'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
|
||||
return Response({'ok': True, 'path': data_path})
|
||||
|
||||
@action(detail=False, methods=['post'], url_path='reset', name='reset')
|
||||
def reset(self, request):
|
||||
template_name = request.data.get('template_name')
|
||||
data_path = _get_data_template_path(template_name)
|
||||
try:
|
||||
if os.path.exists(data_path):
|
||||
os.remove(data_path)
|
||||
except Exception as e:
|
||||
return Response({'ok': False, 'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return Response({'ok': True, 'path': data_path})
|
||||
|
@@ -54,7 +54,24 @@ def publish_task(receive_user_ids, backends_msg_mapper):
|
||||
Message.send_msg(receive_user_ids, backends_msg_mapper)
|
||||
|
||||
|
||||
class Message(metaclass=MessageType):
|
||||
class CustomMsgTemplateBase:
|
||||
_registry = []
|
||||
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
super().__init_subclass__(**kwargs)
|
||||
if cls is not CustomMsgTemplateBase and getattr(cls, "template_name", None):
|
||||
CustomMsgTemplateBase._registry.append(cls)
|
||||
|
||||
@classmethod
|
||||
def as_dict(cls):
|
||||
return {
|
||||
"template_name": cls.template_name,
|
||||
"subject": cls.subject,
|
||||
"contexts": cls.contexts,
|
||||
}
|
||||
|
||||
|
||||
class Message(CustomMsgTemplateBase, metaclass=MessageType):
|
||||
"""
|
||||
这里封装了什么?
|
||||
封装不同消息的模板,提供统一的发送消息的接口
|
||||
|
@@ -1,3 +1,4 @@
|
||||
from django.template import Engine, TemplateSyntaxError
|
||||
from rest_framework import serializers
|
||||
|
||||
from common.serializers import BulkModelSerializer
|
||||
@@ -40,3 +41,16 @@ class UserMsgSubscriptionSerializer(BulkModelSerializer):
|
||||
class Meta:
|
||||
model = UserMsgSubscription
|
||||
fields = ('user_id', 'receive_backends',)
|
||||
|
||||
|
||||
class TemplateEditSerializer(serializers.Serializer):
|
||||
EMAIL_TEMPLATE_NAME = serializers.CharField(max_length=256)
|
||||
EMAIL_TEMPLATE_CONTENT = serializers.CharField()
|
||||
|
||||
def validate_EMAIL_TEMPLATE_CONTENT(self, value):
|
||||
safe_engine = Engine(debug=False, libraries={}, builtins=[])
|
||||
try:
|
||||
safe_engine.from_string(value)
|
||||
except TemplateSyntaxError as e:
|
||||
raise serializers.ValidationError(f'Template syntax error at: {e.token.lineno}')
|
||||
return value
|
||||
|
@@ -10,9 +10,9 @@ router = BulkRouter()
|
||||
router.register('system-msg-subscription', api.SystemMsgSubscriptionViewSet, 'system-msg-subscription')
|
||||
router.register('user-msg-subscription', api.UserMsgSubscriptionViewSet, 'user-msg-subscription')
|
||||
router.register('site-messages', api.SiteMessageViewSet, 'site-message')
|
||||
|
||||
router.register('templates', api.TemplateViewSet, 'template')
|
||||
urlpatterns = [
|
||||
path('backends/', api.BackendListView.as_view(), name='backends')
|
||||
path('backends/', api.BackendListView.as_view(), name='backends'),
|
||||
]
|
||||
urlpatterns += router.urls
|
||||
|
||||
|
@@ -8,7 +8,7 @@ from common.serializers.fields import EncryptedField
|
||||
|
||||
__all__ = [
|
||||
'MailTestSerializer', 'EmailSettingSerializer',
|
||||
'EmailContentSettingSerializer', 'SMSBackendSerializer',
|
||||
'EmailContentSettingSerializer', 'SMSBackendSerializer'
|
||||
]
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ class EmailSettingSerializer(serializers.Serializer):
|
||||
)
|
||||
EMAIL_HOST_PASSWORD = EncryptedField(
|
||||
max_length=1024, required=False, label=_("Password"),
|
||||
help_text=_("Password to use for the email server. It is used in conjunction with `Account` when authenticating to the email server")
|
||||
help_text=_(
|
||||
"Password to use for the email server. It is used in conjunction with `Account` when authenticating to the email server")
|
||||
)
|
||||
EMAIL_FROM = serializers.CharField(
|
||||
max_length=128, allow_blank=True, required=False, label=_('Sender'),
|
||||
|
@@ -8,6 +8,7 @@ from common.sdk.im.wecom import wecom_tool
|
||||
from common.utils import get_logger, reverse
|
||||
from common.utils import lazyproperty
|
||||
from common.utils.timezone import local_now_display
|
||||
from common.views.template import custom_render_to_string
|
||||
from notifications.backends import BACKEND
|
||||
from notifications.models import SystemMsgSubscription
|
||||
from notifications.notifications import SystemMessage, UserMessage
|
||||
@@ -280,7 +281,17 @@ class StorageConnectivityMessage(SystemMessage):
|
||||
|
||||
|
||||
class SessionSharingMessage(UserMessage):
|
||||
subject = _('Session sharing')
|
||||
message_type_label = _('Session sharing')
|
||||
template_name = 'terminal/_msg_session_sharing.html'
|
||||
contexts = [
|
||||
{"name": "asset", "label": _('Asset'), "default": "dev server"},
|
||||
{"name": "created_by", "label": _('Created by'), "default": "2025-01-01 10:00:00"},
|
||||
{"name": "account", "label": _('Account'), "default": "root"},
|
||||
{"name": "url", "label": _('URL'), "default": "http://example.com/session/xxxx"},
|
||||
{"name": "verify_code", "label": _('Verify code'), "default": "123456"},
|
||||
{"name": "org", "label": _('Organization'), "default": "Default Default"},
|
||||
]
|
||||
|
||||
def __init__(self, user, instance):
|
||||
super().__init__(user)
|
||||
@@ -289,14 +300,14 @@ class SessionSharingMessage(UserMessage):
|
||||
def get_html_msg(self) -> dict:
|
||||
instance = self.instance
|
||||
context = {
|
||||
'asset': instance.session.asset,
|
||||
'asset': str(instance.session.asset),
|
||||
'created_by': instance.created_by,
|
||||
'account': instance.session.account,
|
||||
'account': str(instance.session.account),
|
||||
'url': instance.url,
|
||||
'verify_code': instance.verify_code,
|
||||
'org': instance.org_name,
|
||||
}
|
||||
message = render_to_string('terminal/_msg_session_sharing.html', context)
|
||||
message = custom_render_to_string(self.template_name, context)
|
||||
return {
|
||||
'subject': self.message_type_label + ' ' + self.instance.created_by,
|
||||
'message': message
|
||||
|
@@ -7,10 +7,25 @@ from django.utils import timezone
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from common.utils import reverse, get_request_ip_or_data, get_request_user_agent
|
||||
from common.views.template import custom_render_to_string
|
||||
from notifications.notifications import UserMessage
|
||||
|
||||
|
||||
class UserCreatedMsg(UserMessage):
|
||||
subject = str(settings.EMAIL_CUSTOM_USER_CREATED_SUBJECT)
|
||||
template_name = 'users/_msg_user_created.html'
|
||||
contexts = [
|
||||
{"name": "honorific", "label": _('Honorific'), "default": "zhangsan"},
|
||||
{"name": "content", "label": _('Content'), "default": "Welcome to use our system."},
|
||||
{"name": "username", "label": _('Username'), "default": "zhangsan"},
|
||||
{"name": "name", "label": _('Name'), "default": "张三"},
|
||||
{"name": "email", "label": _('Email'), "default": "123456@qq.com"},
|
||||
{"name": "rest_password_url", "label": _('Reset password url'),
|
||||
"default": "https://example.com/reset-password"},
|
||||
{"name": "rest_password_token", "label": _('Reset password token'), "default": "abcdefg1234567"},
|
||||
{"name": "forget_password_url", "label": _('Login url'), "default": "https://example.com/forget-password"},
|
||||
]
|
||||
|
||||
def get_html_msg(self) -> dict:
|
||||
user = self.user
|
||||
|
||||
@@ -27,13 +42,12 @@ class UserCreatedMsg(UserMessage):
|
||||
|
||||
context = {
|
||||
**mail_context,
|
||||
'user': user,
|
||||
**user_info,
|
||||
'rest_password_url': reverse('authentication:reset-password', external=True),
|
||||
'rest_password_token': user.generate_reset_token(),
|
||||
'forget_password_url': reverse('authentication:forgot-password', external=True),
|
||||
'login_url': reverse('authentication:login', external=True),
|
||||
'forget_password_url': reverse('authentication:login', external=True),
|
||||
}
|
||||
message = render_to_string('users/_msg_user_created.html', context)
|
||||
message = custom_render_to_string(self.template_name, context)
|
||||
return {
|
||||
'subject': mail_context['subject'],
|
||||
'message': message
|
||||
@@ -46,15 +60,25 @@ class UserCreatedMsg(UserMessage):
|
||||
|
||||
|
||||
class ResetPasswordMsg(UserMessage):
|
||||
subject = _('Reset password')
|
||||
template_name = 'authentication/_msg_reset_password.html'
|
||||
contexts = [
|
||||
{"name": "email", "label": _('Email'), "default": "123456@qq.com"},
|
||||
{"name": "rest_password_url", "label": _('Reset password url'),
|
||||
"default": "https://example.com/reset-password"},
|
||||
{"name": "rest_password_token", "label": _('Reset password token'), "default": "abcdefg1234567"},
|
||||
{"name": "forget_password_url", "label": _('Login url'), "default": "https://example.com/forget-password"},
|
||||
{"name": "login_url", "label": _('Login url'), "default": "https://example.com/login"},
|
||||
]
|
||||
|
||||
def __init__(self, user):
|
||||
super().__init__(user)
|
||||
self.reset_passwd_token = user.generate_reset_token()
|
||||
|
||||
def get_html_msg(self) -> dict:
|
||||
user = self.user
|
||||
subject = _('Reset password')
|
||||
context = {
|
||||
'user': user,
|
||||
'email': user.email,
|
||||
'rest_password_url': reverse('authentication:reset-password', external=True),
|
||||
'rest_password_token': self.reset_passwd_token,
|
||||
'forget_password_url': reverse('authentication:forgot-password', external=True),
|
||||
@@ -62,7 +86,7 @@ class ResetPasswordMsg(UserMessage):
|
||||
}
|
||||
message = render_to_string('authentication/_msg_reset_password.html', context)
|
||||
return {
|
||||
'subject': subject,
|
||||
'subject': self.subject,
|
||||
'message': message
|
||||
}
|
||||
|
||||
@@ -74,6 +98,14 @@ class ResetPasswordMsg(UserMessage):
|
||||
|
||||
|
||||
class ResetPasswordSuccessMsg(UserMessage):
|
||||
subject = _('Reset password success')
|
||||
template_name = 'authentication/_msg_rest_password_success.html'
|
||||
contexts = [
|
||||
{"name": "name", "label": _('Name'), "default": "张三"},
|
||||
{"name": "ip_address", "label": _('IP address'), "default": "192.168.1.1"},
|
||||
{"name": "browser", "label": _('Browser'), "default": "Mozilla/firefox"}
|
||||
]
|
||||
|
||||
def __init__(self, user, request):
|
||||
super().__init__(user)
|
||||
self.ip_address = get_request_ip_or_data(request)
|
||||
@@ -82,15 +114,14 @@ class ResetPasswordSuccessMsg(UserMessage):
|
||||
def get_html_msg(self) -> dict:
|
||||
user = self.user
|
||||
|
||||
subject = _('Reset password success')
|
||||
context = {
|
||||
'name': user.name,
|
||||
'ip_address': self.ip_address,
|
||||
'browser': self.browser,
|
||||
}
|
||||
message = render_to_string('authentication/_msg_rest_password_success.html', context)
|
||||
message = custom_render_to_string(self.template_name, context)
|
||||
return {
|
||||
'subject': subject,
|
||||
'subject': self.subject,
|
||||
'message': message
|
||||
}
|
||||
|
||||
@@ -106,6 +137,14 @@ class ResetPasswordSuccessMsg(UserMessage):
|
||||
|
||||
|
||||
class ResetPublicKeySuccessMsg(UserMessage):
|
||||
subject = _('Reset public key success')
|
||||
template_name = 'authentication/_msg_rest_public_key_success.html'
|
||||
contexts = [
|
||||
{"name": "name", "label": _('Name'), "default": "张三"},
|
||||
{"name": "ip_address", "label": _('IP address'), "default": "192.168.1.1"},
|
||||
{"name": "browser", "label": _('Browser'), "default": "Mozilla/firefox"}
|
||||
]
|
||||
|
||||
def __init__(self, user, request):
|
||||
super().__init__(user)
|
||||
self.ip_address = get_request_ip_or_data(request)
|
||||
@@ -114,15 +153,14 @@ class ResetPublicKeySuccessMsg(UserMessage):
|
||||
def get_html_msg(self) -> dict:
|
||||
user = self.user
|
||||
|
||||
subject = _('Reset public key success')
|
||||
context = {
|
||||
'name': user.name,
|
||||
'ip_address': self.ip_address,
|
||||
'browser': self.browser,
|
||||
}
|
||||
message = render_to_string('authentication/_msg_rest_public_key_success.html', context)
|
||||
message = custom_render_to_string(self.template_name, context)
|
||||
return {
|
||||
'subject': subject,
|
||||
'subject': self.subject,
|
||||
'message': message
|
||||
}
|
||||
|
||||
@@ -138,9 +176,20 @@ class ResetPublicKeySuccessMsg(UserMessage):
|
||||
|
||||
|
||||
class PasswordExpirationReminderMsg(UserMessage):
|
||||
subject = _('Password is about expire')
|
||||
template_name = 'users/_msg_password_expire_reminder.html'
|
||||
contexts = [
|
||||
{"name": "name", "label": _('Name'), "default": "张三"},
|
||||
{"name": "date_password_expired", "label": _('Password expiration date'), "default": "2025-01-01 12:00:00"},
|
||||
{"name": "update_password_url", "label": _('Update password url'),
|
||||
"default": "https://example.com/update-password"},
|
||||
{"name": "forget_password_url", "label": _('Login url'), "default": "https://example.com/forget-password"},
|
||||
{"name": "email", "label": _('Email'), "default": "123456@qq.com"},
|
||||
{"name": "login_url", "label": _('Login url'), "default": "https://example.com/login"},
|
||||
]
|
||||
|
||||
def get_html_msg(self) -> dict:
|
||||
user = self.user
|
||||
subject = _('Password is about expire')
|
||||
|
||||
date_password_expired_local = timezone.localtime(user.date_password_expired)
|
||||
update_password_url = urljoin(settings.SITE_URL, '/ui/#/profile/index')
|
||||
@@ -153,9 +202,9 @@ class PasswordExpirationReminderMsg(UserMessage):
|
||||
'email': user.email,
|
||||
'login_url': reverse('authentication:login', external=True),
|
||||
}
|
||||
message = render_to_string('users/_msg_password_expire_reminder.html', context)
|
||||
message = custom_render_to_string(self.template_name, context)
|
||||
return {
|
||||
'subject': subject,
|
||||
'subject': self.subject,
|
||||
'message': message
|
||||
}
|
||||
|
||||
@@ -167,17 +216,23 @@ class PasswordExpirationReminderMsg(UserMessage):
|
||||
|
||||
|
||||
class UserExpirationReminderMsg(UserMessage):
|
||||
subject = _('Account is about expire')
|
||||
template_name = 'users/_msg_account_expire_reminder.html'
|
||||
contexts = [
|
||||
{"name": "name", "label": _('Name'), "default": "张三"},
|
||||
{"name": "date_expired", "label": _('Expiration date'), "default": "2025-01-01 12:00:00"}
|
||||
]
|
||||
|
||||
def get_html_msg(self) -> dict:
|
||||
subject = _('Account is about expire')
|
||||
date_expired_local = timezone.localtime(self.user.date_expired)
|
||||
date_expired = date_expired_local.strftime('%Y-%m-%d %H:%M:%S')
|
||||
context = {
|
||||
'name': self.user.name,
|
||||
'date_expired': date_expired
|
||||
}
|
||||
message = render_to_string('users/_msg_account_expire_reminder.html', context)
|
||||
message = render_to_string(self.template_name, context)
|
||||
return {
|
||||
'subject': subject,
|
||||
'subject': self.subject,
|
||||
'message': message
|
||||
}
|
||||
|
||||
@@ -189,16 +244,22 @@ class UserExpirationReminderMsg(UserMessage):
|
||||
|
||||
|
||||
class ResetSSHKeyMsg(UserMessage):
|
||||
subject = _('Reset SSH Key')
|
||||
template_name = 'users/_msg_reset_ssh_key.html'
|
||||
contexts = [
|
||||
{"name": "name", "label": _('Name'), "default": "张三"},
|
||||
{"name": "url", "label": _('Update SSH Key url'), "default": "https://example.com/profile/password-and-ssh-key"}
|
||||
]
|
||||
|
||||
def get_html_msg(self) -> dict:
|
||||
subject = _('Reset SSH Key')
|
||||
update_url = urljoin(settings.SITE_URL, '/ui/#/profile/password-and-ssh-key/?tab=SSHKey')
|
||||
context = {
|
||||
'name': self.user.name,
|
||||
'url': update_url,
|
||||
}
|
||||
message = render_to_string('users/_msg_reset_ssh_key.html', context)
|
||||
message = custom_render_to_string(self.template_name, context)
|
||||
return {
|
||||
'subject': subject,
|
||||
'subject': self.subject,
|
||||
'message': message
|
||||
}
|
||||
|
||||
@@ -210,15 +271,21 @@ class ResetSSHKeyMsg(UserMessage):
|
||||
|
||||
|
||||
class ResetMFAMsg(UserMessage):
|
||||
subject = _('Reset MFA')
|
||||
template_name = 'users/_msg_reset_mfa.html'
|
||||
contexts = [
|
||||
{"name": "name", "label": _('Name'), "default": "张三"},
|
||||
{"name": "url", "label": _('Reset MFA url'), "default": "https://example.com/profile/mfa"}
|
||||
]
|
||||
|
||||
def get_html_msg(self) -> dict:
|
||||
subject = _('Reset MFA')
|
||||
context = {
|
||||
'name': self.user.name,
|
||||
'url': reverse('authentication:user-otp-enable-start', external=True),
|
||||
}
|
||||
message = render_to_string('users/_msg_reset_mfa.html', context)
|
||||
message = custom_render_to_string('users/_msg_reset_mfa.html', context)
|
||||
return {
|
||||
'subject': subject,
|
||||
'subject': self.subject,
|
||||
'message': message
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{% load i18n %}
|
||||
|
||||
<p>
|
||||
{{ honorific }} {{ user }},
|
||||
{{ honorific }} {{ name }},
|
||||
</p>
|
||||
|
||||
<div>
|
||||
@@ -9,15 +9,15 @@
|
||||
{{ content | safe }}
|
||||
</p>
|
||||
<p>
|
||||
{% trans 'Username' %}: {{ user.username }} <br />
|
||||
{% trans 'Username' %}: {{ username }} <br/>
|
||||
{% trans 'Password' %}:
|
||||
<a href="{{ rest_password_url}}?token={{ rest_password_token }}">
|
||||
<a href="{{ rest_password_url }}?token={{ rest_password_token }}">
|
||||
{% trans 'click here to set your password' %}
|
||||
</a>
|
||||
</p>
|
||||
<br>
|
||||
<p>
|
||||
{% trans 'This link is valid for 1 hour. After it expires' %}
|
||||
<a href="{{ forget_password_url }}?email={{ user.email }}">{% trans 'request new one' %}</a>
|
||||
{% trans 'This link is valid for 1 hour. After it expires' %}
|
||||
<a href="{{ forget_password_url }}?email={{ email }}">{% trans 'request new one' %}</a>
|
||||
</p>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user