X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Fvideo%2Fvideo-comment.ts;h=f84c1880c6a564fc11c61959514450e25e121e3c;hb=d9eaee3939bf2e93e5d775d32bce77842201faba;hp=dbb2fe42910f9cbffbff7ca66b9769bb5faa5c28;hpb=d7e70384a360cda51fe23712331110a5c8b1124c;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts index dbb2fe429..f84c1880c 100644 --- a/server/models/video/video-comment.ts +++ b/server/models/video/video-comment.ts @@ -1,6 +1,6 @@ import * as Sequelize from 'sequelize' import { - AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table, + AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects' @@ -104,6 +104,13 @@ enum ScopeNames { }, { fields: [ 'videoId', 'originCommentId' ] + }, + { + fields: [ 'url' ], + unique: true + }, + { + fields: [ 'accountId' ] } ] }) @@ -149,7 +156,7 @@ export class VideoCommentModel extends Model { as: 'InReplyToVideoComment', onDelete: 'CASCADE' }) - InReplyToVideoComment: VideoCommentModel + InReplyToVideoComment: VideoCommentModel | null @ForeignKey(() => VideoModel) @Column @@ -175,10 +182,38 @@ export class VideoCommentModel extends Model { }) Account: AccountModel - @AfterDestroy - static async sendDeleteIfOwned (instance: VideoCommentModel) { + @BeforeDestroy + static async sendDeleteIfOwned (instance: VideoCommentModel, options) { + if (!instance.Account || !instance.Account.Actor) { + instance.Account = await instance.$get('Account', { + include: [ ActorModel ], + transaction: options.transaction + }) as AccountModel + } + + if (!instance.Video) { + instance.Video = await instance.$get('Video', { + include: [ + { + model: VideoChannelModel, + include: [ + { + model: AccountModel, + include: [ + { + model: ActorModel + } + ] + } + ] + } + ], + transaction: options.transaction + }) as VideoModel + } + if (instance.isOwned()) { - await sendDeleteVideoComment(instance, undefined) + await sendDeleteVideoComment(instance, options.transaction) } } @@ -208,7 +243,7 @@ export class VideoCommentModel extends Model { .findOne(query) } - static loadByUrl (url: string, t?: Sequelize.Transaction) { + static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) { const query: IFindOptions = { where: { url @@ -217,10 +252,10 @@ export class VideoCommentModel extends Model { if (t !== undefined) query.transaction = t - return VideoCommentModel.findOne(query) + return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT ]).findOne(query) } - static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) { + static loadByUrlAndPopulateReplyAndVideo (url: string, t?: Sequelize.Transaction) { const query: IFindOptions = { where: { url @@ -229,14 +264,14 @@ export class VideoCommentModel extends Model { if (t !== undefined) query.transaction = t - return VideoCommentModel.scope([ ScopeNames.WITH_ACCOUNT ]).findOne(query) + return VideoCommentModel.scope([ ScopeNames.WITH_IN_REPLY_TO, ScopeNames.WITH_VIDEO ]).findOne(query) } static listThreadsForApi (videoId: number, start: number, count: number, sort: string) { const query = { offset: start, limit: count, - order: [ getSort(sort) ], + order: getSort(sort), where: { videoId, inReplyToCommentId: null @@ -253,7 +288,7 @@ export class VideoCommentModel extends Model { static listThreadCommentsForApi (videoId: number, threadId: number) { const query = { - order: [ [ 'createdAt', 'ASC' ] ], + order: [ [ 'createdAt', 'ASC' ], [ 'updatedAt', 'ASC' ] ], where: { videoId, [ Sequelize.Op.or ]: [ @@ -271,15 +306,18 @@ export class VideoCommentModel extends Model { }) } - static listThreadParentComments (comment: VideoCommentModel, t: Sequelize.Transaction) { + static listThreadParentComments (comment: VideoCommentModel, t: Sequelize.Transaction, order: 'ASC' | 'DESC' = 'ASC') { const query = { - order: [ [ 'createdAt', 'ASC' ] ], + order: [ [ 'createdAt', order ] ], where: { - [ Sequelize.Op.or ]: [ - { id: comment.getThreadId() }, - { originCommentId: comment.getThreadId() } - ], id: { + [ Sequelize.Op.in ]: Sequelize.literal('(' + + 'WITH RECURSIVE children (id, "inReplyToCommentId") AS ( ' + + 'SELECT id, "inReplyToCommentId" FROM "videoComment" WHERE id = ' + comment.id + ' UNION ' + + 'SELECT p.id, p."inReplyToCommentId" from "videoComment" p ' + + 'INNER JOIN children c ON c."inReplyToCommentId" = p.id) ' + + 'SELECT id FROM children' + + ')'), [ Sequelize.Op.ne ]: comment.id } }, @@ -291,6 +329,68 @@ export class VideoCommentModel extends Model { .findAll(query) } + static listAndCountByVideoId (videoId: number, start: number, count: number, t?: Sequelize.Transaction, order: 'ASC' | 'DESC' = 'ASC') { + const query = { + order: [ [ 'createdAt', order ] ], + offset: start, + limit: count, + where: { + videoId + }, + transaction: t + } + + return VideoCommentModel.findAndCountAll(query) + } + + static listForFeed (start: number, count: number, videoId?: number) { + const query = { + order: [ [ 'createdAt', 'DESC' ] ], + offset: start, + limit: count, + where: {}, + include: [ + { + attributes: [ 'name', 'uuid' ], + model: VideoModel.unscoped(), + required: true + } + ] + } + + if (videoId) query.where['videoId'] = videoId + + return VideoCommentModel + .scope([ ScopeNames.WITH_ACCOUNT ]) + .findAll(query) + } + + static async getStats () { + const totalLocalVideoComments = await VideoCommentModel.count({ + include: [ + { + model: AccountModel, + required: true, + include: [ + { + model: ActorModel, + required: true, + where: { + serverId: null + } + } + ] + } + ] + }) + const totalVideoComments = await VideoCommentModel.count() + + return { + totalLocalVideoComments, + totalVideoComments + } + } + getThreadId (): number { return this.originCommentId || this.id }