aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2017-12-22 09:14:50 +0100
committerChocobozzz <me@florianbigard.com>2017-12-22 09:14:50 +0100
commit6d8524702874120a4667269a81a61e3c7c5e300d (patch)
treea462d95c56b558a4cfc42db08ec1cb66b1f99680
parentfb4fd623d5e5adcfdc9ecf3dffef702b3786f486 (diff)
downloadPeerTube-6d8524702874120a4667269a81a61e3c7c5e300d.tar.gz
PeerTube-6d8524702874120a4667269a81a61e3c7c5e300d.tar.zst
PeerTube-6d8524702874120a4667269a81a61e3c7c5e300d.zip
Create comment on replied mastodon statutes
-rw-r--r--client/yarn.lock4
-rw-r--r--package.json2
-rw-r--r--server/controllers/api/videos/comment.ts88
-rw-r--r--server/helpers/custom-validators/activitypub/activity.ts4
-rw-r--r--server/helpers/custom-validators/activitypub/video-comments.ts40
-rw-r--r--server/initializers/constants.ts3
-rw-r--r--server/initializers/database.ts4
-rw-r--r--server/lib/activitypub/process/process-create.ts53
-rw-r--r--server/models/video/video-comment.ts95
-rw-r--r--server/models/video/video.ts12
-rw-r--r--shared/models/activitypub/activity.ts3
-rw-r--r--shared/models/activitypub/objects/video-comment-object.ts8
-rw-r--r--yarn.lock94
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
2071es5-shim@^4.5.1: 2071es5-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
2075es6-iterator@^2.0.1, es6-iterator@~2.0.1: 2075es6-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'
6import { isDislikeActivityValid, isLikeActivityValid } from './rate' 6import { isDislikeActivityValid, isLikeActivityValid } from './rate'
7import { isUndoActivityValid } from './undo' 7import { isUndoActivityValid } from './undo'
8import { isVideoChannelDeleteActivityValid, isVideoChannelUpdateActivityValid } from './video-channels' 8import { isVideoChannelDeleteActivityValid, isVideoChannelUpdateActivityValid } from './video-channels'
9import { isVideoCommentCreateActivityValid } from './video-comments'
9import { 10import {
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
65function checkUpdateActivity (activity: any) { 67function 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 @@
1import * as validator from 'validator'
2import { exists, isDateValid } from '../misc'
3import { isActivityPubUrlValid, isBaseActivityValid } from './misc'
4import * as sanitizeHtml from 'sanitize-html'
5
6function isVideoCommentCreateActivityValid (activity: any) {
7 return isBaseActivityValid(activity, 'Create') &&
8 isVideoCommentObjectValid(activity.object)
9}
10
11function 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
23export {
24 isVideoCommentCreateActivityValid
25}
26
27// ---------------------------------------------------------------------------
28
29function sanitizeCommentHTML (comment: any) {
30 return sanitizeHtml(comment.content, {
31 allowedTags: [ 'b', 'i', 'em', 'span', 'a' ],
32 allowedAttributes: {
33 'a': [ 'href' ]
34 }
35 })
36}
37
38function 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'
18import { VideoAbuseModel } from '../models/video/video-abuse' 18import { VideoAbuseModel } from '../models/video/video-abuse'
19import { VideoBlacklistModel } from '../models/video/video-blacklist' 19import { VideoBlacklistModel } from '../models/video/video-blacklist'
20import { VideoChannelModel } from '../models/video/video-channel' 20import { VideoChannelModel } from '../models/video/video-channel'
21import { VideoCommentModel } from '../models/video/video-comment'
21import { VideoFileModel } from '../models/video/video-file' 22import { VideoFileModel } from '../models/video/video-file'
22import { VideoShareModel } from '../models/video/video-share' 23import { VideoShareModel } from '../models/video/video-share'
23import { VideoTagModel } from '../models/video/video-tag' 24import { 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 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import { ActivityCreate, VideoTorrentObject } from '../../../../shared' 2import { ActivityCreate, VideoTorrentObject } from '../../../../shared'
3import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects' 3import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects'
4import { VideoCommentObject } from '../../../../shared/models/activitypub/objects/video-comment-object'
4import { VideoRateType } from '../../../../shared/models/videos' 5import { VideoRateType } from '../../../../shared/models/videos'
5import { logger, retryTransactionWrapper } from '../../../helpers' 6import { logger, retryTransactionWrapper } from '../../../helpers'
6import { sequelizeTypescript } from '../../../initializers' 7import { sequelizeTypescript } from '../../../initializers'
@@ -9,6 +10,7 @@ import { ActorModel } from '../../../models/activitypub/actor'
9import { TagModel } from '../../../models/video/tag' 10import { TagModel } from '../../../models/video/tag'
10import { VideoModel } from '../../../models/video/video' 11import { VideoModel } from '../../../models/video/video'
11import { VideoAbuseModel } from '../../../models/video/video-abuse' 12import { VideoAbuseModel } from '../../../models/video/video-abuse'
13import { VideoCommentModel } from '../../../models/video/video-comment'
12import { VideoFileModel } from '../../../models/video/video-file' 14import { VideoFileModel } from '../../../models/video/video-file'
13import { getOrCreateActorAndServerAndModel } from '../actor' 15import { getOrCreateActorAndServerAndModel } from '../actor'
14import { forwardActivity } from '../send/misc' 16import { 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
187async function processCreateView (byAccount: ActorModel, activity: ActivityCreate) { 191async 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
244function 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
253function 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 @@
1import * as Sequelize from 'sequelize'
2import {
3 AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, IFindOptions, Is, IsUUID, Model, Table,
4 UpdatedAt
5} from 'sequelize-typescript'
6import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub'
7import { CONSTRAINTS_FIELDS } from '../../initializers'
8import { ActorModel } from '../activitypub/actor'
9import { throwIfNotValid } from '../utils'
10import { VideoModel } from './video'
11
12@Table({
13 tableName: 'videoComment',
14 indexes: [
15 {
16 fields: [ 'videoId' ]
17 }
18 ]
19})
20export 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'
2import { VideoTorrentObject } from './objects' 2import { VideoTorrentObject } from './objects'
3import { DislikeObject } from './objects/dislike-object' 3import { DislikeObject } from './objects/dislike-object'
4import { VideoAbuseObject } from './objects/video-abuse-object' 4import { VideoAbuseObject } from './objects/video-abuse-object'
5import { VideoCommentObject } from './objects/video-comment-object'
5import { ViewObject } from './objects/view-object' 6import { ViewObject } from './objects/view-object'
6 7
7export type Activity = ActivityCreate | ActivityUpdate | 8export type Activity = ActivityCreate | ActivityUpdate |
@@ -27,7 +28,7 @@ export interface BaseActivity {
27 28
28export interface ActivityCreate extends BaseActivity { 29export 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
33export interface ActivityUpdate extends BaseActivity { 34export 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 @@
1export interface VideoCommentObject {
2 type: 'Note'
3 id: string
4 content: string
5 inReplyTo: string
6 published: string
7 url: string
8}
diff --git a/yarn.lock b/yarn.lock
index 8f8f8235a..101428df8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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
345array-uniq@^1.0.1: 349array-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
786chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0: 790chalk@^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
1203dom-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
1210domelementtype@1, domelementtype@^1.3.0:
1211 version "1.3.0"
1212 resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
1213
1214domelementtype@~1.1.1:
1215 version "1.1.3"
1216 resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
1217
1218domhandler@^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
1224domutils@^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
1199dot-prop@^4.1.0: 1231dot-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
1285entities@^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
1253error-ex@^1.2.0: 1289error-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
2072htmlparser2@^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
2036http-errors@1.6.2, http-errors@~1.6.2: 2083http-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
2601lodash.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
2554lodash.cond@^4.3.0: 2605lodash.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
2616lodash.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
2565lodash.isarguments@^3.0.0: 2620lodash.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
2636lodash.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
2581lodash.restparam@^3.0.0: 2640lodash.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
3329postcss@^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
3270postgres-array@~1.0.0: 3337postgres-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
3717sanitize-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
3650scripty@^1.5.0: 3729scripty@^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
3857source-map@^0.6.0: 3936source-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
3964srcset@^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
3885sshpk@^1.7.0: 3971sshpk@^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
4070supports-color@^4.0.0: 4156supports-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: