From 47aaf044813662931fbaddd965272267fd94ed6a Mon Sep 17 00:00:00 2001 From: MeiMei <30769358+mei23@users.noreply.github.com> Date: Sun, 23 May 2021 18:57:12 +0900 Subject: Fix search-by-tag (#7531) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix search-by-tag * Revert "Fix search-by-tag" This reverts commit c971d1d5d82f2d8b58fdec76e42f4404339ab83a. * Fix typo * Remove unused var * インジェクションは[]を返すように --- src/server/api/endpoints/notes/search-by-tag.ts | 35 ++++++++++++++----------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'src/server/api') diff --git a/src/server/api/endpoints/notes/search-by-tag.ts b/src/server/api/endpoints/notes/search-by-tag.ts index 61f62dd5a6..463c5fff5a 100644 --- a/src/server/api/endpoints/notes/search-by-tag.ts +++ b/src/server/api/endpoints/notes/search-by-tag.ts @@ -104,22 +104,25 @@ export default define(meta, async (ps, me) => { generateVisibilityQuery(query, me); if (me) generateMutedUserQuery(query, me); - if (ps.tag) { - if (!safeForSql(ps.tag)) return; - query.andWhere(`'{"${normalizeForSearch(ps.tag)}"}' <@ note.tags`); - } else { - let i = 0; - query.andWhere(new Brackets(qb => { - for (const tags of ps.query!) { - qb.orWhere(new Brackets(qb => { - for (const tag of tags) { - if (!safeForSql(tag)) return; - qb.andWhere(`'{"${normalizeForSearch(ps.tag)}"}' <@ note.tags`); - i++; - } - })); - } - })); + try { + if (ps.tag) { + if (!safeForSql(ps.tag)) throw 'Injection'; + query.andWhere(`'{"${normalizeForSearch(ps.tag)}"}' <@ note.tags`); + } else { + query.andWhere(new Brackets(qb => { + for (const tags of ps.query!) { + qb.orWhere(new Brackets(qb => { + for (const tag of tags) { + if (!safeForSql(tag)) throw 'Injection'; + qb.andWhere(`'{"${normalizeForSearch(tag)}"}' <@ note.tags`); + } + })); + } + })); + } + } catch (e) { + if (e === 'Injection') return []; + throw e; } if (ps.reply != null) { -- cgit v1.2.3-freya From db3724cf33c402d66700f89b319b423887466757 Mon Sep 17 00:00:00 2001 From: syuilo Date: Fri, 28 May 2021 09:34:42 +0900 Subject: improve types --- src/server/api/define.ts | 7 +++++-- src/server/api/endpoints/gallery/posts/create.ts | 3 ++- src/server/api/endpoints/gallery/posts/update.ts | 3 ++- src/services/chart/core.ts | 21 ++++++++++----------- 4 files changed, 19 insertions(+), 15 deletions(-) (limited to 'src/server/api') diff --git a/src/server/api/define.ts b/src/server/api/define.ts index 432d5017e8..cba69cfdc4 100644 --- a/src/server/api/define.ts +++ b/src/server/api/define.ts @@ -5,6 +5,8 @@ import { ApiError } from './error'; import { SchemaType } from '@/misc/schema'; import { AccessToken } from '../../models/entities/access-token'; +type NonOptional = T extends undefined ? never : T; + type SimpleUserInfo = { id: ILocalUser['id']; host: ILocalUser['host']; @@ -17,11 +19,12 @@ type SimpleUserInfo = { isSilenced: ILocalUser['isSilenced']; }; -// TODO: defaultが設定されている場合はその型も考慮する type Params = { [P in keyof T['params']]: NonNullable[P]['transform'] extends Function ? ReturnType[P]['transform']> - : ReturnType[P]['validator']['get']>[0]; + : NonNullable[P]['default'] extends null | number | string + ? NonOptional[P]['validator']['get']>[0]> + : ReturnType[P]['validator']['get']>[0]; }; export type Response = Record | void; diff --git a/src/server/api/endpoints/gallery/posts/create.ts b/src/server/api/endpoints/gallery/posts/create.ts index d1ae68b126..ed24a45f88 100644 --- a/src/server/api/endpoints/gallery/posts/create.ts +++ b/src/server/api/endpoints/gallery/posts/create.ts @@ -6,6 +6,7 @@ import { DriveFiles, GalleryPosts } from '../../../../../models'; import { genId } from '../../../../../misc/gen-id'; import { GalleryPost } from '../../../../../models/entities/gallery-post'; import { ApiError } from '../../../error'; +import { DriveFile } from '@/models/entities/drive-file'; export const meta = { tags: ['gallery'], @@ -55,7 +56,7 @@ export default define(meta, async (ps, user) => { id: fileId, userId: user.id }) - ))).filter(file => file != null); + ))).filter((file): file is DriveFile => file != null); if (files.length === 0) { throw new Error(); diff --git a/src/server/api/endpoints/gallery/posts/update.ts b/src/server/api/endpoints/gallery/posts/update.ts index c8bb8d48c9..d9176ea407 100644 --- a/src/server/api/endpoints/gallery/posts/update.ts +++ b/src/server/api/endpoints/gallery/posts/update.ts @@ -5,6 +5,7 @@ import { ID } from '../../../../../misc/cafy-id'; import { DriveFiles, GalleryPosts } from '../../../../../models'; import { GalleryPost } from '../../../../../models/entities/gallery-post'; import { ApiError } from '../../../error'; +import { DriveFile } from '@/models/entities/drive-file'; export const meta = { tags: ['gallery'], @@ -58,7 +59,7 @@ export default define(meta, async (ps, user) => { id: fileId, userId: user.id }) - ))).filter(file => file != null); + ))).filter((file): file is DriveFile => file != null); if (files.length === 0) { throw new Error(); diff --git a/src/services/chart/core.ts b/src/services/chart/core.ts index d956d33bd7..4a554daa78 100644 --- a/src/services/chart/core.ts +++ b/src/services/chart/core.ts @@ -93,7 +93,7 @@ export default abstract class Chart> { } @autobind - private static convertFlattenColumnsToObject(x: Record) { + private static convertFlattenColumnsToObject(x: Record): Record { const obj = {} as any; for (const k of Object.keys(x).filter(k => k.startsWith(Chart.columnPrefix))) { // now k is ___x_y_z @@ -285,8 +285,7 @@ export default abstract class Chart> { const latest = await this.getLatestLog(group); if (latest != null) { - const obj = Chart.convertFlattenColumnsToObject( - latest as Record); + const obj = Chart.convertFlattenColumnsToObject(latest) as T; // 空ログデータを作成 data = this.getNewLog(obj); @@ -474,13 +473,13 @@ export default abstract class Chart> { const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current)); if (log) { - const data = Chart.convertFlattenColumnsToObject(log as Record); - chart.unshift(Chart.countUniqueFields(data)); + const data = Chart.convertFlattenColumnsToObject(log); + chart.unshift(Chart.countUniqueFields(data) as T); } else { // 隙間埋め const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); - const data = latest ? Chart.convertFlattenColumnsToObject(latest as Record) : null; - chart.unshift(Chart.countUniqueFields(this.getNewLog(data))); + const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null; + chart.unshift(Chart.countUniqueFields(this.getNewLog(data)) as T); } } } else if (span === 'day') { @@ -497,14 +496,14 @@ export default abstract class Chart> { if (log) { if (logsForEachDays[currentDayIndex]) { - logsForEachDays[currentDayIndex].unshift(Chart.convertFlattenColumnsToObject(log)); + logsForEachDays[currentDayIndex].unshift(Chart.convertFlattenColumnsToObject(log) as T); } else { - logsForEachDays[currentDayIndex] = [Chart.convertFlattenColumnsToObject(log)]; + logsForEachDays[currentDayIndex] = [Chart.convertFlattenColumnsToObject(log) as T]; } } else { // 隙間埋め const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); - const data = latest ? Chart.convertFlattenColumnsToObject(latest as Record) : null; + const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null; const newLog = this.getNewLog(data); if (logsForEachDays[currentDayIndex]) { logsForEachDays[currentDayIndex].unshift(newLog); @@ -516,7 +515,7 @@ export default abstract class Chart> { for (const logs of logsForEachDays) { const log = this.aggregate(logs); - chart.unshift(Chart.countUniqueFields(log)); + chart.unshift(Chart.countUniqueFields(log) as T); } } -- cgit v1.2.3-freya From ffb9646ce9c3d2326a3e922e58702674eb65646c Mon Sep 17 00:00:00 2001 From: nullobsi Date: Thu, 27 May 2021 17:38:09 -0700 Subject: Add image description support (#7518) * recieve image descriptions under the name property * fix other components * use comment for alt and title * allow editing of file comment * allow editing of file comment in note dialog * federate note comments * use file instead of this * backend should accept comment on update * update now actually accepts comment * allow multiline descriptions * image should also have description attached * Update locales/ja-JP.yml Co-authored-by: rinsuki <428rinsuki+git@gmail.com> * Use custom component with side-by-side image * improve usability on mobile devices * revert changes * Update post-form-attaches.vue * Update drive.file.vue * Update media-caption.vue Co-authored-by: rinsuki <428rinsuki+git@gmail.com> Co-authored-by: syuilo --- locales/ja-JP.yml | 3 + src/client/components/drive.file.vue | 24 +++ src/client/components/image-viewer.vue | 2 +- src/client/components/media-caption.vue | 238 +++++++++++++++++++++++++ src/client/components/media-image.vue | 4 +- src/client/components/post-form-attaches.vue | 25 +++ src/remote/activitypub/models/image.ts | 2 +- src/remote/activitypub/renderer/document.ts | 3 +- src/remote/activitypub/renderer/image.ts | 3 +- src/server/api/endpoints/drive/files/update.ts | 11 ++ src/services/drive/upload-from-url.ts | 6 + 11 files changed, 315 insertions(+), 6 deletions(-) create mode 100644 src/client/components/media-caption.vue (limited to 'src/server/api') diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e869f5b015..23f3bf7296 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -279,6 +279,7 @@ emptyDrive: "ドライブは空です" emptyFolder: "フォルダーは空です" unableToDelete: "削除できません" inputNewFileName: "新しいファイル名を入力してください" +inputNewDescription: "新しいキャプションを入力してください" inputNewFolderName: "新しいフォルダ名を入力してください" circularReferenceFolder: "移動先のフォルダーは、移動するフォルダーのサブフォルダーです。" hasChildFilesOrFolders: "このフォルダは空でないため、削除できません。" @@ -546,6 +547,8 @@ disablePlayer: "プレイヤーを閉じる" expandTweet: "ツイートを展開する" themeEditor: "テーマエディター" description: "説明" +describeFile: "キャプションを付ける" +enterFileDescription: "キャプションを入力" author: "作者" leaveConfirm: "未保存の変更があります。破棄しますか?" manage: "管理" diff --git a/src/client/components/drive.file.vue b/src/client/components/drive.file.vue index 37b1afc1b3..3d20de23e9 100644 --- a/src/client/components/drive.file.vue +++ b/src/client/components/drive.file.vue @@ -87,6 +87,10 @@ export default defineComponent({ text: this.file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive, icon: this.file.isSensitive ? 'fas fa-eye' : 'fas fa-eye-slash', action: this.toggleSensitive + }, { + text: this.$ts.describeFile, + icon: 'fas fa-i-cursor', + action: this.describe }, null, { text: this.$ts.copyUrl, icon: 'fas fa-link', @@ -150,6 +154,26 @@ export default defineComponent({ }); }, + describe() { + os.popup(import('@client/components/media-caption.vue'), { + title: this.$ts.describeFile, + input: { + placeholder: this.$ts.inputNewDescription, + default: this.file.comment !== null ? this.file.comment : '', + }, + image: this.file + }, { + done: result => { + if (!result || result.canceled) return; + let comment = result.result; + os.api('drive/files/update', { + fileId: this.file.id, + comment: comment.length == 0 ? null : comment + }); + } + }, 'closed'); + }, + toggleSensitive() { os.api('drive/files/update', { fileId: this.file.id, diff --git a/src/client/components/image-viewer.vue b/src/client/components/image-viewer.vue index ec22bd98ec..7701ae926f 100644 --- a/src/client/components/image-viewer.vue +++ b/src/client/components/image-viewer.vue @@ -2,7 +2,7 @@
{{ image.name }}
- +