aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video/video-comment.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/video/video-comment.ts')
-rw-r--r--server/models/video/video-comment.ts132
1 files changed, 128 insertions, 4 deletions
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index de27b3d87..70aed75d6 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -1,6 +1,6 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import { uniq } from 'lodash' 2import { uniq } from 'lodash'
3import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction, WhereOptions } from 'sequelize' 3import { FindAndCountOptions, FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction, WhereOptions } from 'sequelize'
4import { 4import {
5 AllowNull, 5 AllowNull,
6 BelongsTo, 6 BelongsTo,
@@ -20,13 +20,14 @@ import { MAccount, MAccountId, MUserAccountId } from '@server/types/models'
20import { VideoPrivacy } from '@shared/models' 20import { VideoPrivacy } from '@shared/models'
21import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects' 21import { ActivityTagObject, ActivityTombstoneObject } from '../../../shared/models/activitypub/objects/common-objects'
22import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' 22import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object'
23import { VideoComment } from '../../../shared/models/videos/video-comment.model' 23import { VideoComment, VideoCommentAdmin } from '../../../shared/models/videos/video-comment.model'
24import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor' 24import { actorNameAlphabet } from '../../helpers/custom-validators/activitypub/actor'
25import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' 25import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
26import { regexpCapture } from '../../helpers/regexp' 26import { regexpCapture } from '../../helpers/regexp'
27import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants' 27import { CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants'
28import { 28import {
29 MComment, 29 MComment,
30 MCommentAdminFormattable,
30 MCommentAP, 31 MCommentAP,
31 MCommentFormattable, 32 MCommentFormattable,
32 MCommentId, 33 MCommentId,
@@ -40,7 +41,14 @@ import {
40import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' 41import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse'
41import { AccountModel } from '../account/account' 42import { AccountModel } from '../account/account'
42import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor' 43import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor'
43import { buildBlockedAccountSQL, buildBlockedAccountSQLOptimized, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils' 44import {
45 buildBlockedAccountSQL,
46 buildBlockedAccountSQLOptimized,
47 buildLocalAccountIdsIn,
48 getCommentSort,
49 searchAttribute,
50 throwIfNotValid
51} from '../utils'
44import { VideoModel } from './video' 52import { VideoModel } from './video'
45import { VideoChannelModel } from './video-channel' 53import { VideoChannelModel } from './video-channel'
46 54
@@ -303,6 +311,90 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
303 return VideoCommentModel.scope([ ScopeNames.WITH_IN_REPLY_TO, ScopeNames.WITH_ACCOUNT ]).findOne(query) 311 return VideoCommentModel.scope([ ScopeNames.WITH_IN_REPLY_TO, ScopeNames.WITH_ACCOUNT ]).findOne(query)
304 } 312 }
305 313
314 static listCommentsForApi (parameters: {
315 start: number
316 count: number
317 sort: string
318
319 isLocal?: boolean
320 search?: string
321 searchAccount?: string
322 searchVideo?: string
323 }) {
324 const { start, count, sort, isLocal, search, searchAccount, searchVideo } = parameters
325
326 const query: FindAndCountOptions = {
327 offset: start,
328 limit: count,
329 order: getCommentSort(sort)
330 }
331
332 const where: WhereOptions = {
333 isDeleted: false
334 }
335
336 const whereAccount: WhereOptions = {}
337 const whereActor: WhereOptions = {}
338 const whereVideo: WhereOptions = {}
339
340 if (isLocal === true) {
341 Object.assign(where, {
342 serverId: null
343 })
344 } else if (isLocal === false) {
345 Object.assign(where, {
346 serverId: {
347 [Op.ne]: null
348 }
349 })
350 }
351
352 if (search) {
353 Object.assign(where, searchAttribute(search, 'text'))
354 Object.assign(whereActor, searchAttribute(search, 'preferredUsername'))
355 Object.assign(whereAccount, searchAttribute(search, 'name'))
356 Object.assign(whereVideo, searchAttribute(search, 'name'))
357 }
358
359 if (searchAccount) {
360 Object.assign(whereActor, searchAttribute(search, 'preferredUsername'))
361 Object.assign(whereAccount, searchAttribute(search, 'name'))
362 }
363
364 if (searchVideo) {
365 Object.assign(whereVideo, searchAttribute(search, 'name'))
366 }
367
368 query.include = [
369 {
370 model: AccountModel.unscoped(),
371 required: !!searchAccount,
372 where: whereAccount,
373 include: [
374 {
375 attributes: {
376 exclude: unusedActorAttributesForAPI
377 },
378 model: ActorModel, // Default scope includes avatar and server
379 required: true,
380 where: whereActor
381 }
382 ]
383 },
384 {
385 model: VideoModel.unscoped(),
386 required: true,
387 where: whereVideo
388 }
389 ]
390
391 return VideoCommentModel
392 .findAndCountAll(query)
393 .then(({ rows, count }) => {
394 return { total: count, data: rows }
395 })
396 }
397
306 static async listThreadsForApi (parameters: { 398 static async listThreadsForApi (parameters: {
307 videoId: number 399 videoId: number
308 isVideoOwned: boolean 400 isVideoOwned: boolean
@@ -656,19 +748,51 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
656 id: this.id, 748 id: this.id,
657 url: this.url, 749 url: this.url,
658 text: this.text, 750 text: this.text,
751
659 threadId: this.getThreadId(), 752 threadId: this.getThreadId(),
660 inReplyToCommentId: this.inReplyToCommentId || null, 753 inReplyToCommentId: this.inReplyToCommentId || null,
661 videoId: this.videoId, 754 videoId: this.videoId,
755
662 createdAt: this.createdAt, 756 createdAt: this.createdAt,
663 updatedAt: this.updatedAt, 757 updatedAt: this.updatedAt,
664 deletedAt: this.deletedAt, 758 deletedAt: this.deletedAt,
759
665 isDeleted: this.isDeleted(), 760 isDeleted: this.isDeleted(),
761
666 totalRepliesFromVideoAuthor: this.get('totalRepliesFromVideoAuthor') || 0, 762 totalRepliesFromVideoAuthor: this.get('totalRepliesFromVideoAuthor') || 0,
667 totalReplies: this.get('totalReplies') || 0, 763 totalReplies: this.get('totalReplies') || 0,
668 account: this.Account ? this.Account.toFormattedJSON() : null 764
765 account: this.Account
766 ? this.Account.toFormattedJSON()
767 : null
669 } as VideoComment 768 } as VideoComment
670 } 769 }
671 770
771 toFormattedAdminJSON (this: MCommentAdminFormattable) {
772 return {
773 id: this.id,
774 url: this.url,
775 text: this.text,
776
777 threadId: this.getThreadId(),
778 inReplyToCommentId: this.inReplyToCommentId || null,
779 videoId: this.videoId,
780
781 createdAt: this.createdAt,
782 updatedAt: this.updatedAt,
783
784 video: {
785 id: this.Video.id,
786 uuid: this.Video.uuid,
787 name: this.Video.name
788 },
789
790 account: this.Account
791 ? this.Account.toFormattedJSON()
792 : null
793 } as VideoCommentAdmin
794 }
795
672 toActivityPubObject (this: MCommentAP, threadParentComments: MCommentOwner[]): VideoCommentObject | ActivityTombstoneObject { 796 toActivityPubObject (this: MCommentAP, threadParentComments: MCommentOwner[]): VideoCommentObject | ActivityTombstoneObject {
673 let inReplyTo: string 797 let inReplyTo: string
674 // New thread, so in AS we reply to the video 798 // New thread, so in AS we reply to the video