カスタム絵文字の管理を権限を持つユーザーが行えるように

Resolve #9542
This commit is contained in:
syuilo 2023-01-13 14:58:27 +09:00
parent 0a6e237d09
commit b250456814
21 changed files with 55 additions and 16 deletions

View file

@ -73,6 +73,7 @@ You should also include the user name that made the change.
- AVIF support @tamaina - AVIF support @tamaina
- Add Cloudflare Turnstile CAPTCHA support @CyberRex0 - Add Cloudflare Turnstile CAPTCHA support @CyberRex0
- 非モデレーターでも、権限を持つロールをアサインされたユーザーはインスタンスの招待コードを発行できるように - 非モデレーターでも、権限を持つロールをアサインされたユーザーはインスタンスの招待コードを発行できるように
- 非モデレーターでも、権限を持つロールをアサインされたユーザーはカスタム絵文字の追加、編集、削除を行えるように
- Server: signToActivityPubGet is set to true by default @syuilo - Server: signToActivityPubGet is set to true by default @syuilo
- Server: improve syslog performance @syuilo - Server: improve syslog performance @syuilo
- Server: Use undici instead of node-fetch and got @tamaina - Server: Use undici instead of node-fetch and got @tamaina

View file

@ -931,6 +931,7 @@ undefined: "未定義"
assign: "アサイン" assign: "アサイン"
unassign: "アサインを解除" unassign: "アサインを解除"
color: "色" color: "色"
manageCustomEmojis: "カスタム絵文字の管理"
_role: _role:
new: "ロールの作成" new: "ロールの作成"
@ -958,6 +959,7 @@ _role:
ltlAvailable: "ローカルタイムラインの閲覧" ltlAvailable: "ローカルタイムラインの閲覧"
canPublicNote: "パブリック投稿の許可" canPublicNote: "パブリック投稿の許可"
canInvite: "インスタンス招待コードの発行" canInvite: "インスタンス招待コードの発行"
canManageCustomEmojis: "カスタム絵文字の管理"
driveCapacity: "ドライブ容量" driveCapacity: "ドライブ容量"
antennaMax: "アンテナの作成可能数" antennaMax: "アンテナの作成可能数"
_condition: _condition:

View file

@ -17,6 +17,7 @@ export type RoleOptions = {
ltlAvailable: boolean; ltlAvailable: boolean;
canPublicNote: boolean; canPublicNote: boolean;
canInvite: boolean; canInvite: boolean;
canManageCustomEmojis: boolean;
driveCapacityMb: number; driveCapacityMb: number;
antennaLimit: number; antennaLimit: number;
}; };
@ -26,6 +27,7 @@ export const DEFAULT_ROLE: RoleOptions = {
ltlAvailable: true, ltlAvailable: true,
canPublicNote: true, canPublicNote: true,
canInvite: false, canInvite: false,
canManageCustomEmojis: false,
driveCapacityMb: 100, driveCapacityMb: 100,
antennaLimit: 5, antennaLimit: 5,
}; };
@ -182,6 +184,7 @@ export class RoleService implements OnApplicationShutdown {
ltlAvailable: getOptionValues('ltlAvailable').some(x => x === true), ltlAvailable: getOptionValues('ltlAvailable').some(x => x === true),
canPublicNote: getOptionValues('canPublicNote').some(x => x === true), canPublicNote: getOptionValues('canPublicNote').some(x => x === true),
canInvite: getOptionValues('canInvite').some(x => x === true), canInvite: getOptionValues('canInvite').some(x => x === true),
canManageCustomEmojis: getOptionValues('canManageCustomEmojis').some(x => x === true),
driveCapacityMb: Math.max(...getOptionValues('driveCapacityMb')), driveCapacityMb: Math.max(...getOptionValues('driveCapacityMb')),
antennaLimit: Math.max(...getOptionValues('antennaLimit')), antennaLimit: Math.max(...getOptionValues('antennaLimit')),
}; };

View file

@ -8,7 +8,7 @@ export const meta = {
tags: ['admin'], tags: ['admin'],
requireCredential: true, requireCredential: true,
requireModerator: true, requireRoleOption: 'canManageCustomEmojis',
} as const; } as const;
export const paramDef = { export const paramDef = {

View file

@ -14,7 +14,7 @@ export const meta = {
tags: ['admin'], tags: ['admin'],
requireCredential: true, requireCredential: true,
requireModerator: true, requireRoleOption: 'canManageCustomEmojis',
errors: { errors: {
noSuchFile: { noSuchFile: {

View file

@ -14,7 +14,7 @@ export const meta = {
tags: ['admin'], tags: ['admin'],
requireCredential: true, requireCredential: true,
requireModerator: true, requireRoleOption: 'canManageCustomEmojis',
errors: { errors: {
noSuchEmoji: { noSuchEmoji: {

View file

@ -9,7 +9,7 @@ export const meta = {
tags: ['admin'], tags: ['admin'],
requireCredential: true, requireCredential: true,
requireModerator: true, requireRoleOption: 'canManageCustomEmojis',
} as const; } as const;
export const paramDef = { export const paramDef = {

View file

@ -10,7 +10,7 @@ export const meta = {
tags: ['admin'], tags: ['admin'],
requireCredential: true, requireCredential: true,
requireModerator: true, requireRoleOption: 'canManageCustomEmojis',
errors: { errors: {
noSuchEmoji: { noSuchEmoji: {

View file

@ -5,7 +5,7 @@ import { QueueService } from '@/core/QueueService.js';
export const meta = { export const meta = {
secure: true, secure: true,
requireCredential: true, requireCredential: true,
requireModerator: true, requireRoleOption: 'canManageCustomEmojis',
} as const; } as const;
export const paramDef = { export const paramDef = {

View file

@ -11,7 +11,7 @@ export const meta = {
tags: ['admin'], tags: ['admin'],
requireCredential: true, requireCredential: true,
requireModerator: true, requireRoleOption: 'canManageCustomEmojis',
res: { res: {
type: 'array', type: 'array',

View file

@ -11,7 +11,7 @@ export const meta = {
tags: ['admin'], tags: ['admin'],
requireCredential: true, requireCredential: true,
requireModerator: true, requireRoleOption: 'canManageCustomEmojis',
res: { res: {
type: 'array', type: 'array',

View file

@ -8,7 +8,7 @@ export const meta = {
tags: ['admin'], tags: ['admin'],
requireCredential: true, requireCredential: true,
requireModerator: true, requireRoleOption: 'canManageCustomEmojis',
} as const; } as const;
export const paramDef = { export const paramDef = {

View file

@ -8,7 +8,7 @@ export const meta = {
tags: ['admin'], tags: ['admin'],
requireCredential: true, requireCredential: true,
requireModerator: true, requireRoleOption: 'canManageCustomEmojis',
} as const; } as const;
export const paramDef = { export const paramDef = {

View file

@ -8,7 +8,7 @@ export const meta = {
tags: ['admin'], tags: ['admin'],
requireCredential: true, requireCredential: true,
requireModerator: true, requireRoleOption: 'canManageCustomEmojis',
} as const; } as const;
export const paramDef = { export const paramDef = {

View file

@ -9,7 +9,7 @@ export const meta = {
tags: ['admin'], tags: ['admin'],
requireCredential: true, requireCredential: true,
requireModerator: true, requireRoleOption: 'canManageCustomEmojis',
errors: { errors: {
noSuchEmoji: { noSuchEmoji: {

View file

@ -90,6 +90,19 @@
</div> </div>
</MkFolder> </MkFolder>
<MkFolder>
<template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template>
<template #suffix>{{ options_canManageCustomEmojis_useDefault ? i18n.ts._role.useBaseValue : (options_canManageCustomEmojis_value ? i18n.ts.yes : i18n.ts.no) }}</template>
<div class="_gaps">
<MkSwitch v-model="options_canManageCustomEmojis_useDefault" :readonly="readonly">
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
</MkSwitch>
<MkSwitch v-model="options_canManageCustomEmojis_value" :disabled="options_canManageCustomEmojis_useDefault" :readonly="readonly">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
</div>
</MkFolder>
<MkFolder> <MkFolder>
<template #label>{{ i18n.ts._role._options.driveCapacity }}</template> <template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
<template #suffix>{{ options_driveCapacityMb_useDefault ? i18n.ts._role.useBaseValue : (options_driveCapacityMb_value + 'MB') }}</template> <template #suffix>{{ options_driveCapacityMb_useDefault ? i18n.ts._role.useBaseValue : (options_driveCapacityMb_value + 'MB') }}</template>
@ -175,6 +188,8 @@ let options_canPublicNote_useDefault = $ref(role?.options?.canPublicNote?.useDef
let options_canPublicNote_value = $ref(role?.options?.canPublicNote?.value ?? false); let options_canPublicNote_value = $ref(role?.options?.canPublicNote?.value ?? false);
let options_canInvite_useDefault = $ref(role?.options?.canInvite?.useDefault ?? true); let options_canInvite_useDefault = $ref(role?.options?.canInvite?.useDefault ?? true);
let options_canInvite_value = $ref(role?.options?.canInvite?.value ?? false); let options_canInvite_value = $ref(role?.options?.canInvite?.value ?? false);
let options_canManageCustomEmojis_useDefault = $ref(role?.options?.canManageCustomEmojis?.useDefault ?? true);
let options_canManageCustomEmojis_value = $ref(role?.options?.canManageCustomEmojis?.value ?? false);
let options_driveCapacityMb_useDefault = $ref(role?.options?.driveCapacityMb?.useDefault ?? true); let options_driveCapacityMb_useDefault = $ref(role?.options?.driveCapacityMb?.useDefault ?? true);
let options_driveCapacityMb_value = $ref(role?.options?.driveCapacityMb?.value ?? 0); let options_driveCapacityMb_value = $ref(role?.options?.driveCapacityMb?.value ?? 0);
let options_antennaLimit_useDefault = $ref(role?.options?.antennaLimit?.useDefault ?? true); let options_antennaLimit_useDefault = $ref(role?.options?.antennaLimit?.useDefault ?? true);
@ -192,6 +207,7 @@ function getOptions() {
ltlAvailable: { useDefault: options_ltlAvailable_useDefault, value: options_ltlAvailable_value }, ltlAvailable: { useDefault: options_ltlAvailable_useDefault, value: options_ltlAvailable_value },
canPublicNote: { useDefault: options_canPublicNote_useDefault, value: options_canPublicNote_value }, canPublicNote: { useDefault: options_canPublicNote_useDefault, value: options_canPublicNote_value },
canInvite: { useDefault: options_canInvite_useDefault, value: options_canInvite_value }, canInvite: { useDefault: options_canInvite_useDefault, value: options_canInvite_value },
canManageCustomEmojis: { useDefault: options_canManageCustomEmojis_useDefault, value: options_canManageCustomEmojis_value },
driveCapacityMb: { useDefault: options_driveCapacityMb_useDefault, value: options_driveCapacityMb_value }, driveCapacityMb: { useDefault: options_driveCapacityMb_useDefault, value: options_driveCapacityMb_value },
antennaLimit: { useDefault: options_antennaLimit_useDefault, value: options_antennaLimit_value }, antennaLimit: { useDefault: options_antennaLimit_useDefault, value: options_antennaLimit_value },
}; };

View file

@ -40,6 +40,14 @@
</MkSwitch> </MkSwitch>
</MkFolder> </MkFolder>
<MkFolder>
<template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template>
<template #suffix>{{ options_canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template>
<MkSwitch v-model="options_canManageCustomEmojis">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
</MkFolder>
<MkFolder> <MkFolder>
<template #label>{{ i18n.ts._role._options.driveCapacity }}</template> <template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
<template #suffix>{{ options_driveCapacityMb }}MB</template> <template #suffix>{{ options_driveCapacityMb }}MB</template>
@ -90,6 +98,7 @@ let options_gtlAvailable = $ref(instance.baseRole.gtlAvailable);
let options_ltlAvailable = $ref(instance.baseRole.ltlAvailable); let options_ltlAvailable = $ref(instance.baseRole.ltlAvailable);
let options_canPublicNote = $ref(instance.baseRole.canPublicNote); let options_canPublicNote = $ref(instance.baseRole.canPublicNote);
let options_canInvite = $ref(instance.baseRole.canInvite); let options_canInvite = $ref(instance.baseRole.canInvite);
let options_canManageCustomEmojis = $ref(instance.baseRole.canManageCustomEmojis);
let options_driveCapacityMb = $ref(instance.baseRole.driveCapacityMb); let options_driveCapacityMb = $ref(instance.baseRole.driveCapacityMb);
let options_antennaLimit = $ref(instance.baseRole.antennaLimit); let options_antennaLimit = $ref(instance.baseRole.antennaLimit);
@ -100,6 +109,7 @@ async function updateBaseRole() {
ltlAvailable: options_ltlAvailable, ltlAvailable: options_ltlAvailable,
canPublicNote: options_canPublicNote, canPublicNote: options_canPublicNote,
canInvite: options_canInvite, canInvite: options_canInvite,
canManageCustomEmojis: options_canManageCustomEmojis,
driveCapacityMb: options_driveCapacityMb, driveCapacityMb: options_driveCapacityMb,
antennaLimit: options_antennaLimit, antennaLimit: options_antennaLimit,
}, },

View file

@ -1,7 +1,7 @@
<template> <template>
<div> <div>
<MkStickyContainer> <MkStickyContainer>
<template #header><XHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :content-max="900"> <MkSpacer :content-max="900">
<div class="ogwlenmc"> <div class="ogwlenmc">
<div v-if="tab === 'local'" class="local"> <div v-if="tab === 'local'" class="local">
@ -69,7 +69,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent, defineComponent, ref, shallowRef } from 'vue'; import { computed, defineAsyncComponent, defineComponent, ref, shallowRef } from 'vue';
import XHeader from './_header_.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue'; import MkInput from '@/components/MkInput.vue';
import MkPagination from '@/components/MkPagination.vue'; import MkPagination from '@/components/MkPagination.vue';

View file

@ -305,6 +305,9 @@ export const routes = [{
}, { }, {
path: '/channels', path: '/channels',
component: page(() => import('./pages/channels.vue')), component: page(() => import('./pages/channels.vue')),
}, {
path: '/custom-emojis-manager',
component: page(() => import('./pages/custom-emojis-manager.vue')),
}, { }, {
path: '/registry/keys/system/:path(*)?', path: '/registry/keys/system/:path(*)?',
component: page(() => import('./pages/registry.keys.vue')), component: page(() => import('./pages/registry.keys.vue')),
@ -331,7 +334,7 @@ export const routes = [{
}, { }, {
path: '/emojis', path: '/emojis',
name: 'emojis', name: 'emojis',
component: page(() => import('./pages/admin/emojis.vue')), component: page(() => import('./pages/custom-emojis-manager.vue')),
}, { }, {
path: '/queue', path: '/queue',
name: 'queue', name: 'queue',

View file

@ -47,7 +47,7 @@ export function openInstanceMenu(ev: MouseEvent) {
to: '/clicker', to: '/clicker',
text: '🍪👈', text: '🍪👈',
icon: 'ti ti-cookie', icon: 'ti ti-cookie',
}, ($i && ($i.isRoot || $i.role.canInvite) && instance.disableRegistration) ? { }, ($i && ($i.isAdmin || $i.role.canInvite) && instance.disableRegistration) ? {
text: i18n.ts.invite, text: i18n.ts.invite,
icon: 'ti ti-user-plus', icon: 'ti ti-user-plus',
action: () => { action: () => {
@ -63,6 +63,11 @@ export function openInstanceMenu(ev: MouseEvent) {
}); });
}); });
}, },
} : undefined, ($i && ($i.isAdmin || $i.role.canManageCustomEmojis)) ? {
type: 'link',
to: '/custom-emojis-manager',
text: i18n.ts.manageCustomEmojis,
icon: 'ti ti-icons',
} : undefined], } : undefined],
}, null, { }, null, {
text: i18n.ts.help, text: i18n.ts.help,