diff --git a/packages/hydrooj/package.json b/packages/hydrooj/package.json index c15327254..e61000ea5 100644 --- a/packages/hydrooj/package.json +++ b/packages/hydrooj/package.json @@ -1,6 +1,6 @@ { "name": "hydrooj", - "version": "3.6.0", + "version": "3.7.0", "bin": "bin/hydrooj.js", "main": "src/loader", "module": "src/loader", diff --git a/packages/hydrooj/setting.yaml b/packages/hydrooj/setting.yaml index de133e968..5f54b5ebb 100644 --- a/packages/hydrooj/setting.yaml +++ b/packages/hydrooj/setting.yaml @@ -2,10 +2,18 @@ homepage: type: yaml desc: Homepage Config default: - contest: 5 - homework: 10 - training: 10 - discussion: 20 + - width: 9 + bulletin: true + contest: 5 + homework: 10 + training: 10 + ranking: 10 + discussion: 20 + - width: 3 + hitokoto: true + starred_problems: 50 + discussion_nodes: true + suggestion: true langs: type: yaml desc: Code language settings diff --git a/packages/hydrooj/src/handler/home.ts b/packages/hydrooj/src/handler/home.ts index 8a5e8a3aa..f14ea0433 100644 --- a/packages/hydrooj/src/handler/home.ts +++ b/packages/hydrooj/src/handler/home.ts @@ -1,5 +1,6 @@ import yaml from 'js-yaml'; import { ObjectID } from 'mongodb'; +import { camelCase } from '@hydrooj/utils/lib/utils'; import { BlacklistedError, DomainAlreadyExistsError, InvalidTokenError, NotFoundError, PermissionError, UserAlreadyExistError, @@ -43,9 +44,9 @@ class HomeHandler extends Handler { const tsdict = await contest.getListStatus( domainId, this.user._id, tdocs.map((tdoc) => tdoc.docId), ); - return ['homework', tdocs, tsdict]; + return [tdocs, tsdict]; } - return ['homework', [], {}]; + return [[], {}]; } async getContest(domainId: string, limit = 10) { @@ -55,9 +56,9 @@ class HomeHandler extends Handler { const tsdict = await contest.getListStatus( domainId, this.user._id, tdocs.map((tdoc) => tdoc.docId), ); - return ['contest', tdocs, tsdict]; + return [tdocs, tsdict]; } - return ['contest', [], {}]; + return [[], {}]; } async getTraining(domainId: string, limit = 10) { @@ -67,9 +68,9 @@ class HomeHandler extends Handler { const tsdict = await training.getListStatus( domainId, this.user._id, tdocs.map((tdoc) => tdoc.docId), ); - return ['training', tdocs, tsdict]; + return [tdocs, tsdict]; } - return ['training', [], {}]; + return [[], {}]; } async getDiscussion(domainId: string, limit = 20) { @@ -77,9 +78,9 @@ class HomeHandler extends Handler { const ddocs = await discussion.getMulti(domainId).limit(limit).toArray(); const vndict = await discussion.getListVnodes(domainId, ddocs, this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN), this.user.group); this.collectUser(ddocs.map((ddoc) => ddoc.owner)); - return ['discussion', ddocs, vndict]; + return [ddocs, vndict]; } - return ['discussion', [], {}]; + return [[], {}]; } async getRanking(domainId: string, limit = 50) { @@ -88,12 +89,12 @@ class HomeHandler extends Handler { .sort({ rp: -1 }).project({ uid: 1 }).limit(limit).toArray(); const uids = dudocs.map((dudoc) => dudoc.uid); this.collectUser(uids); - return ['ranking', uids]; + return uids; } - return ['ranking', []]; + return []; } - async getStarredProblem(domainId: string, limit = 50) { + async getStarredProblems(domainId: string, limit = 50) { if (this.user.hasPerm(PERM.PERM_VIEW_PROBLEM)) { const psdocs = await ProblemModel.getMultiStatus(domainId, { uid: this.user._id, star: true }) .sort('_id', 1).limit(limit).toArray(); @@ -104,35 +105,45 @@ class HomeHandler extends Handler { this.user.hasPerm(PERM.PERM_VIEW_PROBLEM_HIDDEN) || this.user._id, this.user.group, false, ); const pdocs = Object.keys(pdict).filter((i) => +i).map((i) => pdict[i]); - return ['problem', pdocs, psdict]; + return [pdocs, psdict]; } - return ['problem', [], {}]; + return [[], {}]; + } + + getDiscussionNodes(domainId: string) { + return discussion.getNodes(domainId); } async get({ domainId }) { - const homepageConfig = system.get('hydrooj.homepage'); - const tasks = []; + const homepageConfig = system.get('hydrooj.homepage1'); const info = yaml.load(homepageConfig) as any; - for (const key in info) { - const func = `get${key.replace(/^[a-z]/, (i) => i.toUpperCase())}`; - if (!this[func]) continue; - tasks.push(this[func](domainId, +info[key])); + const contents = []; + for (const column of info) { + const tasks = []; + for (const name in column) { + if (name === 'width') continue; + const func = `get${camelCase(name).replace(/^[a-z]/, (i) => i.toUpperCase())}`; + if (!this[func]) tasks.push([name, column[name]]); + else { + tasks.push( + this[func](domainId, column[name]) + .then((res) => [name, res]) + .catch((err) => ['error', err.message]), + ); + } + } + contents.push({ + width: column.width, + // eslint-disable-next-line no-await-in-loop + sections: await Promise.all(tasks), + }); } - const [, pdocs, psdict] = await this.getStarredProblem(domainId); - const contents = await Promise.all(tasks); - const [udict, dodoc, vnodes] = await Promise.all([ - user.getList(domainId, Array.from(this.uids)), - domain.get(domainId), - discussion.getNodes(domainId), - ]); + const udict = await user.getList(domainId, Array.from(this.uids)); this.response.template = 'main.html'; this.response.body = { contents, udict, - domain: dodoc, - vnodes, - pdocs, - psdict, + domain: this.domain, }; } } diff --git a/packages/hydrooj/src/upgrade.ts b/packages/hydrooj/src/upgrade.ts index fd8aff4ae..8e5ec3f47 100644 --- a/packages/hydrooj/src/upgrade.ts +++ b/packages/hydrooj/src/upgrade.ts @@ -688,6 +688,19 @@ const scripts: UpgradeScript[] = [ await system.set('hydrooj.langs', langs.replace(/\$\{dir\}/g, '/w').replace(/\$\{name\}/g, 'foo')); return true; }, + async function _60_61() { + const config = await system.get('hydrooj.homepage'); + const data = yaml.load(config) as any; + if (!(data instanceof Array)) { + await system.set('hydrooj.homepage', yaml.dump([ + { width: 9, bulletin: true, ...data }, + { + width: 3, hitokoto: true, starred_problems: 50, discussion_nodes: true, suggestion: true, + }, + ])); + } + return true; + }, ]; export default scripts; diff --git a/packages/ui-default/backendlib/template.js b/packages/ui-default/backendlib/template.js index c0875aafe..07faee7f3 100644 --- a/packages/ui-default/backendlib/template.js +++ b/packages/ui-default/backendlib/template.js @@ -57,6 +57,14 @@ const replacer = (k, v) => { class Nunjucks extends nunjucks.Environment { constructor() { super(new Loader(), { autoescape: true, trimBlocks: true }); + this.addFilter('await', async (promise, callback) => { + try { + const result = await promise; + callback(null, result); + } catch (error) { + callback(error); + } + }, true); this.addFilter('json', (self) => (self ? JSON.stringify(self, replacer) : '')); this.addFilter('parseYaml', (self) => yaml.load(self)); this.addFilter('dumpYaml', (self) => yaml.dump(self)); @@ -140,6 +148,7 @@ env.addGlobal('set', (obj, key, val) => { return ''; }); env.addGlobal('findSubModule', (prefix) => Object.keys(global.Hydro.ui.template).filter((n) => n.startsWith(prefix))); +env.addGlobal('templateExists', (name) => !!global.Hydro.ui.template[name]); async function render(name, state) { // eslint-disable-next-line no-return-await diff --git a/packages/ui-default/package.json b/packages/ui-default/package.json index 6b308da19..c779ec4ab 100644 --- a/packages/ui-default/package.json +++ b/packages/ui-default/package.json @@ -1,6 +1,6 @@ { "name": "@hydrooj/ui-default", - "version": "4.32.14", + "version": "4.33.0", "author": "undefined ", "license": "AGPL-3.0", "main": "hydro.js", diff --git a/packages/ui-default/templates/main.html b/packages/ui-default/templates/main.html index 2a5039472..5852953de 100644 --- a/packages/ui-default/templates/main.html +++ b/packages/ui-default/templates/main.html @@ -4,103 +4,18 @@ {% import "components/problem.html" as problem with context %} {% block content %}
-
- {% if domain['bulletin'] %} -
-
- {{ domain['bulletin']|markdown|safe }} -
-
- {% endif %} - {% for node in contents %} - {% if node[0] == 'contest' %} - {% set tdocs = node[1] %} - {% set tsdict = node[2] %} - {% include "partials/homepage/contest.html" %} - {% elif node[0] == 'homework' %} - {% set htdocs = node[1] %} - {% set htsdict = node[2] %} - {% include "partials/homepage/homework.html" %} - {% elif node[0] == 'training' %} - {% set tdocs = node[1] %} - {% set tsdict = node[2] %} - {% include "partials/homepage/training.html" %} - {% elif node[0] == 'ranking' %} - {% set uids = node[1] %} - {% include "partials/homepage/ranking.html" %} - {% elif node[0] == 'discussion' %} - {% set ddocs = node[1] %} - {% set vndict = node[2] %} - {% if ddocs.length %} -
-
-

{{ _('Discussion') }}

-
- {% include "partials/discussion_list.html" %} -
+ {% for column in contents %} +
+ {% for s in column.sections %} + {% if templateExists("partials/homepage/" + s[0] + ".html") %} + {% set payload = s[1] %} + {% include "partials/homepage/" + s[0] + ".html" %} + {% else %} + {% set payload = _("Template {0} not found.").format(s[0]) %} + {% include "partials/homepage/error.html" %} {% endif %} - {% endif %} - {% endfor %} -
-
-
-
-

{{ _('Hitokoto') }}

-
-
+ {% endfor %}
- {% if pdocs.length %} -
-
-

{{ _('Starred Problems') }}

-
-
- {%- for pdoc in pdocs -%} -
- - - -
- {{ problem.render_problem_title(pdoc, show_tags=false, show_invisible_flag=false) }}
- {%- endfor -%} -
-
- {% endif %} - {% if handler.user.hasPerm(perm.PERM_VIEW_DISCUSSION) %} - {% include 'partials/discussion_nodes_widget.html' %} - {% endif %} -
-
-

{{ _('Recommended') }}

-
-
-
-
+ {% endfor %}
{% endblock %} diff --git a/packages/ui-default/templates/partials/homepage/bulletin.html b/packages/ui-default/templates/partials/homepage/bulletin.html new file mode 100644 index 000000000..2c1707e8e --- /dev/null +++ b/packages/ui-default/templates/partials/homepage/bulletin.html @@ -0,0 +1,7 @@ +{% if domain.bulletin %} +
+
+ {{ domain.bulletin|markdown|safe }} +
+
+{% endif %} \ No newline at end of file diff --git a/packages/ui-default/templates/partials/homepage/contest.html b/packages/ui-default/templates/partials/homepage/contest.html index eadac44d9..1b7c2c298 100644 --- a/packages/ui-default/templates/partials/homepage/contest.html +++ b/packages/ui-default/templates/partials/homepage/contest.html @@ -1,3 +1,5 @@ +{% set tdocs = payload[0] %} +{% set tsdict = payload[1] %} {% if tdocs.length %}
diff --git a/packages/ui-default/templates/partials/homepage/discussion.html b/packages/ui-default/templates/partials/homepage/discussion.html new file mode 100644 index 000000000..1b0b2d282 --- /dev/null +++ b/packages/ui-default/templates/partials/homepage/discussion.html @@ -0,0 +1,10 @@ +{% set ddocs = payload[0] %} +{% set vndict = payload[1] %} +{% if ddocs.length %} +
+
+

{{ _('Discussion') }}

+
+ {% include "partials/discussion_list.html" %} +
+{% endif %} \ No newline at end of file diff --git a/packages/ui-default/templates/partials/homepage/discussion_nodes.html b/packages/ui-default/templates/partials/homepage/discussion_nodes.html new file mode 100644 index 000000000..d022fe968 --- /dev/null +++ b/packages/ui-default/templates/partials/homepage/discussion_nodes.html @@ -0,0 +1,4 @@ +{% if handler.user.hasPerm(perm.PERM_VIEW_DISCUSSION) %} + {% set vnodes = payload %} + {% include 'partials/discussion_nodes_widget.html' %} +{% endif %} \ No newline at end of file diff --git a/packages/ui-default/templates/partials/homepage/error.html b/packages/ui-default/templates/partials/homepage/error.html new file mode 100644 index 000000000..11db406ec --- /dev/null +++ b/packages/ui-default/templates/partials/homepage/error.html @@ -0,0 +1,5 @@ +
+
+ {{ payload }} +
+
\ No newline at end of file diff --git a/packages/ui-default/templates/partials/homepage/hitokoto.html b/packages/ui-default/templates/partials/homepage/hitokoto.html new file mode 100644 index 000000000..7e17978b4 --- /dev/null +++ b/packages/ui-default/templates/partials/homepage/hitokoto.html @@ -0,0 +1,6 @@ +
+
+

{{ _('Hitokoto') }}

+
+
+
diff --git a/packages/ui-default/templates/partials/homepage/homework.html b/packages/ui-default/templates/partials/homepage/homework.html index ae6350576..a26fa7a81 100644 --- a/packages/ui-default/templates/partials/homepage/homework.html +++ b/packages/ui-default/templates/partials/homepage/homework.html @@ -1,3 +1,5 @@ +{% set htdocs = payload[0] %} +{% set htsdict = payload[1] %} {% if htdocs.length %}
diff --git a/packages/ui-default/templates/partials/homepage/ranking.html b/packages/ui-default/templates/partials/homepage/ranking.html index 9e15044cd..761e00b00 100644 --- a/packages/ui-default/templates/partials/homepage/ranking.html +++ b/packages/ui-default/templates/partials/homepage/ranking.html @@ -1,4 +1,4 @@ -{% if uids.length %} +{% if payload.length %}

{{ _('Ranking') }}

@@ -21,7 +21,7 @@ - {%- for uid in uids -%} + {%- for uid in payload -%} {% set udoc = udict[uid] %} {{ loop.index }} diff --git a/packages/ui-default/templates/partials/homepage/starred_problems.html b/packages/ui-default/templates/partials/homepage/starred_problems.html new file mode 100644 index 000000000..9636c0a50 --- /dev/null +++ b/packages/ui-default/templates/partials/homepage/starred_problems.html @@ -0,0 +1,22 @@ +{% set pdocs = payload[0] %} +{% set psdict = payload[1] %} +{% if pdocs.length %} +
+
+

{{ _('Starred Problems') }}

+
+
+ {%- for pdoc in pdocs -%} +
+ + + +
+ {{ problem.render_problem_title(pdoc, show_tags=false, show_invisible_flag=false) }}
+ {%- endfor -%} +
+
+{% endif %} \ No newline at end of file diff --git a/packages/ui-default/templates/partials/homepage/suggestion.html b/packages/ui-default/templates/partials/homepage/suggestion.html new file mode 100644 index 000000000..72151c69e --- /dev/null +++ b/packages/ui-default/templates/partials/homepage/suggestion.html @@ -0,0 +1,29 @@ +
+
+

{{ _('Recommended') }}

+
+
+
diff --git a/packages/ui-default/templates/partials/homepage/training.html b/packages/ui-default/templates/partials/homepage/training.html index c4bb891e1..27e124013 100644 --- a/packages/ui-default/templates/partials/homepage/training.html +++ b/packages/ui-default/templates/partials/homepage/training.html @@ -1,3 +1,5 @@ +{% set tdocs = payload[0] %} +{% set tsdict = payload[1] %} {% if tdocs.length %}