mirror of
https://gitcode.com/gh_mirrors/hy/Hydro.git
synced 2026-10-20 01:38:19 +08:00
blog: move to a seperate package
This commit is contained in:
18 files changed
+251
-253
No files matched your search
@@ -0,0 +1,244 @@
|
|||||||
|
import {
|
||||||
|
_, Context, DiscussionNotFoundError, DocumentModel, Filter,
|
||||||
|
Handler, NumberKeys, ObjectId, OplogModel, paginate,
|
||||||
|
param, PRIV, Types, UserModel,
|
||||||
|
} from 'hydrooj';
|
||||||
|
|
||||||
|
export const TYPE_BLOG: 70 = 70;
|
||||||
|
export interface BlogDoc {
|
||||||
|
docType: 70;
|
||||||
|
docId: ObjectId;
|
||||||
|
owner: number;
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
ip: string;
|
||||||
|
updateAt: Date;
|
||||||
|
nReply: number;
|
||||||
|
views: number;
|
||||||
|
reply: any[];
|
||||||
|
react: Record<string, number>;
|
||||||
|
}
|
||||||
|
declare module 'hydrooj' {
|
||||||
|
interface Model {
|
||||||
|
blog: typeof BlogModel;
|
||||||
|
}
|
||||||
|
interface DocType {
|
||||||
|
[TYPE_BLOG]: BlogDoc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BlogModel {
|
||||||
|
static async add(
|
||||||
|
owner: number, title: string, content: string, ip?: string,
|
||||||
|
): Promise<ObjectId> {
|
||||||
|
const payload: Partial<BlogDoc> = {
|
||||||
|
content,
|
||||||
|
owner,
|
||||||
|
title,
|
||||||
|
ip,
|
||||||
|
nReply: 0,
|
||||||
|
updateAt: new Date(),
|
||||||
|
views: 0,
|
||||||
|
};
|
||||||
|
const res = await DocumentModel.add(
|
||||||
|
'system', payload.content!, payload.owner!, TYPE_BLOG,
|
||||||
|
null, null, null, _.omit(payload, ['domainId', 'content', 'owner']),
|
||||||
|
);
|
||||||
|
payload.docId = res;
|
||||||
|
return payload.docId;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async get(did: ObjectId): Promise<BlogDoc> {
|
||||||
|
return await DocumentModel.get('system', TYPE_BLOG, did);
|
||||||
|
}
|
||||||
|
|
||||||
|
static edit(did: ObjectId, title: string, content: string): Promise<BlogDoc> {
|
||||||
|
const payload = { title, content };
|
||||||
|
return DocumentModel.set('system', TYPE_BLOG, did, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inc(did: ObjectId, key: NumberKeys<BlogDoc>, value: number): Promise<BlogDoc | null> {
|
||||||
|
return DocumentModel.inc('system', TYPE_BLOG, did, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static del(did: ObjectId): Promise<never> {
|
||||||
|
return Promise.all([
|
||||||
|
DocumentModel.deleteOne('system', TYPE_BLOG, did),
|
||||||
|
DocumentModel.deleteMultiStatus('system', TYPE_BLOG, { docId: did }),
|
||||||
|
]) as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
static count(query: Filter<BlogDoc>) {
|
||||||
|
return DocumentModel.count('system', TYPE_BLOG, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getMulti(query: Filter<BlogDoc> = {}) {
|
||||||
|
return DocumentModel.getMulti('system', TYPE_BLOG, query)
|
||||||
|
.sort({ _id: -1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async addReply(did: ObjectId, owner: number, content: string, ip: string): Promise<ObjectId> {
|
||||||
|
const [[, drid]] = await Promise.all([
|
||||||
|
DocumentModel.push('system', TYPE_BLOG, did, 'reply', content, owner, { ip }),
|
||||||
|
DocumentModel.incAndSet('system', TYPE_BLOG, did, 'nReply', 1, { updateAt: new Date() }),
|
||||||
|
]);
|
||||||
|
return drid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static setStar(did: ObjectId, uid: number, star: boolean) {
|
||||||
|
return DocumentModel.setStatus('system', TYPE_BLOG, did, uid, { star });
|
||||||
|
}
|
||||||
|
|
||||||
|
static getStatus(did: ObjectId, uid: number) {
|
||||||
|
return DocumentModel.getStatus('system', TYPE_BLOG, did, uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static setStatus(did: ObjectId, uid: number, $set) {
|
||||||
|
return DocumentModel.setStatus('system', TYPE_BLOG, did, uid, $set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global.Hydro.model.blog = BlogModel;
|
||||||
|
|
||||||
|
class BlogHandler extends Handler {
|
||||||
|
ddoc?: BlogDoc;
|
||||||
|
|
||||||
|
@param('did', Types.ObjectId, true)
|
||||||
|
async _prepare(domainId: string, did: ObjectId) {
|
||||||
|
if (did) {
|
||||||
|
this.ddoc = await BlogModel.get(did);
|
||||||
|
if (!this.ddoc) throw new DiscussionNotFoundError(domainId, did);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BlogUserHandler extends BlogHandler {
|
||||||
|
@param('uid', Types.Int)
|
||||||
|
@param('page', Types.PositiveInt, true)
|
||||||
|
async get(domainId: string, uid: number, page = 1) {
|
||||||
|
const [ddocs, dpcount] = await paginate(
|
||||||
|
BlogModel.getMulti({ owner: uid }),
|
||||||
|
page,
|
||||||
|
10,
|
||||||
|
);
|
||||||
|
const udoc = await UserModel.getById(domainId, uid);
|
||||||
|
this.response.template = 'blog_main.html';
|
||||||
|
this.response.body = {
|
||||||
|
ddocs,
|
||||||
|
dpcount,
|
||||||
|
udoc,
|
||||||
|
page,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BlogDetailHandler extends BlogHandler {
|
||||||
|
@param('did', Types.ObjectId)
|
||||||
|
async get(domainId: string, did: ObjectId) {
|
||||||
|
const dsdoc = this.user.hasPriv(PRIV.PRIV_USER_PROFILE)
|
||||||
|
? await BlogModel.getStatus(did, this.user._id)
|
||||||
|
: null;
|
||||||
|
const udoc = await UserModel.getById(domainId, this.ddoc!.owner);
|
||||||
|
if (!dsdoc?.view) {
|
||||||
|
await Promise.all([
|
||||||
|
BlogModel.inc(did, 'views', 1),
|
||||||
|
BlogModel.setStatus(did, this.user._id, { view: true }),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
this.response.template = 'blog_detail.html';
|
||||||
|
this.response.body = {
|
||||||
|
ddoc: this.ddoc, dsdoc, udoc,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async post() {
|
||||||
|
this.checkPriv(PRIV.PRIV_USER_PROFILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@param('did', Types.ObjectId)
|
||||||
|
async postStar(domainId: string, did: ObjectId) {
|
||||||
|
await BlogModel.setStar(did, this.user._id, true);
|
||||||
|
this.back({ star: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
@param('did', Types.ObjectId)
|
||||||
|
async postUnstar(domainId: string, did: ObjectId) {
|
||||||
|
await BlogModel.setStar(did, this.user._id, false);
|
||||||
|
this.back({ star: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BlogEditHandler extends BlogHandler {
|
||||||
|
async get() {
|
||||||
|
this.response.template = 'blog_edit.html';
|
||||||
|
this.response.body = { ddoc: this.ddoc };
|
||||||
|
}
|
||||||
|
|
||||||
|
@param('title', Types.Title)
|
||||||
|
@param('content', Types.Content)
|
||||||
|
async postCreate(domainId: string, title: string, content: string) {
|
||||||
|
await this.limitRate('add_blog', 3600, 60);
|
||||||
|
const did = await BlogModel.add(this.user._id, title, content, this.request.ip);
|
||||||
|
this.response.body = { did };
|
||||||
|
this.response.redirect = this.url('blog_detail', { uid: this.user._id, did });
|
||||||
|
}
|
||||||
|
|
||||||
|
@param('did', Types.ObjectId)
|
||||||
|
@param('title', Types.Title)
|
||||||
|
@param('content', Types.Content)
|
||||||
|
async postUpdate(domainId: string, did: ObjectId, title: string, content: string) {
|
||||||
|
if (!this.user.own(this.ddoc!)) this.checkPriv(PRIV.PRIV_EDIT_SYSTEM);
|
||||||
|
await Promise.all([
|
||||||
|
BlogModel.edit(did, title, content),
|
||||||
|
OplogModel.log(this, 'blog.edit', this.ddoc),
|
||||||
|
]);
|
||||||
|
this.response.body = { did };
|
||||||
|
this.response.redirect = this.url('blog_detail', { uid: this.user._id, did });
|
||||||
|
}
|
||||||
|
|
||||||
|
@param('did', Types.ObjectId)
|
||||||
|
async postDelete(domainId: string, did: ObjectId) {
|
||||||
|
if (!this.user.own(this.ddoc!)) this.checkPriv(PRIV.PRIV_EDIT_SYSTEM);
|
||||||
|
await Promise.all([
|
||||||
|
BlogModel.del(did),
|
||||||
|
OplogModel.log(this, 'blog.delete', this.ddoc),
|
||||||
|
]);
|
||||||
|
this.response.redirect = this.url('blog_main', { uid: this.ddoc!.owner });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function apply(ctx: Context) {
|
||||||
|
ctx.Route('blog_main', '/blog/:uid', BlogUserHandler);
|
||||||
|
ctx.Route('blog_create', '/blog/:uid/create', BlogEditHandler, PRIV.PRIV_USER_PROFILE);
|
||||||
|
ctx.Route('blog_detail', '/blog/:uid/:did', BlogDetailHandler);
|
||||||
|
ctx.Route('blog_edit', '/blog/:uid/:did/edit', BlogEditHandler, PRIV.PRIV_USER_PROFILE);
|
||||||
|
ctx.inject('UserDropdown', 'blog_main', {
|
||||||
|
icon: 'book', displayName: 'Blog', args: (h) => ({ uid: h.user._id }),
|
||||||
|
}, PRIV.PRIV_USER_PROFILE);
|
||||||
|
ctx.i18n.load('zh', {
|
||||||
|
"{0}'s blog": '{0} 的博客',
|
||||||
|
Blog: '博客',
|
||||||
|
blog_detail: '博客详情',
|
||||||
|
blog_edit: '编辑博客',
|
||||||
|
blog_main: '博客',
|
||||||
|
});
|
||||||
|
ctx.i18n.load('zh_TW', {
|
||||||
|
"{0}'s blog": '{0} 的部落格',
|
||||||
|
Blog: '部落格',
|
||||||
|
blog_detail: '部落格詳情',
|
||||||
|
blog_edit: '編輯部落格',
|
||||||
|
blog_main: '部落格',
|
||||||
|
});
|
||||||
|
ctx.i18n.load('kr', {
|
||||||
|
"{0}'s blog": '{0}의 블로그',
|
||||||
|
Blog: '블로그',
|
||||||
|
blog_main: '블로그',
|
||||||
|
blog_detail: '블로그 상세',
|
||||||
|
blog_edit: '블로그 수정',
|
||||||
|
});
|
||||||
|
ctx.i18n.load('en', {
|
||||||
|
blog_main: 'Blog',
|
||||||
|
blog_detail: 'Blog Detail',
|
||||||
|
blog_edit: 'Edit Blog',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "@hydrooj/blog",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"license": "AGPL-3.0-or-later"
|
||||||
|
}
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -84,7 +84,6 @@ Begin Time: 시작 시각
|
|||||||
Belongs to: 소유
|
Belongs to: 소유
|
||||||
Bio Visibility: 소개 공개
|
Bio Visibility: 소개 공개
|
||||||
Bio: 소개
|
Bio: 소개
|
||||||
Blog: Blog
|
|
||||||
Bold: 굵게
|
Bold: 굵게
|
||||||
Boom!: 펑!
|
Boom!: 펑!
|
||||||
Browser: 브라우저
|
Browser: 브라우저
|
||||||
|
|||||||
@@ -97,10 +97,6 @@ Begin Time: 开始时间
|
|||||||
Belongs to: 属于
|
Belongs to: 属于
|
||||||
Bio Visibility: 个人简介可见性
|
Bio Visibility: 个人简介可见性
|
||||||
Bio: 个人简介
|
Bio: 个人简介
|
||||||
blog_detail: 博客详情
|
|
||||||
blog_edit: 编辑博客
|
|
||||||
blog_main: 博客
|
|
||||||
Blog: 博客
|
|
||||||
Bold: 加粗
|
Bold: 加粗
|
||||||
Boom!: 炸了!
|
Boom!: 炸了!
|
||||||
Browser: 浏览器
|
Browser: 浏览器
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ Begin Time: 開始時間
|
|||||||
Belongs to: 屬於
|
Belongs to: 屬於
|
||||||
Bio Visibility: 個人簡介可見性
|
Bio Visibility: 個人簡介可見性
|
||||||
Bio: 個人簡介
|
Bio: 個人簡介
|
||||||
Blog: 部落格
|
|
||||||
Boom!: 炸了!
|
Boom!: 炸了!
|
||||||
Browser: 瀏覽器
|
Browser: 瀏覽器
|
||||||
Built-in: 內建
|
Built-in: 內建
|
||||||
|
|||||||
@@ -1,125 +0,0 @@
|
|||||||
import { ObjectId } from 'mongodb';
|
|
||||||
import { DiscussionNotFoundError } from '../error';
|
|
||||||
import { BlogDoc } from '../interface';
|
|
||||||
import paginate from '../lib/paginate';
|
|
||||||
import * as blog from '../model/blog';
|
|
||||||
import { PRIV } from '../model/builtin';
|
|
||||||
import * as oplog from '../model/oplog';
|
|
||||||
import * as system from '../model/system';
|
|
||||||
import user from '../model/user';
|
|
||||||
import { Handler, param, Types } from '../service/server';
|
|
||||||
|
|
||||||
class BlogHandler extends Handler {
|
|
||||||
ddoc?: BlogDoc;
|
|
||||||
|
|
||||||
@param('did', Types.ObjectId, true)
|
|
||||||
async _prepare(domainId: string, did: ObjectId) {
|
|
||||||
if (did) {
|
|
||||||
this.ddoc = await blog.get(did);
|
|
||||||
if (!this.ddoc) throw new DiscussionNotFoundError(domainId, did);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BlogUserHandler extends BlogHandler {
|
|
||||||
@param('uid', Types.Int)
|
|
||||||
@param('page', Types.PositiveInt, true)
|
|
||||||
async get(domainId: string, uid: number, page = 1) {
|
|
||||||
const [ddocs, dpcount] = await paginate(
|
|
||||||
blog.getMulti({ owner: uid }),
|
|
||||||
page,
|
|
||||||
10,
|
|
||||||
);
|
|
||||||
const udoc = await user.getById(domainId, uid);
|
|
||||||
this.response.template = 'blog_main.html';
|
|
||||||
this.response.body = {
|
|
||||||
ddocs,
|
|
||||||
dpcount,
|
|
||||||
udoc,
|
|
||||||
page,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BlogDetailHandler extends BlogHandler {
|
|
||||||
@param('did', Types.ObjectId)
|
|
||||||
async get(domainId: string, did: ObjectId) {
|
|
||||||
const dsdoc = this.user.hasPriv(PRIV.PRIV_USER_PROFILE)
|
|
||||||
? await blog.getStatus(did, this.user._id)
|
|
||||||
: null;
|
|
||||||
const udoc = await user.getById(domainId, this.ddoc.owner);
|
|
||||||
if (!dsdoc?.view) {
|
|
||||||
await Promise.all([
|
|
||||||
blog.inc(did, 'views', 1),
|
|
||||||
blog.setStatus(did, this.user._id, { view: true }),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
this.response.template = 'blog_detail.html';
|
|
||||||
this.response.body = {
|
|
||||||
ddoc: this.ddoc, dsdoc, udoc,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async post() {
|
|
||||||
this.checkPriv(PRIV.PRIV_USER_PROFILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@param('did', Types.ObjectId)
|
|
||||||
async postStar(domainId: string, did: ObjectId) {
|
|
||||||
await blog.setStar(did, this.user._id, true);
|
|
||||||
this.back({ star: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
@param('did', Types.ObjectId)
|
|
||||||
async postUnstar(domainId: string, did: ObjectId) {
|
|
||||||
await blog.setStar(did, this.user._id, false);
|
|
||||||
this.back({ star: false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BlogEditHandler extends BlogHandler {
|
|
||||||
async get() {
|
|
||||||
this.response.template = 'blog_edit.html';
|
|
||||||
this.response.body = { ddoc: this.ddoc };
|
|
||||||
}
|
|
||||||
|
|
||||||
@param('title', Types.Title)
|
|
||||||
@param('content', Types.Content)
|
|
||||||
async postCreate(domainId: string, title: string, content: string) {
|
|
||||||
await this.limitRate('add_blog', 3600, 60);
|
|
||||||
const did = await blog.add(this.user._id, title, content, this.request.ip);
|
|
||||||
this.response.body = { did };
|
|
||||||
this.response.redirect = this.url('blog_detail', { uid: this.user._id, did });
|
|
||||||
}
|
|
||||||
|
|
||||||
@param('did', Types.ObjectId)
|
|
||||||
@param('title', Types.Title)
|
|
||||||
@param('content', Types.Content)
|
|
||||||
async postUpdate(domainId: string, did: ObjectId, title: string, content: string) {
|
|
||||||
if (!this.user.own(this.ddoc)) this.checkPriv(PRIV.PRIV_EDIT_SYSTEM);
|
|
||||||
await Promise.all([
|
|
||||||
blog.edit(did, title, content),
|
|
||||||
oplog.log(this, 'blog.edit', this.ddoc),
|
|
||||||
]);
|
|
||||||
this.response.body = { did };
|
|
||||||
this.response.redirect = this.url('blog_detail', { uid: this.user._id, did });
|
|
||||||
}
|
|
||||||
|
|
||||||
@param('did', Types.ObjectId)
|
|
||||||
async postDelete(domainId: string, did: ObjectId) {
|
|
||||||
if (!this.user.own(this.ddoc)) this.checkPriv(PRIV.PRIV_EDIT_SYSTEM);
|
|
||||||
await Promise.all([
|
|
||||||
blog.del(did),
|
|
||||||
oplog.log(this, 'blog.delete', this.ddoc),
|
|
||||||
]);
|
|
||||||
this.response.redirect = this.url('blog_main', { uid: this.ddoc.owner });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function apply(ctx) {
|
|
||||||
if (!system.get('server.blog')) return;
|
|
||||||
ctx.Route('blog_main', '/blog/:uid', BlogUserHandler);
|
|
||||||
ctx.Route('blog_create', '/blog/:uid/create', BlogEditHandler, PRIV.PRIV_USER_PROFILE);
|
|
||||||
ctx.Route('blog_detail', '/blog/:uid/:did', BlogDetailHandler);
|
|
||||||
ctx.Route('blog_edit', '/blog/:uid/:did/edit', BlogEditHandler, PRIV.PRIV_USER_PROFILE);
|
|
||||||
}
|
|
||||||
@@ -486,20 +486,6 @@ export interface DiscussionTailReplyDoc {
|
|||||||
editor?: number;
|
editor?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlogDoc {
|
|
||||||
docType: document['TYPE_BLOG'];
|
|
||||||
docId: ObjectId;
|
|
||||||
owner: number;
|
|
||||||
title: string;
|
|
||||||
content: string;
|
|
||||||
ip: string;
|
|
||||||
updateAt: Date;
|
|
||||||
nReply: number;
|
|
||||||
views: number;
|
|
||||||
reply: any[];
|
|
||||||
react: Record<string, number>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TokenDoc {
|
export interface TokenDoc {
|
||||||
_id: string,
|
_id: string,
|
||||||
tokenType: number,
|
tokenType: number,
|
||||||
@@ -677,7 +663,6 @@ declare module './service/db' {
|
|||||||
|
|
||||||
export interface Model {
|
export interface Model {
|
||||||
blacklist: typeof import('./model/blacklist').default,
|
blacklist: typeof import('./model/blacklist').default,
|
||||||
blog: typeof import('./model/blog'),
|
|
||||||
builtin: typeof import('./model/builtin'),
|
builtin: typeof import('./model/builtin'),
|
||||||
contest: typeof import('./model/contest'),
|
contest: typeof import('./model/contest'),
|
||||||
discussion: typeof import('./model/discussion'),
|
discussion: typeof import('./model/discussion'),
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
import { omit } from 'lodash';
|
|
||||||
import { Filter, ObjectId } from 'mongodb';
|
|
||||||
import { BlogDoc } from '../interface';
|
|
||||||
import { NumberKeys } from '../typeutils';
|
|
||||||
import * as document from './document';
|
|
||||||
|
|
||||||
export async function add(
|
|
||||||
owner: number, title: string, content: string,
|
|
||||||
ip: string | null = null,
|
|
||||||
): Promise<ObjectId> {
|
|
||||||
const payload: Partial<BlogDoc> = {
|
|
||||||
content,
|
|
||||||
owner,
|
|
||||||
title,
|
|
||||||
ip,
|
|
||||||
nReply: 0,
|
|
||||||
updateAt: new Date(),
|
|
||||||
views: 0,
|
|
||||||
};
|
|
||||||
const res = await document.add(
|
|
||||||
'system', payload.content!, payload.owner!, document.TYPE_BLOG,
|
|
||||||
null, null, null, omit(payload, ['domainId', 'content', 'owner']),
|
|
||||||
);
|
|
||||||
payload.docId = res;
|
|
||||||
return payload.docId;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function get(did: ObjectId): Promise<BlogDoc> {
|
|
||||||
return await document.get('system', document.TYPE_BLOG, did);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function edit(did: ObjectId, title: string, content: string): Promise<BlogDoc> {
|
|
||||||
const payload = { title, content };
|
|
||||||
return document.set('system', document.TYPE_BLOG, did, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inc(did: ObjectId, key: NumberKeys<BlogDoc>, value: number): Promise<BlogDoc | null> {
|
|
||||||
return document.inc('system', document.TYPE_BLOG, did, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function del(did: ObjectId): Promise<never> {
|
|
||||||
return Promise.all([
|
|
||||||
document.deleteOne('system', document.TYPE_BLOG, did),
|
|
||||||
document.deleteMultiStatus('system', document.TYPE_BLOG, { docId: did }),
|
|
||||||
]) as any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function count(query: Filter<BlogDoc>) {
|
|
||||||
return document.count('system', document.TYPE_BLOG, query);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getMulti(query: Filter<BlogDoc> = {}) {
|
|
||||||
return document.getMulti('system', document.TYPE_BLOG, query)
|
|
||||||
.sort({ _id: -1 });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function addReply(did: ObjectId, owner: number, content: string, ip: string): Promise<ObjectId> {
|
|
||||||
const [[, drid]] = await Promise.all([
|
|
||||||
document.push('system', document.TYPE_BLOG, did, 'reply', content, owner, { ip }),
|
|
||||||
document.incAndSet('system', document.TYPE_BLOG, did, 'nReply', 1, { updateAt: new Date() }),
|
|
||||||
]);
|
|
||||||
return drid;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setStar(did: ObjectId, uid: number, star: boolean) {
|
|
||||||
return document.setStatus('system', document.TYPE_BLOG, did, uid, { star });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getStatus(did: ObjectId, uid: number) {
|
|
||||||
return document.getStatus('system', document.TYPE_BLOG, did, uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setStatus(did: ObjectId, uid: number, $set) {
|
|
||||||
return document.setStatus('system', document.TYPE_BLOG, did, uid, $set);
|
|
||||||
}
|
|
||||||
|
|
||||||
global.Hydro.model.blog = {
|
|
||||||
add,
|
|
||||||
get,
|
|
||||||
inc,
|
|
||||||
edit,
|
|
||||||
del,
|
|
||||||
count,
|
|
||||||
getMulti,
|
|
||||||
addReply,
|
|
||||||
setStar,
|
|
||||||
getStatus,
|
|
||||||
setStatus,
|
|
||||||
};
|
|
||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
} from 'mongodb';
|
} from 'mongodb';
|
||||||
import { Context } from '../context';
|
import { Context } from '../context';
|
||||||
import {
|
import {
|
||||||
BlogDoc, Content, DiscussionDoc,
|
Content, DiscussionDoc,
|
||||||
DiscussionReplyDoc, ProblemDoc, ProblemStatusDoc,
|
DiscussionReplyDoc, ProblemDoc, ProblemStatusDoc,
|
||||||
Tdoc, TrainingDoc,
|
Tdoc, TrainingDoc,
|
||||||
} from '../interface';
|
} from '../interface';
|
||||||
@@ -30,7 +30,6 @@ export const TYPE_CONTEST: 30 = 30;
|
|||||||
export const TYPE_TRAINING: 40 = 40;
|
export const TYPE_TRAINING: 40 = 40;
|
||||||
/** @deprecated use `TYPE_CONTEST` with rule `homework` instead. */
|
/** @deprecated use `TYPE_CONTEST` with rule `homework` instead. */
|
||||||
export const TYPE_HOMEWORK: 60 = 60;
|
export const TYPE_HOMEWORK: 60 = 60;
|
||||||
export const TYPE_BLOG: 70 = 70;
|
|
||||||
|
|
||||||
export interface DocType {
|
export interface DocType {
|
||||||
[TYPE_PROBLEM]: ProblemDoc;
|
[TYPE_PROBLEM]: ProblemDoc;
|
||||||
@@ -41,7 +40,6 @@ export interface DocType {
|
|||||||
[TYPE_DISCUSSION_REPLY]: DiscussionReplyDoc;
|
[TYPE_DISCUSSION_REPLY]: DiscussionReplyDoc;
|
||||||
[TYPE_CONTEST]: Tdoc;
|
[TYPE_CONTEST]: Tdoc;
|
||||||
[TYPE_TRAINING]: TrainingDoc;
|
[TYPE_TRAINING]: TrainingDoc;
|
||||||
[TYPE_BLOG]: BlogDoc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DocStatusType {
|
export interface DocStatusType {
|
||||||
@@ -499,5 +497,4 @@ global.Hydro.model.document = {
|
|||||||
TYPE_PROBLEM_LIST,
|
TYPE_PROBLEM_LIST,
|
||||||
TYPE_PROBLEM_SOLUTION,
|
TYPE_PROBLEM_SOLUTION,
|
||||||
TYPE_TRAINING,
|
TYPE_TRAINING,
|
||||||
TYPE_BLOG,
|
|
||||||
};
|
};
|
||||||
@@ -254,7 +254,6 @@ SystemSetting(
|
|||||||
Setting('setting_server', 'server.language', 'zh_CN', langRange, 'server.language', 'Default display language'),
|
Setting('setting_server', 'server.language', 'zh_CN', langRange, 'server.language', 'Default display language'),
|
||||||
Setting('setting_server', 'server.login', true, 'boolean', 'server.login', 'Allow builtin-login', FLAG_PRO),
|
Setting('setting_server', 'server.login', true, 'boolean', 'server.login', 'Allow builtin-login', FLAG_PRO),
|
||||||
Setting('setting_server', 'server.message', true, 'boolean', 'server.message', 'Allow users send messages'),
|
Setting('setting_server', 'server.message', true, 'boolean', 'server.message', 'Allow users send messages'),
|
||||||
Setting('setting_server', 'server.blog', true, 'boolean', 'server.blog', 'Allow users post blog'),
|
|
||||||
Setting('setting_server', 'server.checkUpdate', true, 'boolean', 'server.checkUpdate', 'Daily update check'),
|
Setting('setting_server', 'server.checkUpdate', true, 'boolean', 'server.checkUpdate', 'Daily update check'),
|
||||||
Setting('setting_server', 'server.ignoreUA', ignoreUA, 'textarea', 'server.ignoreUA', 'ignoredUA'),
|
Setting('setting_server', 'server.ignoreUA', ignoreUA, 'textarea', 'server.ignoreUA', 'ignoredUA'),
|
||||||
ServerLangSettingNode,
|
ServerLangSettingNode,
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export * as SystemModel from './model/system';
|
|||||||
export * as TrainingModel from './model/training';
|
export * as TrainingModel from './model/training';
|
||||||
export * as OpcountModel from './model/opcount';
|
export * as OpcountModel from './model/opcount';
|
||||||
export * as OplogModel from './model/oplog';
|
export * as OplogModel from './model/oplog';
|
||||||
export * as BlogModel from './model/blog';
|
|
||||||
export * as SettingModel from './model/setting';
|
export * as SettingModel from './model/setting';
|
||||||
export * as DiscussionModel from './model/discussion';
|
export * as DiscussionModel from './model/discussion';
|
||||||
export * as DocumentModel from './model/document';
|
export * as DocumentModel from './model/document';
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ Begin Time: 시작 시각
|
|||||||
Belongs to: 소유
|
Belongs to: 소유
|
||||||
Bio Visibility: 소개 공개
|
Bio Visibility: 소개 공개
|
||||||
Bio: 소개
|
Bio: 소개
|
||||||
Blog: Blog
|
|
||||||
Bold: 굵게
|
Bold: 굵게
|
||||||
Boom!: 펑!
|
Boom!: 펑!
|
||||||
Browser: 브라우저
|
Browser: 브라우저
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ __langname: 简体中文
|
|||||||
'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}` 已经存在。'
|
||||||
'Yes': 是
|
'Yes': 是
|
||||||
"{0}'s blog": "{0} 的博客"
|
|
||||||
"Effects only when Difficulty is not 'Use algorithm calculated'.": 仅当难度不为“使用算法计算”时才起效。
|
"Effects only when Difficulty is not 'Use algorithm calculated'.": 仅当难度不为“使用算法计算”时才起效。
|
||||||
"Split by ', '.": 由“, ”或“,”分隔。
|
"Split by ', '.": 由“, ”或“,”分隔。
|
||||||
"What's file?": 什么是文件?
|
"What's file?": 什么是文件?
|
||||||
@@ -112,7 +111,6 @@ Begin Time: 开始时间
|
|||||||
Belongs to: 属于
|
Belongs to: 属于
|
||||||
Bio Visibility: 个人简介可见性
|
Bio Visibility: 个人简介可见性
|
||||||
Bio: 个人简介
|
Bio: 个人简介
|
||||||
Blog: 博客
|
|
||||||
Bold: 加粗
|
Bold: 加粗
|
||||||
Boom!: 炸了!
|
Boom!: 炸了!
|
||||||
Browser: 浏览器
|
Browser: 浏览器
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ Begin Time: 開始時間
|
|||||||
Belongs to: 屬於
|
Belongs to: 屬於
|
||||||
Bio Visibility: 個人簡介可見性
|
Bio Visibility: 個人簡介可見性
|
||||||
Bio: 個人簡介
|
Bio: 個人簡介
|
||||||
Blog: 部落格
|
|
||||||
Boom!: 炸了!
|
Boom!: 炸了!
|
||||||
Browser: 瀏覽器
|
Browser: 瀏覽器
|
||||||
Built-in: 內建
|
Built-in: 內建
|
||||||
|
|||||||
@@ -79,18 +79,11 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if handler.user.hasPriv(PRIV.PRIV_USER_PROFILE) and model.system.get('server.blog') %}
|
|
||||||
<li class="menu__item">
|
|
||||||
<a href="{{ url('blog_main', uid=handler.user._id) }}" class="menu__link">
|
|
||||||
<span class="icon icon-book"></span> {{ _('Blog') }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% if ui.getNodes('UserDropdown').length %}
|
{% if ui.getNodes('UserDropdown').length %}
|
||||||
<li class="menu__seperator"></li>
|
<li class="menu__seperator"></li>
|
||||||
{%- for item in ui.getNodes('UserDropdown') -%}
|
{%- for item in ui.getNodes('UserDropdown') -%}
|
||||||
<li class="menu__item nojs--hide">
|
<li class="menu__item nojs--hide">
|
||||||
<a href="{{ url(item.name) }}" class="menu__link">
|
<a href="{{ url(item.name, (item.args(handler) if typeof(item.args)=='function' else item.args) or {}) }}" class="menu__link">
|
||||||
<span class="icon icon-{{ item.args.icon }}"></span> {{ _(item.args.displayName or item.name) }}
|
<span class="icon icon-{{ item.args.icon }}"></span> {{ _(item.args.displayName or item.name) }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
Reference in new issue
Block a user