From 7694d17b2e0c4cc0ade106db07171ae5dc35d610 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih Date: Sat, 11 Nov 2023 18:53:25 +0900 Subject: [PATCH] =?UTF-8?q?(add)=20=E3=83=AD=E3=83=BC=E3=83=AB=E5=88=B6?= =?UTF-8?q?=E5=BE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/backend/src/core/RoleService.ts | 3 +++ .../src/server/api/endpoints/notes/create.ts | 13 +++++++++++ .../api/endpoints/notes/schedule/list.ts | 1 + .../frontend/src/components/MkPostForm.vue | 6 ++--- packages/frontend/src/const.ts | 1 + packages/frontend/src/navbar.ts | 1 + .../frontend/src/pages/admin/roles.editor.vue | 22 ++++++++++++++++++- packages/frontend/src/pages/admin/roles.vue | 8 +++++++ 10 files changed, 53 insertions(+), 4 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 00dc9eb9e7..e671747c26 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1665,6 +1665,7 @@ export interface Locale { "gtlAvailable": string; "ltlAvailable": string; "canPublicNote": string; + "canScheduleNote": string; "canInvite": string; "inviteLimit": string; "inviteLimitCycle": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 459692f74e..454fd32aec 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1574,6 +1574,7 @@ _role: gtlAvailable: "グローバルタイムラインの閲覧" ltlAvailable: "ローカルタイムラインの閲覧" canPublicNote: "パブリック投稿の許可" + canScheduleNote: "予約投稿の許可" canInvite: "サーバー招待コードの発行" inviteLimit: "招待コードの作成可能数" inviteLimitCycle: "招待コードの発行間隔" diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index d6a414694a..d70665ab89 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -27,6 +27,7 @@ export type RolePolicies = { gtlAvailable: boolean; ltlAvailable: boolean; canPublicNote: boolean; + canScheduleNote: boolean; canInvite: boolean; inviteLimit: number; inviteLimitCycle: number; @@ -53,6 +54,7 @@ export const DEFAULT_POLICIES: RolePolicies = { gtlAvailable: true, ltlAvailable: true, canPublicNote: true, + canScheduleNote: true, canInvite: false, inviteLimit: 0, inviteLimitCycle: 60 * 24 * 7, @@ -303,6 +305,7 @@ export class RoleService implements OnApplicationShutdown { gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)), ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)), canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)), + canScheduleNote: calc('canScheduleNote', vs => vs.some(v => v === true)), canInvite: calc('canInvite', vs => vs.some(v => v === true)), inviteLimit: calc('inviteLimit', vs => Math.max(...vs)), inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)), diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 8a04ec540f..698b57ade9 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -18,6 +18,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; import { QueueService } from '@/core/QueueService.js'; import { IdService } from '@/core/IdService.js'; +import { RoleService } from '@/core/RoleService.js'; import { DI } from '@/di-symbols.js'; import { isPureRenote } from '@/misc/is-pure-renote.js'; import { ApiError } from '../../error.js'; @@ -132,6 +133,12 @@ export const meta = { code: 'NO_SUCH_SCHEDULE', id: '44dee229-8da1-4a61-856d-e3a4bbc12032', }, + rolePermissionDenied: { + message: 'You are not assigned to a required role.', + code: 'ROLE_PERMISSION_DENIED', + kind: 'permission', + id: '7f86f06f-7e15-4057-8561-f4b6d4ac755a', + }, }, } as const; @@ -234,6 +241,7 @@ export default class extends Endpoint { // eslint- private noteEntityService: NoteEntityService, private noteCreateService: NoteCreateService, + private roleService: RoleService, private queueService: QueueService, private idService: IdService, ) { @@ -375,6 +383,11 @@ export default class extends Endpoint { // eslint- }; if (ps.schedule) { + const canCreateScheduledNote = (await this.roleService.getUserPolicies(me.id)).canScheduleNote; + if (!canCreateScheduledNote) { + throw new ApiError(meta.errors.rolePermissionDenied); + } + if (!ps.schedule.expiresAt) { throw new ApiError(meta.errors.specifyScheduleDate); } diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts index 166f3fa3bb..0362805465 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts @@ -15,6 +15,7 @@ export const meta = { tags: ['notes'], requireCredential: true, + requireRolePolicy: 'canScheduleNote', res: { type: 'array', optional: false, nullable: false, diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 2458de5a5e..e43c540ee2 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -929,13 +929,13 @@ function openOtherSettingsMenu(ev: MouseEvent) { text: i18n.ts.reactionAcceptance, icon: reactionAcceptanceIcon, action: toggleReactionAcceptance, - }, { + }, ($i.policies?.canScheduleNote) ? { type: 'button', text: i18n.ts.schedulePost, icon: 'ti ti-calendar-time', indicate: (schedule != null), action: toggleSchedule, - }, null, { + } : undefined, ...(($i.policies?.canScheduleNote) ? [ null, { type: 'button', text: i18n.ts._schedulePost.list, icon: 'ti ti-calendar-event', @@ -944,7 +944,7 @@ function openOtherSettingsMenu(ev: MouseEvent) { emit('cancel'); listSchedulePost(); }, - }], ev.currentTarget ?? ev.target, { + }] : [])], ev.currentTarget ?? ev.target, { align: 'right', }); } diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index b3071fd924..dd135b14cd 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -61,6 +61,7 @@ export const ROLE_POLICIES = [ 'gtlAvailable', 'ltlAvailable', 'canPublicNote', + 'canScheduleNote', 'canInvite', 'inviteLimit', 'inviteLimitCycle', diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index 99a02671eb..c0e0d40cf6 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -174,6 +174,7 @@ export const navbarItemDef = reactive({ scheduledNotes: { title: i18n.ts._schedulePost.list, icon: 'ti ti-calendar-event', + show: computed(() => $i && $i.policies?.canScheduleNote), action: (ev) => { os.listSchedulePost(); }, diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 1db99e61f4..ace76753ce 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -160,6 +160,26 @@ SPDX-License-Identifier: AGPL-3.0-only + + + +
+ + + + + + + + + +
+
+