diff options
author | Chocobozzz <me@florianbigard.com> | 2017-12-28 11:16:08 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2017-12-28 11:16:08 +0100 |
commit | da854ddd502cd70685ef779c673b9e63757b8aa0 (patch) | |
tree | 21501d170cceaa044a5f23449cbd2eb47fd6415d /server/models | |
parent | f40bbe3146553ef45515ee6b6d93ce6028f045ca (diff) | |
download | PeerTube-da854ddd502cd70685ef779c673b9e63757b8aa0.tar.gz PeerTube-da854ddd502cd70685ef779c673b9e63757b8aa0.tar.zst PeerTube-da854ddd502cd70685ef779c673b9e63757b8aa0.zip |
Propagate old comment on new follow
Diffstat (limited to 'server/models')
-rw-r--r-- | server/models/account/user.ts | 2 | ||||
-rw-r--r-- | server/models/activitypub/actor.ts | 9 | ||||
-rw-r--r-- | server/models/oauth/oauth-token.ts | 2 | ||||
-rw-r--r-- | server/models/server/server.ts | 2 | ||||
-rw-r--r-- | server/models/video/video-blacklist.ts | 2 | ||||
-rw-r--r-- | server/models/video/video-comment.ts | 33 | ||||
-rw-r--r-- | server/models/video/video.ts | 121 |
7 files changed, 104 insertions, 67 deletions
diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 1d5759ea3..d7e09e328 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts | |||
@@ -4,11 +4,11 @@ import { | |||
4 | Scopes, Table, UpdatedAt | 4 | Scopes, Table, UpdatedAt |
5 | } from 'sequelize-typescript' | 5 | } from 'sequelize-typescript' |
6 | import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' | 6 | import { hasUserRight, USER_ROLE_LABELS, UserRight } from '../../../shared' |
7 | import { comparePassword, cryptPassword } from '../../helpers' | ||
8 | import { | 7 | import { |
9 | isUserAutoPlayVideoValid, isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid, | 8 | isUserAutoPlayVideoValid, isUserDisplayNSFWValid, isUserPasswordValid, isUserRoleValid, isUserUsernameValid, |
10 | isUserVideoQuotaValid | 9 | isUserVideoQuotaValid |
11 | } from '../../helpers/custom-validators/users' | 10 | } from '../../helpers/custom-validators/users' |
11 | import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' | ||
12 | import { OAuthTokenModel } from '../oauth/oauth-token' | 12 | import { OAuthTokenModel } from '../oauth/oauth-token' |
13 | import { getSort, throwIfNotValid } from '../utils' | 13 | import { getSort, throwIfNotValid } from '../utils' |
14 | import { VideoChannelModel } from '../video/video-channel' | 14 | import { VideoChannelModel } from '../video/video-channel' |
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index e7eb35e2c..3d96b3706 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts | |||
@@ -7,11 +7,12 @@ import { | |||
7 | } from 'sequelize-typescript' | 7 | } from 'sequelize-typescript' |
8 | import { ActivityPubActorType } from '../../../shared/models/activitypub' | 8 | import { ActivityPubActorType } from '../../../shared/models/activitypub' |
9 | import { Avatar } from '../../../shared/models/avatars/avatar.model' | 9 | import { Avatar } from '../../../shared/models/avatars/avatar.model' |
10 | import { activityPubContextify } from '../../helpers' | 10 | import { activityPubContextify } from '../../helpers/activitypub' |
11 | import { | 11 | import { |
12 | isActivityPubUrlValid, isActorFollowersCountValid, isActorFollowingCountValid, isActorPreferredUsernameValid, | 12 | isActorFollowersCountValid, isActorFollowingCountValid, isActorPreferredUsernameValid, isActorPrivateKeyValid, |
13 | isActorPrivateKeyValid, isActorPublicKeyValid | 13 | isActorPublicKeyValid |
14 | } from '../../helpers/custom-validators/activitypub' | 14 | } from '../../helpers/custom-validators/activitypub/actor' |
15 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | ||
15 | import { ACTIVITY_PUB_ACTOR_TYPES, AVATARS_DIR, CONFIG, CONSTRAINTS_FIELDS } from '../../initializers' | 16 | import { ACTIVITY_PUB_ACTOR_TYPES, AVATARS_DIR, CONFIG, CONSTRAINTS_FIELDS } from '../../initializers' |
16 | import { AccountModel } from '../account/account' | 17 | import { AccountModel } from '../account/account' |
17 | import { AvatarModel } from '../avatar/avatar' | 18 | import { AvatarModel } from '../avatar/avatar' |
diff --git a/server/models/oauth/oauth-token.ts b/server/models/oauth/oauth-token.ts index 995fa33d5..9d1b63813 100644 --- a/server/models/oauth/oauth-token.ts +++ b/server/models/oauth/oauth-token.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { logger } from '../../helpers' | 2 | import { logger } from '../../helpers/logger' |
3 | import { AccountModel } from '../account/account' | 3 | import { AccountModel } from '../account/account' |
4 | import { UserModel } from '../account/user' | 4 | import { UserModel } from '../account/user' |
5 | import { OAuthClientModel } from './oauth-client' | 5 | import { OAuthClientModel } from './oauth-client' |
diff --git a/server/models/server/server.ts b/server/models/server/server.ts index edfd8010b..d35aa0ca4 100644 --- a/server/models/server/server.ts +++ b/server/models/server/server.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { AllowNull, Column, CreatedAt, Default, Is, IsInt, Max, Model, Table, UpdatedAt } from 'sequelize-typescript' | 2 | import { AllowNull, Column, CreatedAt, Default, Is, IsInt, Max, Model, Table, UpdatedAt } from 'sequelize-typescript' |
3 | import { logger } from '../../helpers' | ||
4 | import { isHostValid } from '../../helpers/custom-validators/servers' | 3 | import { isHostValid } from '../../helpers/custom-validators/servers' |
4 | import { logger } from '../../helpers/logger' | ||
5 | import { SERVERS_SCORE } from '../../initializers' | 5 | import { SERVERS_SCORE } from '../../initializers' |
6 | import { throwIfNotValid } from '../utils' | 6 | import { throwIfNotValid } from '../utils' |
7 | 7 | ||
diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts index 6db562719..3adcec149 100644 --- a/server/models/video/video-blacklist.ts +++ b/server/models/video/video-blacklist.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { SortType } from '../../helpers' | 2 | import { SortType } from '../../helpers/utils' |
3 | import { getSortOnModel } from '../utils' | 3 | import { getSortOnModel } from '../utils' |
4 | import { VideoModel } from './video' | 4 | import { VideoModel } from './video' |
5 | 5 | ||
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts index 8ceeb563a..1992c2dd8 100644 --- a/server/models/video/video-comment.ts +++ b/server/models/video/video-comment.ts | |||
@@ -5,7 +5,7 @@ import { | |||
5 | } from 'sequelize-typescript' | 5 | } from 'sequelize-typescript' |
6 | import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' | 6 | import { VideoCommentObject } from '../../../shared/models/activitypub/objects/video-comment-object' |
7 | import { VideoComment } from '../../../shared/models/videos/video-comment.model' | 7 | import { VideoComment } from '../../../shared/models/videos/video-comment.model' |
8 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub' | 8 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
9 | import { CONSTRAINTS_FIELDS } from '../../initializers' | 9 | import { CONSTRAINTS_FIELDS } from '../../initializers' |
10 | import { AccountModel } from '../account/account' | 10 | import { AccountModel } from '../account/account' |
11 | import { ActorModel } from '../activitypub/actor' | 11 | import { ActorModel } from '../activitypub/actor' |
@@ -16,6 +16,7 @@ import { VideoModel } from './video' | |||
16 | enum ScopeNames { | 16 | enum ScopeNames { |
17 | WITH_ACCOUNT = 'WITH_ACCOUNT', | 17 | WITH_ACCOUNT = 'WITH_ACCOUNT', |
18 | WITH_IN_REPLY_TO = 'WITH_IN_REPLY_TO', | 18 | WITH_IN_REPLY_TO = 'WITH_IN_REPLY_TO', |
19 | WITH_VIDEO = 'WITH_VIDEO', | ||
19 | ATTRIBUTES_FOR_API = 'ATTRIBUTES_FOR_API' | 20 | ATTRIBUTES_FOR_API = 'ATTRIBUTES_FOR_API' |
20 | } | 21 | } |
21 | 22 | ||
@@ -56,7 +57,15 @@ enum ScopeNames { | |||
56 | include: [ | 57 | include: [ |
57 | { | 58 | { |
58 | model: () => VideoCommentModel, | 59 | model: () => VideoCommentModel, |
59 | as: 'InReplyTo' | 60 | as: 'InReplyToVideoComment' |
61 | } | ||
62 | ] | ||
63 | }, | ||
64 | [ScopeNames.WITH_VIDEO]: { | ||
65 | include: [ | ||
66 | { | ||
67 | model: () => VideoModel, | ||
68 | required: false | ||
60 | } | 69 | } |
61 | ] | 70 | ] |
62 | } | 71 | } |
@@ -108,7 +117,7 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
108 | foreignKey: { | 117 | foreignKey: { |
109 | allowNull: true | 118 | allowNull: true |
110 | }, | 119 | }, |
111 | as: 'InReplyTo', | 120 | as: 'InReplyToVideoComment', |
112 | onDelete: 'CASCADE' | 121 | onDelete: 'CASCADE' |
113 | }) | 122 | }) |
114 | InReplyToVideoComment: VideoCommentModel | 123 | InReplyToVideoComment: VideoCommentModel |
@@ -155,6 +164,20 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
155 | return VideoCommentModel.findOne(query) | 164 | return VideoCommentModel.findOne(query) |
156 | } | 165 | } |
157 | 166 | ||
167 | static loadByIdAndPopulateVideoAndAccountAndReply (id: number, t?: Sequelize.Transaction) { | ||
168 | const query: IFindOptions<VideoCommentModel> = { | ||
169 | where: { | ||
170 | id | ||
171 | } | ||
172 | } | ||
173 | |||
174 | if (t !== undefined) query.transaction = t | ||
175 | |||
176 | return VideoCommentModel | ||
177 | .scope([ ScopeNames.WITH_VIDEO, ScopeNames.WITH_ACCOUNT, ScopeNames.WITH_IN_REPLY_TO ]) | ||
178 | .findOne(query) | ||
179 | } | ||
180 | |||
158 | static loadByUrl (url: string, t?: Sequelize.Transaction) { | 181 | static loadByUrl (url: string, t?: Sequelize.Transaction) { |
159 | const query: IFindOptions<VideoCommentModel> = { | 182 | const query: IFindOptions<VideoCommentModel> = { |
160 | where: { | 183 | where: { |
@@ -238,8 +261,10 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
238 | id: this.url, | 261 | id: this.url, |
239 | content: this.text, | 262 | content: this.text, |
240 | inReplyTo, | 263 | inReplyTo, |
264 | updated: this.updatedAt.toISOString(), | ||
241 | published: this.createdAt.toISOString(), | 265 | published: this.createdAt.toISOString(), |
242 | url: this.url | 266 | url: this.url, |
267 | attributedTo: this.Account.Actor.url | ||
243 | } | 268 | } |
244 | } | 269 | } |
245 | } | 270 | } |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index b6a2ce6b5..2504ae58a 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -5,65 +5,25 @@ import * as parseTorrent from 'parse-torrent' | |||
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import * as Sequelize from 'sequelize' | 6 | import * as Sequelize from 'sequelize' |
7 | import { | 7 | import { |
8 | AfterDestroy, | 8 | AfterDestroy, AllowNull, BelongsTo, BelongsToMany, Column, CreatedAt, DataType, Default, ForeignKey, HasMany, IFindOptions, Is, |
9 | AllowNull, | 9 | IsInt, IsUUID, Min, Model, Scopes, Table, UpdatedAt |
10 | BelongsTo, | ||
11 | BelongsToMany, | ||
12 | Column, | ||
13 | CreatedAt, | ||
14 | DataType, | ||
15 | Default, | ||
16 | ForeignKey, | ||
17 | HasMany, | ||
18 | IFindOptions, | ||
19 | Is, | ||
20 | IsInt, | ||
21 | IsUUID, | ||
22 | Min, | ||
23 | Model, | ||
24 | Scopes, | ||
25 | Table, | ||
26 | UpdatedAt | ||
27 | } from 'sequelize-typescript' | 10 | } from 'sequelize-typescript' |
28 | import { IIncludeOptions } from 'sequelize-typescript/lib/interfaces/IIncludeOptions' | 11 | import { IIncludeOptions } from 'sequelize-typescript/lib/interfaces/IIncludeOptions' |
29 | import { VideoPrivacy, VideoResolution } from '../../../shared' | 12 | import { VideoPrivacy, VideoResolution } from '../../../shared' |
30 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' | 13 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' |
31 | import { Video, VideoDetails } from '../../../shared/models/videos' | 14 | import { Video, VideoDetails } from '../../../shared/models/videos' |
15 | import { activityPubCollection } from '../../helpers/activitypub' | ||
16 | import { createTorrentPromise, renamePromise, statPromise, unlinkPromise, writeFilePromise } from '../../helpers/core-utils' | ||
17 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | ||
32 | import { | 18 | import { |
33 | activityPubCollection, | 19 | isVideoCategoryValid, isVideoDescriptionValid, isVideoDurationValid, isVideoLanguageValid, isVideoLicenceValid, isVideoNameValid, |
34 | createTorrentPromise, | 20 | isVideoNSFWValid, isVideoPrivacyValid |
35 | generateImageFromVideoFile, | ||
36 | getVideoFileHeight, | ||
37 | logger, | ||
38 | renamePromise, | ||
39 | statPromise, | ||
40 | transcode, | ||
41 | unlinkPromise, | ||
42 | writeFilePromise | ||
43 | } from '../../helpers' | ||
44 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub' | ||
45 | import { | ||
46 | isVideoCategoryValid, | ||
47 | isVideoDescriptionValid, | ||
48 | isVideoDurationValid, | ||
49 | isVideoLanguageValid, | ||
50 | isVideoLicenceValid, | ||
51 | isVideoNameValid, | ||
52 | isVideoNSFWValid, | ||
53 | isVideoPrivacyValid | ||
54 | } from '../../helpers/custom-validators/videos' | 21 | } from '../../helpers/custom-validators/videos' |
22 | import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils' | ||
23 | import { logger } from '../../helpers/logger' | ||
55 | import { | 24 | import { |
56 | API_VERSION, | 25 | API_VERSION, CONFIG, CONSTRAINTS_FIELDS, PREVIEWS_SIZE, REMOTE_SCHEME, STATIC_PATHS, THUMBNAILS_SIZE, VIDEO_CATEGORIES, |
57 | CONFIG, | 26 | VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES |
58 | CONSTRAINTS_FIELDS, | ||
59 | PREVIEWS_SIZE, | ||
60 | REMOTE_SCHEME, | ||
61 | STATIC_PATHS, | ||
62 | THUMBNAILS_SIZE, | ||
63 | VIDEO_CATEGORIES, | ||
64 | VIDEO_LANGUAGES, | ||
65 | VIDEO_LICENCES, | ||
66 | VIDEO_PRIVACIES | ||
67 | } from '../../initializers' | 27 | } from '../../initializers' |
68 | import { getAnnounceActivityPubUrl } from '../../lib/activitypub' | 28 | import { getAnnounceActivityPubUrl } from '../../lib/activitypub' |
69 | import { sendDeleteVideo } from '../../lib/activitypub/send' | 29 | import { sendDeleteVideo } from '../../lib/activitypub/send' |
@@ -75,6 +35,7 @@ import { getSort, throwIfNotValid } from '../utils' | |||
75 | import { TagModel } from './tag' | 35 | import { TagModel } from './tag' |
76 | import { VideoAbuseModel } from './video-abuse' | 36 | import { VideoAbuseModel } from './video-abuse' |
77 | import { VideoChannelModel } from './video-channel' | 37 | import { VideoChannelModel } from './video-channel' |
38 | import { VideoCommentModel } from './video-comment' | ||
78 | import { VideoFileModel } from './video-file' | 39 | import { VideoFileModel } from './video-file' |
79 | import { VideoShareModel } from './video-share' | 40 | import { VideoShareModel } from './video-share' |
80 | import { VideoTagModel } from './video-tag' | 41 | import { VideoTagModel } from './video-tag' |
@@ -85,7 +46,8 @@ enum ScopeNames { | |||
85 | WITH_TAGS = 'WITH_TAGS', | 46 | WITH_TAGS = 'WITH_TAGS', |
86 | WITH_FILES = 'WITH_FILES', | 47 | WITH_FILES = 'WITH_FILES', |
87 | WITH_SHARES = 'WITH_SHARES', | 48 | WITH_SHARES = 'WITH_SHARES', |
88 | WITH_RATES = 'WITH_RATES' | 49 | WITH_RATES = 'WITH_RATES', |
50 | WITH_COMMENTS = 'WITH_COMMENTS' | ||
89 | } | 51 | } |
90 | 52 | ||
91 | @Scopes({ | 53 | @Scopes({ |
@@ -151,6 +113,13 @@ enum ScopeNames { | |||
151 | include: [ () => AccountModel ] | 113 | include: [ () => AccountModel ] |
152 | } | 114 | } |
153 | ] | 115 | ] |
116 | }, | ||
117 | [ScopeNames.WITH_COMMENTS]: { | ||
118 | include: [ | ||
119 | { | ||
120 | model: () => VideoCommentModel | ||
121 | } | ||
122 | ] | ||
154 | } | 123 | } |
155 | }) | 124 | }) |
156 | @Table({ | 125 | @Table({ |
@@ -322,6 +291,15 @@ export class VideoModel extends Model<VideoModel> { | |||
322 | }) | 291 | }) |
323 | AccountVideoRates: AccountVideoRateModel[] | 292 | AccountVideoRates: AccountVideoRateModel[] |
324 | 293 | ||
294 | @HasMany(() => VideoCommentModel, { | ||
295 | foreignKey: { | ||
296 | name: 'videoId', | ||
297 | allowNull: false | ||
298 | }, | ||
299 | onDelete: 'cascade' | ||
300 | }) | ||
301 | VideoComments: VideoCommentModel[] | ||
302 | |||
325 | @AfterDestroy | 303 | @AfterDestroy |
326 | static removeFilesAndSendDelete (instance: VideoModel) { | 304 | static removeFilesAndSendDelete (instance: VideoModel) { |
327 | const tasks = [] | 305 | const tasks = [] |
@@ -417,7 +395,8 @@ export class VideoModel extends Model<VideoModel> { | |||
417 | include: [ AccountModel ] | 395 | include: [ AccountModel ] |
418 | }, | 396 | }, |
419 | VideoFileModel, | 397 | VideoFileModel, |
420 | TagModel | 398 | TagModel, |
399 | VideoCommentModel | ||
421 | ] | 400 | ] |
422 | } | 401 | } |
423 | 402 | ||
@@ -536,7 +515,7 @@ export class VideoModel extends Model<VideoModel> { | |||
536 | } | 515 | } |
537 | 516 | ||
538 | return VideoModel | 517 | return VideoModel |
539 | .scope([ ScopeNames.WITH_RATES, ScopeNames.WITH_SHARES, ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT ]) | 518 | .scope([ ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT ]) |
540 | .findById(id, options) | 519 | .findById(id, options) |
541 | } | 520 | } |
542 | 521 | ||
@@ -561,7 +540,27 @@ export class VideoModel extends Model<VideoModel> { | |||
561 | } | 540 | } |
562 | 541 | ||
563 | return VideoModel | 542 | return VideoModel |
564 | .scope([ ScopeNames.WITH_RATES, ScopeNames.WITH_SHARES, ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT ]) | 543 | .scope([ ScopeNames.WITH_TAGS, ScopeNames.WITH_FILES, ScopeNames.WITH_ACCOUNT ]) |
544 | .findOne(options) | ||
545 | } | ||
546 | |||
547 | static loadAndPopulateAll (id: number) { | ||
548 | const options = { | ||
549 | order: [ [ 'Tags', 'name', 'ASC' ] ], | ||
550 | where: { | ||
551 | id | ||
552 | } | ||
553 | } | ||
554 | |||
555 | return VideoModel | ||
556 | .scope([ | ||
557 | ScopeNames.WITH_RATES, | ||
558 | ScopeNames.WITH_SHARES, | ||
559 | ScopeNames.WITH_TAGS, | ||
560 | ScopeNames.WITH_FILES, | ||
561 | ScopeNames.WITH_ACCOUNT, | ||
562 | ScopeNames.WITH_COMMENTS | ||
563 | ]) | ||
565 | .findOne(options) | 564 | .findOne(options) |
566 | } | 565 | } |
567 | 566 | ||
@@ -865,6 +864,17 @@ export class VideoModel extends Model<VideoModel> { | |||
865 | sharesObject = activityPubCollection(shares) | 864 | sharesObject = activityPubCollection(shares) |
866 | } | 865 | } |
867 | 866 | ||
867 | let commentsObject | ||
868 | if (Array.isArray(this.VideoComments)) { | ||
869 | const comments: string[] = [] | ||
870 | |||
871 | for (const videoComment of this.VideoComments) { | ||
872 | comments.push(videoComment.url) | ||
873 | } | ||
874 | |||
875 | commentsObject = activityPubCollection(comments) | ||
876 | } | ||
877 | |||
868 | const url = [] | 878 | const url = [] |
869 | for (const file of this.VideoFiles) { | 879 | for (const file of this.VideoFiles) { |
870 | url.push({ | 880 | url.push({ |
@@ -925,6 +935,7 @@ export class VideoModel extends Model<VideoModel> { | |||
925 | likes: likesObject, | 935 | likes: likesObject, |
926 | dislikes: dislikesObject, | 936 | dislikes: dislikesObject, |
927 | shares: sharesObject, | 937 | shares: sharesObject, |
938 | comments: commentsObject, | ||
928 | attributedTo: [ | 939 | attributedTo: [ |
929 | { | 940 | { |
930 | type: 'Group', | 941 | type: 'Group', |