mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-17 09:36:08 +08:00
core: add avatar provider API
This commit is contained in:
1 parent
2db1570d1b
commit
f1f956202f
42 files changed
+122
-80
No files matched your search
+1
-1
@@ -7,7 +7,7 @@
|
||||
"main": "package.json",
|
||||
"scripts": {
|
||||
"build": "node build/prepare.js && tsc -b",
|
||||
"build:watch": "node build/build.js && tsc -b --watch",
|
||||
"build:watch": "node build/prepare.js && tsc -b --watch",
|
||||
"build:ui": "node packages/ui-default/build",
|
||||
"build:ui:dev": "node packages/ui-default/build --dev",
|
||||
"build:ui:production": "node packages/ui-default/build --production",
|
||||
|
||||
@@ -35,7 +35,6 @@ __langname: 한국어
|
||||
'Role {1} already exists in domain {0}.': '역할 {1}이(가) 이미 {0}에 존재합니다.'
|
||||
'Solved {0} problems, RP: {1} (No. {2})': '총 {0} 문제 해결.RP: {1} (No. {2})'
|
||||
'The value `{1}` of {0} already exists.': '{0} 의 값 `{1}` 이 이미 존재합니다.'
|
||||
'We use <a href="https://en.gravatar.com/" target="_blank">Gravatar</a> to present your avatar icon.': '<a href="https://cn.gravatar.com/" target="_blank">Gravatar</a>를 만들어 보세요.'
|
||||
'Yes': 예
|
||||
"Effects only when Difficulty is not 'Use algorithm calculated'.": 난이도가 알고리즘에 의해 정해지지 않은 경우에만 적용됩니다.
|
||||
"Split by ', '.": “, ”“,”으로 구분
|
||||
@@ -312,7 +311,6 @@ Formula blocks: 공식 블록
|
||||
fs_upload: 업로드
|
||||
Gender Visibility: 성별 공개
|
||||
Gender: 성
|
||||
Gravatar Email: Gravatar 이메일
|
||||
Hard Deadline: 마감일
|
||||
Hash: 해시
|
||||
Have ALL PERMISSIONS in this domain: 이 그룹에 대한 모든 권한
|
||||
|
||||
@@ -35,7 +35,6 @@ __langname: 简体中文
|
||||
'Role {1} already exists in domain {0}.': '域 {0} 中已存在角色 {1}。'
|
||||
'Solved {0} problems, RP: {1} (No. {2})': '解决了 {0} 道题目,RP: {1} (No. {2})'
|
||||
'The value `{1}` of {0} already exists.': '{0} 的值 `{1}` 已经存在。'
|
||||
'We use <a href="https://en.gravatar.com/" target="_blank">Gravatar</a> to present your avatar icon.': '我们使用 <a href="https://cn.gravatar.com/" target="_blank">Gravatar</a> 服务来呈现您的头像。'
|
||||
'Yes': 是
|
||||
"Effects only when Difficulty is not 'Use algorithm calculated'.": 仅当难度不为“使用算法计算”时才起效。
|
||||
"Split by ',' '.'": 由“, ”或“,”分隔。
|
||||
@@ -81,6 +80,7 @@ Attend Contest: 参加比赛
|
||||
Attend contests: 参加比赛
|
||||
Attended: 已参加
|
||||
author: 作者
|
||||
Avatar: 头像
|
||||
Be Copied: 被复制
|
||||
Begin at: 开始于
|
||||
Begin Date: 开始日期
|
||||
@@ -326,7 +326,6 @@ Formula blocks: 公式区块
|
||||
fs_upload: 上传文件
|
||||
Gender Visibility: 性别可见性
|
||||
Gender: 性别
|
||||
Gravatar Email: Gravatar Email 地址
|
||||
Hard Deadline: 最终截止时间
|
||||
Hash: 散列
|
||||
Have ALL PERMISSIONS in this domain: 在此域中拥有全部权限
|
||||
|
||||
@@ -195,7 +195,6 @@ Forgot password or username?: 忘記密碼或者使用者名?
|
||||
Formula blocks: 公式區塊
|
||||
Gender Visibility: 性別可見性
|
||||
Gender: 性別
|
||||
Gravatar Email: Gravatar Email 地址
|
||||
Hard Deadline: 最終截止時間
|
||||
Have ALL PERMISSIONS in this domain: 在此域中擁有全部許可權
|
||||
Help: 幫助
|
||||
@@ -539,7 +538,6 @@ Visible to registered users: 對註冊使用者可見
|
||||
Visit: 檢視
|
||||
Vote problem solutions: 為題解投票
|
||||
Vote: 投票
|
||||
We use Gravatar to present your avatar icon.: 我們使用 Gravatar 服務來呈現您的頭像。
|
||||
We will check code similarity after this contest.: 我們會在賽後檢查程式碼相似度。
|
||||
WeChat Visibility: 微信可見性
|
||||
WeChat: 微信
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hydrooj",
|
||||
"version": "2.23.6",
|
||||
"version": "2.23.7",
|
||||
"bin": "bin/hydrooj.js",
|
||||
"main": "dist/loader.js",
|
||||
"typings": "dist/loader.d.ts",
|
||||
|
||||
@@ -13,6 +13,7 @@ export const builtinLib = [
|
||||
'jwt', 'download', 'i18n', 'mail', 'useragent',
|
||||
'crypto', 'misc', 'paginate', 'hash.hydro', 'rank',
|
||||
'validator', 'ui', 'testdataConfig', 'difficulty', 'content',
|
||||
'avatar',
|
||||
];
|
||||
|
||||
export const builtinModel = [
|
||||
|
||||
@@ -14,7 +14,7 @@ import { DOMAIN_SETTINGS, DOMAIN_SETTINGS_BY_KEY } from '../model/setting';
|
||||
import {
|
||||
DEFAULT_NODES, PERM, PERMS_BY_FAMILY, PRIV,
|
||||
} from '../model/builtin';
|
||||
import { gravatar } from '../lib/misc';
|
||||
import avatar from '../lib/avatar';
|
||||
import paginate from '../lib/paginate';
|
||||
import {
|
||||
Route, Handler, Types, param, query, post,
|
||||
@@ -297,7 +297,7 @@ class DomainSearchHandler extends Handler {
|
||||
async get(domainId: string, q: string) {
|
||||
const ddocs = await domain.getPrefixSearch(q, 20);
|
||||
for (let i = 0; i < ddocs.length; i++) {
|
||||
ddocs[i].gravatar = ddocs[i].gravatar ? gravatar(ddocs[i].gravatar) : '/img/team_avatar.png';
|
||||
ddocs[i].avatarUrl = ddocs[i].avatar ? avatar(ddocs[i].avatar, 64) : '/img/team_avatar.png';
|
||||
}
|
||||
this.response.body = ddocs;
|
||||
}
|
||||
|
||||
@@ -10,8 +10,9 @@ import * as bus from '../service/bus';
|
||||
import {
|
||||
Route, Connection, Handler, ConnectionHandler, param, Types,
|
||||
} from '../service/server';
|
||||
import * as misc from '../lib/misc';
|
||||
import { md5 } from '../lib/crypto';
|
||||
import { isPassword, isEmail } from '../lib/validator';
|
||||
import avatar from '../lib/avatar';
|
||||
import * as mail from '../lib/mail';
|
||||
import * as contest from '../model/contest';
|
||||
import message from '../model/message';
|
||||
@@ -24,7 +25,6 @@ import * as discussion from '../model/discussion';
|
||||
import token from '../model/token';
|
||||
import * as training from '../model/training';
|
||||
import { PERM, PRIV } from '../model/builtin';
|
||||
import { isPassword, isEmail } from '../lib/validator';
|
||||
|
||||
const { geoip, useragent } = global.Hydro.lib;
|
||||
|
||||
@@ -309,13 +309,13 @@ class HomeDomainCreateHandler extends Handler {
|
||||
@param('id', Types.Name)
|
||||
@param('name', Types.Title)
|
||||
@param('bulletin', Types.Content)
|
||||
@param('gravatar', Types.Content, true, isEmail)
|
||||
async post(_: string, id: string, name: string, bulletin: string, gravatar: string) {
|
||||
@param('avatar', Types.Content, true, isEmail)
|
||||
async post(_: string, id: string, name: string, bulletin: string, avatar: string) {
|
||||
const doc = await domain.get(id);
|
||||
if (doc) throw new DomainAlreadyExistsError(id);
|
||||
gravatar = gravatar || this.user.gravatar || this.user.mail || 'guest@hydro.local';
|
||||
avatar = avatar || this.user.avatar || `gravatar:${this.user.mail}` || 'gravatar:guest@hydro.local';
|
||||
const domainId = await domain.add(id, this.user._id, name, bulletin);
|
||||
await domain.edit(domainId, { gravatar });
|
||||
await domain.edit(domainId, { avatar });
|
||||
await domain.setUserRole(domainId, this.user._id, 'root');
|
||||
this.response.redirect = this.url('domain_dashboard', { domainId });
|
||||
this.response.body = { domainId };
|
||||
@@ -338,7 +338,7 @@ class HomeMessagesHandler extends Handler {
|
||||
if (!parsed[target]) {
|
||||
parsed[target] = {
|
||||
_id: target,
|
||||
udoc: { ...udict[target], gravatar: misc.gravatar(udict[target].gravatar) },
|
||||
udoc: { ...udict[target], avatarUrl: avatar(udict[target].avatar) },
|
||||
messages: [],
|
||||
};
|
||||
}
|
||||
@@ -358,7 +358,7 @@ class HomeMessagesHandler extends Handler {
|
||||
async postSend(domainId: string, uid: number, content: string) {
|
||||
const udoc = await user.getById('system', uid);
|
||||
if (!udoc) throw new UserNotFoundError(uid);
|
||||
if (udoc.gravatar) udoc.gravatar = misc.gravatar(udoc.gravatar);
|
||||
if (udoc.avatar) udoc.avatarUrl = avatar(udoc.avatar);
|
||||
const mdoc = await message.send(this.user._id, uid, content, message.FLAG_UNREAD);
|
||||
this.back({ mdoc, udoc });
|
||||
}
|
||||
@@ -391,7 +391,7 @@ class HomeMessagesConnectionHandler extends ConnectionHandler {
|
||||
async onMessageReceived(uid: number, mdoc: Mdoc) {
|
||||
if (uid !== this.user._id) return;
|
||||
const udoc = await user.getById(this.domainId, mdoc.from);
|
||||
udoc.gravatar_url = misc.gravatar(udoc.gravatar, 64);
|
||||
udoc.avatarUrl = avatar(udoc.avatar, 64);
|
||||
this.send({ udoc, mdoc });
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
UserNotFoundError, SystemError, BlacklistedError,
|
||||
UserFacingError, LoginError,
|
||||
} from '../error';
|
||||
import { User } from '../interface';
|
||||
import {
|
||||
Route, Handler, Types, param, post,
|
||||
} from '../service/server';
|
||||
@@ -17,9 +18,8 @@ import * as system from '../model/system';
|
||||
import { PERM, PRIV } from '../model/builtin';
|
||||
import { isEmail, isPassword, isUname } from '../lib/validator';
|
||||
import { sendMail } from '../lib/mail';
|
||||
import * as misc from '../lib/misc';
|
||||
import paginate from '../lib/paginate';
|
||||
import { User } from '../interface';
|
||||
import avatar from '../lib/avatar';
|
||||
|
||||
class UserLoginHandler extends Handler {
|
||||
async get() {
|
||||
@@ -241,7 +241,7 @@ class UserSearchHandler extends Handler {
|
||||
if (udoc) udocs.push(udoc);
|
||||
if (!exactMatch) udocs.push(...await user.getPrefixList(domainId, q, 20));
|
||||
for (const i in udocs) {
|
||||
udocs[i].gravatar = misc.gravatar(udocs[i].gravatar || '');
|
||||
udocs[i].avatarUrl = avatar(udocs[i].avatar);
|
||||
}
|
||||
this.response.body = udocs;
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ export interface DomainDoc extends Record<string, any> {
|
||||
_id: string,
|
||||
owner: number,
|
||||
roles: Dictionary<string>,
|
||||
gravatar: string,
|
||||
avatar: string,
|
||||
bulletin: string,
|
||||
_join?: any,
|
||||
host?: string,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { md5 } from './crypto';
|
||||
|
||||
type AvatarProvider = (src: string, size: number) => string;
|
||||
|
||||
export const providers: Record<string, AvatarProvider> = {
|
||||
gravatar: (email, size) => `//gravatar.loli.net/avatar/${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${size || 32}`,
|
||||
qq: (id) => `//q1.qlogo.cn/g?b=qq&nk=${/(\d+)/g.exec(id)[1]}&s=640`,
|
||||
github: (id, size) => `//github.com/${id}.png?size=${Math.min(size, 460)}`,
|
||||
};
|
||||
|
||||
function avatar(src: string, size = 64) {
|
||||
const index = src.indexOf(':');
|
||||
if (index === -1) return providers.gravatar('', size);
|
||||
const [provider, str] = [src.substr(0, index), src.substr(index + 1, src.length)];
|
||||
if (!providers[provider] || !str) return providers.gravatar('', size);
|
||||
return providers[provider](str, size);
|
||||
}
|
||||
|
||||
export default avatar;
|
||||
global.Hydro.lib.avatar = avatar;
|
||||
@@ -1,7 +1,6 @@
|
||||
import AnsiUp from 'ansi_up';
|
||||
import { ObjectID } from 'mongodb';
|
||||
import { size, formatSeconds } from '@hydrooj/utils/lib/utils';
|
||||
import { md5 } from './crypto';
|
||||
|
||||
export { size, formatSeconds } from '@hydrooj/utils/lib/utils';
|
||||
|
||||
@@ -11,10 +10,6 @@ export function ansiToHtml(str: string) {
|
||||
return AU.ansi_to_html(str);
|
||||
}
|
||||
|
||||
export function gravatar(email: string, s = 32) {
|
||||
return `//gravatar.loli.net/avatar/${md5((email || '').toString().trim().toLowerCase())}?d=mm&s=${s}`;
|
||||
}
|
||||
|
||||
export function datetimeSpan(dt: Date | ObjectID, relative = true, format = '%Y-%m-%d %H:%M:%S') {
|
||||
if (!dt) return 'DATETIME_SPAN_ERROR';
|
||||
if (dt instanceof ObjectID) dt = new Date(dt.generationTime * 1000);
|
||||
@@ -53,5 +48,5 @@ export function* paginate(page: number, numPages: number) {
|
||||
}
|
||||
|
||||
global.Hydro.lib.misc = {
|
||||
gravatar, datetimeSpan, paginate, size, formatSeconds, ansiToHtml,
|
||||
datetimeSpan, paginate, size, formatSeconds, ansiToHtml,
|
||||
};
|
||||
@@ -47,7 +47,7 @@ class DomainModel {
|
||||
name,
|
||||
bulletin,
|
||||
roles: {},
|
||||
gravatar: '',
|
||||
avatar: '',
|
||||
};
|
||||
await bus.serial('domain/create', ddoc);
|
||||
await coll.insertOne(ddoc);
|
||||
|
||||
@@ -107,8 +107,8 @@ PreferenceSetting(
|
||||
);
|
||||
|
||||
AccountSetting(
|
||||
Setting('setting_info', 'gravatar', null, 'text', 'Gravatar Email',
|
||||
'We use <a href="https://en.gravatar.com/" target="_blank">Gravatar</a> to present your avatar icon.'),
|
||||
Setting('setting_info', 'avatar', '', 'text', 'Avatar',
|
||||
'Allow using gravatar:email qq:id github:name format.'),
|
||||
Setting('setting_info', 'qq', null, 'text', 'QQ'),
|
||||
Setting('setting_info', 'gender', builtin.USER_GENDER_OTHER, builtin.USER_GENDER_RANGE, 'Gender'),
|
||||
Setting('setting_info', 'bio', null, 'markdown', 'Bio'),
|
||||
@@ -122,7 +122,7 @@ AccountSetting(
|
||||
|
||||
DomainSetting(
|
||||
Setting('setting_domain', 'name', 'New domain', 'text', 'name'),
|
||||
Setting('setting_domain', 'gravatar', '', 'text', 'gravatar', 'Will be used as the domain icon.'),
|
||||
Setting('setting_domain', 'avatar', '', 'text', 'avatar', 'Will be used as the domain icon.'),
|
||||
Setting('setting_domain', 'bulletin', '', 'markdown', 'Bulletin'),
|
||||
Setting('setting_storage', 'host', '', 'text', 'Custom host', null, FLAG_HIDDEN | FLAG_DISABLED),
|
||||
);
|
||||
|
||||
@@ -101,7 +101,7 @@ class UserModel {
|
||||
_id: 0,
|
||||
uname: 'Unknown User',
|
||||
unameLower: 'unknown user',
|
||||
gravatar: 'unknown@hydro.local',
|
||||
avatar: 'gravatar:unknown@hydro.local',
|
||||
mail: 'unknown@hydro.local',
|
||||
mailLower: 'unknown@hydro.local',
|
||||
salt: '',
|
||||
@@ -213,7 +213,7 @@ class UserModel {
|
||||
loginat: new Date(),
|
||||
loginip: regip,
|
||||
priv,
|
||||
gravatar: mail,
|
||||
avatar: `gravatar:${mail}`,
|
||||
});
|
||||
} catch (e) {
|
||||
logger.warn('%o', e);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import { DomainDoc } from './loader';
|
||||
import type { DomainDoc, Udoc } from './interface';
|
||||
import domain from './model/domain';
|
||||
import user from './model/user';
|
||||
import problem, { Field, Pdoc } from './model/problem';
|
||||
|
||||
export async function iterateAllDomain(cb: (ddoc: DomainDoc, current?: number, total?: number) => Promise<any>) {
|
||||
@@ -8,6 +9,11 @@ export async function iterateAllDomain(cb: (ddoc: DomainDoc, current?: number, t
|
||||
for (const i in ddocs) await cb(ddocs[i], +i, ddocs.length);
|
||||
}
|
||||
|
||||
export async function iterateAllUser(cb: (udoc: Udoc, current?: number, total?: number) => Promise<any>) {
|
||||
const udocs = await user.getMulti().toArray();
|
||||
for (const i in udocs) await cb(udocs[i], +i, udocs.length);
|
||||
}
|
||||
|
||||
interface PartialPdoc extends Pdoc {
|
||||
[key: string]: any,
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ import {
|
||||
CsrfTokenError, InvalidOperationError, MethodNotAllowedError,
|
||||
NotFoundError, HydroError, SystemError,
|
||||
} from '../error';
|
||||
import * as misc from '../lib/misc';
|
||||
import { isContent, isName, isTitle } from '../lib/validator';
|
||||
import avatar from '../lib/avatar';
|
||||
import user from '../model/user';
|
||||
import domain from '../model/domain';
|
||||
import * as system from '../model/system';
|
||||
@@ -322,7 +322,7 @@ export class HandlerCommon {
|
||||
async renderHTML(name: string, context: any): Promise<string> {
|
||||
const UserContext: any = {
|
||||
...this.user,
|
||||
gravatar: misc.gravatar(this.user.gravatar || '', 128),
|
||||
avatar: avatar(this.user.avatar || '', 128),
|
||||
perm: this.user.perm.toString(),
|
||||
viewLang: this.translate('__id'),
|
||||
};
|
||||
@@ -508,6 +508,7 @@ export class Handler extends HandlerCommon {
|
||||
this.user = await user.getById(domainId, this.session.uid, this.session.scope);
|
||||
}
|
||||
if (this.user._id === 0 && this.session.viewLang) this.user.viewLang = this.session.viewLang;
|
||||
this.user.avatarUrl = avatar(this.user.avatar, 128);
|
||||
this.csrfToken = this.getCsrfToken(this.session._id || String.random(32));
|
||||
this.UiContext.csrfToken = this.csrfToken;
|
||||
this.loginMethods = filter(Object.keys(global.Hydro.lib), (str) => str.startsWith('oauth_'))
|
||||
|
||||
@@ -10,12 +10,13 @@ import { Progress } from './ui';
|
||||
import { Rdoc } from './interface';
|
||||
import { Logger } from './logger';
|
||||
import { streamToBuffer } from './utils';
|
||||
import { iterateAllDomain, iterateAllProblem } from './pipelineUtils';
|
||||
import { iterateAllDomain, iterateAllProblem, iterateAllUser } from './pipelineUtils';
|
||||
import gridfs from './service/gridfs';
|
||||
import storage from './service/storage';
|
||||
import db from './service/db';
|
||||
import difficultyAlgorithm from './lib/difficulty';
|
||||
import problem from './model/problem';
|
||||
import user from './model/user';
|
||||
import domain from './model/domain';
|
||||
import * as document from './model/document';
|
||||
import * as system from './model/system';
|
||||
@@ -299,6 +300,13 @@ const scripts: UpgradeScript[] = [
|
||||
});
|
||||
return true;
|
||||
},
|
||||
async function _22_23() {
|
||||
await iterateAllUser(async (udoc) => {
|
||||
if (!udoc.gravatar) return;
|
||||
await user.setById(udoc._id, { avatar: `gravatar:${udoc.gravatar}` }, { gravatar: '' });
|
||||
});
|
||||
return true;
|
||||
},
|
||||
];
|
||||
|
||||
export default scripts;
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/migrate-vijos",
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.2",
|
||||
"main": "package.json",
|
||||
"repository": "git@github.com:hydro-dev/Hydro.git",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
|
||||
@@ -23,7 +23,7 @@ const tasks = {
|
||||
hash: doc.hash.split('|')[1],
|
||||
hashType: doc.hash.split('|')[0] === 'vj4' ? 'hydro' : doc.hash.split('|')[0],
|
||||
priv: doc.priv,
|
||||
gravatar: doc.gravatar,
|
||||
avatar: `gravatar:${doc.gravatar}`,
|
||||
mail: doc.mail,
|
||||
mailLower: doc.mail_lower,
|
||||
regat: doc.regat,
|
||||
@@ -256,7 +256,10 @@ const tasks = {
|
||||
return res;
|
||||
},
|
||||
},
|
||||
gravatar: 'gravatar',
|
||||
gravatar: {
|
||||
field: 'avatar',
|
||||
processer: (gravatar) => `gavatar:${gravatar}`,
|
||||
},
|
||||
bulletin: 'bulletin',
|
||||
},
|
||||
'fs.files': async (doc) => doc,
|
||||
|
||||
@@ -104,8 +104,8 @@ export class QueryProblemSetAPIHandler extends Handler {
|
||||
nickname: udoc.displayName || udoc.uname,
|
||||
bio: udoc.bio,
|
||||
avatar: {
|
||||
type: 'gravatar',
|
||||
key: udoc.gravatar,
|
||||
type: udoc.avatar.split(':')[0],
|
||||
key: udoc.avatar.split(':')[1],
|
||||
},
|
||||
isAdmin: udoc.hasPriv(PRIV.PRIV_MANAGE_ALL_DOMAIN) || udoc.hasPerm(PERM.PERM_ADMIN),
|
||||
acceptedProblemCount: udoc.nAccept,
|
||||
|
||||
@@ -9,7 +9,7 @@ const Xss = require('xss');
|
||||
const { findFileSync } = require('@hydrooj/utils/lib/utils');
|
||||
const markdown = require('./markdown');
|
||||
|
||||
const { misc, buildContent } = global.Hydro.lib;
|
||||
const { misc, buildContent, avatar } = global.Hydro.lib;
|
||||
|
||||
const xss = new Xss.FilterXSS({
|
||||
whiteList: {
|
||||
@@ -187,7 +187,7 @@ env.addGlobal('typeof', (o) => typeof o);
|
||||
env.addGlobal('datetimeSpan', misc.datetimeSpan);
|
||||
env.addGlobal('paginate', misc.paginate);
|
||||
env.addGlobal('size', misc.size);
|
||||
env.addGlobal('gravatar', misc.gravatar);
|
||||
env.addGlobal('avatarUrl', avatar);
|
||||
env.addGlobal('formatSeconds', misc.formatSeconds);
|
||||
env.addGlobal('model', global.Hydro.model);
|
||||
env.addGlobal('ui', global.Hydro.ui);
|
||||
|
||||
@@ -16,7 +16,7 @@ function renderItem(domain) {
|
||||
return tpl`
|
||||
<div class="media">
|
||||
<div class="media__left medium">
|
||||
<img class="small domain-profile-avatar" src="${domain.gravatar}" width="30" height="30">
|
||||
<img class="small domain-profile-avatar" src="${domain.avatarUrl}" width="30" height="30">
|
||||
</div>
|
||||
<div class="media__body medium">
|
||||
<div class="domain-select__name">${domain.name}</div>
|
||||
|
||||
@@ -16,7 +16,7 @@ function renderItem(user) {
|
||||
return tpl`
|
||||
<div class="media">
|
||||
<div class="media__left medium">
|
||||
<img class="small user-profile-avatar" src="${user.gravatar}" width="30" height="30">
|
||||
<img class="small user-profile-avatar" src="${user.avatarUrl}" width="30" height="30">
|
||||
</div>
|
||||
<div class="media__body medium">
|
||||
<div class="user-select__uname">${user.uname}</div>
|
||||
|
||||
@@ -23,7 +23,7 @@ const messagePage = new AutoloadPage('messagePage', () => {
|
||||
console.log('Received message', msg);
|
||||
if (msg.mdoc.flag & FLAG_ALERT) {
|
||||
// Is alert
|
||||
new InfoDialog({
|
||||
new InfoDialog({
|
||||
$body: tpl`
|
||||
<div class="typo">
|
||||
<p>${i18n('Message from {0}:', msg.mdoc.from)}</p>
|
||||
@@ -34,7 +34,7 @@ const messagePage = new AutoloadPage('messagePage', () => {
|
||||
// Is message
|
||||
new Notification({
|
||||
title: msg.udoc.uname,
|
||||
avatar: msg.udoc.gravatar_url,
|
||||
avatar: msg.udoc.avatarUrl,
|
||||
message: msg.mdoc.content,
|
||||
duration: 15000,
|
||||
action: () => window.open(`/home/messages?uid=${msg.udoc._id}`, '_blank'),
|
||||
|
||||
@@ -52,8 +52,8 @@ export default connect(mapStateToProps, null)(class MessagePadDialogueContentCon
|
||||
isSelf={reply.from === UserContext._id}
|
||||
faceUrl={
|
||||
reply.from === UserContext._id
|
||||
? UserContext.gravatar
|
||||
: this.props.item.udoc.gravatar
|
||||
? UserContext.avatarUrl
|
||||
: this.props.item.udoc.avatarUrl
|
||||
}
|
||||
>
|
||||
<div>{reply.content}</div>
|
||||
|
||||
@@ -39,7 +39,7 @@ export default connect(mapStateToProps, mapDispatchToProps)(class MessagePadDial
|
||||
key={dialogue._id}
|
||||
userName={dialogue.udoc.uname}
|
||||
summary={(_.last(dialogue.messages)?.content) || ''}
|
||||
faceUrl={dialogue.udoc.gravatar}
|
||||
faceUrl={dialogue.udoc.avatarUrl}
|
||||
active={dialogue._id === this.props.activeId}
|
||||
onClick={() => this.props.handleClick(dialogue._id)}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
|
||||
@@ -34,7 +34,6 @@ __langname: 简体中文
|
||||
'Role {1} already exists in domain {0}.': '域 {0} 中已存在角色 {1}。'
|
||||
'Solved {0} problems, RP: {1} (No. {2})': '解决了 {0} 道题目,RP: {1} (No. {2})'
|
||||
'The value `{1}` of {0} already exists.': '{0} 的值 `{1}` 已经存在。'
|
||||
'We use <a href="https://en.gravatar.com/" target="_blank">Gravatar</a> to present your avatar icon.': '我们使用 <a href="https://cn.gravatar.com/" target="_blank">Gravatar</a> 服务来呈现您的头像。'
|
||||
'Yes': 是
|
||||
"Effects only when Difficulty is not 'Use algorithm calculated'.": 仅当难度不为“使用算法计算”时才起效。
|
||||
"Split by ', '.": 由“, ”或“,”分隔。
|
||||
@@ -317,7 +316,6 @@ Formula blocks: 公式区块
|
||||
fs_upload: 上传文件
|
||||
Gender Visibility: 性别可见性
|
||||
Gender: 性别
|
||||
Gravatar Email: Gravatar Email 地址
|
||||
Hard Deadline: 最终截止时间
|
||||
Hash: 散列
|
||||
Have ALL PERMISSIONS in this domain: 在此域中拥有全部权限
|
||||
|
||||
@@ -194,7 +194,6 @@ Forgot password or username?: 忘記密碼或者使用者名?
|
||||
Formula blocks: 公式區塊
|
||||
Gender Visibility: 性別可見性
|
||||
Gender: 性別
|
||||
Gravatar Email: Gravatar Email 地址
|
||||
Hard Deadline: 最終截止時間
|
||||
Have ALL PERMISSIONS in this domain: 在此域中擁有全部許可權
|
||||
Help: 幫助
|
||||
@@ -538,7 +537,6 @@ Visible to registered users: 對註冊使用者可見
|
||||
Visit: 檢視
|
||||
Vote problem solutions: 為題解投票
|
||||
Vote: 投票
|
||||
We use Gravatar to present your avatar icon.: 我們使用 Gravatar 服務來呈現您的頭像。
|
||||
We will check code similarity after this contest.: 我們會在賽後檢查程式碼相似度。
|
||||
WeChat Visibility: 微信可見性
|
||||
WeChat: 微信
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/ui-default",
|
||||
"version": "4.6.6",
|
||||
"version": "4.6.7",
|
||||
"author": "undefined <i@undefined.moe>",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "hydro.js",
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<li class="dczcomments__item">
|
||||
<div class="media">
|
||||
<div class="media__left top">
|
||||
<img src="{{ gravatar(handler.user.gravatar, 60) }}" width="60" height="60" class="medium user-profile-avatar">
|
||||
<img src="{{ avatarUrl(handler.user.avatar, 60) }}" width="60" height="60" class="medium user-profile-avatar">
|
||||
</div>
|
||||
<div class="media__body top no-heading">
|
||||
<div class="hasjs--hide">
|
||||
@@ -55,7 +55,7 @@
|
||||
<li class="dczcomments__reply commentbox-container">
|
||||
<div class="media">
|
||||
<div class="media__left top">
|
||||
<img src="{{ gravatar(handler.user.gravatar, 50) }}" width="50" height="50" class="medium user-profile-avatar">
|
||||
<img src="{{ avatarUrl(handler.user.avatar, 50) }}" width="50" height="50" class="medium user-profile-avatar">
|
||||
</div>
|
||||
<div class="media__body top no-heading">
|
||||
<div class="commentbox-placeholder"></div>
|
||||
@@ -72,7 +72,7 @@
|
||||
<li class="dczcomments__item">
|
||||
<div class="media">
|
||||
<div class="media__left top">
|
||||
<img src="{{ gravatar(udoc.gravatar, 60) }}" width="60" height="60" class="medium user-profile-avatar">
|
||||
<img src="{{ avatarUrl(udoc.avatar, 60) }}" width="60" height="60" class="medium user-profile-avatar">
|
||||
</div>
|
||||
<div class="media__body top no-heading">
|
||||
<div class="clearfix">
|
||||
@@ -108,7 +108,7 @@
|
||||
<li class="dczcomments__reply{% if mode_create %} commentbox-container{% endif %}">
|
||||
<div class="media">
|
||||
<div class="media__left top">
|
||||
<img src="{{ gravatar(udoc.gravatar, 50) }}" width="50" height="50" class="medium user-profile-avatar">
|
||||
<img src="{{ avatarUrl(udoc.avatar, 50) }}" width="50" height="50" class="medium user-profile-avatar">
|
||||
</div>
|
||||
<div class="media__body top no-heading">
|
||||
<div class="clearfix">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{% if udoc %}
|
||||
<span class="user-profile-link">
|
||||
{% if avatar %}
|
||||
<img class="small user-profile-avatar v-center" src="{{ gravatar(udoc.gravatar|default('')) }}" width="20" height="20">
|
||||
<img class="small user-profile-avatar v-center" src="{{ avatarUrl(udoc.avatar|default('')) }}" width="20" height="20">
|
||||
{% endif %}
|
||||
<a class="user-profile-name" href="{{ url('user_detail', uid=udoc._id) }}">
|
||||
{% if udoc.displayName %}
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
<div class="section__body">
|
||||
<div class="media sidebar-user-profile">
|
||||
<div class="media__left">
|
||||
<img src="{{ gravatar(udoc.gravatar, 80) }}" width="80" height="80" class="medium user-profile-avatar">
|
||||
<img src="{{ avatarUrl(udoc.avatar, 80) }}" width="80" height="80" class="medium user-profile-avatar">
|
||||
</div>
|
||||
<div class="media__body">
|
||||
<p>
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
required:true
|
||||
}) }}
|
||||
{{ form.form_text({
|
||||
label:'Gravatar',
|
||||
name:'gravatar'
|
||||
label:'Avatar',
|
||||
name:'avatar'
|
||||
}) }}
|
||||
<div class="row"><div class="columns">
|
||||
<input type="hidden" name="csrfToken" value="{{ handler.csrfToken }}">
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</div>
|
||||
<div class="section__body typo">
|
||||
<dl class="large horizontal">
|
||||
<dt>{{ _('Gravatar') }}</dt><dd><img src="{{ gravatar(domain.gravatar) }}" width="32" height="32"></dd>
|
||||
<dt>{{ _('Avatar') }}</dt><dd><img src="{{ avatarUrl(domain.avatar) }}" width="32" height="32"></dd>
|
||||
<dt>{{ _('Name') }}</dt><dd>{{ domain.name }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
{%- for ddoc in ddocs -%}
|
||||
<tr>
|
||||
<td class="col--icon">
|
||||
<img src="{{ gravatar(ddoc.gravatar) }}" width="32" height="32">
|
||||
<img src="{{ avatarUrl(ddoc.avatar, 32) }}" width="32" height="32">
|
||||
</td>
|
||||
<td class="col--name">
|
||||
{{ ddoc['name'] }} ({{ ddoc['_id'] }})
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</div>
|
||||
<div class="section__body typo">
|
||||
<dl class="large horizontal">
|
||||
<dt>{{ _('Gravatar') }}</dt><dd><img src="{{ gravatar(domain.gravatar) }}" width="32" height="32"></dd>
|
||||
<dt>{{ _('Avatar') }}</dt><dd><img src="{{ avatarUrl(domain.avatar) }}" width="32" height="32"></dd>
|
||||
<dt>{{ _('Name') }}</dt><dd>{{ domain.name }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div class="profile-header__content">
|
||||
<div class="media">
|
||||
<div class="media__left">
|
||||
<img src="{{ gravatar(udoc.gravatar, 120) }}" width="120" height="120" class="large user-profile-avatar">
|
||||
<img src="{{ avatarUrl(udoc.avatar, 120) }}" width="120" height="120" class="large user-profile-avatar">
|
||||
</div>
|
||||
<div class="media__body profile-header__main">
|
||||
<h1>
|
||||
|
||||
@@ -10,7 +10,7 @@ const name = process.argv[2];
|
||||
|
||||
const IGNORE_CHECK = ['en'];
|
||||
const IGNORE_MISSING = [
|
||||
'?', 'AC', 'Gravatar', 'ID', 'MD5', 'URL',
|
||||
'?', 'AC', 'ID', 'MD5', 'URL',
|
||||
];
|
||||
const RE_TEMPLATE = /_\(['"]([\s\S])*?['"]\)/gmi;
|
||||
const RE_UI = /i18n\(['"]([\s\S])*?['"](,.*?)?\)/gmi;
|
||||
|
||||
@@ -265,14 +265,14 @@ export namespace Time {
|
||||
|
||||
export function errorMessage(err: Error | string) {
|
||||
const t = typeof err === 'string' ? err : err.stack;
|
||||
const q = t.split('\n');
|
||||
for (let i = 0; i < q.length; i++) {
|
||||
if (!q[i].startsWith(' at')) continue;
|
||||
if (q[i].includes(`${sep}@hydrooj${sep}`)) q[i] = q[i].split(`@hydrooj${sep}`)[1];
|
||||
else if (q[i].includes(`${sep}hydrooj${sep}`)) q[i] = ` at hydrooj${sep}${q[i].split(`hydrooj${sep}`)[1]}`;
|
||||
}
|
||||
if (typeof err === 'string') return q.join('\n');
|
||||
err.stack = q.join('\n');
|
||||
const parsed = t
|
||||
.replace(/[A-Z]:\\.+\\@hydrooj\\/g, '\\')
|
||||
.replace(/\/.+\/@hydrooj\//g, '\\')
|
||||
.replace(/[A-Z]:\\.+\\hydrooj\\/g, 'hydrooj\\')
|
||||
.replace(/\/.+\/hydrooj\//g, 'hydrooj/')
|
||||
.replace(/\\/g, '/');
|
||||
if (typeof err === 'string') return parsed;
|
||||
err.stack = parsed;
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -376,3 +376,17 @@ export async function retry(arg0: number | Function, func: any, ...args: any[])
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
export function CallableInstance(property = '__call__') {
|
||||
let func;
|
||||
if (typeof property === 'function') func = property;
|
||||
else func = this.constructor.prototype[property];
|
||||
const apply = function __call__(...args) { return func.apply(apply, ...args); };
|
||||
Object.setPrototypeOf(apply, this.constructor.prototype);
|
||||
Object.getOwnPropertyNames(func).forEach((p) => {
|
||||
Object.defineProperty(apply, p, Object.getOwnPropertyDescriptor(func, p));
|
||||
});
|
||||
return apply;
|
||||
}
|
||||
|
||||
CallableInstance.prototype = Object.create(Function.prototype);
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hydrooj/utils",
|
||||
"version": "1.0.5",
|
||||
"version": "1.0.6",
|
||||
"description": "hydrooj utils",
|
||||
"main": "package.json",
|
||||
"repository": "https://github.com/hydro-dev/Hydro.git",
|
||||
|
||||
Reference in new issue
Block a user