From 00b134ce1ecfd2103677c3ed4fdda96c6748d687 Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Wed, 30 Jan 2019 17:25:56 +0900 Subject: [PATCH] Introduce silence (#4043) * Introduce silence * Fix icon --- locales/ja-JP.yml | 2 + src/client/app/admin/views/users.user.vue | 5 +- src/client/app/admin/views/users.vue | 46 ++++++++++++++++- src/models/user.ts | 5 ++ .../api/endpoints/admin/silence-user.ts | 49 +++++++++++++++++++ .../api/endpoints/admin/unsilence-user.ts | 45 +++++++++++++++++ src/services/note/create.ts | 5 ++ 7 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 src/server/api/endpoints/admin/silence-user.ts create mode 100644 src/server/api/endpoints/admin/unsilence-user.ts diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 91a6add5df..ed8331c523 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1274,6 +1274,8 @@ admin/views/users.vue: unsuspend: "凍結の解除" unsuspend-confirm: "凍結を解除しますか?" unsuspended: "凍結を解除しました" + make-silence: "サイレンス" + unmake-silence: "サイレンスの解除" verify: "公式アカウントにする" verify-confirm: "公式アカウントにしますか?" verified: "公式アカウントにしました" diff --git a/src/client/app/admin/views/users.user.vue b/src/client/app/admin/views/users.user.vue index afece18e82..096e017e6a 100644 --- a/src/client/app/admin/views/users.user.vue +++ b/src/client/app/admin/views/users.user.vue @@ -12,6 +12,7 @@ <span class="is-admin" v-if="user.isAdmin">admin</span> <span class="is-moderator" v-if="user.isModerator">moderator</span> <span class="is-verified" v-if="user.isVerified" :title="$t('@.verified-user')"><fa icon="star"/></span> + <span class="is-silenced" v-if="user.isSilenced" :title="$t('@.silenced-user')"><fa :icon="faMicrophoneSlash"/></span> <span class="is-suspended" v-if="user.isSuspended" :title="$t('@.suspended-user')"><fa :icon="faSnowflake"/></span> </header> <div> @@ -27,6 +28,7 @@ <script lang="ts"> import Vue from 'vue'; import i18n from '../../i18n'; +import { faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons'; import { faSnowflake } from '@fortawesome/free-regular-svg-icons'; export default Vue.extend({ @@ -34,7 +36,7 @@ export default Vue.extend({ props: ['user'], data() { return { - faSnowflake + faSnowflake, faMicrophoneSlash }; }, }); @@ -76,6 +78,7 @@ export default Vue.extend({ color var(--noteHeaderAdminFg) > .is-verified + > .is-silenced > .is-suspended margin 0 0 0 .5em color #4dabf7 diff --git a/src/client/app/admin/views/users.vue b/src/client/app/admin/views/users.vue index 09d074eee2..f2306c26f2 100644 --- a/src/client/app/admin/views/users.vue +++ b/src/client/app/admin/views/users.vue @@ -16,6 +16,10 @@ <ui-button @click="verifyUser" :disabled="verifying"><fa :icon="faCertificate"/> {{ $t('verify') }}</ui-button> <ui-button @click="unverifyUser" :disabled="unverifying">{{ $t('unverify') }}</ui-button> </ui-horizon-group> + <ui-horizon-group> + <ui-button @click="silenceUser"><fa :icon="faMicrophoneSlash"/> {{ $t('make-silence') }}</ui-button> + <ui-button @click="unsilenceUser">{{ $t('unmake-silence') }}</ui-button> + </ui-horizon-group> <ui-horizon-group> <ui-button @click="suspendUser" :disabled="suspending"><fa :icon="faSnowflake"/> {{ $t('suspend') }}</ui-button> <ui-button @click="unsuspendUser" :disabled="unsuspending">{{ $t('unsuspend') }}</ui-button> @@ -66,7 +70,7 @@ import Vue from 'vue'; import i18n from '../../i18n'; import parseAcct from "../../../../misc/acct/parse"; -import { faCertificate, faUsers, faTerminal, faSearch, faKey, faSync } from '@fortawesome/free-solid-svg-icons'; +import { faCertificate, faUsers, faTerminal, faSearch, faKey, faSync, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons'; import { faSnowflake } from '@fortawesome/free-regular-svg-icons'; import XUser from './users.user.vue'; @@ -90,7 +94,7 @@ export default Vue.extend({ offset: 0, users: [], existMore: false, - faTerminal, faCertificate, faUsers, faSnowflake, faSearch, faKey, faSync + faTerminal, faCertificate, faUsers, faSnowflake, faSearch, faKey, faSync, faMicrophoneSlash }; }, @@ -216,6 +220,44 @@ export default Vue.extend({ this.refreshUser(); }, + async silenceUser() { + const process = async () => { + await this.$root.api('admin/silence-user', { userId: this.user._id }); + this.$root.dialog({ + type: 'success', + splash: true + }); + }; + + await process().catch(e => { + this.$root.dialog({ + type: 'error', + text: e.toString() + }); + }); + + this.refreshUser(); + }, + + async unsilenceUser() { + const process = async () => { + await this.$root.api('admin/unsilence-user', { userId: this.user._id }); + this.$root.dialog({ + type: 'success', + splash: true + }); + }; + + await process().catch(e => { + this.$root.dialog({ + type: 'error', + text: e.toString() + }); + }); + + this.refreshUser(); + }, + async suspendUser() { if (!await this.getConfirmed(this.$t('suspend-confirm'))) return; diff --git a/src/models/user.ts b/src/models/user.ts index 6987bd3da8..df0e3c22a2 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -54,6 +54,11 @@ type IUserBase = { */ isSuspended: boolean; + /** + * サイレンスされているか否か + */ + isSilenced: boolean; + /** * 鍵アカウントか否か */ diff --git a/src/server/api/endpoints/admin/silence-user.ts b/src/server/api/endpoints/admin/silence-user.ts new file mode 100644 index 0000000000..7b1090a895 --- /dev/null +++ b/src/server/api/endpoints/admin/silence-user.ts @@ -0,0 +1,49 @@ +import $ from 'cafy'; +import ID, { transform } from '../../../../misc/cafy-id'; +import define from '../../define'; +import User from '../../../../models/user'; + +export const meta = { + desc: { + 'ja-JP': '指定したユーザーをサイレンスにします。', + 'en-US': 'Make silence a user.' + }, + + requireCredential: true, + requireModerator: true, + + params: { + userId: { + validator: $.type(ID), + transform: transform, + desc: { + 'ja-JP': '対象のユーザーID', + 'en-US': 'The user ID which you want to make silence' + } + }, + } +}; + +export default define(meta, (ps) => new Promise(async (res, rej) => { + const user = await User.findOne({ + _id: ps.userId + }); + + if (user == null) { + return rej('user not found'); + } + + if (user.isAdmin) { + return rej('cannot silence admin'); + } + + await User.findOneAndUpdate({ + _id: user._id + }, { + $set: { + isSilenced: true + } + }); + + res(); +})); diff --git a/src/server/api/endpoints/admin/unsilence-user.ts b/src/server/api/endpoints/admin/unsilence-user.ts new file mode 100644 index 0000000000..a01bfbb6d2 --- /dev/null +++ b/src/server/api/endpoints/admin/unsilence-user.ts @@ -0,0 +1,45 @@ +import $ from 'cafy'; +import ID, { transform } from '../../../../misc/cafy-id'; +import define from '../../define'; +import User from '../../../../models/user'; + +export const meta = { + desc: { + 'ja-JP': '指定したユーザーのサイレンスを解除します。', + 'en-US': 'Unsilence a user.' + }, + + requireCredential: true, + requireModerator: true, + + params: { + userId: { + validator: $.type(ID), + transform: transform, + desc: { + 'ja-JP': '対象のユーザーID', + 'en-US': 'The user ID which you want to unsilence' + } + }, + } +}; + +export default define(meta, (ps) => new Promise(async (res, rej) => { + const user = await User.findOne({ + _id: ps.userId + }); + + if (user == null) { + return rej('user not found'); + } + + await User.findOneAndUpdate({ + _id: user._id + }, { + $set: { + isSilenced: false + } + }); + + res(); +})); diff --git a/src/services/note/create.ts b/src/services/note/create.ts index 344672bd63..4e8e707961 100644 --- a/src/services/note/create.ts +++ b/src/services/note/create.ts @@ -116,6 +116,11 @@ export default async (user: IUser, data: Option, silent = false) => new Promise< if (data.viaMobile == null) data.viaMobile = false; if (data.localOnly == null) data.localOnly = false; + // サイレンス + if (user.isSilenced && data.visibility == 'public') { + data.visibility = 'home'; + } + if (data.visibleUsers) { data.visibleUsers = erase(null, data.visibleUsers); }