diff options
-rw-r--r-- | client/yarn.lock | 4 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | server/controllers/api/videos/comment.ts | 88 | ||||
-rw-r--r-- | server/helpers/custom-validators/activitypub/activity.ts | 4 | ||||
-rw-r--r-- | server/helpers/custom-validators/activitypub/video-comments.ts | 40 | ||||
-rw-r--r-- | server/initializers/constants.ts | 3 | ||||
-rw-r--r-- | server/initializers/database.ts | 4 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-create.ts | 53 | ||||
-rw-r--r-- | server/models/video/video-comment.ts | 95 | ||||
-rw-r--r-- | server/models/video/video.ts | 12 | ||||
-rw-r--r-- | shared/models/activitypub/activity.ts | 3 | ||||
-rw-r--r-- | shared/models/activitypub/objects/video-comment-object.ts | 8 | ||||
-rw-r--r-- | yarn.lock | 94 |
13 files changed, 399 insertions, 11 deletions
diff --git a/client/yarn.lock b/client/yarn.lock index 10af86a55..a3928ef40 100644 --- a/client/yarn.lock +++ b/client/yarn.lock | |||
@@ -2069,8 +2069,8 @@ es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: | |||
2069 | es6-symbol "~3.1.1" | 2069 | es6-symbol "~3.1.1" |
2070 | 2070 | ||
2071 | es5-shim@^4.5.1: | 2071 | es5-shim@^4.5.1: |
2072 | version "4.5.9" | 2072 | version "4.5.10" |
2073 | resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.5.9.tgz#2a1e2b9e583ff5fed0c20a3ee2cbf3f75230a5c0" | 2073 | resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.5.10.tgz#b7e17ef4df2a145b821f1497b50c25cf94026205" |
2074 | 2074 | ||
2075 | es6-iterator@^2.0.1, es6-iterator@~2.0.1: | 2075 | es6-iterator@^2.0.1, es6-iterator@~2.0.1: |
2076 | version "2.0.3" | 2076 | version "2.0.3" |
diff --git a/package.json b/package.json index 71da491be..f6e10d709 100644 --- a/package.json +++ b/package.json | |||
@@ -82,6 +82,7 @@ | |||
82 | "request": "^2.81.0", | 82 | "request": "^2.81.0", |
83 | "rimraf": "^2.5.4", | 83 | "rimraf": "^2.5.4", |
84 | "safe-buffer": "^5.0.1", | 84 | "safe-buffer": "^5.0.1", |
85 | "sanitize-html": "^1.16.3", | ||
85 | "scripty": "^1.5.0", | 86 | "scripty": "^1.5.0", |
86 | "sequelize": "4.25.2", | 87 | "sequelize": "4.25.2", |
87 | "sequelize-typescript": "^0.6.1", | 88 | "sequelize-typescript": "^0.6.1", |
@@ -110,6 +111,7 @@ | |||
110 | "@types/node": "^8.0.3", | 111 | "@types/node": "^8.0.3", |
111 | "@types/pem": "^1.9.3", | 112 | "@types/pem": "^1.9.3", |
112 | "@types/request": "^2.0.3", | 113 | "@types/request": "^2.0.3", |
114 | "@types/sanitize-html": "^1.14.0", | ||
113 | "@types/sequelize": "^4.0.55", | 115 | "@types/sequelize": "^4.0.55", |
114 | "@types/supertest": "^2.0.3", | 116 | "@types/supertest": "^2.0.3", |
115 | "@types/validator": "^6.2.0", | 117 | "@types/validator": "^6.2.0", |
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts new file mode 100644 index 000000000..b69aa5d40 --- /dev/null +++ b/server/controllers/api/videos/comment.ts | |||
@@ -0,0 +1,88 @@ | |||
1 | // import * as express from 'express' | ||
2 | // import { logger, getFormattedObjects } from '../../../helpers' | ||
3 | // import { | ||
4 | // authenticate, | ||
5 | // ensureUserHasRight, | ||
6 | // videosBlacklistAddValidator, | ||
7 | // videosBlacklistRemoveValidator, | ||
8 | // paginationValidator, | ||
9 | // blacklistSortValidator, | ||
10 | // setBlacklistSort, | ||
11 | // setPagination, | ||
12 | // asyncMiddleware | ||
13 | // } from '../../../middlewares' | ||
14 | // import { BlacklistedVideo, UserRight } from '../../../../shared' | ||
15 | // import { VideoBlacklistModel } from '../../../models/video/video-blacklist' | ||
16 | // | ||
17 | // const videoCommentRouter = express.Router() | ||
18 | // | ||
19 | // videoCommentRouter.get('/:videoId/comment', | ||
20 | // authenticate, | ||
21 | // ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST), | ||
22 | // asyncMiddleware(listVideoCommentsThreadsValidator), | ||
23 | // asyncMiddleware(listVideoCommentsThreads) | ||
24 | // ) | ||
25 | // | ||
26 | // videoCommentRouter.post('/:videoId/comment', | ||
27 | // authenticate, | ||
28 | // ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST), | ||
29 | // asyncMiddleware(videosBlacklistAddValidator), | ||
30 | // asyncMiddleware(addVideoToBlacklist) | ||
31 | // ) | ||
32 | // | ||
33 | // videoCommentRouter.get('/blacklist', | ||
34 | // authenticate, | ||
35 | // ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST), | ||
36 | // paginationValidator, | ||
37 | // blacklistSortValidator, | ||
38 | // setBlacklistSort, | ||
39 | // setPagination, | ||
40 | // asyncMiddleware(listBlacklist) | ||
41 | // ) | ||
42 | // | ||
43 | // videoCommentRouter.delete('/:videoId/blacklist', | ||
44 | // authenticate, | ||
45 | // ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST), | ||
46 | // asyncMiddleware(videosBlacklistRemoveValidator), | ||
47 | // asyncMiddleware(removeVideoFromBlacklistController) | ||
48 | // ) | ||
49 | // | ||
50 | // // --------------------------------------------------------------------------- | ||
51 | // | ||
52 | // export { | ||
53 | // videoCommentRouter | ||
54 | // } | ||
55 | // | ||
56 | // // --------------------------------------------------------------------------- | ||
57 | // | ||
58 | // async function addVideoToBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
59 | // const videoInstance = res.locals.video | ||
60 | // | ||
61 | // const toCreate = { | ||
62 | // videoId: videoInstance.id | ||
63 | // } | ||
64 | // | ||
65 | // await VideoBlacklistModel.create(toCreate) | ||
66 | // return res.type('json').status(204).end() | ||
67 | // } | ||
68 | // | ||
69 | // async function listBlacklist (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
70 | // const resultList = await VideoBlacklistModel.listForApi(req.query.start, req.query.count, req.query.sort) | ||
71 | // | ||
72 | // return res.json(getFormattedObjects<BlacklistedVideo, VideoBlacklistModel>(resultList.data, resultList.total)) | ||
73 | // } | ||
74 | // | ||
75 | // async function removeVideoFromBlacklistController (req: express.Request, res: express.Response, next: express.NextFunction) { | ||
76 | // const blacklistedVideo = res.locals.blacklistedVideo as VideoBlacklistModel | ||
77 | // | ||
78 | // try { | ||
79 | // await blacklistedVideo.destroy() | ||
80 | // | ||
81 | // logger.info('Video %s removed from blacklist.', res.locals.video.uuid) | ||
82 | // | ||
83 | // return res.sendStatus(204) | ||
84 | // } catch (err) { | ||
85 | // logger.error('Some error while removing video %s from blacklist.', res.locals.video.uuid, err) | ||
86 | // throw err | ||
87 | // } | ||
88 | // } | ||
diff --git a/server/helpers/custom-validators/activitypub/activity.ts b/server/helpers/custom-validators/activitypub/activity.ts index c402800a4..f2e137061 100644 --- a/server/helpers/custom-validators/activitypub/activity.ts +++ b/server/helpers/custom-validators/activitypub/activity.ts | |||
@@ -6,6 +6,7 @@ import { isActivityPubUrlValid } from './misc' | |||
6 | import { isDislikeActivityValid, isLikeActivityValid } from './rate' | 6 | import { isDislikeActivityValid, isLikeActivityValid } from './rate' |
7 | import { isUndoActivityValid } from './undo' | 7 | import { isUndoActivityValid } from './undo' |
8 | import { isVideoChannelDeleteActivityValid, isVideoChannelUpdateActivityValid } from './video-channels' | 8 | import { isVideoChannelDeleteActivityValid, isVideoChannelUpdateActivityValid } from './video-channels' |
9 | import { isVideoCommentCreateActivityValid } from './video-comments' | ||
9 | import { | 10 | import { |
10 | isVideoFlagValid, | 11 | isVideoFlagValid, |
11 | isVideoTorrentCreateActivityValid, | 12 | isVideoTorrentCreateActivityValid, |
@@ -59,7 +60,8 @@ function checkCreateActivity (activity: any) { | |||
59 | return isViewActivityValid(activity) || | 60 | return isViewActivityValid(activity) || |
60 | isDislikeActivityValid(activity) || | 61 | isDislikeActivityValid(activity) || |
61 | isVideoTorrentCreateActivityValid(activity) || | 62 | isVideoTorrentCreateActivityValid(activity) || |
62 | isVideoFlagValid(activity) | 63 | isVideoFlagValid(activity) || |
64 | isVideoCommentCreateActivityValid(activity) | ||
63 | } | 65 | } |
64 | 66 | ||
65 | function checkUpdateActivity (activity: any) { | 67 | function checkUpdateActivity (activity: any) { |
diff --git a/server/helpers/custom-validators/activitypub/video-comments.ts b/server/helpers/custom-validators/activitypub/video-comments.ts new file mode 100644 index 000000000..489ff27de --- /dev/null +++ b/server/helpers/custom-validators/activitypub/video-comments.ts | |||
@@ -0,0 +1,40 @@ | |||
1 | import * as validator from 'validator' | ||
2 | import { exists, isDateValid } from '../misc' | ||
3 | import { isActivityPubUrlValid, isBaseActivityValid } from './misc' | ||
4 | import * as sanitizeHtml from 'sanitize-html' | ||
5 | |||
6 | function isVideoCommentCreateActivityValid (activity: any) { | ||
7 | return isBaseActivityValid(activity, 'Create') && | ||
8 | isVideoCommentObjectValid(activity.object) | ||
9 | } | ||
10 | |||
11 | function isVideoCommentObjectValid (comment: any) { | ||
12 | return comment.type === 'Note' && | ||
13 | isActivityPubUrlValid(comment.id) && | ||
14 | sanitizeCommentHTML(comment) && | ||
15 | isCommentContentValid(comment.content) && | ||
16 | isActivityPubUrlValid(comment.inReplyTo) && | ||
17 | isDateValid(comment.published) && | ||
18 | isActivityPubUrlValid(comment.url) | ||
19 | } | ||
20 | |||
21 | // --------------------------------------------------------------------------- | ||
22 | |||
23 | export { | ||
24 | isVideoCommentCreateActivityValid | ||
25 | } | ||
26 | |||
27 | // --------------------------------------------------------------------------- | ||
28 | |||
29 | function sanitizeCommentHTML (comment: any) { | ||
30 | return sanitizeHtml(comment.content, { | ||
31 | allowedTags: [ 'b', 'i', 'em', 'span', 'a' ], | ||
32 | allowedAttributes: { | ||
33 | 'a': [ 'href' ] | ||
34 | } | ||
35 | }) | ||
36 | } | ||
37 | |||
38 | function isCommentContentValid (content: any) { | ||
39 | return exists(content) && validator.isLength('' + content, { min: 1 }) | ||
40 | } | ||
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 100a77622..c8b21d10d 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -175,6 +175,9 @@ const CONSTRAINTS_FIELDS = { | |||
175 | }, | 175 | }, |
176 | VIDEO_EVENTS: { | 176 | VIDEO_EVENTS: { |
177 | COUNT: { min: 0 } | 177 | COUNT: { min: 0 } |
178 | }, | ||
179 | COMMENT: { | ||
180 | URL: { min: 3, max: 2000 } // Length | ||
178 | } | 181 | } |
179 | } | 182 | } |
180 | 183 | ||
diff --git a/server/initializers/database.ts b/server/initializers/database.ts index 0b3f695f7..852db68a0 100644 --- a/server/initializers/database.ts +++ b/server/initializers/database.ts | |||
@@ -18,6 +18,7 @@ import { VideoModel } from '../models/video/video' | |||
18 | import { VideoAbuseModel } from '../models/video/video-abuse' | 18 | import { VideoAbuseModel } from '../models/video/video-abuse' |
19 | import { VideoBlacklistModel } from '../models/video/video-blacklist' | 19 | import { VideoBlacklistModel } from '../models/video/video-blacklist' |
20 | import { VideoChannelModel } from '../models/video/video-channel' | 20 | import { VideoChannelModel } from '../models/video/video-channel' |
21 | import { VideoCommentModel } from '../models/video/video-comment' | ||
21 | import { VideoFileModel } from '../models/video/video-file' | 22 | import { VideoFileModel } from '../models/video/video-file' |
22 | import { VideoShareModel } from '../models/video/video-share' | 23 | import { VideoShareModel } from '../models/video/video-share' |
23 | import { VideoTagModel } from '../models/video/video-tag' | 24 | import { VideoTagModel } from '../models/video/video-tag' |
@@ -73,7 +74,8 @@ async function initDatabaseModels (silent: boolean) { | |||
73 | VideoFileModel, | 74 | VideoFileModel, |
74 | VideoBlacklistModel, | 75 | VideoBlacklistModel, |
75 | VideoTagModel, | 76 | VideoTagModel, |
76 | VideoModel | 77 | VideoModel, |
78 | VideoCommentModel | ||
77 | ]) | 79 | ]) |
78 | 80 | ||
79 | if (!silent) logger.info('Database %s is ready.', dbname) | 81 | if (!silent) logger.info('Database %s is ready.', dbname) |
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts index 1ddd817db..102e54b19 100644 --- a/server/lib/activitypub/process/process-create.ts +++ b/server/lib/activitypub/process/process-create.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { ActivityCreate, VideoTorrentObject } from '../../../../shared' | 2 | import { ActivityCreate, VideoTorrentObject } from '../../../../shared' |
3 | import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects' | 3 | import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects' |
4 | import { VideoCommentObject } from '../../../../shared/models/activitypub/objects/video-comment-object' | ||
4 | import { VideoRateType } from '../../../../shared/models/videos' | 5 | import { VideoRateType } from '../../../../shared/models/videos' |
5 | import { logger, retryTransactionWrapper } from '../../../helpers' | 6 | import { logger, retryTransactionWrapper } from '../../../helpers' |
6 | import { sequelizeTypescript } from '../../../initializers' | 7 | import { sequelizeTypescript } from '../../../initializers' |
@@ -9,6 +10,7 @@ import { ActorModel } from '../../../models/activitypub/actor' | |||
9 | import { TagModel } from '../../../models/video/tag' | 10 | import { TagModel } from '../../../models/video/tag' |
10 | import { VideoModel } from '../../../models/video/video' | 11 | import { VideoModel } from '../../../models/video/video' |
11 | import { VideoAbuseModel } from '../../../models/video/video-abuse' | 12 | import { VideoAbuseModel } from '../../../models/video/video-abuse' |
13 | import { VideoCommentModel } from '../../../models/video/video-comment' | ||
12 | import { VideoFileModel } from '../../../models/video/video-file' | 14 | import { VideoFileModel } from '../../../models/video/video-file' |
13 | import { getOrCreateActorAndServerAndModel } from '../actor' | 15 | import { getOrCreateActorAndServerAndModel } from '../actor' |
14 | import { forwardActivity } from '../send/misc' | 16 | import { forwardActivity } from '../send/misc' |
@@ -28,6 +30,8 @@ async function processCreateActivity (activity: ActivityCreate) { | |||
28 | return processCreateVideo(actor, activity) | 30 | return processCreateVideo(actor, activity) |
29 | } else if (activityType === 'Flag') { | 31 | } else if (activityType === 'Flag') { |
30 | return processCreateVideoAbuse(actor, activityObject as VideoAbuseObject) | 32 | return processCreateVideoAbuse(actor, activityObject as VideoAbuseObject) |
33 | } else if (activityType === 'Note') { | ||
34 | return processCreateVideoComment(actor, activity) | ||
31 | } | 35 | } |
32 | 36 | ||
33 | logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) | 37 | logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) |
@@ -184,7 +188,7 @@ function createVideoDislike (byActor: ActorModel, activity: ActivityCreate) { | |||
184 | }) | 188 | }) |
185 | } | 189 | } |
186 | 190 | ||
187 | async function processCreateView (byAccount: ActorModel, activity: ActivityCreate) { | 191 | async function processCreateView (byActor: ActorModel, activity: ActivityCreate) { |
188 | const view = activity.object as ViewObject | 192 | const view = activity.object as ViewObject |
189 | 193 | ||
190 | const video = await VideoModel.loadByUrlAndPopulateAccount(view.object) | 194 | const video = await VideoModel.loadByUrlAndPopulateAccount(view.object) |
@@ -198,7 +202,7 @@ async function processCreateView (byAccount: ActorModel, activity: ActivityCreat | |||
198 | 202 | ||
199 | if (video.isOwned()) { | 203 | if (video.isOwned()) { |
200 | // Don't resend the activity to the sender | 204 | // Don't resend the activity to the sender |
201 | const exceptions = [ byAccount ] | 205 | const exceptions = [ byActor ] |
202 | await forwardActivity(activity, undefined, exceptions) | 206 | await forwardActivity(activity, undefined, exceptions) |
203 | } | 207 | } |
204 | } | 208 | } |
@@ -236,3 +240,48 @@ function addRemoteVideoAbuse (actor: ActorModel, videoAbuseToCreateData: VideoAb | |||
236 | logger.info('Remote abuse for video uuid %s created', videoAbuseToCreateData.object) | 240 | logger.info('Remote abuse for video uuid %s created', videoAbuseToCreateData.object) |
237 | }) | 241 | }) |
238 | } | 242 | } |
243 | |||
244 | function processCreateVideoComment (byActor: ActorModel, activity: ActivityCreate) { | ||
245 | const options = { | ||
246 | arguments: [ byActor, activity ], | ||
247 | errorMessage: 'Cannot create video comment with many retries.' | ||
248 | } | ||
249 | |||
250 | return retryTransactionWrapper(createVideoComment, options) | ||
251 | } | ||
252 | |||
253 | function createVideoComment (byActor: ActorModel, activity: ActivityCreate) { | ||
254 | const comment = activity.object as VideoCommentObject | ||
255 | const byAccount = byActor.Account | ||
256 | |||
257 | if (!byAccount) throw new Error('Cannot create video comment with the non account actor ' + byActor.url) | ||
258 | |||
259 | return sequelizeTypescript.transaction(async t => { | ||
260 | const video = await VideoModel.loadByUrl(comment.inReplyTo, t) | ||
261 | |||
262 | // This is a new thread | ||
263 | if (video) { | ||
264 | return VideoCommentModel.create({ | ||
265 | url: comment.id, | ||
266 | text: comment.content, | ||
267 | originCommentId: null, | ||
268 | inReplyToComment: null, | ||
269 | videoId: video.id, | ||
270 | actorId: byActor.id | ||
271 | }, { transaction: t }) | ||
272 | } | ||
273 | |||
274 | const inReplyToComment = await VideoCommentModel.loadByUrl(comment.inReplyTo, t) | ||
275 | if (!inReplyToComment) throw new Error('Unknown replied comment ' + comment.inReplyTo) | ||
276 | |||
277 | const originCommentId = inReplyToComment.originCommentId || inReplyToComment.id | ||
278 | return VideoCommentModel.create({ | ||
279 | url: comment.id, | ||
280 | text: comment.content, | ||
281 | originCommentId, | ||
282 | inReplyToCommentId: inReplyToComment.id, | ||
283 | videoId: inReplyToComment.videoId, | ||
284 | actorId: byActor.id | ||
285 | }, { transaction: t }) | ||
286 | }) | ||
287 | } | ||
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts new file mode 100644 index 000000000..92c0c6112 --- /dev/null +++ b/server/models/video/video-comment.ts | |||
@@ -0,0 +1,95 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import { | ||
3 | AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, IFindOptions, Is, IsUUID, Model, Table, | ||
4 | UpdatedAt | ||
5 | } from 'sequelize-typescript' | ||
6 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub' | ||
7 | import { CONSTRAINTS_FIELDS } from '../../initializers' | ||
8 | import { ActorModel } from '../activitypub/actor' | ||
9 | import { throwIfNotValid } from '../utils' | ||
10 | import { VideoModel } from './video' | ||
11 | |||
12 | @Table({ | ||
13 | tableName: 'videoComment', | ||
14 | indexes: [ | ||
15 | { | ||
16 | fields: [ 'videoId' ] | ||
17 | } | ||
18 | ] | ||
19 | }) | ||
20 | export class VideoCommentModel extends Model<VideoCommentModel> { | ||
21 | @CreatedAt | ||
22 | createdAt: Date | ||
23 | |||
24 | @UpdatedAt | ||
25 | updatedAt: Date | ||
26 | |||
27 | @AllowNull(false) | ||
28 | @Is('VideoCommentUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url')) | ||
29 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEOS.URL.max)) | ||
30 | url: string | ||
31 | |||
32 | @AllowNull(false) | ||
33 | @Column(DataType.TEXT) | ||
34 | text: string | ||
35 | |||
36 | @ForeignKey(() => VideoCommentModel) | ||
37 | @Column | ||
38 | originCommentId: number | ||
39 | |||
40 | @BelongsTo(() => VideoCommentModel, { | ||
41 | foreignKey: { | ||
42 | allowNull: true | ||
43 | }, | ||
44 | onDelete: 'CASCADE' | ||
45 | }) | ||
46 | OriginVideoComment: VideoCommentModel | ||
47 | |||
48 | @ForeignKey(() => VideoCommentModel) | ||
49 | @Column | ||
50 | inReplyToCommentId: number | ||
51 | |||
52 | @BelongsTo(() => VideoCommentModel, { | ||
53 | foreignKey: { | ||
54 | allowNull: true | ||
55 | }, | ||
56 | onDelete: 'CASCADE' | ||
57 | }) | ||
58 | InReplyToVideoComment: VideoCommentModel | ||
59 | |||
60 | @ForeignKey(() => VideoModel) | ||
61 | @Column | ||
62 | videoId: number | ||
63 | |||
64 | @BelongsTo(() => VideoModel, { | ||
65 | foreignKey: { | ||
66 | allowNull: false | ||
67 | }, | ||
68 | onDelete: 'CASCADE' | ||
69 | }) | ||
70 | Video: VideoModel | ||
71 | |||
72 | @ForeignKey(() => ActorModel) | ||
73 | @Column | ||
74 | actorId: number | ||
75 | |||
76 | @BelongsTo(() => ActorModel, { | ||
77 | foreignKey: { | ||
78 | allowNull: false | ||
79 | }, | ||
80 | onDelete: 'CASCADE' | ||
81 | }) | ||
82 | Actor: ActorModel | ||
83 | |||
84 | static loadByUrl (url: string, t?: Sequelize.Transaction) { | ||
85 | const query: IFindOptions<VideoCommentModel> = { | ||
86 | where: { | ||
87 | url | ||
88 | } | ||
89 | } | ||
90 | |||
91 | if (t !== undefined) query.transaction = t | ||
92 | |||
93 | return VideoCommentModel.findOne(query) | ||
94 | } | ||
95 | } | ||
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 8c49bc3af..b6a2ce6b5 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -491,6 +491,18 @@ export class VideoModel extends Model<VideoModel> { | |||
491 | return VideoModel.findById(id) | 491 | return VideoModel.findById(id) |
492 | } | 492 | } |
493 | 493 | ||
494 | static loadByUrl (url: string, t?: Sequelize.Transaction) { | ||
495 | const query: IFindOptions<VideoModel> = { | ||
496 | where: { | ||
497 | url | ||
498 | } | ||
499 | } | ||
500 | |||
501 | if (t !== undefined) query.transaction = t | ||
502 | |||
503 | return VideoModel.findOne(query) | ||
504 | } | ||
505 | |||
494 | static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) { | 506 | static loadByUrlAndPopulateAccount (url: string, t?: Sequelize.Transaction) { |
495 | const query: IFindOptions<VideoModel> = { | 507 | const query: IFindOptions<VideoModel> = { |
496 | where: { | 508 | where: { |
diff --git a/shared/models/activitypub/activity.ts b/shared/models/activitypub/activity.ts index d5359eba1..48b52d2cb 100644 --- a/shared/models/activitypub/activity.ts +++ b/shared/models/activitypub/activity.ts | |||
@@ -2,6 +2,7 @@ import { ActivityPubSignature } from './activitypub-signature' | |||
2 | import { VideoTorrentObject } from './objects' | 2 | import { VideoTorrentObject } from './objects' |
3 | import { DislikeObject } from './objects/dislike-object' | 3 | import { DislikeObject } from './objects/dislike-object' |
4 | import { VideoAbuseObject } from './objects/video-abuse-object' | 4 | import { VideoAbuseObject } from './objects/video-abuse-object' |
5 | import { VideoCommentObject } from './objects/video-comment-object' | ||
5 | import { ViewObject } from './objects/view-object' | 6 | import { ViewObject } from './objects/view-object' |
6 | 7 | ||
7 | export type Activity = ActivityCreate | ActivityUpdate | | 8 | export type Activity = ActivityCreate | ActivityUpdate | |
@@ -27,7 +28,7 @@ export interface BaseActivity { | |||
27 | 28 | ||
28 | export interface ActivityCreate extends BaseActivity { | 29 | export interface ActivityCreate extends BaseActivity { |
29 | type: 'Create' | 30 | type: 'Create' |
30 | object: VideoTorrentObject | VideoAbuseObject | ViewObject | DislikeObject | 31 | object: VideoTorrentObject | VideoAbuseObject | ViewObject | DislikeObject | VideoCommentObject |
31 | } | 32 | } |
32 | 33 | ||
33 | export interface ActivityUpdate extends BaseActivity { | 34 | export interface ActivityUpdate extends BaseActivity { |
diff --git a/shared/models/activitypub/objects/video-comment-object.ts b/shared/models/activitypub/objects/video-comment-object.ts new file mode 100644 index 000000000..fc2a9e837 --- /dev/null +++ b/shared/models/activitypub/objects/video-comment-object.ts | |||
@@ -0,0 +1,8 @@ | |||
1 | export interface VideoCommentObject { | ||
2 | type: 'Note' | ||
3 | id: string | ||
4 | content: string | ||
5 | inReplyTo: string | ||
6 | published: string | ||
7 | url: string | ||
8 | } | ||
@@ -150,6 +150,10 @@ | |||
150 | "@types/form-data" "*" | 150 | "@types/form-data" "*" |
151 | "@types/node" "*" | 151 | "@types/node" "*" |
152 | 152 | ||
153 | "@types/sanitize-html@^1.14.0": | ||
154 | version "1.14.0" | ||
155 | resolved "https://registry.yarnpkg.com/@types/sanitize-html/-/sanitize-html-1.14.0.tgz#9a03ec58306e24feaa3fbdb8ab593934d53ecb05" | ||
156 | |||
153 | "@types/sequelize@4.0.79", "@types/sequelize@^4.0.55": | 157 | "@types/sequelize@4.0.79", "@types/sequelize@^4.0.55": |
154 | version "4.0.79" | 158 | version "4.0.79" |
155 | resolved "https://registry.yarnpkg.com/@types/sequelize/-/sequelize-4.0.79.tgz#74c366407a978e493e70d7cea3d80c681aed15c0" | 159 | resolved "https://registry.yarnpkg.com/@types/sequelize/-/sequelize-4.0.79.tgz#74c366407a978e493e70d7cea3d80c681aed15c0" |
@@ -342,7 +346,7 @@ array-union@^1.0.1: | |||
342 | dependencies: | 346 | dependencies: |
343 | array-uniq "^1.0.1" | 347 | array-uniq "^1.0.1" |
344 | 348 | ||
345 | array-uniq@^1.0.1: | 349 | array-uniq@^1.0.1, array-uniq@^1.0.2: |
346 | version "1.0.3" | 350 | version "1.0.3" |
347 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" | 351 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" |
348 | 352 | ||
@@ -783,7 +787,7 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: | |||
783 | strip-ansi "^3.0.0" | 787 | strip-ansi "^3.0.0" |
784 | supports-color "^2.0.0" | 788 | supports-color "^2.0.0" |
785 | 789 | ||
786 | chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0: | 790 | chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0: |
787 | version "2.3.0" | 791 | version "2.3.0" |
788 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" | 792 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" |
789 | dependencies: | 793 | dependencies: |
@@ -1196,6 +1200,34 @@ doctrine@^2.0.0: | |||
1196 | dependencies: | 1200 | dependencies: |
1197 | esutils "^2.0.2" | 1201 | esutils "^2.0.2" |
1198 | 1202 | ||
1203 | dom-serializer@0: | ||
1204 | version "0.1.0" | ||
1205 | resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" | ||
1206 | dependencies: | ||
1207 | domelementtype "~1.1.1" | ||
1208 | entities "~1.1.1" | ||
1209 | |||
1210 | domelementtype@1, domelementtype@^1.3.0: | ||
1211 | version "1.3.0" | ||
1212 | resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" | ||
1213 | |||
1214 | domelementtype@~1.1.1: | ||
1215 | version "1.1.3" | ||
1216 | resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" | ||
1217 | |||
1218 | domhandler@^2.3.0: | ||
1219 | version "2.4.1" | ||
1220 | resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259" | ||
1221 | dependencies: | ||
1222 | domelementtype "1" | ||
1223 | |||
1224 | domutils@^1.5.1: | ||
1225 | version "1.6.2" | ||
1226 | resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.6.2.tgz#1958cc0b4c9426e9ed367fb1c8e854891b0fa3ff" | ||
1227 | dependencies: | ||
1228 | dom-serializer "0" | ||
1229 | domelementtype "1" | ||
1230 | |||
1199 | dot-prop@^4.1.0: | 1231 | dot-prop@^4.1.0: |
1200 | version "4.2.0" | 1232 | version "4.2.0" |
1201 | resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" | 1233 | resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" |
@@ -1250,6 +1282,10 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: | |||
1250 | dependencies: | 1282 | dependencies: |
1251 | once "^1.4.0" | 1283 | once "^1.4.0" |
1252 | 1284 | ||
1285 | entities@^1.1.1, entities@~1.1.1: | ||
1286 | version "1.1.1" | ||
1287 | resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" | ||
1288 | |||
1253 | error-ex@^1.2.0: | 1289 | error-ex@^1.2.0: |
1254 | version "1.3.1" | 1290 | version "1.3.1" |
1255 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" | 1291 | resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" |
@@ -2033,6 +2069,17 @@ homedir-polyfill@^1.0.1: | |||
2033 | dependencies: | 2069 | dependencies: |
2034 | parse-passwd "^1.0.0" | 2070 | parse-passwd "^1.0.0" |
2035 | 2071 | ||
2072 | htmlparser2@^3.9.0: | ||
2073 | version "3.9.2" | ||
2074 | resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" | ||
2075 | dependencies: | ||
2076 | domelementtype "^1.3.0" | ||
2077 | domhandler "^2.3.0" | ||
2078 | domutils "^1.5.1" | ||
2079 | entities "^1.1.1" | ||
2080 | inherits "^2.0.1" | ||
2081 | readable-stream "^2.0.2" | ||
2082 | |||
2036 | http-errors@1.6.2, http-errors@~1.6.2: | 2083 | http-errors@1.6.2, http-errors@~1.6.2: |
2037 | version "1.6.2" | 2084 | version "1.6.2" |
2038 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" | 2085 | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" |
@@ -2551,6 +2598,10 @@ lodash.assign@^3.0.0: | |||
2551 | lodash._createassigner "^3.0.0" | 2598 | lodash._createassigner "^3.0.0" |
2552 | lodash.keys "^3.0.0" | 2599 | lodash.keys "^3.0.0" |
2553 | 2600 | ||
2601 | lodash.clonedeep@^4.5.0: | ||
2602 | version "4.5.0" | ||
2603 | resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" | ||
2604 | |||
2554 | lodash.cond@^4.3.0: | 2605 | lodash.cond@^4.3.0: |
2555 | version "4.5.2" | 2606 | version "4.5.2" |
2556 | resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" | 2607 | resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" |
@@ -2562,6 +2613,10 @@ lodash.defaults@^3.1.2: | |||
2562 | lodash.assign "^3.0.0" | 2613 | lodash.assign "^3.0.0" |
2563 | lodash.restparam "^3.0.0" | 2614 | lodash.restparam "^3.0.0" |
2564 | 2615 | ||
2616 | lodash.escaperegexp@^4.1.2: | ||
2617 | version "4.1.2" | ||
2618 | resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" | ||
2619 | |||
2565 | lodash.isarguments@^3.0.0: | 2620 | lodash.isarguments@^3.0.0: |
2566 | version "3.1.0" | 2621 | version "3.1.0" |
2567 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" | 2622 | resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" |
@@ -2578,6 +2633,10 @@ lodash.keys@^3.0.0: | |||
2578 | lodash.isarguments "^3.0.0" | 2633 | lodash.isarguments "^3.0.0" |
2579 | lodash.isarray "^3.0.0" | 2634 | lodash.isarray "^3.0.0" |
2580 | 2635 | ||
2636 | lodash.mergewith@^4.6.0: | ||
2637 | version "4.6.0" | ||
2638 | resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55" | ||
2639 | |||
2581 | lodash.restparam@^3.0.0: | 2640 | lodash.restparam@^3.0.0: |
2582 | version "3.6.1" | 2641 | version "3.6.1" |
2583 | resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" | 2642 | resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" |
@@ -3267,6 +3326,14 @@ pluralize@^1.2.1: | |||
3267 | version "1.2.1" | 3326 | version "1.2.1" |
3268 | resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" | 3327 | resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" |
3269 | 3328 | ||
3329 | postcss@^6.0.14: | ||
3330 | version "6.0.14" | ||
3331 | resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.14.tgz#5534c72114739e75d0afcf017db853099f562885" | ||
3332 | dependencies: | ||
3333 | chalk "^2.3.0" | ||
3334 | source-map "^0.6.1" | ||
3335 | supports-color "^4.4.0" | ||
3336 | |||
3270 | postgres-array@~1.0.0: | 3337 | postgres-array@~1.0.0: |
3271 | version "1.0.2" | 3338 | version "1.0.2" |
3272 | resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.2.tgz#8e0b32eb03bf77a5c0a7851e0441c169a256a238" | 3339 | resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-1.0.2.tgz#8e0b32eb03bf77a5c0a7851e0441c169a256a238" |
@@ -3647,6 +3714,18 @@ safe-buffer@5.1.1, safe-buffer@^5.0.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, s | |||
3647 | version "5.1.1" | 3714 | version "5.1.1" |
3648 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" | 3715 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" |
3649 | 3716 | ||
3717 | sanitize-html@^1.16.3: | ||
3718 | version "1.16.3" | ||
3719 | resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.16.3.tgz#96c1b44a36ff7312e1c22a14b05274370ac8bd56" | ||
3720 | dependencies: | ||
3721 | htmlparser2 "^3.9.0" | ||
3722 | lodash.clonedeep "^4.5.0" | ||
3723 | lodash.escaperegexp "^4.1.2" | ||
3724 | lodash.mergewith "^4.6.0" | ||
3725 | postcss "^6.0.14" | ||
3726 | srcset "^1.0.0" | ||
3727 | xtend "^4.0.0" | ||
3728 | |||
3650 | scripty@^1.5.0: | 3729 | scripty@^1.5.0: |
3651 | version "1.7.2" | 3730 | version "1.7.2" |
3652 | resolved "https://registry.yarnpkg.com/scripty/-/scripty-1.7.2.tgz#92367b724cb77b086729691f7b01aa57f3ddd356" | 3731 | resolved "https://registry.yarnpkg.com/scripty/-/scripty-1.7.2.tgz#92367b724cb77b086729691f7b01aa57f3ddd356" |
@@ -3854,7 +3933,7 @@ source-map@^0.5.6: | |||
3854 | version "0.5.7" | 3933 | version "0.5.7" |
3855 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" | 3934 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" |
3856 | 3935 | ||
3857 | source-map@^0.6.0: | 3936 | source-map@^0.6.0, source-map@^0.6.1: |
3858 | version "0.6.1" | 3937 | version "0.6.1" |
3859 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" | 3938 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" |
3860 | 3939 | ||
@@ -3882,6 +3961,13 @@ sprintf-js@~1.0.2: | |||
3882 | version "1.0.3" | 3961 | version "1.0.3" |
3883 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" | 3962 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" |
3884 | 3963 | ||
3964 | srcset@^1.0.0: | ||
3965 | version "1.0.0" | ||
3966 | resolved "https://registry.yarnpkg.com/srcset/-/srcset-1.0.0.tgz#a5669de12b42f3b1d5e83ed03c71046fc48f41ef" | ||
3967 | dependencies: | ||
3968 | array-uniq "^1.0.2" | ||
3969 | number-is-nan "^1.0.0" | ||
3970 | |||
3885 | sshpk@^1.7.0: | 3971 | sshpk@^1.7.0: |
3886 | version "1.13.1" | 3972 | version "1.13.1" |
3887 | resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" | 3973 | resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" |
@@ -4067,7 +4153,7 @@ supports-color@^3.2.3: | |||
4067 | dependencies: | 4153 | dependencies: |
4068 | has-flag "^1.0.0" | 4154 | has-flag "^1.0.0" |
4069 | 4155 | ||
4070 | supports-color@^4.0.0: | 4156 | supports-color@^4.0.0, supports-color@^4.4.0: |
4071 | version "4.5.0" | 4157 | version "4.5.0" |
4072 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" | 4158 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" |
4073 | dependencies: | 4159 | dependencies: |