Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: センシティブな画像をアイコン・バナーに指定できないように #13676

Open
wants to merge 35 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
fafe980
fix: センシティブな画像をアイコン・バナーに指定できないように
kakkokari-gtyih Apr 8, 2024
16e92e6
センシティブなファイルを選択できないように
kakkokari-gtyih Apr 8, 2024
b13446f
tweak design
kakkokari-gtyih Apr 8, 2024
aa2ab69
fix
kakkokari-gtyih Apr 8, 2024
3c3decc
Update Changelog
kakkokari-gtyih Apr 8, 2024
7c45dc1
enhance(frontend): アイコン画像・バナー画像を外せるように
kakkokari-gtyih Apr 8, 2024
ec79544
use option
kakkokari-gtyih Apr 8, 2024
466835a
use options
kakkokari-gtyih Apr 8, 2024
991cbf5
Merge branch 'develop' into fix-7311
syuilo Apr 8, 2024
db27623
いろいろ変えた
kakkokari-gtyih Apr 9, 2024
5b0db2b
Merge branch 'fix-7311' of https://github.com/kakkokari-gtyih/misskey…
kakkokari-gtyih Apr 9, 2024
599f7ad
Merge branch 'develop' into fix-7311
kakkokari-gtyih Apr 9, 2024
08e8b31
Merge branch 'develop' into fix-7311
kakkokari-gtyih Apr 11, 2024
168eaec
Merge branch 'develop' into fix-7311
kakkokari-gtyih Apr 13, 2024
0db9ec2
Merge branch 'develop' into fix-7311
kakkokari-gtyih Apr 14, 2024
54a41d3
refactor
kakkokari-gtyih Apr 14, 2024
5f29c23
Merge branch 'develop' into fix-7311
kakkokari-gtyih Apr 14, 2024
7dfd93f
Merge branch 'develop' into fix-7311
kakkokari-gtyih Apr 16, 2024
c6c18db
Merge branch 'develop' into fix-7311
kakkokari-gtyih Apr 20, 2024
4ca1598
Merge branch 'develop' into fix-7311
kakkokari-gtyih Apr 27, 2024
66bbe0e
Merge branch 'develop' into fix-7311
kakkokari-gtyih Apr 27, 2024
21e5fe6
Merge branch 'develop' into fix-7311
kakkokari-gtyih Jun 1, 2024
b6704a8
Update packages/frontend/src/os.ts
kakkokari-gtyih Jun 3, 2024
9a27e42
Update os.ts
kakkokari-gtyih Jun 3, 2024
a5424f4
Merge branch 'develop' into fix-7311
kakkokari-gtyih Jun 9, 2024
1650d49
Merge branch 'develop' into fix-7311
kakkokari-gtyih Jun 22, 2024
ac3d65a
Merge branch 'develop' into fix-7311
kakkokari-gtyih Jun 22, 2024
f3d2956
Merge branch 'develop' into fix-7311
kakkokari-gtyih Jun 23, 2024
30441b2
Merge branch 'develop' into fix-7311
kakkokari-gtyih Aug 23, 2024
0f7ee33
Merge branch 'develop' into fix-7311
kakkokari-gtyih Nov 5, 2024
e2c9043
Update os.ts
kakkokari-gtyih Nov 5, 2024
784931c
Update profile.vue
kakkokari-gtyih Nov 5, 2024
2016c8a
Update CHANGELOG.md
kakkokari-gtyih Nov 5, 2024
a631446
Update CHANGELOG.md
kakkokari-gtyih Nov 5, 2024
aee1c34
Update CHANGELOG.md
kakkokari-gtyih Nov 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
## Unreleased

### General
-
- Fix: センシティブな画像をアイコン・バナーに指定できないように

### Client
-
- Enhance: アイコン画像・バナー画像を外せるように

### Server
-
Expand Down
9 changes: 9 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4984,6 +4984,15 @@ export interface Locale extends ILocale {
* お問い合わせ
*/
"inquiry": string;
/**
* センシティブなメディアは選択できません
*/
"cannotSelectSensitiveMedia": string;
/**
* 自分でセンシティブ設定を行っていないのにこのエラーが出ている場合、自動判定によりセンシティブなメディアとされている可能性があります。
* サーバーの規則に照らして不要な場合は、ファイルのセンシティブ設定を解除してもう一度お試しください。
*/
"cannotSelectSensitiveMediaDescription": string;
"_delivery": {
/**
* 配信状態
Expand Down
2 changes: 2 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,8 @@ keepOriginalFilenameDescription: "この設定をオフにすると、アップ
noDescription: "説明文はありません"
alwaysConfirmFollow: "フォローの際常に確認する"
inquiry: "お問い合わせ"
cannotSelectSensitiveMedia: "センシティブなメディアは選択できません"
cannotSelectSensitiveMediaDescription: "自分でセンシティブ設定を行っていないのにこのエラーが出ている場合、自動判定によりセンシティブなメディアとされている可能性があります。\nサーバーの規則に照らして不要な場合は、ファイルのセンシティブ設定を解除してもう一度お試しください。"

_delivery:
status: "配信状態"
Expand Down
14 changes: 14 additions & 0 deletions packages/backend/src/server/api/endpoints/i/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@ export const meta = {
id: '75aedb19-2afd-4e6d-87fc-67941256fa60',
},

avatarIsSensitive: {
message: 'The file specified as an avatar is marked as sensitive.',
code: 'AVATAR_IS_SENSITIVE',
id: '71bb5e53-4742-4609-b465-36081e131208',
},

bannerIsSensitive: {
message: 'The file specified as a banner is marked as sensitive.',
code: 'BANNER_IS_SENSITIVE',
id: 'e148b34c-9f33-4300-93e0-7817008fb366',
},

noSuchPage: {
message: 'No such page.',
code: 'NO_SUCH_PAGE',
Expand Down Expand Up @@ -326,6 +338,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-

if (avatar == null || avatar.userId !== user.id) throw new ApiError(meta.errors.noSuchAvatar);
if (!avatar.type.startsWith('image/')) throw new ApiError(meta.errors.avatarNotAnImage);
if (avatar.isSensitive) throw new ApiError(meta.errors.avatarIsSensitive);

updates.avatarId = avatar.id;
updates.avatarUrl = this.driveFileEntityService.getPublicUrl(avatar, 'avatar');
Expand All @@ -341,6 +354,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-

if (banner == null || banner.userId !== user.id) throw new ApiError(meta.errors.noSuchBanner);
if (!banner.type.startsWith('image/')) throw new ApiError(meta.errors.bannerNotAnImage);
if (banner.isSensitive) throw new ApiError(meta.errors.bannerIsSensitive);

updates.bannerId = banner.id;
updates.bannerUrl = this.driveFileEntityService.getPublicUrl(banner);
Expand Down
25 changes: 24 additions & 1 deletion packages/frontend/src/components/MkDrive.file.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only

<template>
<div
:class="[$style.root, { [$style.isSelected]: isSelected }]"
:class="[$style.root, { [$style.isSelected]: isSelected, [$style.isDisabled]: isDisabled }]"
draggable="true"
:title="title"
@click="onClick"
Expand Down Expand Up @@ -55,9 +55,11 @@ const props = withDefaults(defineProps<{
file: Misskey.entities.DriveFile;
folder: Misskey.entities.DriveFolder | null;
isSelected?: boolean;
isDisabled?: boolean;
selectMode?: boolean;
}>(), {
isSelected: false,
isDisabled: false,
selectMode: false,
});

Expand All @@ -72,6 +74,8 @@ const isDragging = ref(false);
const title = computed(() => `${props.file.name}\n${props.file.type} ${bytes(props.file.size)}`);

function onClick(ev: MouseEvent) {
if (props.isDisabled) return;

if (props.selectMode) {
emit('chosen', props.file);
} else {
Expand All @@ -88,6 +92,8 @@ function onContextmenu(ev: MouseEvent) {
}

function onDragstart(ev: DragEvent) {
if (props.isDisabled) return;

if (ev.dataTransfer) {
ev.dataTransfer.effectAllowed = 'move';
ev.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FILE_, JSON.stringify(props.file));
Expand Down Expand Up @@ -173,6 +179,23 @@ function onDragend() {
color: #fff;
}
}

&.isDisabled {
cursor: not-allowed;

.thumbnail {
opacity: 0.5;
}

.name {
opacity: 0.5;
}

&:hover,
&:active {
background: none;
}
}
}

.label {
Expand Down
3 changes: 3 additions & 0 deletions packages/frontend/src/components/MkDrive.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:folder="folder"
:selectMode="select === 'file'"
:isSelected="selectedFiles.some(x => x.id === file.id)"
:isDisabled="excludeSensitive && file.isSensitive"
@chosen="chooseFile"
@dragstart="isDragSource = true"
@dragend="isDragSource = false"
Expand Down Expand Up @@ -114,9 +115,11 @@ const props = withDefaults(defineProps<{
initialFolder?: Misskey.entities.DriveFolder;
type?: string;
multiple?: boolean;
excludeSensitive?: boolean;
select?: 'file' | 'folder' | null;
}>(), {
multiple: false,
excludeSensitive: false,
select: null,
});

Expand Down
3 changes: 2 additions & 1 deletion packages/frontend/src/components/MkDriveSelectDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ multiple ? ((type === 'file') ? i18n.ts.selectFiles : i18n.ts.selectFolders) : ((type === 'file') ? i18n.ts.selectFile : i18n.ts.selectFolder) }}
<span v-if="selected.length > 0" style="margin-left: 8px; opacity: 0.5;">({{ number(selected.length) }})</span>
</template>
<XDrive :multiple="multiple" :select="type" @changeSelection="onChangeSelection" @selected="ok()"/>
<XDrive :multiple="multiple" :excludeSensitive="excludeSensitive" :select="type" @changeSelection="onChangeSelection" @selected="ok()"/>
</MkModalWindow>
</template>

Expand All @@ -34,6 +34,7 @@ import { i18n } from '@/i18n.js';
withDefaults(defineProps<{
type?: 'file' | 'folder';
multiple: boolean;
excludeSensitive: boolean;
}>(), {
type: 'file',
});
Expand Down
17 changes: 13 additions & 4 deletions packages/frontend/src/os.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey
endpoint: E,
data: P = {} as any,
token?: string | null | undefined,
customErrors?: Record<string, { title?: string; text: string; }>,
) => {
const promise = misskeyApi(endpoint, data, token);
promiseDialog(promise, null, async (err) => {
Expand Down Expand Up @@ -67,6 +68,10 @@ export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey
} else if (err.code === 'ROLE_PERMISSION_DENIED') {
title = i18n.ts.permissionDeniedError;
text = i18n.ts.permissionDeniedErrorDescription;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
} else if (customErrors && customErrors[err.code] != null) {
title = customErrors[err.code].title;
text = customErrors[err.code].text;
} else if (err.code.startsWith('TOO_MANY')) {
title = i18n.ts.youCannotCreateAnymore;
text = `${i18n.ts.error}: ${err.id}`;
Expand All @@ -82,11 +87,13 @@ export const apiWithDialog = (<E extends keyof Misskey.Endpoints = keyof Misskey
});

return promise;
}) as typeof misskeyApi;
});

type Unpromise<T> = T extends Promise<infer U> ? U : never;

export function promiseDialog<T extends Promise<any>>(
promise: T,
onSuccess?: ((res: any) => void) | null,
onSuccess?: ((res: Unpromise<T>) => void) | null,
kakkokari-gtyih marked this conversation as resolved.
Show resolved Hide resolved
onFailure?: ((err: Misskey.api.APIError) => void) | null,
text?: string,
): T {
Expand Down Expand Up @@ -541,11 +548,12 @@ export async function selectUser(opts: { includeSelf?: boolean; localOnly?: bool
});
}

export async function selectDriveFile(multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
export async function selectDriveFile(multiple: boolean, excludeSensitive: boolean): Promise<Misskey.entities.DriveFile[]> {
return new Promise(resolve => {
popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), {
type: 'file',
multiple,
excludeSensitive,
}, {
done: files => {
if (files) {
Expand All @@ -556,11 +564,12 @@ export async function selectDriveFile(multiple: boolean): Promise<Misskey.entiti
});
}

export async function selectDriveFolder(multiple: boolean): Promise<Misskey.entities.DriveFolder[]> {
export async function selectDriveFolder(multiple: boolean, excludeSensitive: boolean): Promise<Misskey.entities.DriveFolder[]> {
return new Promise(resolve => {
popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), {
type: 'folder',
multiple,
excludeSensitive,
}, {
done: folders => {
if (folders) {
Expand Down
73 changes: 62 additions & 11 deletions packages/frontend/src/pages/settings/profile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ import { defaultStore } from '@/store.js';
import { globalEvents } from '@/events.js';
import MkInfo from '@/components/MkInfo.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import { misskeyApi } from '@/scripts/misskey-api.js';

const $i = signinRequired();

Expand Down Expand Up @@ -204,7 +205,26 @@ function save() {
}

function changeAvatar(ev) {
selectFile(ev.currentTarget ?? ev.target, i18n.ts.avatar).then(async (file) => {
selectFile(ev.currentTarget ?? ev.target, i18n.ts.avatar, {
excludeSensitive: true,
additionalMenu: $i.avatarId ? [
{ type: 'divider' },
{
type: 'button',
text: i18n.ts.detach,
icon: 'ti ti-circle-x',
action: () => {
os.apiWithDialog('i/update', {
avatarId: null,
}).then(() => {
$i.avatarId = null;
$i.avatarUrl = null;
globalEvents.emit('requestClearPageCache');
});
},
},
] : undefined,
}).then(async (file) => {
let originalOrCropped = file;

const { canceled } = await os.confirm({
Expand All @@ -220,18 +240,43 @@ function changeAvatar(ev) {
});
}

const i = await os.apiWithDialog('i/update', {
await os.apiWithDialog('i/update', {
avatarId: originalOrCropped.id,
}, undefined, {
'AVATAR_IS_SENSITIVE': {
title: i18n.ts.cannotSelectSensitiveMedia,
text: i18n.ts.cannotSelectSensitiveMediaDescription,
},
}).then(() => {
$i.avatarId = originalOrCropped.id;
$i.avatarUrl = originalOrCropped.url;
globalEvents.emit('requestClearPageCache');
claimAchievement('profileFilled');
});
$i.avatarId = i.avatarId;
$i.avatarUrl = i.avatarUrl;
globalEvents.emit('requestClearPageCache');
claimAchievement('profileFilled');
});
}

function changeBanner(ev) {
selectFile(ev.currentTarget ?? ev.target, i18n.ts.banner).then(async (file) => {
selectFile(ev.currentTarget ?? ev.target, i18n.ts.banner, {
excludeSensitive: true,
additionalMenu: $i.bannerId ? [
{ type: 'divider' },
{
type: 'button',
text: i18n.ts.detach,
icon: 'ti ti-circle-x',
action: () => {
os.apiWithDialog('i/update', {
bannerId: null,
}).then(() => {
$i.bannerId = null;
$i.bannerUrl = null;
globalEvents.emit('requestClearPageCache');
});
},
},
] : undefined,
}).then(async (file) => {
let originalOrCropped = file;

const { canceled } = await os.confirm({
Expand All @@ -247,12 +292,18 @@ function changeBanner(ev) {
});
}

const i = await os.apiWithDialog('i/update', {
await os.apiWithDialog('i/update', {
bannerId: originalOrCropped.id,
}, undefined, {
'BANNER_IS_SENSITIVE': {
title: i18n.ts.cannotSelectSensitiveMedia,
text: i18n.ts.cannotSelectSensitiveMediaDescription,
},
}).then(() => {
$i.bannerId = originalOrCropped.id;
$i.bannerUrl = originalOrCropped.url;
globalEvents.emit('requestClearPageCache');
});
$i.bannerId = i.bannerId;
$i.bannerUrl = i.bannerUrl;
globalEvents.emit('requestClearPageCache');
});
}

Expand Down
Loading
Loading