core&ui: implement domain leave and kick

This commit is contained in:
undefined committed 2025-03-22 05:55:56 +08:00
1 parent f8ed636b30
commit a0f38d9ad1
13 files changed
+64 -28

No files matched your search

+3
View File
@@ -133,6 +133,8 @@ Hydro 用户群:1085853538
- import-qduoj: 修复 spj=null
- core: 修复文件复制
- core: 支持存储并显示提交记录重测历史
- core: 新的加域逻辑
- ui: UserSelect.Multi: 支持批量粘贴用户名
### Breaking API Changes
@@ -144,6 +146,7 @@ Hydro 用户群:1085853538
- core: 移除 global.Hydro.ui.template
- core: 移除 global.Hydro.lib
- core: 移除 String.prototype.translate (使用 app.i18n.translate)
- core: 升级至 Mongo Driver 6
## Hydro 4.19.0 / UI 4.57.0
+1 -1
View File
@@ -688,7 +688,6 @@ The role to assign when user joining the domain.: 사용자가 그룹에 참여
The uploaded file is too long.: 업로드한 파일이 너무 깁니다.
The user is already a member of the domain.: 이미 그룹에 속한 사용자입니다.
The user is too lazy to leave something here...: 설명이 없습니다... (´・ω・`)
Their account will not be deleted and they will be with the default role.: 계정은 삭제되지 않을 것이고, default 역할을 가질 것입니다.
There are no contests...: 대회 없음
There is no homework so far ╰(*°▽°*)╯: 과제 없음 ╰(*°▽°*)╯
This contest is not live.: 대회가 실시간이 아닙니다.
@@ -810,6 +809,7 @@ You cannot visit this domain.: 이 그룹을 방문할 수 없습니다.
You don't have the required permission ({0}) in this domain.: 그룹 권한이 없습니다. ({0})。
You don't have the required privilege.: 권한이 없습니다.
You don't own any domain and don't have privilege to create domain.: 그룹을 만들 권한이 없습니다.
You have been kicked from domain {0} by {1}.: 당신은 {1}에 의해 {0}에서 추방되었습니다.
You have not submitted any solutions for this problem: 아직 답안을 제출하지 않았습니다.
You have permission to create problems for this domain.: 이 그룹에서 문제를 만들기 위한 권한이 있습니다.
You have uncompleted domain creation operation. You can click "Continue" to finish the creation.: 그룹 생성이 끝나지 않았습니다. "계속"을 눌러 생성을 완료하십시오.
+1 -1
View File
@@ -792,7 +792,6 @@ The role to assign when user joining the domain.: 用户加入此域时分配的
The uploaded file is too long.: 上传的文件太长。
The user is already a member of the domain.: 该用户已是域的成员。
The user is too lazy to leave something here...: 该用户太懒,这里啥也没写 (´・ω・`)
Their account will not be deleted and they will be with the default role.: 他们的账号不会被删除,并且之后将以 default 角色访问。
theme: 主题
There are no contests...: 没有比赛…
There is no homework so far ╰(*°▽°*)╯: 目前还没有作业 ╰(*°▽°*)╯
@@ -934,6 +933,7 @@ You cannot visit this domain.: 您不能访问此域。
You don't have the required permission ({0}) in this domain.: 您在该域中无相应权限 ({0})。
You don't have the required privilege.: 您没有相应的特权。
You don't own any domain and don't have privilege to create domain.: 您不拥有任何域也没有创建域的特权。
You have been kicked from domain {0} by {1}.: 您已被 {1} 从域 {0} 中移除。
You have not submitted any solutions for this problem: 您在该题下没有递交过任何解答
You have permission to create problems for this domain.: 您有权限为此域创建题目。
You have uncompleted domain creation operation. You can click "Continue" to finish the creation.: 您有未完成的创建域操作。您可以点击“继续”来完成创建。
+1 -1
View File
@@ -463,7 +463,6 @@ Technical Information: 技術資訊
Temporary session: 臨時會話
Text: 文字
The homework's deadline is due but in extension. You can still submit for this problem but your score will be penalized.: 功課已經截止,但處於延期時間内。您此時遞交的題目無法獲得全部分數。
Their account will not be deleted and they will be with the default role.: 他們的賬號不會被刪除,並且之後將以 default 角色訪問。
There are no contests...: 沒有比賽…
There is no homework so far ╰(*°▽°*)╯: 目前沒有功課 ╰(*°▽°*)╯
This contest is not live.: 比賽沒有開始。
@@ -551,6 +550,7 @@ You cannot submit for this problem because the homework's deadline is due.: 功
You don't have the required permission ({0}) in this domain.: 您在該域中無相應許可權 ({0})。
You don't have the required privilege.: 您沒有相應的特權。
You don't own any domain and don't have privilege to create domain.: 您不擁有任何域也沒有創建域的特權。
You have been kicked from domain {0} by {1}.: 您已被 {1} 從域 {0} 中移除。
You have not submitted any solutions for this problem: 您在該題下沒有遞交過任何解答
You have permission to create problems for this domain.: 您有許可權為此域創建題目。
You haven't attended this contest yet.: 您還沒有參加本次比賽。
+21 -10
View File
@@ -11,6 +11,7 @@ import avatar from '../lib/avatar';
import { PERM, PERMS_BY_FAMILY, PRIV } from '../model/builtin';
import * as discussion from '../model/discussion';
import domain from '../model/domain';
import MessageModel from '../model/message';
import * as oplog from '../model/oplog';
import { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } from '../model/setting';
import * as system from '../model/system';
@@ -163,11 +164,15 @@ class DomainUserHandler extends ManageHandler {
};
}
@param('uids', Types.NumericArray)
async post({ }, uids: number[]) {
if (uids.includes(this.domain.owner)) throw new ForbiddenError();
}
@requireSudo
@post('uid', Types.Int)
@post('role', Types.Role)
async postSetUser(domainId: string, uid: number, role: string) {
if (uid === this.domain.owner) throw new ForbiddenError();
@param('uids', Types.NumericArray)
@param('role', Types.Role)
async postSetUsers(domainId: string, uid: number[], role: string) {
await Promise.all([
domain.setUserRole(domainId, uid, role),
oplog.log(this, 'domain.setRole', { uid, role }),
@@ -176,14 +181,20 @@ class DomainUserHandler extends ManageHandler {
}
@requireSudo
@param('uid', Types.NumericArray)
@param('role', Types.Role)
async postSetUsers(domainId: string, uid: number[], role: string) {
if (uid.includes(this.domain.owner)) throw new ForbiddenError();
@param('uids', Types.NumericArray)
async postKick({ domainId }, uids: number[]) {
const original = await domain.getMultiUserInDomain(domainId, { uid: { $in: uids } }).toArray();
const needUpdate = uids.filter((uid) => original.find((i) => i.uid === uid)?.join);
if (!needUpdate.length) return;
await Promise.all([
domain.setUserRole(domainId, uid, role),
oplog.log(this, 'domain.setRole', { uid, role }),
domain.setJoin(domainId, needUpdate.length > 1 ? needUpdate : needUpdate[0], false),
oplog.log(this, 'domain.kick', { uids: needUpdate }),
]);
const msg = JSON.stringify({
message: 'You have been kicked from domain {0} by {1}.',
params: [this.domain.name, this.user.uname],
});
await Promise.all(needUpdate.map((i) => MessageModel.send(1, i, msg, MessageModel.FLAG_RICHTEXT | MessageModel.FLAG_UNREAD)));
this.back();
}
}
+7 -1
View File
@@ -488,7 +488,7 @@ class HomeDomainHandler extends Handler {
} else {
for (const ddoc of ddocs) {
// eslint-disable-next-line no-await-in-loop
const udoc = (await user.getById(ddoc._id, this.user._id))!;
const udoc = await user.getById(ddoc._id, this.user._id);
canManage[ddoc._id] = udoc.hasPerm(PERM.PERM_EDIT_DOMAIN);
role[ddoc._id] = udoc.role;
}
@@ -504,6 +504,12 @@ class HomeDomainHandler extends Handler {
else user.setById(this.user._id, { pinnedDomains: this.user.pinnedDomains.filter((i) => i !== id) });
this.back({ star });
}
@param('id', Types.String)
async postLeave({ }, id: string) {
await domain.setJoin(id, this.user._id, false);
this.back();
}
}
class HomeDomainCreateHandler extends Handler {
+10 -1
View File
@@ -135,7 +135,7 @@ class DomainModel {
@ArgMethod
static async setUserRole(domainId: string, uid: MaybeArray<number>, role: string, autojoin = false) {
const update = { $set: { role, ...(autojoin ? { join: true } : {}) } };
if (!(uid instanceof Array)) {
if (!(Array.isArray(uid))) {
const res = await collUser.findOneAndUpdate(
{ domainId, uid },
update,
@@ -152,6 +152,15 @@ class DomainModel {
return await collUser.updateMany({ domainId, uid: { $in: uid } }, update, { upsert: true });
}
static async setJoin(domainId: string, uid: MaybeArray<number>, join: boolean) {
if (!(Array.isArray(uid))) {
await DomainModel.updateUserInDomain(domainId, uid, { $set: { join } });
return;
}
await collUser.updateMany({ domainId, uid: { $in: uid } }, { $set: { join } });
deleteUserCache(domainId);
}
static async getRoles(domainId: string, count?: boolean): Promise<any[]>;
static async getRoles(domain: DomainDoc, count?: boolean): Promise<any[]>;
@ArgMethod
+2 -1
View File
@@ -680,6 +680,7 @@ Terms of Service: 이용 약관
Test data comes from: Testdata 출처
Testdata Config: Testdata 설정
Text: 본문
Leave: 나가기
The homework's deadline is due but in extension. You can still submit for this problem but your score will be penalized.: 추가 접수 중입니다. 제출할 수 있지만 감점될 수 있습니다.
The invitation code to enter to successfully join the domain. You can only use letters and numbers in the code and it should not be longer than 64 characters.: 초대 코드는 64글자 미만입니다.
The invitation code you provided is invalid.: 잘못된 초대 코드
@@ -688,7 +689,7 @@ The role to assign when user joining the domain.: 사용자가 그룹에 참여
The uploaded file is too long.: 업로드한 파일이 너무 깁니다.
The user is already a member of the domain.: 이미 그룹에 속한 사용자입니다.
The user is too lazy to leave something here...: 설명이 없습니다... (´・ω・`)
Their account will not be deleted and they will be with the default role.: 계정은 삭제되지 않을 것이고, default 역할을 가질 것입니다.
Their account will not be deleted and they will be with the guest role until they re-join the domain.: 계정은 삭제되지 않을 것이고, guest 역할을 가질 것입니다. 그룹에 다시 참여할 때까지 말이죠.
There are no contests...: 대회 없음
There is no homework so far ╰(*°▽°*)╯: 과제 없음 ╰(*°▽°*)╯
This contest is not live.: 대회가 실시간이 아닙니다.
+2 -1
View File
@@ -512,6 +512,7 @@ LaTeX Expressions: LaTeX 表达式
Learn More: 了解更多
Leave blank to use numeric pid.: 留空来使用数字题号。
Leave empty for default: 留空以使用默认
Leave: 离开
Ledo: 乐多
Legacy mode: 兼容模式
Light: 亮色
@@ -916,7 +917,7 @@ The scoreboard was frozen with {0} minutes remaining at {1} - submissions in the
The uploaded file is too long.: 上传的文件太长。
The user is already a member of the domain.: 该用户已是域的成员。
The user is too lazy to leave something here...: 该用户太懒,这里啥也没写 (´・ω・`)
Their account will not be deleted and they will be with the default role.: 他们的账号不会被删除,并且之后将以 default 角色访问。
Their account will not be deleted and they will be with the guest role until they re-join the domain.: 他们的账号不会被删除,并且之后将以 guest 角色访问,直到他们重新加入域。
Theme: 主题
There are no contests...: 没有比赛…
There is no homework so far ╰(*°▽°*)╯: 目前还没有作业 ╰(*°▽°*)╯
+2 -1
View File
@@ -238,6 +238,7 @@ Last Submit At: 最後遞交於
Last Update At: 最後活動時間
LaTeX Expressions: LaTeX 表示式
Learn More: 瞭解更多
Leave: 離開
Links: 連結
List View: 列表視圖
Live: 正在進行
@@ -462,7 +463,7 @@ Technical Information: 技術資訊
Temporary session: 臨時會話
Text: 文字
The homework's deadline is due but in extension. You can still submit for this problem but your score will be penalized.: 功課已經截止,但處於延期時間内。您此時遞交的題目無法獲得全部分數。
Their account will not be deleted and they will be with the default role.: 他們的賬號不會被刪除,並且之後將以 default 角色訪問。
Their account will not be deleted and they will be with the guest role until they re-join the domain.: 他們的賬號不會被刪除,並且之後將以 guest 角色訪問,直到他們重新加入域。
There are no contests...: 沒有比賽…
There is no homework so far ╰(*°▽°*)╯: 目前沒有功課 ╰(*°▽°*)╯
This contest is not live.: 比賽沒有開始。
@@ -60,8 +60,8 @@ const page = new NamedPage('domain_user', () => {
const role = addUserDialog.$dom.find('[name="role"]').val();
try {
await request.post('', {
operation: 'set_user',
uid: user._id,
operation: 'set_users',
uids: [user._id],
role,
});
window.location.reload();
@@ -91,15 +91,14 @@ const page = new NamedPage('domain_user', () => {
$body: tpl`
<div class="typo">
<p>${i18n('Confirm removing the selected users?')}</p>
<p>${i18n('Their account will not be deleted and they will be with the default role.')}</p>
<p>${i18n('Their account will not be deleted and they will be with the guest role until they re-join the domain.')}</p>
</div>`,
}).open();
if (action !== 'yes') return;
try {
await request.post('', {
operation: 'set_users',
uid: selectedUsers,
role: 'default',
operation: 'kick',
uids: selectedUsers,
});
Notification.success(i18n('Selected users have been removed from the domain.'));
await delay(2000);
@@ -122,7 +121,7 @@ const page = new NamedPage('domain_user', () => {
try {
await request.post('', {
operation: 'set_users',
uid: selectedUsers,
uids: selectedUsers,
role,
});
Notification.success(i18n('Role has been updated to {0} for selected users.', role));
@@ -138,8 +137,8 @@ const page = new NamedPage('domain_user', () => {
const role = $(ev.currentTarget).val();
try {
await request.post('', {
operation: 'set_user',
uid: row.attr('data-uid'),
operation: 'set_users',
uids: [row.attr('data-uid')],
role,
});
Notification.success(i18n('Role has been updated to {0}.', role));
@@ -11,7 +11,7 @@
border-left: 1px solid $table-border-color
.col--action
width: rem(135px)
width: rem(200px)
+mobile()
.col--icon, .col--role
@@ -44,6 +44,11 @@
{% if canManage[ddoc['_id']] %}
· <a class="typo-a" href="{{ url('domain_dashboard', domainId=ddoc['_id']) }}">{{ _('Manage') }}</a>
{% endif %}
· <form class="form--inline" method="post">
<input type="hidden" name="operation" value="leave">
<input type="hidden" name="id" value="{{ ddoc['_id'] }}">
<button class="typo-a" type="submit">{{ _('Leave') }}</button>
</form>
</td>
</tr>
{%- endfor -%}