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.ts140
1 files changed, 136 insertions, 4 deletions
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index de27b3d87..ed4a345eb 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,98 @@ 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 where: WhereOptions = {
327 deletedAt: null
328 }
329
330 const whereAccount: WhereOptions = {}
331 const whereActor: WhereOptions = {}
332 const whereVideo: WhereOptions = {}
333
334 if (isLocal === true) {
335 Object.assign(whereActor, {
336 serverId: null
337 })
338 } else if (isLocal === false) {
339 Object.assign(whereActor, {
340 serverId: {
341 [Op.ne]: null
342 }
343 })
344 }
345
346 if (search) {
347 Object.assign(where, {
348 [Op.or]: [
349 searchAttribute(search, 'text'),
350 searchAttribute(search, '$Account.Actor.preferredUsername$'),
351 searchAttribute(search, '$Account.name$'),
352 searchAttribute(search, '$Video.name$')
353 ]
354 })
355 }
356
357 if (searchAccount) {
358 Object.assign(whereActor, {
359 [Op.or]: [
360 searchAttribute(searchAccount, '$Account.Actor.preferredUsername$'),
361 searchAttribute(searchAccount, '$Account.name$')
362 ]
363 })
364 }
365
366 if (searchVideo) {
367 Object.assign(whereVideo, searchAttribute(searchVideo, 'name'))
368 }
369
370 const query: FindAndCountOptions = {
371 offset: start,
372 limit: count,
373 order: getCommentSort(sort),
374 where,
375 include: [
376 {
377 model: AccountModel.unscoped(),
378 required: true,
379 where: whereAccount,
380 include: [
381 {
382 attributes: {
383 exclude: unusedActorAttributesForAPI
384 },
385 model: ActorModel, // Default scope includes avatar and server
386 required: true,
387 where: whereActor
388 }
389 ]
390 },
391 {
392 model: VideoModel.unscoped(),
393 required: true,
394 where: whereVideo
395 }
396 ]
397 }
398
399 return VideoCommentModel
400 .findAndCountAll(query)
401 .then(({ rows, count }) => {
402 return { total: count, data: rows }
403 })
404 }
405
306 static async listThreadsForApi (parameters: { 406 static async listThreadsForApi (parameters: {
307 videoId: number 407 videoId: number
308 isVideoOwned: boolean 408 isVideoOwned: boolean
@@ -656,19 +756,51 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
656 id: this.id, 756 id: this.id,
657 url: this.url, 757 url: this.url,
658 text: this.text, 758 text: this.text,
759
659 threadId: this.getThreadId(), 760 threadId: this.getThreadId(),
660 inReplyToCommentId: this.inReplyToCommentId || null, 761 inReplyToCommentId: this.inReplyToCommentId || null,
661 videoId: this.videoId, 762 videoId: this.videoId,
763
662 createdAt: this.createdAt, 764 createdAt: this.createdAt,
663 updatedAt: this.updatedAt, 765 updatedAt: this.updatedAt,
664 deletedAt: this.deletedAt, 766 deletedAt: this.deletedAt,
767
665 isDeleted: this.isDeleted(), 768 isDeleted: this.isDeleted(),
769
666 totalRepliesFromVideoAuthor: this.get('totalRepliesFromVideoAuthor') || 0, 770 totalRepliesFromVideoAuthor: this.get('totalRepliesFromVideoAuthor') || 0,
667 totalReplies: this.get('totalReplies') || 0, 771 totalReplies: this.get('totalReplies') || 0,
668 account: this.Account ? this.Account.toFormattedJSON() : null 772
773 account: this.Account
774 ? this.Account.toFormattedJSON()
775 : null
669 } as VideoComment 776 } as VideoComment
670 } 777 }
671 778
779 toFormattedAdminJSON (this: MCommentAdminFormattable) {
780 return {
781 id: this.id,
782 url: this.url,
783 text: this.text,
784
785 threadId: this.getThreadId(),
786 inReplyToCommentId: this.inReplyToCommentId || null,
787 videoId: this.videoId,
788
789 createdAt: this.createdAt,
790 updatedAt: this.updatedAt,
791
792 video: {
793 id: this.Video.id,
794 uuid: this.Video.uuid,
795 name: this.Video.name
796 },
797
798 account: this.Account
799 ? this.Account.toFormattedJSON()
800 : null
801 } as VideoCommentAdmin
802 }
803
672 toActivityPubObject (this: MCommentAP, threadParentComments: MCommentOwner[]): VideoCommentObject | ActivityTombstoneObject { 804 toActivityPubObject (this: MCommentAP, threadParentComments: MCommentOwner[]): VideoCommentObject | ActivityTombstoneObject {
673 let inReplyTo: string 805 let inReplyTo: string
674 // New thread, so in AS we reply to the video 806 // New thread, so in AS we reply to the video