HydroOJ 從入門到入土(9)源碼簡易修改記錄——卍解!

隨着 OJ 的使用越來越深入, 本強迫症總會覺得一些細節有時候不那麼符合自己的習慣, 但是想改又無處下手, 最終還是走上了修改源碼的邪路.

0. 重要

  1. 做好備份, 並先用測試機做好測試.
  2. 慎用各種腳本, 除非知道都做了些什麼, 並能全部恢復.
  3. 能寫插件修改的儘量用插件, 方便很多, 風險也小.
  4. 修改源碼相當於打開了基因鎖, 或者卍解. 帶來強大的自定義功能的同時, 也可能會導致一些不可預測的風險, 所以一定要保存好每一次的修改記錄. 建議改一處就重啓一次看效果, 方便失敗恢復.
  5. 儘量使用現代化的IDE, 方法提示很有用.
  6. 每次更新之後, 所有修改都需要重新再來一遍, 修改方法可能會因版本更新而失效.
  7. 本人不懂 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. 超級管理員隱身查看比賽 / 作業題目

在使用過程中, 我發現在一個作業或者比賽中, 如果在 成績表所有遞交中, 點擊題目, 會顯示沒參加所以沒有權限訪問.

不改源碼的解決方法有上中下三策:

  1. 上策: 寫插件, 但是這個屬於後端插件, 需要對項目結構有一定理解, 不然會浪費大量時間.
  2. 中策: 仔細觀察, 是帶上了?tid=xxx這樣一個參數, 讓沒參賽的人無法訪問, 於是刪之即可正常訪問.
  3. 下策: 參加/認領後再查看, 但是成績榜單上會留下自己的名字.

最後我果然還是選了中策, 但每次都要刪除那一串東西, 實在很麻煩, 而且不符合超級管理員的身份, 所以只能想辦法改之.

觀察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就提前返回一個含pidtitle即可.

# 可能行數不一致, 上下瞅瞅
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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章