diff --git a/build/buildModule.js b/build/buildModule.js index f44afb481..4b328c162 100644 --- a/build/buildModule.js +++ b/build/buildModule.js @@ -39,10 +39,7 @@ const build = async (type) => { } for (const j of prepare) { if (exist(`module/${i}/${j}.js`)) { - const file = fs.readFileSync(root(`module/${i}/${j}.js`)); - if (file.includes('require')) { - config.entry[`${i}/${j}`] = root(`module/${i}/${j}.js`); - } + config.entry[`${i}/${j}`] = root(`module/${i}/${j}.js`); } } } catch (e) { @@ -70,12 +67,7 @@ const build = async (type) => { const current = {}; for (const j of prepare) { if (exist(`module/${i}/${j}.js`)) { - const file = fs.readFileSync(root(`module/${i}/${j}.js`)); - if (file.includes('require')) { - current[j] = fs.readFileSync(root(`.build/module/${i}/${j}.js`)).toString(); - } else { - current[j] = fs.readFileSync(root(`module/${i}/${j}.js`)).toString(); - } + current[j] = fs.readFileSync(root(`.build/module/${i}/${j}.js`)).toString(); } } if (exist(`module/${i}/locale`)) { @@ -101,6 +93,7 @@ const build = async (type) => { current.description = m.description || ''; current.requirements = m.requirements || []; current.version = m.version || 'unknown'; + current.id = m.id; if (m.os) current.os = m.os; const data = zlib.gzipSync(Buffer.from(yaml.safeDump(current)), { level: -1 }); fs.writeFileSync(root(`.build/module/${i}.hydro`), data); diff --git a/build/index.js b/build/index.js index 7fed8eaa5..da6e21f39 100644 --- a/build/index.js +++ b/build/index.js @@ -33,6 +33,7 @@ async function build(type) { 'bsod.html', ]; const builtin = { + id: 'builtin', locale: lang, template: template('templates', exclude), file: {}, diff --git a/hydro/handler/contest.js b/hydro/handler/contest.js index 76f666227..46c1979d0 100644 --- a/hydro/handler/contest.js +++ b/hydro/handler/contest.js @@ -12,6 +12,7 @@ const system = require('../model/system'); const { Route, Handler } = require('../service/server'); const ContestHandler = contest.ContestHandlerMixin(Handler); + class ContestListHandler extends ContestHandler { async get({ rule = 0, page = 1 }) { this.response.template = 'contest_main.html'; @@ -31,11 +32,16 @@ class ContestListHandler extends ContestHandler { const tids = []; for (const tdoc of tdocs) tids.push(tdoc._id); const tsdict = await contest.getListStatus(this.user._id, tids); + const path = [ + ['Hydro', '/'], + ['contest_main', null], + ]; this.response.body = { - page, tpcount, qs, rule, tdocs, tsdict, + page, tpcount, qs, rule, tdocs, tsdict, path, }; } } + class ContestDetailHandler extends ContestHandler { async _prepare({ tid }) { this.tdoc = await contest.get(tid); @@ -62,6 +68,7 @@ class ContestDetailHandler extends ContestHandler { } else attended = false; const udict = await user.getList([this.tdoc.owner]); const path = [ + ['Hydro', '/'], ['contest_main', '/c'], [this.tdoc.title, null, true], ]; @@ -76,10 +83,12 @@ class ContestDetailHandler extends ContestHandler { this.back(); } } + class ContestScoreboardHandler extends ContestDetailHandler { async get({ tid }) { const [tdoc, rows, udict] = await this.getScoreboard(tid); const path = [ + ['Hydro', '/'], ['contest_main', '/c'], [tdoc.title, `/c/${tid}`, true], ['contest_scoreboard', null], @@ -90,6 +99,7 @@ class ContestScoreboardHandler extends ContestDetailHandler { }; } } + class ContestScoreboardDownloadHandler extends ContestDetailHandler { async get({ tid, ext }) { const getContent = { @@ -101,6 +111,7 @@ class ContestScoreboardDownloadHandler extends ContestDetailHandler { this.binary(await getContent[ext](rows), `${this.tdoc.title}.${ext}`); } } + class ContestEditHandler extends ContestDetailHandler { async prepare({ tid }) { const tdoc = await contest.get(tid); @@ -113,6 +124,7 @@ class ContestEditHandler extends ContestDetailHandler { for (const i in contest.RULES) { rules[i] = contest.RULES[i].TEXT; } const duration = (this.tdoc.endAt.getTime() - this.tdoc.beginAt.getTime()) / 3600 / 1000; const path = [ + ['Hydro', '/'], ['contest_main', '/c'], [this.tdoc.title, `/c/${this.tdoc._id}`, true], ['contest_edit', null], @@ -151,6 +163,7 @@ class ContestEditHandler extends ContestDetailHandler { else this.response.redirect = `/c/${this.tdoc._id}`; } } + class ContestProblemHandler extends ContestDetailHandler { async prepare({ tid, pid }) { [this.tdoc, this.pdoc] = await Promise.all([ @@ -175,6 +188,7 @@ class ContestProblemHandler extends ContestDetailHandler { async get({ tid }) { const path = [ + ['Hydro', '/'], ['contest_main', '/c'], [this.tdoc.title, `/c/${tid}`, true], [this.pdoc.title, null, true], @@ -189,6 +203,7 @@ class ContestProblemHandler extends ContestDetailHandler { }; } } + class ContestProblemSubmitHandler extends ContestProblemHandler { async get({ tid, pid }) { let rdocs = []; @@ -198,6 +213,7 @@ class ContestProblemSubmitHandler extends ContestProblemHandler { } this.response.template = 'problem_submit.html'; const path = [ + ['Hydro', '/'], ['contest_main', '/c'], [this.tdoc.title, `/c/${tid}`, true], [this.pdoc.title, `/c/${tid}/p/${pid}`, true], @@ -229,6 +245,7 @@ class ContestProblemSubmitHandler extends ContestProblemHandler { } } } + class ContestCreateHandler extends ContestHandler { async prepare() { this.checkPerm(PERM_CREATE_CONTEST); @@ -242,8 +259,14 @@ class ContestCreateHandler extends ContestHandler { let ts = now.getTime(); ts = ts - (ts % (15 * 60 * 1000)) + 15 * 60 * 1000; const dt = new Date(ts); + const path = [ + ['Hydro', '/'], + ['contest_main', '/c'], + ['contest_create', null], + ]; this.response.body = { rules, + path, page_name: 'contest_create', date_text: `${dt.getFullYear()}-${dt.getMonth() + 1}-${dt.getDate()}`, time_text: `${dt.getHours()}:${dt.getMinutes()}`, diff --git a/hydro/handler/discussion.js b/hydro/handler/discussion.js index 63bb48e2a..eb06e8df4 100644 --- a/hydro/handler/discussion.js +++ b/hydro/handler/discussion.js @@ -88,6 +88,7 @@ class DiscussionNodeHandler extends DiscussionHandler { ); const udict = await user.getList(ddocs.map((ddoc) => ddoc.owner)); const path = [ + ['Hydro', '/'], ['discussion_main', '/discuss'], [this.vnode.title, null, true], ]; @@ -112,6 +113,7 @@ class DiscussionCreateHandler extends DiscussionHandler { async get({ type, docId }) { const path = [ + ['Hydro', '/'], ['discussion_main', '/discuss'], [this.vnode.title, `/discuss/${type}/${docId}`, true], ['discussion_create', null], @@ -155,8 +157,9 @@ class DiscussionDetailHandler extends DiscussionHandler { } const udict = await user.getList(uids); const path = [ + ['Hydro', '/'], ['discussion_main', '/discuss'], - [this.vnode.title, '/discuss/{type}', true], + [this.vnode.title, `/discuss/${this.ddoc.parentType}`, true], [this.ddoc.title, null, true], ]; this.response.template = 'discussion_detail.html'; @@ -243,8 +246,15 @@ class DiscussionTailReplyRawHandler extends DiscussionHandler { class DiscussionEditHandler extends DiscussionHandler { async get() { if (this.ddoc.owner !== this.user._id) this.checkPerm(PERM_EDIT_DISCUSSION); + const path = [ + ['Hydro', '/'], + ['discussion_main', '/discuss'], + [this.vnode.title, `/discuss/${this.ddoc.parentType}`, true], + [this.ddoc.title, `/discuss/${this.ddoc._id}`, true], + ['discussion_edit', null], + ]; this.response.template = 'discussion_edit.html'; - this.response.body = { ddoc: this.ddoc }; + this.response.body = { ddoc: this.ddoc, path }; } async post({ diff --git a/hydro/handler/manage.js b/hydro/handler/manage.js index 8e0e89b02..39cbdab18 100644 --- a/hydro/handler/manage.js +++ b/hydro/handler/manage.js @@ -22,15 +22,25 @@ class ManageMainHandler extends ManageHandler { class ManageDashboardHandler extends ManageHandler { async get() { + const path = [ + ['Hydro', '/'], + ['manage', '/manage'], + ['manage_dashboard', null], + ]; this.response.template = 'manage_dashboard.html'; - this.response.body = { system: this.system }; + this.response.body = { system: this.system, path }; } } class ManageEditHandler extends ManageHandler { async get() { + const path = [ + ['Hydro', '/'], + ['manage', '/manage'], + ['manage_edit', null], + ]; this.response.template = 'manage_edit.html'; - this.response.body = { system: this.system }; + this.response.body = { system: this.system, path }; } async post({ uname, gravatar, bio }) { @@ -46,7 +56,7 @@ class ManageUserHandler extends ManageHandler { async get() { const uids = []; const rudocs = {}; - const [udocs, roles, system] = await Promise.all([ + const [udocs, roles, sys] = await Promise.all([ user.getMulti({ role: { $nin: ['default', 'guest'] } }).toArray(), user.getRoles(), user.getById(0), @@ -58,9 +68,14 @@ class ManageUserHandler extends ManageHandler { } const rolesSelect = roles.map((role) => [role._id, role._id]); const udict = await user.getList(uids); + const path = [ + ['Hydro', '/'], + ['manage', '/manage'], + ['manage_user', null], + ]; this.response.template = 'manage_user.html'; this.response.body = { - roles, rolesSelect, rudocs, udict, system, + roles, rolesSelect, rudocs, udict, system: sys, path, }; } @@ -72,12 +87,17 @@ class ManageUserHandler extends ManageHandler { class ManagePermissionHandler extends ManageHandler { async get() { - const [roles, system] = await Promise.all([ + const [roles, sys] = await Promise.all([ user.getRoles(), user.getById(0), ]); + const path = [ + ['Hydro', '/'], + ['manage', '/manage'], + ['manage_permission', null], + ]; this.response.template = 'manage_permission.html'; - this.response.body = { roles, system }; + this.response.body = { roles, system: sys, path }; } async post({ roles }) { @@ -95,12 +115,17 @@ class ManagePermissionHandler extends ManageHandler { class ManageRoleHandler extends ManageHandler { async get() { - const [roles, system] = await Promise.all([ + const [roles, sys] = await Promise.all([ user.getRoles(), user.getById(0), ]); + const path = [ + ['Hydro', '/'], + ['manage', '/manage'], + ['manage_role', null], + ]; this.response.template = 'manage_role.html'; - this.response.body = { roles, system }; + this.response.body = { roles, system: sys, path }; } async postAdd({ role }) { @@ -127,11 +152,20 @@ class ManageRoleHandler extends ManageHandler { class ManageModuleHandler extends ManageHandler { async get() { const installed = await hpm.getInstalled(); - const active = loader.active.map((module) => module.id); - this.response.body = { installed, active }; + const path = [ + ['Hydro', '/'], + ['manage', '/manage'], + ['manage_module', null], + ]; + this.response.body = { installed, active: loader.active, path }; this.response.template = 'manage_module.html'; } + async postInstall({ url, id }) { + await hpm.install(url, id); + this.back(); + } + async postDelete({ id }) { await hpm.del(id); this.back(); diff --git a/hydro/handler/problem.js b/hydro/handler/problem.js index fdd8a82e4..c4b141076 100644 --- a/hydro/handler/problem.js +++ b/hydro/handler/problem.js @@ -32,7 +32,7 @@ class ProblemHandler extends Handler { this.response.template = 'problem_main.html'; const q = {}; let psdict = {}; - if (category) q.category = category; + if (category) q.$or = [{ category }, { tag: category }]; if (!this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false; const [pdocs, ppcount, pcount] = await paginate( problem.getMulti(q).sort({ pid: 1 }), @@ -50,6 +50,55 @@ class ProblemHandler extends Handler { path, page, pcount, ppcount, pdocs, psdict, category, }; } + + async cleanup() { + if (this.response.template === 'problem_main.html' && this.preferJson) { + const { + path, page, pcount, ppcount, pdocs, psdict, category, + } = this.response.body; + this.response.body = { + title: this.renderTitle(category), + fragments: (await Promise.all([ + this.renderHTML('partials/problem_list.html', { + page, ppcount, pcount, pdocs, psdict, + }), + this.renderHTML('partials/problem_stat.html', { pcount }), + this.renderHTML('partials/problem_lucky.html', { category }), + this.renderHTML('partials/path.html', { path }), + ])).map((i) => ({ html: i })), + raw: { + path, page, pcount, ppcount, pdocs, psdict, category, + }, + }; + } + } +} + +class ProblemCategoryHandler extends ProblemHandler { + async get({ page = 1, category }) { + this.response.template = 'problem_main.html'; + const q = { + $or: [{ category }, { tag: category }], + }; + let psdict = {}; + if (!this.user.hasPerm(PERM_VIEW_PROBLEM_HIDDEN)) q.hidden = false; + const [pdocs, ppcount, pcount] = await paginate( + problem.getMulti(q).sort({ pid: 1 }), + page, + await system.get('PROBLEM_PER_PAGE'), + ); + if (this.user.hasPerm(PERM_LOGGEDIN)) { + psdict = await problem.getListStatus(this.user._id, pdocs.map((pdoc) => pdoc._id)); + } + const path = [ + ['Hydro', '/'], + ['problem_main', '/p'], + [category, null, true], + ]; + this.response.body = { + path, page, pcount, ppcount, pdocs, psdict, category, + }; + } } class ProblemRandomHandler extends ProblemHandler { @@ -59,6 +108,7 @@ class ProblemRandomHandler extends ProblemHandler { const pid = await problem.random(q); if (!pid) throw new NoProblemError(); this.response.body = { pid }; + this.response.redirect = `/p/${pid}`; } } @@ -380,6 +430,7 @@ class ProblemCreateHandler extends Handler { async function apply() { Route('/p', module.exports.ProblemHandler); + Route('/p/category/:category', module.exports.ProblemCategoryHandler); Route('/problem/random', module.exports.ProblemRandomHandler); Route('/p/:pid', module.exports.ProblemDetailHandler); Route('/p/:pid/submit', module.exports.ProblemSubmitHandler); @@ -398,6 +449,7 @@ async function apply() { global.Hydro.handler.problem = module.exports = { ProblemHandler, + ProblemCategoryHandler, ProblemRandomHandler, ProblemDetailHandler, ProblemSubmitHandler, diff --git a/hydro/lib/hpm.js b/hydro/lib/hpm.js index 74e337572..2d2dbe828 100644 --- a/hydro/lib/hpm.js +++ b/hydro/lib/hpm.js @@ -2,6 +2,7 @@ /* eslint-disable no-await-in-loop */ /* eslint-disable no-eval */ const fs = require('fs'); +const os = require('os'); const zlib = require('zlib'); const path = require('path'); const yaml = require('js-yaml'); @@ -11,29 +12,50 @@ function root(name) { return path.resolve(process.cwd(), name); } +const moduleRoots = [ + root('.build/module'), + root('module'), + root(path.resolve(os.homedir(), '.hydro', 'module')), + root('.'), +]; +let moduleRoot; +for (const i of moduleRoots) { + if (fs.existsSync(i) && fs.statSync(i).isDirectory()) { + moduleRoot = i; + break; + } +} + async function getInstalled() { - const files = fs.readdirSync(root('.build/module')); - const installed = []; + const modules = []; + const files = fs.readdirSync(moduleRoot); for (const file of files) { if (file.endsWith('.hydro')) { - const f = fs.readFileSync(root(`.build/module/${file}`)); - const s = fs.statSync(root(`.build/module/${file}`)); - installed.push({ - ...yaml.safeLoad(zlib.gunzipSync(f)), - id: file.split('.')[0], - size: s.size, - }); + try { + const f = fs.readFileSync(root(`${moduleRoot}/${file}`)); + const s = fs.statSync(root(`${moduleRoot}/${file}`)); + const m = { + ...yaml.safeLoad(zlib.gunzipSync(f)), + filename: file.split('.')[0], + size: s.size, + }; + modules.push(m); + } catch (e) { + if (e.code === 'Z_DATA_ERROR') { + console.error(`Module Load Fail: ${file} (File Corrupted)`); + } else console.error(`Module Load Fail: ${file} ${e}`); + } } } - return installed; + return modules; } async function del(id) { - fs.unlinkSync(root(`.build/module/${id}.hydro`)); + fs.unlinkSync(root(`${moduleRoot}/${id}.hydro`)); } -async function install(id, url) { - await download(url, root(`module/${id}.hydro`)); +async function install(url) { + await download(url, root(`${moduleRoot}/${String.random(16)}.hydro`)); } -module.exports = { getInstalled, del, install }; +global.Hydro.lib.hpm = module.exports = { getInstalled, del, install }; diff --git a/hydro/loader.js b/hydro/loader.js index a69b7bc3d..3121f5e36 100644 --- a/hydro/loader.js +++ b/hydro/loader.js @@ -7,9 +7,6 @@ const zlib = require('zlib'); const path = require('path'); const yaml = require('js-yaml'); -function root(name) { - return path.resolve(process.cwd(), name); -} function ensureDir(dir) { if (!fs.existsSync(dir)) { fs.mkdirSync(dir); @@ -19,6 +16,7 @@ function ensureDir(dir) { } } +let pending = []; const active = []; const fail = []; @@ -27,42 +25,16 @@ try { // eslint-disable-next-line import/no-unresolved const f = require('../.build/module.json'); for (const filename in f) { - const m = { ...yaml.safeLoad(zlib.gunzipSync(f[filename])), filename }; - active.push(m); + const m = { ...yaml.safeLoad(zlib.gunzipSync(f[filename])), filename, isBuiltin: true }; + pending.push(m); console.log(filename); } } catch (e) { // Builtin module is in the module directory } -const moduleRoots = [ - root('.build/module'), - root('module'), - root(path.resolve(os.homedir(), '.hydro', 'module')), - root('.'), -]; -let moduleRoot; -for (const i of moduleRoots) { - if (fs.existsSync(i) && fs.statSync(i).isDirectory()) { - moduleRoot = i; - break; - } -} - async function preload() { - const files = fs.readdirSync(moduleRoot); - for (const file of files) { - if (file.endsWith('.hydro')) { - try { - const f = fs.readFileSync(root(`${moduleRoot}/${file}`)); - const m = { ...yaml.safeLoad(zlib.gunzipSync(f)), id: file.split('.')[0] }; - active.push(m); - } catch (e) { - console.error(`Module Load Fail: ${file}`); - } - } - } - for (const i of active) { + for (const i of pending) { try { if (i.os) { if (!i.os.includes(os.platform().toLowerCase())) throw new Error('Unsupported OS'); @@ -90,14 +62,11 @@ async function preload() { } async function handler() { - for (const i of active) { + for (const i of pending) { if (i.handler && !i.fail) { try { console.log(`Handler init: ${i.id}`); console.time(`Handler init: ${i.id}`); - const exports = {}; - // eslint-disable-next-line no-unused-vars - const module = { exports, file: i.files || {} }; eval(i.handler); console.timeEnd(`Handler init: ${i.id}`); } catch (e) { @@ -110,7 +79,7 @@ async function handler() { } async function locale() { - for (const i of active) { + for (const i of pending) { if (i.locale && !i.fail) { try { global.Hydro.lib.i18n(i.locale); @@ -125,7 +94,7 @@ async function locale() { } async function template() { - for (const i of active) { + for (const i of pending) { if (i.template && !i.fail) { try { Object.assign(global.Hydro.template, i.template); @@ -140,15 +109,13 @@ async function template() { } async function model() { - for (const i of active) { + for (const i of pending) { if (i.model && !i.fail) { try { console.log(`Model init: ${i.id}`); console.time(`Model init: ${i.id}`); - const exports = {}; - const module = { exports, file: i.files || {} }; - eval(i.model); - if ((module.exports || {}).index) await module.exports.index(); + const m = eval(i.model); + if ((m || {}).index) await m.index(); console.timeEnd(`Model init: ${i.id}`); } catch (e) { i.fail = true; @@ -160,14 +127,11 @@ async function model() { } async function lib() { - for (const i of active) { + for (const i of pending) { if (i.lib && !i.fail) { try { console.log(`Lib init: ${i.id}`); console.time(`Lib init: ${i.id}`); - const exports = {}; - // eslint-disable-next-line no-unused-vars - const module = { exports, file: i.files || {} }; eval(i.lib); console.timeEnd(`Lib init: ${i.id}`); } catch (e) { @@ -180,14 +144,12 @@ async function lib() { } async function service() { - for (const i of active) { + for (const i of pending) { if (i.service && !i.fail) { try { console.time(`Service init: ${i.id}`); - const exports = {}; // eslint-disable-line no-unused-vars - const module = { exports }; - eval(i.service); - if ((module.exports || {}).init) await module.exports.init(); + const m = eval(i.service); + if ((m || {}).init) await m.init(); console.timeEnd(`Service init: ${i.id}`); } catch (e) { i.fail = true; @@ -200,12 +162,10 @@ async function service() { } async function script() { - for (const i of active) { + for (const i of pending) { if (i.script && !i.fail) { try { console.time(`Script init: ${i.id}`); - const exports = {}; - const module = { exports }; // eslint-disable-line no-unused-vars eval(i.script); console.timeEnd(`Script init: ${i.id}`); } catch (e) { @@ -215,6 +175,7 @@ async function script() { console.error(e); } } + if (!i.fail) active.push(i.name); } } @@ -239,6 +200,7 @@ async function load() { template: {}, ui: {}, }; + pending.push(...await require('./lib/hpm').getInstalled()); await preload(); require('./lib/i18n'); require('./utils'); @@ -303,7 +265,10 @@ async function load() { if (global.Hydro.service[i].postInit) await global.Hydro.service[i].postInit(); } await script(); + pending = []; await server.start(); } -module.exports = { load, active, fail }; +module.exports = { + load, pending, active, fail, +}; diff --git a/hydro/model/problem.js b/hydro/model/problem.js index a00d68d74..397fcd219 100644 --- a/hydro/model/problem.js +++ b/hydro/model/problem.js @@ -123,11 +123,11 @@ function count(query) { return coll.find(query).count(); } async function random(query) { - const pdocs = coll.find(query); - const pcount = await pdocs.count(); + const cursor = coll.find(query); + const pcount = await cursor.count(); if (pcount) { - const pdoc = await pdocs.skip(Math.floor(Math.random() * pcount)).limit(1).toArray()[0]; - return pdoc.pid; + const pdoc = await cursor.skip(Math.floor(Math.random() * pcount)).limit(1).toArray(); + return pdoc[0].pid; } return null; } async function getList(pids) { diff --git a/hydro/service/server.js b/hydro/service/server.js index bc50c7267..c9a63c741 100644 --- a/hydro/service/server.js +++ b/hydro/service/server.js @@ -242,7 +242,7 @@ class Handler { } } -const check = ['tid', 'rid', 'did', 'drid', 'drrid', 'psid', 'psrid', 'docId']; +const check = ['tid', 'rid', 'did', 'drid', 'drrid', 'psid', 'psrid', 'docId', 'mongoId']; function Route(route, RouteHandler) { router.all(route, async (ctx) => { diff --git a/locales/zh_CN.yaml b/locales/zh_CN.yaml index dce8ca9c0..accb01cb5 100644 --- a/locales/zh_CN.yaml +++ b/locales/zh_CN.yaml @@ -315,6 +315,7 @@ manage_join_applications: 加域申请 manage_role: 管理角色 manage_user: 管理用户 manage_permission: 管理权限 +manage_module: 管理模块 domain_join: 加入域 'Join {0}': '加入 {0}' You are not allowed to join the domain. The link is either invalid or expired.: 您无法加入该域,链接无效或已过期。 @@ -772,10 +773,6 @@ home_domain_account: 当前域的设置 'The value `{1}` of {0} already exists.': '{0} 的值 `{1}` 已经存在。' Display name {1} you want to set is used by others.: 您想要设置的显示名 {1} 已经被其他人使用了。 Numeric PID: 数字题号 -Only {0} problems can be copied in one request, got {1}.: 一次请求只能复制 {0} 个题目,但是您输入了 {1} 个。 -Problem is successfully copied.: 题目复制完成。 -problem_copy: 复制题目 -Copy Problem: 复制题目 Be Copied: 被复制 Copy from: 复制自 Problem ID List: 题目 ID 列表 @@ -784,7 +781,6 @@ Destination Domain: 目标域 Domain ID: 域 ID Test data comes from: 测试数据来自 Current dataset comes from: 当前数据集来自 -Copy Problem To: 将题目复制到 What's this?: 这是什么? About test data: 关于测试数据 With this feature, you can copy problems that you can view from a domain to some other domain. Their title, content, tags and categories will be copied. However, their test data are not copied directly.: 您可以通过这个功能将某域下您能查看的题目复制到其它域中,题目的标题、描述、标签与分类将被复制过来。但是测试数据不会被直接复制。 diff --git a/module/contest-rule-homework/hydro.json b/module/contest-rule-homework/hydro.json index f3eb3fee3..7a73a2d71 100644 --- a/module/contest-rule-homework/hydro.json +++ b/module/contest-rule-homework/hydro.json @@ -1,4 +1,4 @@ { - "name": "contest-rule-homework", + "id": "contest-rule-homework", "version": "1.0.0" } \ No newline at end of file diff --git a/module/html2md/hydro.json b/module/html2md/hydro.json index 3b6df7bd9..b8edffbfd 100644 --- a/module/html2md/hydro.json +++ b/module/html2md/hydro.json @@ -1,5 +1,5 @@ { - "name": "html2md", + "id": "html2md", "version": "0.0.1", "description": "Convert html to markdown" } \ No newline at end of file diff --git a/module/migrate-vijos/README.md b/module/migrate-vijos/README.md new file mode 100644 index 000000000..d0b98940c --- /dev/null +++ b/module/migrate-vijos/README.md @@ -0,0 +1,13 @@ +# 迁移须知 + +迁移前,您需要指定数据来源。 +**请不要**将 Hydro 正在使用的数据库设置为数据来源的数据库。 +迁移过程中,Hydro 的以下内容会被**清空**: + +题目列表,提交记录,用户列表,比赛列表,比赛成绩表,训练列表,训练进度,站内消息,题解,讨论 + +以下内容将被迁移: + +题目与测试数据、题解,讨论与回复,比赛与成绩单,训练与进度数据,提交记录,用户列表 + +迁移过程中请保持 \ No newline at end of file diff --git a/module/migrate-vijos/hydro.json b/module/migrate-vijos/hydro.json index 55a0cebdb..c51d5d981 100644 --- a/module/migrate-vijos/hydro.json +++ b/module/migrate-vijos/hydro.json @@ -1,5 +1,5 @@ { - "name": "migrate-vijos", + "id": "migrate-vijos", "version": "0.0.1", "description": "Migrate From Vijos" } \ No newline at end of file diff --git a/module/migrate-vijos/script.js b/module/migrate-vijos/script.js index 2a7062d89..24896af37 100644 --- a/module/migrate-vijos/script.js +++ b/module/migrate-vijos/script.js @@ -20,6 +20,7 @@ const tasks = { salt: doc.salt, hash: doc.hash.split('|')[1], hashType: doc.hash.split('|')[0] === 'vj4' ? 'hydro' : doc.hash.split('|')[0], + role: doc.priv === -1 ? 'root' : 'default', gravatar: doc.gravatar, email: doc.mail, emailLower: doc.mail_lower, diff --git a/module/migrate-vijos/template/migrate_vijos.html b/module/migrate-vijos/template/migrate_vijos.html index 85ccc2a0d..5f7d4bebd 100644 --- a/module/migrate-vijos/template/migrate_vijos.html +++ b/module/migrate-vijos/template/migrate_vijos.html @@ -54,8 +54,5 @@ -