diff --git a/locales/en-US.yml b/locales/en-US.yml index 775327b47..f78d146d0 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -935,6 +935,7 @@ moveFromLabel: "Account you're moving from:" moveFromDescription: "This will set an alias of your old account so that you can move from that account to this current one. Do this BEFORE moving from your older account. Please enter the tag of the account formatted like @person@instance.com" migrationConfirm: "Are you absolutely sure you want to migrate your acccount to {account}? Once you do this, you won't be able to reverse it, and you won't be able to use your account normally again.\nAlso, please ensure that you've set this current account as the account you're moving from." defaultReaction: "Default emoji reaction for outgoing and incoming posts" +license: "License" _sensitiveMediaDetection: description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server." diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index fe672c16e..59bf28d5a 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -935,6 +935,7 @@ moveFromLabel: "引っ越し元のアカウント:" moveFromDescription: "別のアカウントからこのアカウントにフォロワーを引き継いで引っ越したい場合、ここでエイリアスを作成しておく必要があります。必ず引っ越しを実行する前に作成してください!引っ越し元のアカウントをこのように入力してください:@person@instance.com" migrationConfirm: "本当にこのアカウントを {account} に引っ越しますか?一度引っ越しを行うと取り消せず、二度とこのアカウントを元の状態で使用することはできません。\nまた、引っ越し先のアカウントでエイリアスを作成したことを確認してください。" defaultReaction: "リモートとローカルの投稿に対するデフォルトの絵文字リアクション" +license: "ライセンス" _sensitiveMediaDetection: description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" diff --git a/packages/backend/migration/1678945242650-add-props-for-custom-emoji.js b/packages/backend/migration/1678945242650-add-props-for-custom-emoji.js new file mode 100644 index 000000000..656a92177 --- /dev/null +++ b/packages/backend/migration/1678945242650-add-props-for-custom-emoji.js @@ -0,0 +1,11 @@ +export class addPropsForCustomEmoji1678945242650 { + name = 'addPropsForCustomEmoji1678945242650' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "emoji" ADD "license" character varying(1024)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "license"`); + } +} diff --git a/packages/backend/src/models/entities/emoji.ts b/packages/backend/src/models/entities/emoji.ts index f251de897..231568696 100644 --- a/packages/backend/src/models/entities/emoji.ts +++ b/packages/backend/src/models/entities/emoji.ts @@ -55,4 +55,9 @@ export class Emoji { array: true, length: 128, default: '{}', }) public aliases: string[]; + + @Column('varchar', { + length: 1024, nullable: true, + }) + public license: string | null; } diff --git a/packages/backend/src/models/repositories/emoji.ts b/packages/backend/src/models/repositories/emoji.ts index e868fe94f..6eabfe955 100644 --- a/packages/backend/src/models/repositories/emoji.ts +++ b/packages/backend/src/models/repositories/emoji.ts @@ -15,6 +15,7 @@ export const EmojiRepository = db.getRepository(Emoji).extend({ host: emoji.host, // || emoji.originalUrl してるのは後方互換性のため url: emoji.publicUrl || emoji.originalUrl, + license: emoji.license, }; }, diff --git a/packages/backend/src/models/schema/emoji.ts b/packages/backend/src/models/schema/emoji.ts index 1e1dab868..8994381b3 100644 --- a/packages/backend/src/models/schema/emoji.ts +++ b/packages/backend/src/models/schema/emoji.ts @@ -40,5 +40,10 @@ export const packedEmojiSchema = { optional: false, nullable: false, }, + license: { + type: "string", + optional: false, + nullable: true, + }, }, } as const; diff --git a/packages/backend/src/queue/processors/db/import-custom-emojis.ts b/packages/backend/src/queue/processors/db/import-custom-emojis.ts index 45ab5ea9a..373a3a2da 100644 --- a/packages/backend/src/queue/processors/db/import-custom-emojis.ts +++ b/packages/backend/src/queue/processors/db/import-custom-emojis.ts @@ -75,6 +75,7 @@ export async function importCustomEmojis( originalUrl: driveFile.url, publicUrl: driveFile.webpublicUrl ?? driveFile.url, type: driveFile.webpublicType ?? driveFile.type, + license: emojiInfo.license, }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); } diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 7fb5fd320..bfafbaa62 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -29,6 +29,7 @@ import * as ep___admin_emoji_list from "./endpoints/admin/emoji/list.js"; import * as ep___admin_emoji_removeAliasesBulk from "./endpoints/admin/emoji/remove-aliases-bulk.js"; import * as ep___admin_emoji_setAliasesBulk from "./endpoints/admin/emoji/set-aliases-bulk.js"; import * as ep___admin_emoji_setCategoryBulk from "./endpoints/admin/emoji/set-category-bulk.js"; +import * as ep___admin_emoji_setLicenseBulk from "./endpoints/admin/emoji/set-license-bulk.js"; import * as ep___admin_emoji_update from "./endpoints/admin/emoji/update.js"; import * as ep___admin_federation_deleteAllFiles from "./endpoints/admin/federation/delete-all-files.js"; import * as ep___admin_federation_refreshRemoteInstanceMetadata from "./endpoints/admin/federation/refresh-remote-instance-metadata.js"; @@ -131,6 +132,7 @@ import * as ep___drive_folders_show from "./endpoints/drive/folders/show.js"; import * as ep___drive_folders_update from "./endpoints/drive/folders/update.js"; import * as ep___drive_stream from "./endpoints/drive/stream.js"; import * as ep___emailAddress_available from "./endpoints/email-address/available.js"; +import * as ep___emoji from "./endpoints/emoji.js"; import * as ep___endpoint from "./endpoints/endpoint.js"; import * as ep___endpoints from "./endpoints/endpoints.js"; import * as ep___exportCustomEmojis from "./endpoints/export-custom-emojis.js"; @@ -363,6 +365,7 @@ const eps = [ ["admin/emoji/remove-aliases-bulk", ep___admin_emoji_removeAliasesBulk], ["admin/emoji/set-aliases-bulk", ep___admin_emoji_setAliasesBulk], ["admin/emoji/set-category-bulk", ep___admin_emoji_setCategoryBulk], + ["admin/emoji/set-license-bulk", ep___admin_emoji_setLicenseBulk], ["admin/emoji/update", ep___admin_emoji_update], ["admin/federation/delete-all-files", ep___admin_federation_deleteAllFiles], [ @@ -471,6 +474,7 @@ const eps = [ ["drive/folders/update", ep___drive_folders_update], ["drive/stream", ep___drive_stream], ["email-address/available", ep___emailAddress_available], + ["emoji", ep___emoji], ["endpoint", ep___endpoint], ["endpoints", ep___endpoints], ["export-custom-emojis", ep___exportCustomEmojis], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 601d754a0..bfc025834 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -49,6 +49,7 @@ export default define(meta, paramDef, async (ps, me) => { originalUrl: file.url, publicUrl: file.webpublicUrl ?? file.url, type: file.webpublicType ?? file.type, + license: null, }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); await db.queryResultCache!.remove(["meta_emojis"]); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 4a7f2bc61..951158f7d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -73,6 +73,7 @@ export default define(meta, paramDef, async (ps, me) => { originalUrl: driveFile.url, publicUrl: driveFile.webpublicUrl ?? driveFile.url, type: driveFile.webpublicType ?? driveFile.type, + license: emoji.license, }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); await db.queryResultCache!.remove(["meta_emojis"]); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index 63f60bc99..fae986dd9 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -55,6 +55,11 @@ export const meta = { optional: false, nullable: false, }, + license: { + type: "string", + optional: false, + nullable: true, + }, }, }, }, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index bc4f1d29f..aa49f1480 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -55,6 +55,11 @@ export const meta = { optional: false, nullable: false, }, + license: { + type: "string", + optional: false, + nullable: true, + }, }, }, }, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts new file mode 100644 index 000000000..c98ca03fa --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts @@ -0,0 +1,45 @@ +import define from "../../../define.js"; +import { Emojis } from "@/models/index.js"; +import { In } from "typeorm"; +import { ApiError } from "../../../error.js"; +import { db } from "@/db/postgre.js"; + +export const meta = { + tags: ["admin"], + + requireCredential: true, + requireModerator: true, +} as const; + +export const paramDef = { + type: "object", + properties: { + ids: { + type: "array", + items: { + type: "string", + format: "misskey:id", + }, + }, + license: { + type: "string", + nullable: true, + description: "Use `null` to reset the license.", + }, + }, + required: ["ids"], +} as const; + +export default define(meta, paramDef, async (ps) => { + await Emojis.update( + { + id: In(ps.ids), + }, + { + updatedAt: new Date(), + license: ps.license, + }, + ); + + await db.queryResultCache!.remove(["meta_emojis"]); +}); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 3f7f6639f..9e2e85476 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -34,6 +34,10 @@ export const paramDef = { type: "string", }, }, + license: { + type: "string", + nullable: true, + }, }, required: ["id", "name", "aliases"], } as const; @@ -48,6 +52,7 @@ export default define(meta, paramDef, async (ps) => { name: ps.name, category: ps.category, aliases: ps.aliases, + license: ps.license, }); await db.queryResultCache!.remove(["meta_emojis"]); diff --git a/packages/backend/src/server/api/endpoints/emoji.ts b/packages/backend/src/server/api/endpoints/emoji.ts new file mode 100644 index 000000000..5d3c77e5e --- /dev/null +++ b/packages/backend/src/server/api/endpoints/emoji.ts @@ -0,0 +1,38 @@ +import { IsNull } from "typeorm"; +import { Emojis } from "@/models/index.js"; +import define from "../define.js"; + +export const meta = { + tags: ["meta"], + + requireCredential: false, + allowGet: true, + cacheSec: 3600, + + res: { + type: "object", + optional: false, nullable: false, + ref: "Emoji", + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + name: { + type: "string", + }, + }, + required: ["name"], +} as const; + +export default define(meta, paramDef, async (ps, me) => { + const emoji = await Emojis.findOneOrFail({ + where: { + name: ps.name, + host: IsNull(), + }, + }); + + return Emojis.pack(emoji); +}); diff --git a/packages/client/src/components/MkDialog.vue b/packages/client/src/components/MkDialog.vue index 9f93d9f99..5d5ca1e8d 100644 --- a/packages/client/src/components/MkDialog.vue +++ b/packages/client/src/components/MkDialog.vue @@ -14,9 +14,11 @@
- + + + @@ -20,15 +20,26 @@ const props = defineProps<{ function menu(ev) { os.popupMenu([{ - type: 'label', - text: ':' + props.emoji.name + ':', + type: "label", + text: ":" + props.emoji.name + ":", }, { text: i18n.ts.copy, - icon: 'ph-clipboard-text ph-bold ph-lg', + icon: "ph-clipboard-text ph-bold ph-lg", action: () => { copyToClipboard(`:${props.emoji.name}:`); os.success(); - } + }, + }, { + text: i18n.ts.license, + icon: "ph-info ph-bold ph-lg", + action: () => { + os.apiGet("emoji", { name: props.emoji.name }).then(res => { + os.alert({ + type: "info", + text: `${res.license || i18n.ts.notSet}`, + }); + }); + }, }], ev.currentTarget ?? ev.target); }