core&ui: new domain_join logic

This commit is contained in:
undefined committed 2025-03-20 17:55:10 +00:00
1 parent 706a9a0928
commit 7fd01715bc
9 files changed
+81 -39

No files matched your search

+42 -18
View File
@@ -114,12 +114,11 @@ class DomainUserHandler extends ManageHandler {
// TODO: switch to getListForRender for better performance
const udict = await user.getList(domainId, dudocs.map((dudoc) => dudoc.uid));
const users = dudocs.filter((dudoc) => udict[dudoc.uid].priv & PRIV.PRIV_USER_PROFILE);
for (const role of roles) {
rudocs[role._id] = users.filter((dudoc) => dudoc.role === role._id).map((i) => udict[i.uid]);
}
const joined = Object.fromEntries(dudocs.map((dudoc) => [dudoc.uid, dudoc.join]));
for (const role of roles) rudocs[role._id] = users.filter((udoc) => udoc.role === role._id);
this.response.template = format === 'raw' ? 'domain_user_raw.html' : 'domain_user.html';
this.response.body = {
roles, rudocs, udict, domain: this.domain,
roles, rudocs, domain: this.domain, joined,
};
}
@@ -215,7 +214,7 @@ class DomainJoinApplicationsHandler extends ManageHandler {
async get() {
const r = await domain.getRoles(this.domain);
const roles = r.map((role) => role._id).sort();
this.response.body.rolesWithText = roles.filter((i) => !['default', 'guest'].includes(i)).map((role) => [role, role]);
this.response.body.rolesWithText = roles.filter((i) => i !== 'guest').map((role) => [role, role]);
this.response.body.joinSettings = domain.getJoinSettings(this.domain, roles);
this.response.body.expirations = { ...domain.JOIN_EXPIRATION_RANGE };
if (!this.response.body.joinSettings) {
@@ -279,33 +278,58 @@ class DomainJoinHandler extends Handler {
joinSettings: any;
noCheckPermView = true;
async prepare() {
const r = await domain.getRoles(this.domain);
@param('target', Types.DomainId, true)
async prepare({ domainId }, target: string = domainId) {
const [ddoc, dudoc] = await Promise.all([
domain.get(target),
domain.getDomainUser(target, this.user),
]);
if (dudoc.join) throw new DomainJoinAlreadyMemberError(target, this.user._id);
const r = await domain.getRoles(ddoc);
const roles = r.map((role) => role._id);
this.joinSettings = domain.getJoinSettings(this.domain, roles);
if (!this.joinSettings) throw new DomainJoinForbiddenError(this.domain._id);
if (this.user.role !== 'default') throw new DomainJoinAlreadyMemberError(this.domain._id, this.user._id);
this.joinSettings = domain.getJoinSettings(ddoc, roles);
if (dudoc.role !== 'default') delete this.joinSettings;
else if (!this.joinSettings) throw new DomainJoinForbiddenError(target);
}
@param('code', Types.Content, true)
async get(domainId: string, code: string) {
@param('target', Types.DomainId, true)
@param('redirect', Types.Content, true)
async get({ domainId }, code: string, target: string = domainId, redirect: string = '') {
this.response.template = 'domain_join.html';
this.response.body.joinSettings = this.joinSettings;
this.response.body.code = code;
const ddoc = await domain.get(target);
const domainInfo = {
name: ddoc.name,
owner: await user.getById(domainId, ddoc.owner),
avatar: ddoc.avatar,
bulletin: ddoc.showBulletin ? ddoc.bulletin : '',
};
this.response.body = {
joinSettings: this.joinSettings,
code,
redirect,
target,
domainInfo,
};
}
@param('code', Types.Content, true)
async post(domainId: string, code: string) {
if (this.joinSettings.method === domain.JOIN_METHOD_CODE) {
@param('target', Types.DomainId, true)
@param('redirect', Types.Content, true)
async post({ domainId }, code: string, target: string = domainId, redirect: string = '') {
if (this.joinSettings?.method === domain.JOIN_METHOD_CODE) {
if (this.joinSettings.code !== code) {
throw new InvalidJoinInvitationCodeError(this.domain._id);
throw new InvalidJoinInvitationCodeError(target);
}
}
await Promise.all([
domain.setUserRole(this.domain._id, this.user._id, this.joinSettings.role),
domain.setUserInDomain(target, this.user._id, {
join: true,
...(this.joinSettings ? { role: this.joinSettings.role } : {}),
}),
oplog.log(this, 'domain.join', {}),
]);
this.response.redirect = this.url('homepage', { query: { notification: 'Successfully joined domain.' } });
this.response.redirect = redirect || this.url('homepage', { domainId: target, query: { notification: 'Successfully joined domain.' } });
}
}
+1
View File
@@ -207,6 +207,7 @@ class DomainModel {
let dudoc = await collUser.findOne({ domainId, uid: udoc._id });
dudoc ||= { domainId, uid: udoc._id };
if (!(udoc.priv & PRIV.PRIV_USER_PROFILE)) dudoc.role = 'guest';
if (!dudoc.join) dudoc.role = 'guest';
if (udoc.priv & PRIV.PRIV_MANAGE_ALL_DOMAIN) dudoc.role = 'root';
dudoc.role ||= 'default';
const ddoc = await DomainModel.get(domainId);
+8
View File
@@ -212,6 +212,14 @@ export async function apply(ctx: Context) {
redirect: (this.context.originalPath || this.request.path) + this.context.search,
},
});
} else if (!this.user._dudoc.join && error instanceof PermissionError) {
this.response.redirect = this.url('domain_join', {
domainId: 'system',
query: {
redirect: (this.context.originalPath || this.request.path) + this.context.search,
target: this.domain._id,
},
});
} else {
this.response.status = error instanceof UserFacingError ? error.code : 500;
this.response.template = error instanceof UserFacingError ? 'error.html' : 'bsod.html';
+4 -1
View File
@@ -489,6 +489,8 @@ IOI(Strict): IOI(严格)
Is rated: 计入排名
It works!: 它成了!
Italic: 斜体
Join Domain to Create a Discussion: 加入域后创建讨论
Join Domain to Submit: 加入域后递交
Join: 加入
Judge Config: 评测设置
Judge Status: 评测状态
@@ -603,6 +605,7 @@ Not Attended: 未参加
Not available: 不可用
Not Claimed: 未认领
Not Enrolled: 未参加
Not joined yet: 暂未加入
Not started with a number: 不以数字开头
Not wrapped with space: 两侧不含空格
Numeric PID: 数字题号
@@ -698,9 +701,9 @@ problem_import: 导入题目
problem_main: 题库
problem_solution: 题解
problem_submit: 递交代码
problem_type.communication: 通信题
problem_type.default: 传统题
problem_type.interactive: 交互题
problem_type.communication: 通信题
problem_type.objective: 客观题
problem_type.remote_judge: 远端评测题
problem_type.submit_answer: 提交答案题
+17 -2
View File
@@ -5,12 +5,25 @@
<div class="section">
<div class="section__header">
<h1 class="section__title">
{{ _('Join {0}').format(handler.domain['name']) }}
{% if joinSettings %}
{{ _('Join {0}').format(domainInfo['name']) }}
{% else %}
{{ _('You are invited to join {0}').format(domainInfo['name']) }}
{% endif %}
</h1>
</div>
<div class="section__body typo">
<p>{{ _('By clicking the button, you will become a member of the domain {0}.').format(handler.domain['name']) }}</p>
<p>{{ _('By clicking the button, you will become a member of the domain {0}.').format(domainInfo['name']) }}</p>
</div>
<div class="section__body typo">
<p>{{ _('The domain owner:') }}</p>
{{ user.render_inline(domainInfo['owner']) }}
</div>
{% if domainInfo['bulletin'] %}
<div class="section__body typo">
{{ domainInfo['bulletin']|markdown }}
</div>
{% endif %}
<div class="section__body typo">
<form method="post">
{% if joinSettings['method'] == model.domain.JOIN_METHOD_CODE %}
@@ -24,6 +37,8 @@
autofocus:true
}) }}
{% endif %}
<input type="hidden" name="target" value="{{ target }}">
<input type="hidden" name="redirect" value="{{ redirect }}">
<button type="submit" class="primary rounded button">{{ _('Join') }}</button>
</form>
</div>
@@ -25,14 +25,6 @@
<div class="section__header">
<h1 class="section__title">{{ _('Settings') }}</h1>
</div>
{% if rolesWithText.length == 1 %}
<div class="section__body typo">
<blockquote class="warn">
<p>{{ _("The 'default' role applies to ALL REGISTERED USER.")|safe }}</p>
<p>{{ _("You may want to create a new role in 'Manage Role' page.")|safe }}</p>
</blockquote>
</div>
{% endif %}
<div class="section__body">
<form method="POST">
{{ form.form_select({
@@ -5,14 +5,6 @@
<div class="section__header">
<h1 class="section__title">{{ _('{0}: Permissions').format(domain.name) }}</h1>
</div>
{% if roles.length == 3 %}
<div class="section__body typo">
<blockquote class="warn">
<p>{{ _("The 'default' role applies to ALL REGISTERED USER.")|safe }}</p>
<p>{{ _("You may want to create a new role in 'Manage Role' page.")|safe }}</p>
</blockquote>
</div>
{% endif %}
<div class="section__body no-padding domain-users">
<table class="data-table">
<colgroup>
@@ -100,7 +100,8 @@
{{ rudoc._id }}
</td>
<td class="col--user">
{{ user.render_inline(udict[rudoc._id], badge=false) }}
{{ user.render_inline(rudoc, badge=false) }}
{% if not joined[rudoc._id] %}<span class="text-orange">({{ _('Not joined yet') }})</span>{% endif %}
</td>
<td class="col--role">
{{ form.select({
@@ -17,8 +17,10 @@
{% if not handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) %}
<li class="menu__item"><a class="menu__link" href="javascript:showSignInDialog();">
<span class="icon icon-add"></span> {{ _('Login to Create a Discussion') }}</a></li>
{% elif not handler.user._dudoc.join %}
<li class="menu__item"><a class="menu__link" href="javascript:joinDomain();">
<span class="icon icon-add"></span> {{ _('Join Domain to Create a Discussion') }}</a></li>
{% else %}
<li class="menu__item"><a class="menu__link disabled" href="javascript:;">
<span class="icon icon-add"></span> {{ _('No Permission to Create a Discussion') }}</a></li>
{% endif %}
{% endif %}
@@ -46,6 +48,10 @@
<li class="menu__item scratchpad--hide"><a class="menu__link{% if page_name == 'problem_submit' %} active{% endif %}" href="javascript:showSignInDialog();">
<span class="icon icon-send"></span> {{ _('Login to Submit') }}
</a></li>
{% elif not handler.user._dudoc.join %}
<li class="menu__item scratchpad--hide"><a class="menu__link disabled{% if page_name == 'problem_submit' %} active{% endif %}" href="javascript:joinDomain();">
<span class="icon icon-send"></span> {{ _('Join Domain to Submit') }}
</a></li>
{% else %}
<li class="menu__item scratchpad--hide"><a class="menu__link disabled{% if page_name == 'problem_submit' %} active{% endif %}" href="javascript:;">
<span class="icon icon-send"></span> {{ _('No Permission to Submit') }}