From 9164a360e717e56914526177be24f2c3d14e34e0 Mon Sep 17 00:00:00 2001 From: gingmzmzx <2417481092@qq.com> Date: Fri, 4 Oct 2024 15:54:02 +0800 Subject: [PATCH] feat: add broadcast mail --- vj4/handler/admin.py | 18 ++++++ vj4/handler/domain.py | 5 -- vj4/locale/zh_CN.yaml | 5 ++ vj4/ui/pages/admin_mail.page.js | 63 +++++++++++++++++++ vj4/ui/pages/admin_mail.page.styl | 9 +++ vj4/ui/templates/admin_base.html | 1 + vj4/ui/templates/admin_mail.html | 63 +++++++++++++++++++ vj4/ui/templates/broadcast_mail.html | 9 +++ vj4/ui/templates/bsod.html | 4 ++ .../partials/admin_send_mail_form.html | 3 + 10 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 vj4/ui/pages/admin_mail.page.js create mode 100644 vj4/ui/pages/admin_mail.page.styl create mode 100644 vj4/ui/templates/admin_mail.html create mode 100644 vj4/ui/templates/broadcast_mail.html create mode 100644 vj4/ui/templates/partials/admin_send_mail_form.html diff --git a/vj4/handler/admin.py b/vj4/handler/admin.py index 1a3c876..0fcc8e7 100644 --- a/vj4/handler/admin.py +++ b/vj4/handler/admin.py @@ -63,3 +63,21 @@ async def post(self, *, uid: int): await user.set_priv(int(uid), privs) self.json_or_redirect(self.reverse_url("admin_user")) + +@app.route('/admin/mail', 'admin_mail') +class AdminMailHandler(base.OperationHandler): + @base.require_priv(builtin.PRIV_ADMIN) + async def get(self): + udocs = [] + async for udoc in user.get_multi(): + udocs.append(udoc) + self.render('admin_mail.html', udocs=udocs) + + @base.require_priv(builtin.PRIV_ADMIN) + @base.require_csrf_token + @base.sanitize + async def post_send_mail(self, *, uid: int, title: str, content: str): + udict = await user.get_by_uid(uid) + mail = udict['mail'] + await self.send_mail(mail, "通知 - "+str(title), "broadcast_mail.html", mail_content=content, mail_title=title) + self.json_or_redirect(self.url, mail=mail) diff --git a/vj4/handler/domain.py b/vj4/handler/domain.py index c1bb5b3..11db3de 100755 --- a/vj4/handler/domain.py +++ b/vj4/handler/domain.py @@ -26,11 +26,6 @@ from vj4.util import pagination -@app.route('/test/header', 'test_header') -class TestHeaderHandler(base.Handler): - async def get(self): - self.render('bsod.html', args=self.request.headers) - @app.route('/', 'domain_main') class DomainMainHandler(contest.ContestStatusMixin, base.OperationHandler): CONTESTS_ON_MAIN = 5 diff --git a/vj4/locale/zh_CN.yaml b/vj4/locale/zh_CN.yaml index c04a32f..3b616d1 100755 --- a/vj4/locale/zh_CN.yaml +++ b/vj4/locale/zh_CN.yaml @@ -10,6 +10,11 @@ Tools: 工具 admin_dashboard: 管理员面板 admin_user: 管理用户 admin_privilege: 管理特权 +admin_mail: 邮件广播 +Mail Content: 邮件内容 +Send mail to selected users: 发送邮件到指定用户 +Mail successfully sent to uid:{0}.: 发送邮件成功(UID:{0}) +Confirm sending Email to selected users?: 确定要给选定用户发送广播邮件? Updates Manager: 动态管理者 Collapse: 收起 Expand: 展开 diff --git a/vj4/ui/pages/admin_mail.page.js b/vj4/ui/pages/admin_mail.page.js new file mode 100644 index 0000000..0bc3f50 --- /dev/null +++ b/vj4/ui/pages/admin_mail.page.js @@ -0,0 +1,63 @@ +import _ from 'lodash'; + +import { NamedPage } from 'vj/misc/PageLoader'; +import Notification from 'vj/components/notification'; +import { ConfirmDialog } from 'vj/components/dialog'; + +import request from 'vj/utils/request'; +import tpl from 'vj/utils/tpl'; +import delay from 'vj/utils/delay'; +import i18n from 'vj/utils/i18n'; + +const page = new NamedPage('admin_mail', () => { + function ensureAndGetSelectedUsers() { + const users = _.map( + $('.admin_user_list tbody [type="checkbox"]:checked'), + ch => $(ch).closest('tr').attr('data-uid'), + ); + if (users.length === 0) { + Notification.error(i18n('Please select at least one user to perform this operation.')); + return null; + } + return users; + } + + async function sendMail(uid, title, content){ + try { + await request.post('', { + operation: 'send_mail', + uid: uid, + title: title, + content: content + }); + Notification.success(i18n('Mail successfully sent to uid:{0}.', uid)); + } catch (error) { + Notification.error(`Error uid:${uid} ${error.message}`); + } + } + + async function handleClickSend() { + const selectedUsers = ensureAndGetSelectedUsers(); + if (selectedUsers === null) { + return; + } + const action = await new ConfirmDialog({ + $body: tpl` +
+

${i18n('Confirm sending Email to selected users?')}

+
`, + }).open(); + if (action !== 'yes') { + return; + } + const mail_content_container = $("#mail_content_container"); + const mail_title = mail_content_container.find('[name="title"]').val(); + const mail_content = mail_content_container.find('[name="content"]').val(); + _.map(selectedUsers, async ch => { await sendMail(ch, mail_title, mail_content)}); + } + + + $('[name="btn_send_mail"]').click(() => handleClickSend()); +}); + +export default page; diff --git a/vj4/ui/pages/admin_mail.page.styl b/vj4/ui/pages/admin_mail.page.styl new file mode 100644 index 0000000..c1618ed --- /dev/null +++ b/vj4/ui/pages/admin_mail.page.styl @@ -0,0 +1,9 @@ +.page--admin_mail + .col--checkbox + width: 60px + + .col--uid + width: 100px + + .col--role + width: 160px diff --git a/vj4/ui/templates/admin_base.html b/vj4/ui/templates/admin_base.html index ade58b9..82c17ad 100644 --- a/vj4/ui/templates/admin_base.html +++ b/vj4/ui/templates/admin_base.html @@ -14,6 +14,7 @@ diff --git a/vj4/ui/templates/admin_mail.html b/vj4/ui/templates/admin_mail.html new file mode 100644 index 0000000..06ba375 --- /dev/null +++ b/vj4/ui/templates/admin_mail.html @@ -0,0 +1,63 @@ +{% extends "admin_base.html" %} +{% block admin_content %} +
+
+

{{ _('Send mail to selected users') }}

+
+ {{ noscript_note.render() }} +
+ + + + + + + + + + + + + + + {% for udoc in udocs %} + + + + + + {% endfor %} + +
+ + {{ _('User ID') }}{{ _('Username') }}
+ + + {{ udoc['_id'] }} + + {{ user.render_inline(udoc) }} +
+
+
+ +
+
+

{{ _('Mail Content') }}

+
+
+ {% include "partials/admin_send_mail_form.html" %} +
+ + +
+
+
+{% endblock %} diff --git a/vj4/ui/templates/broadcast_mail.html b/vj4/ui/templates/broadcast_mail.html new file mode 100644 index 0000000..6ca242c --- /dev/null +++ b/vj4/ui/templates/broadcast_mail.html @@ -0,0 +1,9 @@ +{% extends "layout/mail.html" %} +{% block title %}{{ mail_title }}{% endblock %} +{% block content %} +{{ mail_content|safe }} +
+
+
+This is a broadcast email. Do not reply. +{% endblock %} diff --git a/vj4/ui/templates/bsod.html b/vj4/ui/templates/bsod.html index afe24ac..eedfd7b 100755 --- a/vj4/ui/templates/bsod.html +++ b/vj4/ui/templates/bsod.html @@ -25,6 +25,10 @@ } +
+Debug Information
+
 {{args}}
+
diff --git a/vj4/ui/templates/partials/admin_send_mail_form.html b/vj4/ui/templates/partials/admin_send_mail_form.html new file mode 100644 index 0000000..a7073fb --- /dev/null +++ b/vj4/ui/templates/partials/admin_send_mail_form.html @@ -0,0 +1,3 @@ +{% import "components/form.html" as form with context %} +{{ form.form_text(label='Title', name='title', autofocus=true, required=true) }} +{{ form.form_textarea(columns=20, label='Content', name='content', hotkeys='ctrl+enter:submit', required=true) }}