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