HydroOJ 从入门到入土(9)查看自测、修改页面标题、客观题评分标准等(需改源码)
随着 OJ 的使用越来越深入, 本强迫症总会觉得一些细节有时候不那么符合自己的习惯, 但是想改又无处下手, 最终还是走上了修改源码的邪路.
0. 重要
- 做好备份, 并先用测试机做好测试.
- 慎用各种脚本, 除非知道都做了些什么, 并能全部恢复.
- 能写插件修改的尽量用插件, 方便很多, 风险也小.
- 修改源码相当于打开了基因锁, 或者卍解. 带来强大的自定义功能的同时, 也可能会导致一些不可预测的风险, 所以一定要保存好每一次的修改记录. 建议改一处就重启一次看效果, 方便失败恢复.
- 尽量使用现代化的IDE, 方法提示很有用.
- 每次更新之后, 所有修改都需要重新再来一遍, 修改方法可能会因版本更新而失效.
- 本人不懂 JS, 甚至 HTML/css 都是现学的, 所以只能照着改改逻辑代码, 这是我个人的操作记录, 并非指南, 仅供参考. 错误在所难免, 欢迎交流指正. 如果出现意外, 本人概不负责.
1. 超级管理员查看自测代码
前置小技巧: 对于任意查询记录的界面, 都可以通过在 url 后边加上 &allDomain=1
来查看所有的提交记录.
第一次打开这个开关之后, 我就看到一些后边标注了 (自测)
的代码, 但是一点击, 发现管理员无法查看.
能查看自测的话, 给学生指导代码会更方便, 所以就想改一下这个功能.
这个问题的核心是权限, 这里出于不污染其他权限的考虑, 选了只有超级管理员才有的权限, 即拥有所有权限(PRIV_ALL).
需要用其他权限的话, 可以参考 HydroOJ 从入门到入土(3)权限管理
先观察 Hydro源码 , 发现有个自测特判, 于是加上个管理权限特判(!this.user.hasPriv(PRIV.PRIV_ALL) &&
)就行.
# 可能行数不一致, 上下瞅瞅
vi +162 /usr/local/share/.config/yarn/global/node_modules/hydrooj/src/handler/record.ts
// if (rdoc.uid !== this.user._id) throw new PermissionError(PERM.PERM_READ_RECORD_CODE);
改成超级管理员特权:
if (!this.user.hasPriv(PRIV.PRIV_ALL) && rdoc.uid !== this.user._id) throw new PermissionError(PERM.PERM_READ_RECORD_CODE);
pm2 restart hydrooj
2. 超级管理员隐身查看比赛 / 作业题目
在使用过程中, 我发现在一个作业
或者比赛
中, 如果在 成绩表
和所有递交
中, 点击题目, 会显示没参加所以没有权限访问.
不改源码的解决方法有上中下三策:
- 上策: 写插件, 但是这个属于后端插件, 需要对项目结构有一定理解, 不然会浪费大量时间.
- 中策: 仔细观察, 是带上了
?tid=xxx
这样一个参数, 让没参赛的人无法访问, 于是删之即可正常访问. - 下策: 参加/认领后再查看, 但是成绩榜单上会留下自己的名字.
最后我果然还是选了中策, 但每次都要删除那一串东西, 实在很麻烦, 而且不符合超级管理员的身份, 所以只能想办法改之.
观察Hydro源码, 发现跟上边的修改类似, 在 !this.tsdoc?.attend
这种判定之前, 加上一个权限特判即可.
# 改这里可以隐身查看作业题目
# 可能行数不一致, 上下瞅瞅
vi +330 /usr/local/share/.config/yarn/global/node_modules/hydrooj/src/handler/problem.ts
// if (!contest.isDone(this.tdoc, this.tsdoc) && (!this.tsdoc?.attend || !this.tsdoc.startAt)) throw new ContestNotAttendedError(tid);
if (!this.user.hasPriv(PRIV.PRIV_ALL) && !contest.isDone(this.tdoc, this.tsdoc) && (!this.tsdoc?.attend || !this.tsdoc.startAt)) throw new ContestNotAttendedError(tid);
pm2 restart hydrooj
3. 超级管理员隐身查看比赛题目列表
同上, 在比赛
中, 点击题目列表
, 会显示没参加所以没有权限访问.
观察Hydro源码, 发现跟上边的修改类似, 在 !this.tsdoc?.attend
这种判定之前, 加上一个权限特判即可.
# 改这里可以隐身查看比赛题目列表
# 可能行数不一致, 上下瞅瞅
vi +245 /usr/local/share/.config/yarn/global/node_modules/hydrooj/src/handler/contest.ts
// if (!this.tsdoc?.attend && !contest.isDone(this.tdoc)) throw new ContestNotAttendedError(domainId, tid);
if (!this.user.hasPriv(PRIV.PRIV_ALL) && !this.tsdoc?.attend && !contest.isDone(this.tdoc)) throw new ContestNotAttendedError(domainId, tid);
pm2 restart hydrooj
4. 关掉客观题的多选题部分分
多选题我倾向于要么全对, 要么全错的判题方式, 加大难度.
Hydro 目前默认是有部分分, 所以改掉.
观察Hydro源码, 注释掉'Partially Correct'
判断即可.
# 可能行数不一致, 上下瞅瞅
vi +71 /usr/local/share/.config/yarn/global/node_modules/@hydrooj/hydrojudge/src/judge/objective.ts
// else if (ans.size && Set.isSuperset(stdSet, ans)) report(STATUS.STATUS_WRONG_ANSWER, Math.floor(fullScore / 2), 'Partially Correct');
pm2 restart hydrooj
5. 修改题目页面title,加上pid
目前的页面 title
长这样:题目详情 - A+B Problem - HydroOJ
, 但是没有题号信息, 而且标签多了之后, 后边的信息就会被挤掉, 所有的标签页都会变成题目详情...
, 难以找到相应的题目, 我希望能改为洛谷这种 title
, 比如 P1001 A+B Problem - 洛谷 | 计算机科学教育新生态
, 这样标签多了也能一眼找到自己要的题.
观察Hydro源码, 发现其实只要检测到pid
就提前返回一个含pid
的title
即可.
# 可能行数不一致, 上下瞅瞅
vi +162 /usr/local/share/.config/yarn/global/node_modules/hydrooj/src/service/server.ts
// 在原来的 162 行前边插入这一行
if (this.pdoc?.pid && this.UiContext.extraTitleContent) return `${this.pdoc.pid} ${this.UiContext.extraTitleContent} - ${name}`;
// 改完之后:
if (this.pdoc?.pid && this.UiContext.extraTitleContent) return `${this.pdoc.pid} ${this.UiContext.extraTitleContent} - ${name}`;
if (this.UiContext.extraTitleContent) return `${this.translate(str)} - ${this.UiContext.extraTitleContent} - ${name}`;
pm2 restart hydrooj
5. 修改作业 / 训练 / 比赛页面title,加上名字
当前title
: 作业/训练/比赛详情 - Hydro
,
改完title
, 以作业为例: xxx的作业 - 作业详情 - Hydro
注: 写的比较麻烦, 因为只有比赛给了单独的 this.tdoc
, 作业和训练没给.
// 如果修改了题目页面 title, 就把这个修改放到 this.pdoc?.pid 判断后边
// 在原来的 162 行前边插入这一行
if (this.response?.body?.tdoc?.title ) return `${this.response.body.tdoc.title} - ${this.translate(str)} - ${name}`;
// 改完之后
if (this.response?.body?.tdoc?.title ) return `${this.response.body.tdoc.title} - ${this.translate(str)} - ${name}`;
if (this.UiContext.extraTitleContent) return `${this.translate(str)} - ${this.UiContext.extraTitleContent} - ${name}`;
pm2 restart hydrooj
5.1 作业详情页增加作业标题块(插件)
此外, 如果需要在作业内部和比赛一样, 显示作业名称, 起止日期等信息, 可以复制一个模版homework_detail.html
, 然后在第 7
行<div class="medium-9 columns">
下边增加一个section
, 之后塞到插件里即可.
section
内容如下, 效果参考比赛详情页的比赛标题块:
<div class="section">
<div class="section__header" style="align: center">
<h1 class="section__title">{{ tdoc.title }}</h1>
</div>
<div class="section__body">
<span class="bp5-tag bp5-large bp5-minimal problem__tag-item">{{ _(model.contest.statusText(tdoc, tsdoc)) }}</span>
<span class="bp5-tag bp5-large bp5-minimal problem__tag-item icon icon-award">{{ _(model.contest.RULES[tdoc.rule].TEXT) }}</span>
<span class="bp5-tag bp5-large bp5-minimal problem__tag-item">{{ _('Start at') }}: {{ datetimeSpan(tdoc['beginAt'])|safe }}</span>
<span class="bp5-tag bp5-large bp5-minimal problem__tag-item icon icon-schedule">{% if model.contest.isExtended(tdoc) or model.contest.isDone(tdoc) %}
{{ _('Hard Deadline') }}: {{ datetimeSpan(tdoc['endAt'])|safe }}
{% else %}
{{ _('Deadline') }}: {{ datetimeSpan(tdoc['penaltySince'])|safe }}
{% endif %}</span>
<span class="bp5-tag bp5-large bp5-minimal problem__tag-item">{{ _('Host') }}: {{ user.render_inline(udict[tdoc.owner], badge=false) }}</span>
<span class="bp5-tag bp5-large bp5-minimal problem__tag-item icon icon-user--multiple">{{ tdoc['attend']|default(0) }}</span>
{% if tsdoc.attend %}
<span class="bp5-tag bp5-large bp5-minimal problem__tag-item icon icon-check">{{ _('Attended') }}</span>
{% endif %}
</div>
</div>
5.2 训练详情页增加训练标题块(插件)
训练详情页看不到题单标题, 有点迷惑.
可以复制一个模版training_detail.html
, 然后在第 44
行<div class="section">
下边增加一个h1的header
, 之后塞到插件里即可.
section
内容如下, 效果就是在训练详情页加入一个标题:
<div class="section__header" style="align: center">
<h1 class="section__title">{{ tdoc.title }}</h1>
</div>
如果觉得不够显眼, 也参考前边可以单独做一个section
, 但是注意, 状态等信息已经在右侧了, 不需要再重复添加.
6. 修改作业开始时间
上课布置作业, 可以马上开始, 而不是 1 天之后.
# 4.9.25版本之后从145行改成了149行
vi +149 /usr/local/share/.config/yarn/global/node_modules/hydrooj/src/handler/homework.ts
moment().add(1, 'day').tz
//改为:
moment().add(0, 'day').tz
pm2 restart hydrooj