Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: add new domain join logic #886

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion framework/framework/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ export type HookType = 'before-prepare' | 'before' | 'before-operation' | 'after

export interface ServerEvents<T extends Handler = Handler, C extends ConnectionHandler = ConnectionHandler> extends HandlerEvents<T, C> {
'handler/create': (thisArg: Handler, type: 'ws' | 'http') => VoidReturn
'handler/init': (thisArg: Handler) => VoidReturn
'handler/init': (thisArg: Handler) => VoidReturn | string | Promise<string>
'handler/error': (thisArg: Handler, e: Error) => VoidReturn
}
1 change: 1 addition & 0 deletions packages/hydrooj/locales/zh.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,7 @@ WYSIWYG: 所见即所得
Year: 年
You are not allowed to join the domain. The link is either invalid or expired.: 您无法加入该域,链接无效或已过期。
You can create your own training plans and share them with others.: 您可以创建您自己的训练计划并且与他人分享。
You cannot join the domain at current. Contact domain administrator for help.: 您当前无法加入此域。联系域管理者以寻求帮助。
You cannot submit for this problem because the contest is ended. You can click "Open in Problem Set" to view this problem in normal mode.: 该比赛已结束,您无法在比赛模式下递交该题目。您可以点击“在题库中打开”以普通模式查看和递交本题。
You cannot submit for this problem because the homework's deadline is due.: 作业已超过截止时间,您无法递交本题目。
You cannot visit this domain.: 您不能访问此域。
Expand Down
1 change: 1 addition & 0 deletions packages/hydrooj/src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const HomeworkNotLiveError = Err('HomeworkNotLiveError', ForbiddenError,
export const HomeworkNotAttendedError = Err('HomeworkNotAttendedError', ForbiddenError, "You haven't claimed this homework yet.");
export const RoleAlreadyExistError = Err('RoleAlreadyExistError', ForbiddenError, 'This role already exists.');
export const DomainAlreadyExistsError = Err('DomainAlreadyExistsError', ForbiddenError, 'The domain {0} already exists.');
export const DomainNotJoinableError = Err('DomainNotJoinableError', ForbiddenError, 'You cannot join the domain at current. Contact domain administrator for help.');
export const DomainJoinForbiddenError = Err('DomainJoinForbiddenError', ForbiddenError, 'You are not allowed to join the domain. The link is either invalid or expired.');
export const DomainJoinAlreadyMemberError = Err('DomainJoinAlreadyMemberError', ForbiddenError, 'Failed to join the domain. You are already a member.');
export const InvalidJoinInvitationCodeError = Err('InvalidJoinInvitationCodeError', ForbiddenError, 'The invitation code you provided is invalid.');
Expand Down
17 changes: 10 additions & 7 deletions packages/hydrooj/src/handler/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Dictionary } from 'lodash';
import moment from 'moment-timezone';
import type { Context } from '../context';
import {
CannotDeleteSystemDomainError, DomainJoinAlreadyMemberError, DomainJoinForbiddenError, ForbiddenError,
CannotDeleteSystemDomainError, DomainJoinAlreadyMemberError, DomainNotJoinableError, ForbiddenError,
InvalidJoinInvitationCodeError, OnlyOwnerCanDeleteDomainError, PermissionError, RoleAlreadyExistError, ValidationError,
} from '../error';
import type { DomainDoc } from '../interface';
Expand All @@ -24,7 +24,7 @@ class DomainRankHandler extends Handler {
@query('page', Types.PositiveInt, true)
async get(domainId: string, page = 1) {
const [dudocs, upcount, ucount] = await this.paginate(
domain.getMultiUserInDomain(domainId, { uid: { $gt: 1 }, rp: { $gt: 0 } }).sort({ rp: -1 }),
domain.getMultiUserInDomain(domainId, { uid: { $gt: 1 }, join: true }).sort({ rp: -1 }),
page,
'ranking',
);
Expand Down Expand Up @@ -109,15 +109,17 @@ class DomainUserHandler extends ManageHandler {
]);
const uids = dudocs.map((dudoc) => dudoc.uid);
const udict = await user.getList(domainId, uids);
const joined = {};
for (const role of roles) rudocs[role._id] = [];
for (const dudoc of dudocs) {
const udoc = udict[dudoc.uid];
if (!(udoc.priv & PRIV.PRIV_USER_PROFILE)) continue;
rudocs[udoc.role || 'default'].push(udoc);
joined[udoc._id] = dudoc.join;
}
this.response.template = 'domain_user.html';
this.response.body = {
roles, rudocs, udict, domain: this.domain,
roles, rudocs, domain: this.domain, joined,
};
}

Expand Down Expand Up @@ -281,26 +283,27 @@ class DomainJoinHandler extends Handler {
const r = await domain.getRoles(this.domain);
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);
if (!this.joinSettings) throw new DomainNotJoinableError(this.domain._id);
if (this.user._dudoc.join) throw new DomainJoinAlreadyMemberError(this.domain._id, this.user._id);
}

@param('code', Types.Content, true)
async get(domainId: string, code: string) {
async get({ }, code: string) {
this.response.template = 'domain_join.html';
this.response.body.joinSettings = this.joinSettings;
this.response.body.code = code;
}

@param('code', Types.Content, true)
async post(domainId: string, code: string) {
async post({ }, code: string) {
if (this.joinSettings.method === domain.JOIN_METHOD_CODE) {
if (this.joinSettings.code !== code) {
throw new InvalidJoinInvitationCodeError(this.domain._id);
}
}
await Promise.all([
domain.setUserRole(this.domain._id, this.user._id, this.joinSettings.role),
domain.updateUserInDomain(this.domain._id, this.user._id, { join: true }),
oplog.log(this, 'domain.join', {}),
]);
this.response.redirect = this.url('homepage', { query: { notification: 'Successfully joined domain.' } });
Expand Down
10 changes: 9 additions & 1 deletion packages/hydrooj/src/handler/home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { pick } from 'lodash';
import { Binary, ObjectId } from 'mongodb';
import { Context } from '../context';
import {
AuthOperationError, BlacklistedError, DomainAlreadyExistsError, InvalidTokenError,
AuthOperationError, BadRequestError, BlacklistedError, DomainAlreadyExistsError, InvalidTokenError,
NotFoundError, PermissionError, UserAlreadyExistError,
UserNotFoundError, ValidationError, VerifyPasswordError,
} from '../error';
Expand Down Expand Up @@ -493,6 +493,14 @@ class HomeDomainHandler extends Handler {
await user.setById(this.user._id, { pinnedDomains: this.user.pinnedDomains.filter((i) => i !== id) });
this.back({ star: false });
}

@param('id', Types.String)
async postLeave({ }, id: string) {
const dudoc = await domain.getDomainUser(id, this.user);
if (!dudoc.join) throw new BadRequestError('You are not in this domain');
await domain.updateUserInDomain(id, this.user._id, { $set: { join: false } });
this.back();
}
}

class HomeDomainCreateHandler extends Handler {
Expand Down
3 changes: 2 additions & 1 deletion packages/hydrooj/src/model/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,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);
Expand Down Expand Up @@ -251,7 +252,7 @@ class DomainModel {

@ArgMethod
static async getDictUserByDomainId(uid: number) {
const dudocs = await collUser.find({ uid }).toArray();
const dudocs = await collUser.find({ uid, join: true }).toArray();
const dudict: Record<string, any> = {};
for (const dudoc of dudocs) dudict[dudoc.domainId] = dudoc;
return dudict;
Expand Down
10 changes: 9 additions & 1 deletion packages/hydrooj/src/service/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,15 @@ export async function apply(ctx: Context) {
icon: global.Hydro.module.oauth[key].icon,
text: global.Hydro.module.oauth[key].text,
}));
if (!h.noCheckPermView && !h.user.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN)) h.checkPerm(PERM.PERM_VIEW);
});
on('handler/init', async (h) => { // eslint-disable-line consistent-return
if (!h.noCheckPermView && !h.user.hasPriv(PRIV.PRIV_VIEW_ALL_DOMAIN)) {
if (!h.user._dudoc.join && !h.user.hasPerm(PERM.PERM_VIEW)) {
h.response.redirect = h.url('domain_join', {});
return 'cleanup';
}
h.checkPerm(PERM.PERM_VIEW);
}
if (h.context.pendingError) throw h.context.pendingError;
});

Expand Down
3 changes: 3 additions & 0 deletions packages/hydrooj/src/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,4 +702,7 @@ export const coreScripts: MigrationScript[] = [
} catch { }
});
},
async function _91_92() {
await domain.collUser.updateMany({ join: { $exists: false } }, { $set: { join: true } });
},
];
2 changes: 2 additions & 0 deletions packages/ui-default/locales/zh.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,7 @@ LaTeX Expressions: LaTeX 表达式
Learn More: 了解更多
Leave blank to use numeric pid.: 留空来使用数字题号。
Leave empty for default: 留空以使用默认
Leave: 退出
Ledo: 乐多
Legacy mode: 兼容模式
Light: 亮色
Expand Down Expand Up @@ -603,6 +604,7 @@ Not Attended: 未参加
Not available: 不可用
Not Claimed: 未认领
Not Enrolled: 未参加
Not joined yet: 暂未加入
Not started with a number: 不以数字开头
Not wrapped with space: 两侧不含空格
Numeric PID: 数字题号
Expand Down
3 changes: 2 additions & 1 deletion packages/ui-default/templates/domain_user.html
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ <h1 class="section__title">{{ _('{0}: Users').format(domain.name) }}</h1>
{{ 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({
Expand Down
5 changes: 5 additions & 0 deletions packages/ui-default/templates/home_domain.html
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ <h1 class="section__title">{{ _('My Domains') }}</h1>
{% 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="id" value="{{ ddoc['_id'] }}">
<input type="hidden" name="operation" value="leave">
<button class="typo-a" type="submit">{{ _('Leave') }}</button>
</form>
</td>
</tr>
{%- endfor -%}
Expand Down
Loading