diff --git a/locales/index.d.ts b/locales/index.d.ts index eba90728a1d0..73c563d388de 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -9497,6 +9497,18 @@ export interface Locale extends ILocale { * 特殊 */ "specialBlocks": string; + /** + * 公開範囲 + */ + "visibility": string; + /** + * 公開 + */ + "public": string; + /** + * 非公開 + */ + "private": string; "blocks": { /** * テキスト diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 746854467de4..923afe654cad 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2495,6 +2495,9 @@ _pages: contentBlocks: "コンテンツ" inputBlocks: "入力" specialBlocks: "特殊" + visibility: "公開範囲" + public: "公開" + private: "非公開" blocks: text: "テキスト" textarea: "テキストエリア" diff --git a/packages/backend/migration/1733563840208-page-visibility.js b/packages/backend/migration/1733563840208-page-visibility.js new file mode 100644 index 000000000000..fc4ad59d2a1c --- /dev/null +++ b/packages/backend/migration/1733563840208-page-visibility.js @@ -0,0 +1,19 @@ +export class PageVisibility1733563840208 { + name = 'PageVisibility1733563840208' + + async up(queryRunner) { + await queryRunner.query(`ALTER TYPE "public"."page_visibility_enum" RENAME TO "page_visibility_enum_old"`); + await queryRunner.query(`CREATE TYPE "public"."page_visibility_enum" AS ENUM('public', 'private')`); + await queryRunner.query(`ALTER TABLE "page" ALTER COLUMN "visibility" TYPE "public"."page_visibility_enum" USING "visibility"::"text"::"public"."page_visibility_enum"`); + await queryRunner.query(`DROP TYPE "public"."page_visibility_enum_old"`); + await queryRunner.query(`ALTER TABLE "page" ALTER COLUMN "visibility" SET DEFAULT 'public'`); + } + + async down(queryRunner) { + await queryRunner.query(`CREATE TYPE "public"."page_visibility_enum_old" AS ENUM('followers', 'public', 'specified')`); + await queryRunner.query(`ALTER TABLE "page" ALTER COLUMN "visibility" TYPE "public"."page_visibility_enum_old" USING "visibility"::"text"::"public"."page_visibility_enum_old"`); + await queryRunner.query(`DROP TYPE "public"."page_visibility_enum"`); + await queryRunner.query(`ALTER TYPE "public"."page_visibility_enum_old" RENAME TO "page_visibility_enum"`); + await queryRunner.query(`ALTER TABLE "page" ALTER COLUMN "visibility" DROP DEFAULT`); + } +} diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts index 5239f3158510..a3e8f4ec4b51 100644 --- a/packages/backend/src/core/entities/PageEntityService.ts +++ b/packages/backend/src/core/entities/PageEntityService.ts @@ -105,6 +105,7 @@ export class PageEntityService { attachedFiles: this.driveFileEntityService.packMany((await Promise.all(attachedFiles)).filter(isNotNull), me), likedCount: page.likedCount, isLiked: meId ? await this.pageLikesRepository.exists({ where: { pageId: page.id, userId: meId } }) : undefined, + visibility: page.visibility, }); } diff --git a/packages/backend/src/models/Page.ts b/packages/backend/src/models/Page.ts index 36acf42c6ebf..709875e6834c 100644 --- a/packages/backend/src/models/Page.ts +++ b/packages/backend/src/models/Page.ts @@ -99,18 +99,13 @@ export class MiPage { /** * public ... 公開 - * followers ... フォロワーのみ - * specified ... visibleUserIds で指定したユーザーのみ + * private ... 非公開 */ - @Column('enum', { enum: ['public', 'followers', 'specified'] }) - public visibility: 'public' | 'followers' | 'specified'; - - @Index() - @Column({ - ...id(), - array: true, default: '{}', + @Column('enum', { + enum: ['public', 'private'], + default: 'public', }) - public visibleUserIds: MiUser['id'][]; + public visibility: 'public' | 'private'; @Column('integer', { default: 0, diff --git a/packages/backend/src/models/json-schema/page.ts b/packages/backend/src/models/json-schema/page.ts index 8e61526d183f..456facbf8b28 100644 --- a/packages/backend/src/models/json-schema/page.ts +++ b/packages/backend/src/models/json-schema/page.ts @@ -205,6 +205,11 @@ export const packedPageSchema = { type: 'boolean', optional: true, nullable: false, }, + visibility: { + type: 'string', + optional: false, nullable: false, + enum: ['public', 'private'], + }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index ee6a1b92c7bc..305b62075835 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -65,6 +65,7 @@ export const paramDef = { font: { type: 'string', enum: ['serif', 'sans-serif'], default: 'sans-serif' }, alignCenter: { type: 'boolean', default: false }, hideTitleWhenPinned: { type: 'boolean', default: false }, + visibility: { type: 'string', enum: ['public', 'private'] }, }, required: ['title', 'name', 'content', 'variables', 'script'], } as const; @@ -114,7 +115,7 @@ export default class extends Endpoint { // eslint- script: ps.script, eyeCatchingImageId: eyeCatchingImage ? eyeCatchingImage.id : null, userId: me.id, - visibility: 'public', + visibility: ps.visibility, alignCenter: ps.alignCenter, hideTitleWhenPinned: ps.hideTitleWhenPinned, font: ps.font, diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index e08b832a3f9a..1bf612753289 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -78,6 +78,10 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.noSuchPage); } + if (page.visibility === 'private' && (me == null || (page.userId !== me.id))) { + throw new ApiError(meta.errors.noSuchPage); + } + return await this.pageEntityService.pack(page, me); }); } diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index a42f6adddd1e..4d053537b5fc 100644 --- a/packages/backend/src/server/api/endpoints/pages/update.ts +++ b/packages/backend/src/server/api/endpoints/pages/update.ts @@ -70,6 +70,7 @@ export const paramDef = { font: { type: 'string', enum: ['serif', 'sans-serif'] }, alignCenter: { type: 'boolean' }, hideTitleWhenPinned: { type: 'boolean' }, + visibility: { type: 'string', enum: ['public', 'private'] }, }, required: ['pageId', 'title', 'name', 'content', 'variables', 'script'], } as const; @@ -129,6 +130,8 @@ export default class extends Endpoint { // eslint- hideTitleWhenPinned: ps.hideTitleWhenPinned === undefined ? page.hideTitleWhenPinned : ps.hideTitleWhenPinned, // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing font: ps.font === undefined ? page.font : ps.font, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + visibility: ps.visibility === undefined ? page.visibility : ps.visibility, eyeCatchingImageId: ps.eyeCatchingImageId === null ? null : ps.eyeCatchingImageId === undefined diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 9e99517f03ed..8c0a0354a56f 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -196,6 +196,7 @@ export const page = async (user: UserToken, page: Partial eyeCatchingImageId: null, font: 'sans-serif' as FIXME, hideTitleWhenPinned: false, + visibility: 'public', name: '1678594845072', script: '', summary: null, diff --git a/packages/frontend/src/components/MkPagePreview.vue b/packages/frontend/src/components/MkPagePreview.vue index c02b0b6f4602..fcb190465a29 100644 --- a/packages/frontend/src/components/MkPagePreview.vue +++ b/packages/frontend/src/components/MkPagePreview.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-

{{ page.title }}

+

{{ page.title || page.name }}

{{ page.summary.length > 85 ? page.summary.slice(0, 85) + '…' : page.summary }}