X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Fvideo%2Fvideo-comment.ts;h=de27b3d875fb3745682f5a056405162b5298f85f;hb=97816649b793bdd0f3df64631ae0ef7cf3d7461c;hp=dfeb1c4e79d2b8c8281d76f714ba023a0fb7e51f;hpb=8adf0a767f0816465ac3a8f4a6c63f53dd05fe3d;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts index dfeb1c4e7..de27b3d87 100644 --- a/server/models/video/video-comment.ts +++ b/server/models/video/video-comment.ts @@ -1,9 +1,22 @@ import * as Bluebird from 'bluebird' import { uniq } from 'lodash' -import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize' -import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' +import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction, WhereOptions } from 'sequelize' +import { + AllowNull, + BelongsTo, + Column, + CreatedAt, + DataType, + ForeignKey, + HasMany, + Is, + Model, + Scopes, + Table, + UpdatedAt +} from 'sequelize-typescript' import { getServerActor } from '@server/models/application/application' -import { MAccount, MAccountId, MUserAccountId } from '@server/typings/models' +import { MAccount, MAccountId, MUserAccountId } from '@server/types/models' import { VideoPrivacy } from '@shared/models' import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects' import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' @@ -21,15 +34,17 @@ import { MCommentOwnerReplyVideoLight, MCommentOwnerVideo, MCommentOwnerVideoFeed, - MCommentOwnerVideoReply -} from '../../typings/models/video' + MCommentOwnerVideoReply, + MVideoImmutable +} from '../../types/models/video' +import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' import { AccountModel } from '../account/account' import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' -import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils' +import { buildBlockedAccountSQL, buildBlockedAccountSQLOptimized, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils' import { VideoModel } from './video' import { VideoChannelModel } from './video-channel' -enum ScopeNames { +export enum ScopeNames { WITH_ACCOUNT = 'WITH_ACCOUNT', WITH_ACCOUNT_FOR_API = 'WITH_ACCOUNT_FOR_API', WITH_IN_REPLY_TO = 'WITH_IN_REPLY_TO', @@ -38,14 +53,14 @@ enum ScopeNames { } @Scopes(() => ({ - [ScopeNames.ATTRIBUTES_FOR_API]: (serverAccountId: number, userAccountId?: number) => { + [ScopeNames.ATTRIBUTES_FOR_API]: (blockerAccountIds: number[]) => { return { attributes: { include: [ [ Sequelize.literal( '(' + - 'WITH "blocklist" AS (' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')' + + 'WITH "blocklist" AS (' + buildBlockedAccountSQL(blockerAccountIds) + ')' + 'SELECT COUNT("replies"."id") - (' + 'SELECT COUNT("replies"."id") ' + 'FROM "videoComment" AS "replies" ' + @@ -143,6 +158,11 @@ enum ScopeNames { }, { fields: [ 'accountId' ] + }, + { + fields: [ + { name: 'createdAt', order: 'DESC' } + ] } ] }) @@ -218,6 +238,15 @@ export class VideoCommentModel extends Model { }) Account: AccountModel + @HasMany(() => VideoCommentAbuseModel, { + foreignKey: { + name: 'videoCommentId', + allowNull: true + }, + onDelete: 'set null' + }) + CommentAbuses: VideoCommentAbuseModel[] + static loadById (id: number, t?: Transaction): Bluebird { const query: FindOptions = { where: { @@ -276,16 +305,15 @@ export class VideoCommentModel extends Model { static async listThreadsForApi (parameters: { videoId: number + isVideoOwned: boolean start: number count: number sort: string user?: MUserAccountId }) { - const { videoId, start, count, sort, user } = parameters + const { videoId, isVideoOwned, start, count, sort, user } = parameters - const serverActor = await getServerActor() - const serverAccountId = serverActor.Account.id - const userAccountId = user ? user.Account.id : undefined + const blockerAccountIds = await VideoCommentModel.buildBlockerAccountIds({ videoId, user, isVideoOwned }) const query = { offset: start, @@ -304,7 +332,7 @@ export class VideoCommentModel extends Model { { accountId: { [Op.notIn]: Sequelize.literal( - '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')' + '(' + buildBlockedAccountSQL(blockerAccountIds) + ')' ) } }, @@ -320,7 +348,7 @@ export class VideoCommentModel extends Model { const scopes: (string | ScopeOptions)[] = [ ScopeNames.WITH_ACCOUNT_FOR_API, { - method: [ ScopeNames.ATTRIBUTES_FOR_API, serverAccountId, userAccountId ] + method: [ ScopeNames.ATTRIBUTES_FOR_API, blockerAccountIds ] } ] @@ -334,14 +362,13 @@ export class VideoCommentModel extends Model { static async listThreadCommentsForApi (parameters: { videoId: number + isVideoOwned: boolean threadId: number user?: MUserAccountId }) { - const { videoId, threadId, user } = parameters + const { videoId, threadId, user, isVideoOwned } = parameters - const serverActor = await getServerActor() - const serverAccountId = serverActor.Account.id - const userAccountId = user ? user.Account.id : undefined + const blockerAccountIds = await VideoCommentModel.buildBlockerAccountIds({ videoId, user, isVideoOwned }) const query = { order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ] as Order, @@ -353,7 +380,7 @@ export class VideoCommentModel extends Model { ], accountId: { [Op.notIn]: Sequelize.literal( - '(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')' + '(' + buildBlockedAccountSQL(blockerAccountIds) + ')' ) } } @@ -362,7 +389,7 @@ export class VideoCommentModel extends Model { const scopes: any[] = [ ScopeNames.WITH_ACCOUNT_FOR_API, { - method: [ ScopeNames.ATTRIBUTES_FOR_API, serverAccountId, userAccountId ] + method: [ ScopeNames.ATTRIBUTES_FOR_API, blockerAccountIds ] } ] @@ -399,13 +426,23 @@ export class VideoCommentModel extends Model { .findAll(query) } - static listAndCountByVideoId (videoId: number, start: number, count: number, t?: Transaction, order: 'ASC' | 'DESC' = 'ASC') { + static async listAndCountByVideoForAP (video: MVideoImmutable, start: number, count: number, t?: Transaction) { + const blockerAccountIds = await VideoCommentModel.buildBlockerAccountIds({ + videoId: video.id, + isVideoOwned: video.isOwned() + }) + const query = { - order: [ [ 'createdAt', order ] ] as Order, + order: [ [ 'createdAt', 'ASC' ] ] as Order, offset: start, limit: count, where: { - videoId + videoId: video.id, + accountId: { + [Op.notIn]: Sequelize.literal( + '(' + buildBlockedAccountSQL(blockerAccountIds) + ')' + ) + } }, transaction: t } @@ -413,8 +450,32 @@ export class VideoCommentModel extends Model { return VideoCommentModel.findAndCountAll(query) } - static async listForFeed (start: number, count: number, videoId?: number): Promise { + static async listForFeed (parameters: { + start: number + count: number + videoId?: number + accountId?: number + videoChannelId?: number + }): Promise { const serverActor = await getServerActor() + const { start, count, videoId, accountId, videoChannelId } = parameters + + const whereAnd: WhereOptions[] = buildBlockedAccountSQLOptimized( + '"VideoCommentModel"."accountId"', + [ serverActor.Account.id, '"Video->VideoChannel"."accountId"' ] + ) + + if (accountId) { + whereAnd.push({ + [Op.eq]: accountId + }) + } + + const accountWhere = { + [Op.and]: whereAnd + } + + const videoChannelWhere = videoChannelId ? { id: videoChannelId } : undefined const query = { order: [ [ 'createdAt', 'DESC' ] ] as Order, @@ -422,11 +483,7 @@ export class VideoCommentModel extends Model { limit: count, where: { deletedAt: null, - accountId: { - [Op.notIn]: Sequelize.literal( - '(' + buildBlockedAccountSQL(serverActor.Account.id) + ')' - ) - } + accountId: accountWhere }, include: [ { @@ -435,7 +492,15 @@ export class VideoCommentModel extends Model { required: true, where: { privacy: VideoPrivacy.PUBLIC - } + }, + include: [ + { + attributes: [ 'accountId' ], + model: VideoChannelModel.unscoped(), + required: true, + where: videoChannelWhere + } + ] } ] } @@ -591,7 +656,7 @@ export class VideoCommentModel extends Model { id: this.id, url: this.url, text: this.text, - threadId: this.originCommentId || this.id, + threadId: this.getThreadId(), inReplyToCommentId: this.inReplyToCommentId || null, videoId: this.videoId, createdAt: this.createdAt, @@ -650,4 +715,24 @@ export class VideoCommentModel extends Model { tag } } + + private static async buildBlockerAccountIds (options: { + videoId: number + isVideoOwned: boolean + user?: MUserAccountId + }) { + const { videoId, user, isVideoOwned } = options + + const serverActor = await getServerActor() + const blockerAccountIds = [ serverActor.Account.id ] + + if (user) blockerAccountIds.push(user.Account.id) + + if (isVideoOwned) { + const videoOwnerAccount = await AccountModel.loadAccountIdFromVideo(videoId) + blockerAccountIds.push(videoOwnerAccount.id) + } + + return blockerAccountIds + } }