enhance: ウォーターマーク機能をロールで制御可能に
This commit is contained in:
parent
d2c4f79886
commit
5ecaf5095e
|
@ -5,6 +5,7 @@
|
||||||
- Feat: クリップ内でノートを検索できるように
|
- Feat: クリップ内でノートを検索できるように
|
||||||
- Feat: Playを検索できるように
|
- Feat: Playを検索できるように
|
||||||
- Feat: モデレーションにおいて、特定のドライブファイルを添付しているチャットメッセージを一覧できるように
|
- Feat: モデレーションにおいて、特定のドライブファイルを添付しているチャットメッセージを一覧できるように
|
||||||
|
- Enhance: ウォーターマーク機能をロールで制御可能に
|
||||||
|
|
||||||
### Client
|
### Client
|
||||||
- Feat: モデログを検索できるように
|
- Feat: モデログを検索できるように
|
||||||
|
|
|
@ -7795,6 +7795,10 @@ export interface Locale extends ILocale {
|
||||||
* サーバーサイドのノートの下書きの作成可能数
|
* サーバーサイドのノートの下書きの作成可能数
|
||||||
*/
|
*/
|
||||||
"noteDraftLimit": string;
|
"noteDraftLimit": string;
|
||||||
|
/**
|
||||||
|
* ウォーターマーク機能の使用可否
|
||||||
|
*/
|
||||||
|
"watermarkAvailable": string;
|
||||||
};
|
};
|
||||||
"_condition": {
|
"_condition": {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2019,6 +2019,7 @@ _role:
|
||||||
uploadableFileTypes_caption: "MIMEタイプを指定します。改行で区切って複数指定できるほか、アスタリスク(*)でワイルドカード指定できます。(例: image/*)"
|
uploadableFileTypes_caption: "MIMEタイプを指定します。改行で区切って複数指定できるほか、アスタリスク(*)でワイルドカード指定できます。(例: image/*)"
|
||||||
uploadableFileTypes_caption2: "ファイルによっては種別を判定できないことがあります。そのようなファイルを許可する場合は {x} を指定に追加してください。"
|
uploadableFileTypes_caption2: "ファイルによっては種別を判定できないことがあります。そのようなファイルを許可する場合は {x} を指定に追加してください。"
|
||||||
noteDraftLimit: "サーバーサイドのノートの下書きの作成可能数"
|
noteDraftLimit: "サーバーサイドのノートの下書きの作成可能数"
|
||||||
|
watermarkAvailable: "ウォーターマーク機能の使用可否"
|
||||||
_condition:
|
_condition:
|
||||||
roleAssignedTo: "マニュアルロールにアサイン済み"
|
roleAssignedTo: "マニュアルロールにアサイン済み"
|
||||||
isLocal: "ローカルユーザー"
|
isLocal: "ローカルユーザー"
|
||||||
|
|
|
@ -67,6 +67,7 @@ export type RolePolicies = {
|
||||||
chatAvailability: 'available' | 'readonly' | 'unavailable';
|
chatAvailability: 'available' | 'readonly' | 'unavailable';
|
||||||
uploadableFileTypes: string[];
|
uploadableFileTypes: string[];
|
||||||
noteDraftLimit: number;
|
noteDraftLimit: number;
|
||||||
|
watermarkAvailable: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_POLICIES: RolePolicies = {
|
export const DEFAULT_POLICIES: RolePolicies = {
|
||||||
|
@ -111,6 +112,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
|
||||||
'audio/*',
|
'audio/*',
|
||||||
],
|
],
|
||||||
noteDraftLimit: 10,
|
noteDraftLimit: 10,
|
||||||
|
watermarkAvailable: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -433,6 +435,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
||||||
return [...set];
|
return [...set];
|
||||||
}),
|
}),
|
||||||
noteDraftLimit: calc('noteDraftLimit', vs => Math.max(...vs)),
|
noteDraftLimit: calc('noteDraftLimit', vs => Math.max(...vs)),
|
||||||
|
watermarkAvailable: calc('watermarkAvailable', vs => vs.some(v => v === true)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -313,6 +313,10 @@ export const packedRolePoliciesSchema = {
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
|
watermarkAvailable: {
|
||||||
|
type: 'boolean',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,7 @@ export const ROLE_POLICIES = [
|
||||||
'chatAvailability',
|
'chatAvailability',
|
||||||
'uploadableFileTypes',
|
'uploadableFileTypes',
|
||||||
'noteDraftLimit',
|
'noteDraftLimit',
|
||||||
|
'watermarkAvailable',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime'];
|
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime'];
|
||||||
|
|
|
@ -104,6 +104,8 @@ export function useUploader(options: {
|
||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
features?: UploaderFeatures;
|
features?: UploaderFeatures;
|
||||||
} = {}) {
|
} = {}) {
|
||||||
|
const $i = ensureSignin();
|
||||||
|
|
||||||
const events = new EventEmitter<{
|
const events = new EventEmitter<{
|
||||||
'itemUploaded': (ctx: { item: UploaderItem; }) => void;
|
'itemUploaded': (ctx: { item: UploaderItem; }) => void;
|
||||||
}>();
|
}>();
|
||||||
|
@ -132,7 +134,7 @@ export function useUploader(options: {
|
||||||
uploaded: null,
|
uploaded: null,
|
||||||
uploadFailed: false,
|
uploadFailed: false,
|
||||||
compressionLevel: prefer.s.defaultImageCompressionLevel,
|
compressionLevel: prefer.s.defaultImageCompressionLevel,
|
||||||
watermarkPresetId: uploaderFeatures.value.watermark ? prefer.s.defaultWatermarkPresetId : null,
|
watermarkPresetId: uploaderFeatures.value.watermark && $i.policies.watermarkAvailable ? prefer.s.defaultWatermarkPresetId : null,
|
||||||
file: markRaw(file),
|
file: markRaw(file),
|
||||||
});
|
});
|
||||||
const reactiveItem = items.value.at(-1)!;
|
const reactiveItem = items.value.at(-1)!;
|
||||||
|
@ -264,6 +266,7 @@ export function useUploader(options: {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
uploaderFeatures.value.watermark &&
|
uploaderFeatures.value.watermark &&
|
||||||
|
$i.policies.watermarkAvailable &&
|
||||||
WATERMARK_SUPPORTED_TYPES.includes(item.file.type) &&
|
WATERMARK_SUPPORTED_TYPES.includes(item.file.type) &&
|
||||||
!item.preprocessing &&
|
!item.preprocessing &&
|
||||||
!item.uploading &&
|
!item.uploading &&
|
||||||
|
@ -500,7 +503,7 @@ export function useUploader(options: {
|
||||||
|
|
||||||
let preprocessedFile: Blob | File = item.file;
|
let preprocessedFile: Blob | File = item.file;
|
||||||
|
|
||||||
const needsWatermark = item.watermarkPresetId != null && WATERMARK_SUPPORTED_TYPES.includes(preprocessedFile.type);
|
const needsWatermark = item.watermarkPresetId != null && WATERMARK_SUPPORTED_TYPES.includes(preprocessedFile.type) && $i.policies.watermarkAvailable;
|
||||||
const preset = prefer.s.watermarkPresets.find(p => p.id === item.watermarkPresetId);
|
const preset = prefer.s.watermarkPresets.find(p => p.id === item.watermarkPresetId);
|
||||||
if (needsWatermark && preset != null) {
|
if (needsWatermark && preset != null) {
|
||||||
const canvas = window.document.createElement('canvas');
|
const canvas = window.document.createElement('canvas');
|
||||||
|
|
|
@ -780,6 +780,26 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.watermarkAvailable, 'watermarkAvailable'])">
|
||||||
|
<template #label>{{ i18n.ts._role._options.watermarkAvailable }}</template>
|
||||||
|
<template #suffix>
|
||||||
|
<span v-if="role.policies.watermarkAvailable.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span>
|
||||||
|
<span v-else>{{ role.policies.watermarkAvailable.value ? i18n.ts.yes : i18n.ts.no }}</span>
|
||||||
|
<span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.watermarkAvailable)"></i></span>
|
||||||
|
</template>
|
||||||
|
<div class="_gaps">
|
||||||
|
<MkSwitch v-model="role.policies.watermarkAvailable.useDefault" :readonly="readonly">
|
||||||
|
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
<MkSwitch v-model="role.policies.watermarkAvailable.value" :disabled="role.policies.watermarkAvailable.useDefault" :readonly="readonly">
|
||||||
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
<MkRange v-model="role.policies.watermarkAvailable.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
|
</MkRange>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
</div>
|
</div>
|
||||||
</FormSlot>
|
</FormSlot>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -291,6 +291,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
<MkInput v-model="policies.noteDraftLimit" type="number" :min="0">
|
<MkInput v-model="policies.noteDraftLimit" type="number" :min="0">
|
||||||
</MkInput>
|
</MkInput>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
|
<MkFolder v-if="matchQuery([i18n.ts._role._options.watermarkAvailable, 'watermarkAvailable'])">
|
||||||
|
<template #label>{{ i18n.ts._role._options.watermarkAvailable }}</template>
|
||||||
|
<template #suffix>{{ policies.watermarkAvailable ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||||
|
<MkSwitch v-model="policies.watermarkAvailable">
|
||||||
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
|
</MkSwitch>
|
||||||
|
</MkFolder>
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
<MkButton primary rounded @click="create"><i class="ti ti-plus"></i> {{ i18n.ts._role.new }}</MkButton>
|
<MkButton primary rounded @click="create"><i class="ti ti-plus"></i> {{ i18n.ts._role.new }}</MkButton>
|
||||||
|
|
|
@ -87,7 +87,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<SearchMarker :keywords="['watermark', 'credit']">
|
<SearchMarker :keywords="['watermark', 'credit']">
|
||||||
<MkFolder>
|
<MkFolder v-if="$i.policies.watermarkAvailable">
|
||||||
<template #icon><i class="ti ti-copyright"></i></template>
|
<template #icon><i class="ti ti-copyright"></i></template>
|
||||||
<template #label><SearchLabel>{{ i18n.ts.watermark }}</SearchLabel></template>
|
<template #label><SearchLabel>{{ i18n.ts.watermark }}</SearchLabel></template>
|
||||||
<template #caption>{{ i18n.ts._watermarkEditor.tip }}</template>
|
<template #caption>{{ i18n.ts._watermarkEditor.tip }}</template>
|
||||||
|
|
|
@ -5225,6 +5225,7 @@ export type components = {
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
chatAvailability: 'available' | 'readonly' | 'unavailable';
|
chatAvailability: 'available' | 'readonly' | 'unavailable';
|
||||||
noteDraftLimit: number;
|
noteDraftLimit: number;
|
||||||
|
watermarkAvailable: boolean;
|
||||||
};
|
};
|
||||||
ReversiGameLite: {
|
ReversiGameLite: {
|
||||||
/** Format: id */
|
/** Format: id */
|
||||||
|
|
Loading…
Reference in New Issue