隨着 OJ 的使用越來越深入, 本強迫症總會覺得一些細節有時候不那麼符合自己的習慣, 但是想改又無處下手, 最終還是走上了修改源碼的邪路.
0. 重要
- 做好備份, 並先用測試機做好測試.
- 慎用各種腳本, 除非知道都做了些什麼, 並能全部恢復.
- 能寫插件修改的儘量用插件, 方便很多, 風險也小.
- 修改源碼相當於打開了基因鎖, 或者卍解. 帶來強大的自定義功能的同時, 也可能會導致一些不可預測的風險, 所以一定要保存好每一次的修改記錄. 建議改一處就重啓一次看效果, 方便失敗恢復.
- 儘量使用現代化的IDE, 方法提示很有用.
- 每次更新之後, 所有修改都需要重新再來一遍, 修改方法可能會因版本更新而失效.
- 本人不懂 JS, 甚至 HTML/css 都是現學的, 所以只能照着改改邏輯代碼, 這是我個人的操作記錄, 並非指南, 僅供參考. 錯誤在所難免, 歡迎交流指正. 如果出現意外, 本人概不負責.
1. 超級管理員查看自測代碼
前置小技巧: 對於任意查詢記錄的界面, 都可以通過在 url 後邊加上 &allDomain=1
來查看所有的提交記錄.
第一次打開這個開關之後, 我就看到一些後邊標註了 (自測)
的代碼, 但是一點擊, 發現管理員無法查看.
能查看自測的話, 給學生指導代碼會更方便, 所以就想改一下這個功能.
這個問題的核心是權限, 這裏出於不污染其他權限的考慮, 選了只有超級管理員纔有的權限, 即擁有所有權限(PRIV_ALL).
需要用其他權限的話, 可以參考 HydroOJ 從入門到入土(3)權限管理
先觀察 Hydro源碼 , 發現有個自測特判, 於是加上個管理權限特判(!this.user.hasPriv(PRIV.PRIV_ALL) &&
)就行.
# 可能行數不一致, 上下瞅瞅
vi +161 /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 +331 /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 +260 /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 目前默認是有部分分, 所以改掉.
觀察Hydro源碼, 發現其實只要檢測到pid
就提前返回一個含pid
的title
即可.
# 可能行數不一致, 上下瞅瞅
vi +151 /usr/local/share/.config/yarn/global/node_modules/hydrooj/src/service/server.ts
// 在原來的 151 行前邊插入這一行
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 判斷後邊
// 在原來的 151 行前邊插入這一行
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