From 13cee2b4f56adaf328c2da5c744a9bef5c9185e6 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 16 Jan 2019 14:54:14 +0900
Subject: [PATCH] Resolve #3896

---
 locales/ja-JP.yml                                    |  1 +
 src/client/app/admin/views/instance.vue              |  4 ++++
 src/client/app/desktop/views/components/timeline.vue |  6 ++++--
 src/client/app/mobile/views/pages/home.vue           |  6 ++++--
 src/models/meta.ts                                   |  1 +
 src/server/api/endpoints/admin/update-meta.ts        | 11 +++++++++++
 src/server/api/endpoints/meta.ts                     |  2 ++
 src/server/api/endpoints/notes/global-timeline.ts    |  4 ++--
 src/server/api/stream/channels/global-timeline.ts    |  2 +-
 9 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index d1e515e819..1ce094b400 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1163,6 +1163,7 @@ admin/views/instance.vue:
   max-note-text-length: "投稿の最大文字数"
   disable-registration: "ユーザー登録の受付を停止する"
   disable-local-timeline: "ローカルタイムラインを無効にする"
+  disable-global-timeline: "グローバルタイムラインを無効にする"
   invite: "招待"
   save: "保存"
   saved: "保存しました"
diff --git a/src/client/app/admin/views/instance.vue b/src/client/app/admin/views/instance.vue
index a4f7c0344d..ad4e5bb006 100644
--- a/src/client/app/admin/views/instance.vue
+++ b/src/client/app/admin/views/instance.vue
@@ -22,6 +22,7 @@
 		<section>
 			<ui-switch v-model="disableRegistration">{{ $t('disable-registration') }}</ui-switch>
 			<ui-switch v-model="disableLocalTimeline">{{ $t('disable-local-timeline') }}</ui-switch>
+			<ui-switch v-model="disableGlobalTimeline">{{ $t('disable-global-timeline') }}</ui-switch>
 		</section>
 		<section class="fit-bottom">
 			<header><fa icon="cloud"/> {{ $t('drive-config') }}</header>
@@ -150,6 +151,7 @@ export default Vue.extend({
 			maintainerEmail: null,
 			disableRegistration: false,
 			disableLocalTimeline: false,
+			disableGlobalTimeline: false,
 			mascotImageUrl: null,
 			bannerUrl: null,
 			errorImageUrl: null,
@@ -198,6 +200,7 @@ export default Vue.extend({
 			this.maintainerEmail = meta.maintainer.email;
 			this.disableRegistration = meta.disableRegistration;
 			this.disableLocalTimeline = meta.disableLocalTimeline;
+			this.disableGlobalTimeline = meta.disableGlobalTimeline;
 			this.mascotImageUrl = meta.mascotImageUrl;
 			this.bannerUrl = meta.bannerUrl;
 			this.errorImageUrl = meta.errorImageUrl;
@@ -256,6 +259,7 @@ export default Vue.extend({
 				maintainerEmail: this.maintainerEmail,
 				disableRegistration: this.disableRegistration,
 				disableLocalTimeline: this.disableLocalTimeline,
+				disableGlobalTimeline: this.disableGlobalTimeline,
 				mascotImageUrl: this.mascotImageUrl,
 				bannerUrl: this.bannerUrl,
 				errorImageUrl: this.errorImageUrl,
diff --git a/src/client/app/desktop/views/components/timeline.vue b/src/client/app/desktop/views/components/timeline.vue
index dfcac31a7f..edeb30d9cd 100644
--- a/src/client/app/desktop/views/components/timeline.vue
+++ b/src/client/app/desktop/views/components/timeline.vue
@@ -4,7 +4,7 @@
 		<span :data-active="src == 'home'" @click="src = 'home'"><fa icon="home"/> {{ $t('home') }}</span>
 		<span :data-active="src == 'local'" @click="src = 'local'" v-if="enableLocalTimeline"><fa :icon="['far', 'comments']"/> {{ $t('local') }}</span>
 		<span :data-active="src == 'hybrid'" @click="src = 'hybrid'" v-if="enableLocalTimeline"><fa icon="share-alt"/> {{ $t('hybrid') }}</span>
-		<span :data-active="src == 'global'" @click="src = 'global'"><fa icon="globe"/> {{ $t('global') }}</span>
+		<span :data-active="src == 'global'" @click="src = 'global'" v-if="enableGlobalTimeline"><fa icon="globe"/> {{ $t('global') }}</span>
 		<span :data-active="src == 'tag'" @click="src = 'tag'" v-if="tagTl"><fa icon="hashtag"/> {{ tagTl.title }}</span>
 		<span :data-active="src == 'list'" @click="src = 'list'" v-if="list"><fa icon="list"/> {{ list.title }}</span>
 		<div class="buttons">
@@ -43,7 +43,8 @@ export default Vue.extend({
 			src: 'home',
 			list: null,
 			tagTl: null,
-			enableLocalTimeline: false
+			enableLocalTimeline: false,
+			enableGlobalTimeline: false,
 		};
 	},
 
@@ -66,6 +67,7 @@ export default Vue.extend({
 	created() {
 		this.$root.getMeta().then(meta => {
 			this.enableLocalTimeline = !meta.disableLocalTimeline || this.$store.state.i.isModerator || this.$store.state.i.isAdmin;
+			this.enableGlobalTimeline = !meta.disableGlobalTimeline || this.$store.state.i.isModerator || this.$store.state.i.isAdmin;
 		});
 
 		if (this.$store.state.device.tl) {
diff --git a/src/client/app/mobile/views/pages/home.vue b/src/client/app/mobile/views/pages/home.vue
index cc30777177..9adf716b32 100644
--- a/src/client/app/mobile/views/pages/home.vue
+++ b/src/client/app/mobile/views/pages/home.vue
@@ -31,7 +31,7 @@
 					<span :data-active="src == 'home'" @click="src = 'home'"><fa icon="home"/> {{ $t('home') }}</span>
 					<span :data-active="src == 'local'" @click="src = 'local'" v-if="enableLocalTimeline"><fa :icon="['far', 'comments']"/> {{ $t('local') }}</span>
 					<span :data-active="src == 'hybrid'" @click="src = 'hybrid'" v-if="enableLocalTimeline"><fa icon="share-alt"/> {{ $t('hybrid') }}</span>
-					<span :data-active="src == 'global'" @click="src = 'global'"><fa icon="globe"/> {{ $t('global') }}</span>
+					<span :data-active="src == 'global'" @click="src = 'global'" v-if="enableGlobalTimeline"><fa icon="globe"/> {{ $t('global') }}</span>
 					<div class="hr"></div>
 					<span :data-active="src == 'mentions'" @click="src = 'mentions'"><fa icon="at"/> {{ $t('mentions') }}<i class="badge" v-if="$store.state.i.hasUnreadMentions"><fa icon="circle"/></i></span>
 					<span :data-active="src == 'messages'" @click="src = 'messages'"><fa :icon="['far', 'envelope']"/> {{ $t('messages') }}<i class="badge" v-if="$store.state.i.hasUnreadSpecifiedNotes"><fa icon="circle"/></i></span>
@@ -79,7 +79,8 @@ export default Vue.extend({
 			lists: null,
 			tagTl: null,
 			showNav: false,
-			enableLocalTimeline: false
+			enableLocalTimeline: false,
+			enableGlobalTimeline: false,
 		};
 	},
 
@@ -113,6 +114,7 @@ export default Vue.extend({
 	created() {
 		this.$root.getMeta().then(meta => {
 			this.enableLocalTimeline = !meta.disableLocalTimeline || this.$store.state.i.isModerator || this.$store.state.i.isAdmin;
+			this.enableGlobalTimeline = !meta.disableGlobalTimeline || this.$store.state.i.isModerator || this.$store.state.i.isAdmin;
 		});
 
 		if (this.$store.state.device.tl) {
diff --git a/src/models/meta.ts b/src/models/meta.ts
index daa7964d6f..4d4b00be6e 100644
--- a/src/models/meta.ts
+++ b/src/models/meta.ts
@@ -184,6 +184,7 @@ export type IMeta = {
 
 	disableRegistration?: boolean;
 	disableLocalTimeline?: boolean;
+	disableGlobalTimeline?: boolean;
 	hidedTags?: string[];
 	mascotImageUrl?: string;
 	bannerUrl?: string;
diff --git a/src/server/api/endpoints/admin/update-meta.ts b/src/server/api/endpoints/admin/update-meta.ts
index cf117ebd71..13663243a2 100644
--- a/src/server/api/endpoints/admin/update-meta.ts
+++ b/src/server/api/endpoints/admin/update-meta.ts
@@ -32,6 +32,13 @@ export const meta = {
 			}
 		},
 
+		disableGlobalTimeline: {
+			validator: $.bool.optional.nullable,
+			desc: {
+				'ja-JP': 'グローバルタイムラインを無効にするか否か'
+			}
+		},
+
 		hidedTags: {
 			validator: $.arr($.str).optional.nullable,
 			desc: {
@@ -331,6 +338,10 @@ export default define(meta, (ps) => new Promise(async (res, rej) => {
 		set.disableLocalTimeline = ps.disableLocalTimeline;
 	}
 
+	if (typeof ps.disableGlobalTimeline === 'boolean') {
+		set.disableGlobalTimeline = ps.disableGlobalTimeline;
+	}
+
 	if (Array.isArray(ps.hidedTags)) {
 		set.hidedTags = ps.hidedTags;
 	}
diff --git a/src/server/api/endpoints/meta.ts b/src/server/api/endpoints/meta.ts
index fa933513b7..3b2a49dbb0 100644
--- a/src/server/api/endpoints/meta.ts
+++ b/src/server/api/endpoints/meta.ts
@@ -59,6 +59,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
 		broadcasts: instance.broadcasts || [],
 		disableRegistration: instance.disableRegistration,
 		disableLocalTimeline: instance.disableLocalTimeline,
+		disableGlobalTimeline: instance.disableGlobalTimeline,
 		driveCapacityPerLocalUserMb: instance.localDriveCapacityMb,
 		driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb,
 		cacheRemoteFiles: instance.cacheRemoteFiles,
@@ -81,6 +82,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
 		response.features = {
 			registration: !instance.disableRegistration,
 			localTimeLine: !instance.disableLocalTimeline,
+			globalTimeLine: !instance.disableGlobalTimeline,
 			elasticsearch: config.elasticsearch ? true : false,
 			recaptcha: instance.enableRecaptcha,
 			objectStorage: config.drive && config.drive.storage === 'minio',
diff --git a/src/server/api/endpoints/notes/global-timeline.ts b/src/server/api/endpoints/notes/global-timeline.ts
index f5fd0d6dd8..f0d052ff98 100644
--- a/src/server/api/endpoints/notes/global-timeline.ts
+++ b/src/server/api/endpoints/notes/global-timeline.ts
@@ -53,9 +53,9 @@ export const meta = {
 
 export default define(meta, (ps, user) => new Promise(async (res, rej) => {
 	const meta = await fetchMeta();
-	if (meta.disableLocalTimeline) {
+	if (meta.disableGlobalTimeline) {
 		if (user == null || (!user.isAdmin && !user.isModerator)) {
-			return rej('local timeline disabled');
+			return rej('global timeline disabled');
 		}
 	}
 
diff --git a/src/server/api/stream/channels/global-timeline.ts b/src/server/api/stream/channels/global-timeline.ts
index 611d5a6aef..b09035daee 100644
--- a/src/server/api/stream/channels/global-timeline.ts
+++ b/src/server/api/stream/channels/global-timeline.ts
@@ -15,7 +15,7 @@ export default class extends Channel {
 	@autobind
 	public async init(params: any) {
 		const meta = await fetchMeta();
-		if (meta.disableLocalTimeline) {
+		if (meta.disableGlobalTimeline) {
 			if (this.user == null || (!this.user.isAdmin && !this.user.isModerator)) return;
 		}