Merge branch 'develop' into twemoji
This commit is contained in:
commit
136f23c7ad
|
@ -167,6 +167,3 @@ drive:
|
||||||
# external: true
|
# external: true
|
||||||
# engine: http://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}
|
# engine: http://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}
|
||||||
# timeout: 300000
|
# timeout: 300000
|
||||||
|
|
||||||
# Max allowed note text length in charactors
|
|
||||||
maxNoteTextLength: 1000
|
|
||||||
|
|
41
.travis.yml
41
.travis.yml
|
@ -1,41 +0,0 @@
|
||||||
# travis file
|
|
||||||
# https://docs.travis-ci.com/user/customizing-the-build
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email: false
|
|
||||||
|
|
||||||
branches:
|
|
||||||
except:
|
|
||||||
- l10n_master
|
|
||||||
|
|
||||||
language: node_js
|
|
||||||
|
|
||||||
node_js:
|
|
||||||
- 11.0.0
|
|
||||||
|
|
||||||
env:
|
|
||||||
- CXX=g++-4.8 NODE_ENV=production
|
|
||||||
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
packages:
|
|
||||||
- g++-4.8
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- node_modules
|
|
||||||
|
|
||||||
services:
|
|
||||||
- mongodb
|
|
||||||
- redis-server
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- npm install
|
|
||||||
|
|
||||||
# 設定ファイルを配置
|
|
||||||
- cp ./.ci/default.yml ./.config
|
|
||||||
- cp ./.ci/test.yml ./.config
|
|
||||||
|
|
||||||
- travis_wait npm run build
|
|
|
@ -23,5 +23,5 @@ Please use [Crowdin](https://crowdin.com/project/misskey) for localization.
|
||||||
* Test codes are located in `/test`.
|
* Test codes are located in `/test`.
|
||||||
|
|
||||||
## Continuous integration
|
## Continuous integration
|
||||||
Misskey uses Travis for automated test.
|
Misskey uses CircleCI for automated test.
|
||||||
Configuration files are located in `/.travis`.
|
Configuration files are located in `/.circleci`.
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
================================================================
|
================================================================
|
||||||
|
|
||||||
[![CircleCI](https://circleci.com/gh/syuilo/misskey.svg?style=svg)](https://circleci.com/gh/syuilo/misskey)
|
[![CircleCI](https://circleci.com/gh/syuilo/misskey.svg?style=svg)](https://circleci.com/gh/syuilo/misskey)
|
||||||
[![][travis-badge]][travis-link]
|
|
||||||
[![][dependencies-badge]][dependencies-link]
|
[![][dependencies-badge]][dependencies-link]
|
||||||
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
|
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
|
||||||
|
|
||||||
|
@ -124,8 +123,6 @@ Misskey is an open-source software licensed under the [GNU AGPLv3](LICENSE).
|
||||||
|
|
||||||
[agpl-3.0]: https://www.gnu.org/licenses/agpl-3.0.en.html
|
[agpl-3.0]: https://www.gnu.org/licenses/agpl-3.0.en.html
|
||||||
[agpl-3.0-badge]: https://img.shields.io/badge/license-AGPL--3.0-444444.svg?style=flat-square
|
[agpl-3.0-badge]: https://img.shields.io/badge/license-AGPL--3.0-444444.svg?style=flat-square
|
||||||
[travis-link]: https://travis-ci.org/syuilo/misskey
|
|
||||||
[travis-badge]: http://img.shields.io/travis/syuilo/misskey/master.svg?style=flat-square
|
|
||||||
[dependencies-link]: https://david-dm.org/syuilo/misskey
|
[dependencies-link]: https://david-dm.org/syuilo/misskey
|
||||||
[dependencies-badge]: https://img.shields.io/david/syuilo/misskey.svg?style=flat-square
|
[dependencies-badge]: https://img.shields.io/david/syuilo/misskey.svg?style=flat-square
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ adduser --disabled-password --disabled-login misskey
|
||||||
Please install and setup these softwares:
|
Please install and setup these softwares:
|
||||||
|
|
||||||
#### Dependencies :package:
|
#### Dependencies :package:
|
||||||
* **[Node.js](https://nodejs.org/en/)**
|
* **[Node.js](https://nodejs.org/en/)** >= 10.0.0
|
||||||
* **[MongoDB](https://www.mongodb.com/)** >= 3.6
|
* **[MongoDB](https://www.mongodb.com/)** >= 3.6
|
||||||
|
|
||||||
##### Optional
|
##### Optional
|
||||||
|
|
|
@ -22,7 +22,7 @@ adduser --disabled-password --disabled-login misskey
|
||||||
これらのソフトウェアをインストール・設定してください:
|
これらのソフトウェアをインストール・設定してください:
|
||||||
|
|
||||||
#### 依存関係 :package:
|
#### 依存関係 :package:
|
||||||
* **[Node.js](https://nodejs.org/en/)**
|
* **[Node.js](https://nodejs.org/en/)** (10.0.0以上)
|
||||||
* **[MongoDB](https://www.mongodb.com/)** (3.6以上)
|
* **[MongoDB](https://www.mongodb.com/)** (3.6以上)
|
||||||
|
|
||||||
##### オプション
|
##### オプション
|
||||||
|
|
|
@ -1077,8 +1077,9 @@ admin/views/instance.vue:
|
||||||
instance-name: "インスタンス名"
|
instance-name: "インスタンス名"
|
||||||
instance-description: "インスタンスの紹介"
|
instance-description: "インスタンスの紹介"
|
||||||
banner-url: "バナー画像URL"
|
banner-url: "バナー画像URL"
|
||||||
disableRegistration: "ユーザー登録の受付を停止する"
|
max-note-text-length: "投稿の最大文字数"
|
||||||
disableLocalTimeline: "ローカルタイムラインを無効にする"
|
disable-registration: "ユーザー登録の受付を停止する"
|
||||||
|
disable-local-timeline: "ローカルタイムラインを無効にする"
|
||||||
invite: "招待"
|
invite: "招待"
|
||||||
save: "保存"
|
save: "保存"
|
||||||
saved: "保存しました"
|
saved: "保存しました"
|
||||||
|
@ -1151,6 +1152,9 @@ admin/views/announcements.vue:
|
||||||
title: "タイトル"
|
title: "タイトル"
|
||||||
text: "内容"
|
text: "内容"
|
||||||
saved: "保存しました"
|
saved: "保存しました"
|
||||||
|
_remove:
|
||||||
|
are-you-sure: "「$1」を削除しますか?"
|
||||||
|
removed: "削除しました"
|
||||||
|
|
||||||
admin/views/hashtags.vue:
|
admin/views/hashtags.vue:
|
||||||
hided-tags: "Hidden Tags"
|
hided-tags: "Hidden Tags"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "10.38.6",
|
"version": "10.38.7",
|
||||||
"clientVersion": "1.0.11516",
|
"clientVersion": "1.0.11530",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<span>%i18n:@text%</span>
|
<span>%i18n:@text%</span>
|
||||||
</ui-textarea>
|
</ui-textarea>
|
||||||
<ui-horizon-group>
|
<ui-horizon-group>
|
||||||
<ui-button @click="save">%fa:save R% %i18n:@save%</ui-button>
|
<ui-button @click="save()">%fa:save R% %i18n:@save%</ui-button>
|
||||||
<ui-button @click="remove(i)">%fa:trash-alt R% %i18n:@remove%</ui-button>
|
<ui-button @click="remove(i)">%fa:trash-alt R% %i18n:@remove%</ui-button>
|
||||||
</ui-horizon-group>
|
</ui-horizon-group>
|
||||||
</section>
|
</section>
|
||||||
|
@ -46,18 +46,31 @@ export default Vue.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
remove(i) {
|
remove(i) {
|
||||||
|
this.$swal({
|
||||||
|
type: 'warning',
|
||||||
|
text: '%i18n:@_remove.are-you-sure%'.replace('$1', this.announcements.find((_, j) => j == i).title),
|
||||||
|
showCancelButton: true
|
||||||
|
}).then(res => {
|
||||||
|
if (!res.value) return;
|
||||||
this.announcements = this.announcements.filter((_, j) => j !== i);
|
this.announcements = this.announcements.filter((_, j) => j !== i);
|
||||||
this.save();
|
this.save(true);
|
||||||
|
this.$swal({
|
||||||
|
type: 'success',
|
||||||
|
text: '%i18n:@_remove.removed%'
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
save() {
|
save(silent) {
|
||||||
(this as any).api('admin/update-meta', {
|
(this as any).api('admin/update-meta', {
|
||||||
broadcasts: this.announcements
|
broadcasts: this.announcements
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
if (!silent) {
|
||||||
this.$swal({
|
this.$swal({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
text: '%i18n:@saved%'
|
text: '%i18n:@saved%'
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
this.$swal({
|
this.$swal({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
<ui-input v-model="name">%i18n:@instance-name%</ui-input>
|
<ui-input v-model="name">%i18n:@instance-name%</ui-input>
|
||||||
<ui-textarea v-model="description">%i18n:@instance-description%</ui-textarea>
|
<ui-textarea v-model="description">%i18n:@instance-description%</ui-textarea>
|
||||||
<ui-input v-model="bannerUrl">%i18n:@banner-url%</ui-input>
|
<ui-input v-model="bannerUrl">%i18n:@banner-url%</ui-input>
|
||||||
|
<ui-input v-model="maxNoteTextLength">%i18n:@max-note-text-length%</ui-input>
|
||||||
<ui-button @click="updateMeta">%i18n:@save%</ui-button>
|
<ui-button @click="updateMeta">%i18n:@save%</ui-button>
|
||||||
</section>
|
</section>
|
||||||
</ui-card>
|
</ui-card>
|
||||||
|
@ -39,6 +40,7 @@ export default Vue.extend({
|
||||||
bannerUrl: null,
|
bannerUrl: null,
|
||||||
name: null,
|
name: null,
|
||||||
description: null,
|
description: null,
|
||||||
|
maxNoteTextLength: null,
|
||||||
inviteCode: null,
|
inviteCode: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -48,6 +50,7 @@ export default Vue.extend({
|
||||||
this.bannerUrl = meta.bannerUrl;
|
this.bannerUrl = meta.bannerUrl;
|
||||||
this.name = meta.name;
|
this.name = meta.name;
|
||||||
this.description = meta.description;
|
this.description = meta.description;
|
||||||
|
this.maxNoteTextLength = meta.maxNoteTextLength;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -69,7 +72,8 @@ export default Vue.extend({
|
||||||
disableLocalTimeline: this.disableLocalTimeline,
|
disableLocalTimeline: this.disableLocalTimeline,
|
||||||
bannerUrl: this.bannerUrl,
|
bannerUrl: this.bannerUrl,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
description: this.description
|
description: this.description,
|
||||||
|
maxNoteTextLength: parseInt(this.maxNoteTextLength, 10)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.$swal({
|
this.$swal({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
|
|
@ -49,8 +49,6 @@ export default function load() {
|
||||||
if (config.localDriveCapacityMb == null) config.localDriveCapacityMb = 256;
|
if (config.localDriveCapacityMb == null) config.localDriveCapacityMb = 256;
|
||||||
if (config.remoteDriveCapacityMb == null) config.remoteDriveCapacityMb = 8;
|
if (config.remoteDriveCapacityMb == null) config.remoteDriveCapacityMb = 8;
|
||||||
|
|
||||||
if (config.maxNoteTextLength == null) config.maxNoteTextLength = 1000;
|
|
||||||
|
|
||||||
return Object.assign(config, mixin);
|
return Object.assign(config, mixin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,8 +107,6 @@ export type Source = {
|
||||||
engine: string;
|
engine: string;
|
||||||
timeout: number;
|
timeout: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
maxNoteTextLength?: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -43,4 +43,9 @@ export type IMeta = {
|
||||||
disableLocalTimeline?: boolean;
|
disableLocalTimeline?: boolean;
|
||||||
hidedTags?: string[];
|
hidedTags?: string[];
|
||||||
bannerUrl?: string;
|
bannerUrl?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Max allowed note text length in charactors
|
||||||
|
*/
|
||||||
|
maxNoteTextLength?: number;
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,6 @@ import Reaction from './note-reaction';
|
||||||
import { packMany as packFileMany, IDriveFile } from './drive-file';
|
import { packMany as packFileMany, IDriveFile } from './drive-file';
|
||||||
import Favorite from './favorite';
|
import Favorite from './favorite';
|
||||||
import Following from './following';
|
import Following from './following';
|
||||||
import config from '../config';
|
|
||||||
import Emoji from './emoji';
|
import Emoji from './emoji';
|
||||||
|
|
||||||
const Note = db.get<INote>('notes');
|
const Note = db.get<INote>('notes');
|
||||||
|
@ -27,10 +26,6 @@ Note.createIndex({ createdAt: -1 });
|
||||||
Note.createIndex({ score: -1 }, { sparse: true });
|
Note.createIndex({ score: -1 }, { sparse: true });
|
||||||
export default Note;
|
export default Note;
|
||||||
|
|
||||||
export function isValidText(text: string): boolean {
|
|
||||||
return length(text.trim()) <= config.maxNoteTextLength && text.trim() != '';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isValidCw(text: string): boolean {
|
export function isValidCw(text: string): boolean {
|
||||||
return length(text.trim()) <= 100;
|
return length(text.trim()) <= 100;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,13 @@ export const meta = {
|
||||||
'ja-JP': 'インスタンスの紹介文'
|
'ja-JP': 'インスタンスの紹介文'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
maxNoteTextLength: {
|
||||||
|
validator: $.num.optional.min(1),
|
||||||
|
desc: {
|
||||||
|
'ja-JP': '投稿の最大文字数'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -93,6 +100,10 @@ export default define(meta, (ps) => new Promise(async (res, rej) => {
|
||||||
set.description = ps.description;
|
set.description = ps.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ps.maxNoteTextLength) {
|
||||||
|
set.maxNoteTextLength = ps.maxNoteTextLength;
|
||||||
|
}
|
||||||
|
|
||||||
await Meta.update({}, {
|
await Meta.update({}, {
|
||||||
$set: set
|
$set: set
|
||||||
}, { upsert: true });
|
}, { upsert: true });
|
||||||
|
|
|
@ -62,7 +62,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
||||||
swPublickey: config.sw ? config.sw.public_key : null,
|
swPublickey: config.sw ? config.sw.public_key : null,
|
||||||
hidedTags: (me && me.isAdmin) ? met.hidedTags : undefined,
|
hidedTags: (me && me.isAdmin) ? met.hidedTags : undefined,
|
||||||
bannerUrl: met.bannerUrl,
|
bannerUrl: met.bannerUrl,
|
||||||
maxNoteTextLength: config.maxNoteTextLength,
|
maxNoteTextLength: met.maxNoteTextLength || 1000,
|
||||||
|
|
||||||
emojis: emojis,
|
emojis: emojis,
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
import $ from 'cafy'; import ID, { transform, transformMany } from '../../../../misc/cafy-id';
|
import $ from 'cafy'; import ID, { transform, transformMany } from '../../../../misc/cafy-id';
|
||||||
const ms = require('ms');
|
const ms = require('ms');
|
||||||
import Note, { INote, isValidText, isValidCw, pack } from '../../../../models/note';
|
import { length } from 'stringz';
|
||||||
|
import Note, { INote, isValidCw, pack } from '../../../../models/note';
|
||||||
import User, { IUser } from '../../../../models/user';
|
import User, { IUser } from '../../../../models/user';
|
||||||
import DriveFile, { IDriveFile } from '../../../../models/drive-file';
|
import DriveFile, { IDriveFile } from '../../../../models/drive-file';
|
||||||
import create from '../../../../services/note/create';
|
import create from '../../../../services/note/create';
|
||||||
import define from '../../define';
|
import define from '../../define';
|
||||||
|
import Meta from '../../../../models/meta';
|
||||||
|
|
||||||
|
let maxNoteTextLength = 1000;
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
Meta.findOne({}).then(m => {
|
||||||
|
if (m.maxNoteTextLength) maxNoteTextLength = m.maxNoteTextLength;
|
||||||
|
});
|
||||||
|
}, 3000);
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
stability: 'stable',
|
stability: 'stable',
|
||||||
|
@ -40,7 +50,9 @@ export const meta = {
|
||||||
},
|
},
|
||||||
|
|
||||||
text: {
|
text: {
|
||||||
validator: $.str.optional.nullable.pipe(isValidText),
|
validator: $.str.optional.nullable.pipe(text =>
|
||||||
|
length(text.trim()) <= maxNoteTextLength && text.trim() != ''
|
||||||
|
),
|
||||||
default: null as any,
|
default: null as any,
|
||||||
desc: {
|
desc: {
|
||||||
'ja-JP': '投稿内容'
|
'ja-JP': '投稿内容'
|
||||||
|
|
Loading…
Reference in a new issue