]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/video/video-comment.ts
Fix preview 404
[github/Chocobozzz/PeerTube.git] / server / models / video / video-comment.ts
CommitLineData
6d852470
C
1import * as Sequelize from 'sequelize'
2import {
bf1f6508 3 AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table,
6d852470
C
4 UpdatedAt
5} from 'sequelize-typescript'
ea44f375 6import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object'
bf1f6508 7import { VideoComment } from '../../../shared/models/videos/video-comment.model'
6d852470
C
8import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub'
9import { CONSTRAINTS_FIELDS } from '../../initializers'
d3ea8975 10import { AccountModel } from '../account/account'
4635f59d
C
11import { ActorModel } from '../activitypub/actor'
12import { ServerModel } from '../server/server'
bf1f6508 13import { getSort, throwIfNotValid } from '../utils'
6d852470
C
14import { VideoModel } from './video'
15
bf1f6508 16enum ScopeNames {
ea44f375 17 WITH_ACCOUNT = 'WITH_ACCOUNT',
4635f59d
C
18 WITH_IN_REPLY_TO = 'WITH_IN_REPLY_TO',
19 ATTRIBUTES_FOR_API = 'ATTRIBUTES_FOR_API'
bf1f6508
C
20}
21
22@Scopes({
4635f59d
C
23 [ScopeNames.ATTRIBUTES_FOR_API]: {
24 attributes: {
25 include: [
26 [
27 Sequelize.literal(
28 '(SELECT COUNT("replies"."id") ' +
29 'FROM "videoComment" AS "replies" ' +
30 'WHERE "replies"."originCommentId" = "VideoCommentModel"."id")'
31 ),
32 'totalReplies'
33 ]
34 ]
35 }
36 },
d3ea8975 37 [ScopeNames.WITH_ACCOUNT]: {
bf1f6508 38 include: [
4635f59d
C
39 {
40 model: () => AccountModel,
41 include: [
42 {
43 model: () => ActorModel,
44 include: [
45 {
46 model: () => ServerModel,
47 required: false
48 }
49 ]
50 }
51 ]
52 }
bf1f6508 53 ]
ea44f375
C
54 },
55 [ScopeNames.WITH_IN_REPLY_TO]: {
56 include: [
57 {
58 model: () => VideoCommentModel,
59 as: 'InReplyTo'
60 }
61 ]
bf1f6508
C
62 }
63})
6d852470
C
64@Table({
65 tableName: 'videoComment',
66 indexes: [
67 {
68 fields: [ 'videoId' ]
bf1f6508
C
69 },
70 {
71 fields: [ 'videoId', 'originCommentId' ]
6d852470
C
72 }
73 ]
74})
75export class VideoCommentModel extends Model<VideoCommentModel> {
76 @CreatedAt
77 createdAt: Date
78
79 @UpdatedAt
80 updatedAt: Date
81
82 @AllowNull(false)
83 @Is('VideoCommentUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
84 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max))
85 url: string
86
87 @AllowNull(false)
88 @Column(DataType.TEXT)
89 text: string
90
91 @ForeignKey(() => VideoCommentModel)
92 @Column
93 originCommentId: number
94
95 @BelongsTo(() => VideoCommentModel, {
96 foreignKey: {
97 allowNull: true
98 },
99 onDelete: 'CASCADE'
100 })
101 OriginVideoComment: VideoCommentModel
102
103 @ForeignKey(() => VideoCommentModel)
104 @Column
105 inReplyToCommentId: number
106
107 @BelongsTo(() => VideoCommentModel, {
108 foreignKey: {
109 allowNull: true
110 },
ea44f375 111 as: 'InReplyTo',
6d852470
C
112 onDelete: 'CASCADE'
113 })
114 InReplyToVideoComment: VideoCommentModel
115
116 @ForeignKey(() => VideoModel)
117 @Column
118 videoId: number
119
120 @BelongsTo(() => VideoModel, {
121 foreignKey: {
122 allowNull: false
123 },
124 onDelete: 'CASCADE'
125 })
126 Video: VideoModel
127
d3ea8975 128 @ForeignKey(() => AccountModel)
6d852470 129 @Column
d3ea8975 130 accountId: number
6d852470 131
d3ea8975 132 @BelongsTo(() => AccountModel, {
6d852470
C
133 foreignKey: {
134 allowNull: false
135 },
136 onDelete: 'CASCADE'
137 })
d3ea8975 138 Account: AccountModel
6d852470 139
bf1f6508
C
140 @AfterDestroy
141 static sendDeleteIfOwned (instance: VideoCommentModel) {
142 // TODO
143 return undefined
144 }
145
146 static loadById (id: number, t?: Sequelize.Transaction) {
147 const query: IFindOptions<VideoCommentModel> = {
148 where: {
149 id
150 }
151 }
152
153 if (t !== undefined) query.transaction = t
154
155 return VideoCommentModel.findOne(query)
156 }
157
6d852470
C
158 static loadByUrl (url: string, t?: Sequelize.Transaction) {
159 const query: IFindOptions<VideoCommentModel> = {
160 where: {
161 url
162 }
163 }
164
165 if (t !== undefined) query.transaction = t
166
167 return VideoCommentModel.findOne(query)
168 }
bf1f6508
C
169
170 static listThreadsForApi (videoId: number, start: number, count: number, sort: string) {
171 const query = {
172 offset: start,
173 limit: count,
174 order: [ getSort(sort) ],
175 where: {
d3ea8975
C
176 videoId,
177 inReplyToCommentId: null
bf1f6508
C
178 }
179 }
180
181 return VideoCommentModel
4635f59d 182 .scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.ATTRIBUTES_FOR_API ])
bf1f6508
C
183 .findAndCountAll(query)
184 .then(({ rows, count }) => {
185 return { total: count, data: rows }
186 })
187 }
188
189 static listThreadCommentsForApi (videoId: number, threadId: number) {
190 const query = {
d3ea8975 191 order: [ [ 'id', 'ASC' ] ],
bf1f6508
C
192 where: {
193 videoId,
194 [ Sequelize.Op.or ]: [
195 { id: threadId },
196 { originCommentId: threadId }
197 ]
198 }
199 }
200
201 return VideoCommentModel
4635f59d 202 .scope([ ScopeNames.WITH_ACCOUNT, ScopeNames.ATTRIBUTES_FOR_API ])
bf1f6508
C
203 .findAndCountAll(query)
204 .then(({ rows, count }) => {
205 return { total: count, data: rows }
206 })
207 }
208
209 toFormattedJSON () {
210 return {
211 id: this.id,
212 url: this.url,
213 text: this.text,
214 threadId: this.originCommentId || this.id,
d50acfab 215 inReplyToCommentId: this.inReplyToCommentId || null,
bf1f6508
C
216 videoId: this.videoId,
217 createdAt: this.createdAt,
d3ea8975 218 updatedAt: this.updatedAt,
4635f59d 219 totalReplies: this.get('totalReplies') || 0,
d3ea8975 220 account: {
4635f59d
C
221 name: this.Account.name,
222 host: this.Account.Actor.getHost()
d3ea8975 223 }
bf1f6508
C
224 } as VideoComment
225 }
ea44f375
C
226
227 toActivityPubObject (): VideoCommentObject {
228 let inReplyTo: string
229 // New thread, so in AS we reply to the video
230 if (this.inReplyToCommentId === null) {
231 inReplyTo = this.Video.url
232 } else {
233 inReplyTo = this.InReplyToVideoComment.url
234 }
235
236 return {
237 type: 'Note' as 'Note',
238 id: this.url,
239 content: this.text,
240 inReplyTo,
241 published: this.createdAt.toISOString(),
242 url: this.url
243 }
244 }
6d852470 245}