strapi-plugin-comments 1.0.4 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.circleci/config.yml +2 -2
- package/README.md +47 -125
- package/admin/src/components/ConfirmationDialog/index.js +56 -0
- package/admin/src/components/DiscussionStatus/index.js +33 -0
- package/admin/src/components/DiscussionThread/index.js +58 -0
- package/admin/src/components/DiscussionThreadItem/index.js +87 -0
- package/admin/src/components/DiscussionThreadItem/styles.js +87 -0
- package/admin/src/components/DiscussionThreadItemActions/index.js +176 -0
- package/admin/src/components/DiscussionThreadItemActions/styles.js +22 -0
- package/admin/src/components/DiscussionThreadItemMeta/index.js +31 -0
- package/admin/src/components/DiscussionThreadItemWarnings/index.js +137 -0
- package/admin/src/components/DiscussionThreadItemWarnings/styles.js +7 -0
- package/admin/src/components/EntityDetails/index.js +53 -0
- package/admin/src/{containers → components}/Initializer/index.js +4 -4
- package/admin/src/components/LoadingIndicatorOverlay/index.js +15 -0
- package/admin/src/components/LoadingIndicatorOverlay/styles.js +20 -0
- package/admin/src/components/Nav/index.js +45 -0
- package/admin/src/components/PluginIcon/index.js +12 -0
- package/admin/src/components/ReportsReviewModal/index.js +45 -0
- package/admin/src/components/ReportsReviewTable/index.js +142 -0
- package/admin/src/components/TableFilters/index.js +53 -0
- package/admin/src/components/TablePagination/index.js +35 -0
- package/admin/src/components/icons/comments.js +9 -0
- package/admin/src/index.js +56 -42
- package/admin/src/pages/App/index.js +32 -0
- package/admin/src/pages/ComingSoonPage/index.js +25 -0
- package/admin/src/pages/Details/index.js +109 -0
- package/admin/src/pages/Details/utils/api.js +41 -0
- package/admin/src/pages/Discover/index.js +185 -0
- package/admin/src/pages/Discover/utils/api.js +23 -0
- package/admin/src/pages/Discover/utils/filtersSchema.js +9 -0
- package/admin/src/permissions.js +13 -37
- package/admin/src/pluginId.js +2 -4
- package/admin/src/translations/en.json +72 -37
- package/admin/src/translations/fr.json +1 -0
- package/admin/src/utils/axiosInstance.js +35 -0
- package/admin/src/utils/constants.js +25 -6
- package/admin/src/utils/getApiUrl.js +5 -0
- package/admin/src/utils/getMessage.js +14 -0
- package/admin/src/utils/getRelatedGroups.js +5 -0
- package/admin/src/utils/getUrl.js +5 -0
- package/admin/src/utils/handleAPIError.js +15 -0
- package/admin/src/utils/index.js +6 -0
- package/codecov.yml +2 -1
- package/content-types/comment.js +97 -0
- package/content-types/index.js +11 -0
- package/content-types/report.js +54 -0
- package/package.json +11 -25
- package/server/bootstrap.js +5 -0
- package/server/config/index.js +6 -0
- package/server/controllers/admin.js +150 -0
- package/server/controllers/client.js +109 -0
- package/server/controllers/index.js +9 -0
- package/{controllers → server/controllers}/utils/__tests__/functions.test.js +0 -0
- package/{controllers → server/controllers}/utils/functions.js +1 -1
- package/server/destroy.js +5 -0
- package/server/index.js +22 -0
- package/server/register.js +5 -0
- package/server/routes/admin.js +92 -0
- package/server/routes/client.js +52 -0
- package/server/routes/index.js +15 -0
- package/server/services/admin.js +180 -0
- package/server/services/client.js +227 -0
- package/server/services/common.js +191 -0
- package/server/services/index.js +11 -0
- package/{services → server/services}/utils/__tests__/functions.test.js +0 -0
- package/{services → server/services}/utils/functions.js +17 -6
- package/server/utils/constants.js +16 -0
- package/{services → server}/utils/error.js +0 -0
- package/server/utils/functions.js +19 -0
- package/strapi-admin.js +3 -0
- package/strapi-server.js +9 -0
- package/admin/src/assets/images/icon-cross-blue.svg +0 -1
- package/admin/src/assets/images/icon_remove.svg +0 -19
- package/admin/src/assets/images/logo.svg +0 -5
- package/admin/src/components/AbuseReportsPopUp/ItemPreviewContainer.js +0 -8
- package/admin/src/components/AbuseReportsPopUp/ModalFooter.js +0 -31
- package/admin/src/components/AbuseReportsPopUp/TableContainer.js +0 -63
- package/admin/src/components/AbuseReportsPopUp/index.js +0 -148
- package/admin/src/components/Container/index.js +0 -7
- package/admin/src/components/EmptyView/index.js +0 -25
- package/admin/src/components/Filters/index.js +0 -151
- package/admin/src/components/Filters/index.styled.js +0 -82
- package/admin/src/components/Filters/utils.js +0 -21
- package/admin/src/components/Item/CardItem.js +0 -30
- package/admin/src/components/Item/CardWrapper.js +0 -10
- package/admin/src/components/Item/index.js +0 -107
- package/admin/src/components/ItemDetails/CardItem.js +0 -33
- package/admin/src/components/ItemDetails/CardLevelCounter.js +0 -46
- package/admin/src/components/ItemDetails/CardLevelCounterLink.js +0 -24
- package/admin/src/components/ItemDetails/CardLevelWrapper.js +0 -22
- package/admin/src/components/ItemDetails/CardWrapper.js +0 -52
- package/admin/src/components/ItemDetails/index.js +0 -167
- package/admin/src/components/ItemFooter/CardItemAuthor.js +0 -10
- package/admin/src/components/ItemFooter/CardItemRelation.js +0 -18
- package/admin/src/components/ItemFooter/Wrapper.js +0 -20
- package/admin/src/components/ItemFooter/index.js +0 -87
- package/admin/src/components/ItemHeader/CardHeaderBlocked.js +0 -24
- package/admin/src/components/ItemHeader/CardHeaderIndicator.js +0 -30
- package/admin/src/components/ItemHeader/CardHeaderIndicatorBlue.js +0 -11
- package/admin/src/components/ItemHeader/CardHeaderIndicatorRed.js +0 -11
- package/admin/src/components/ItemHeader/CardHeaderIndicatorsContainer.js +0 -18
- package/admin/src/components/ItemHeader/CardHeaderPending.js +0 -11
- package/admin/src/components/ItemHeader/CardHeaderReports.js +0 -40
- package/admin/src/components/ItemHeader/Wrapper.js +0 -16
- package/admin/src/components/ItemHeader/index.js +0 -97
- package/admin/src/components/ItemModeration/Wrapper.js +0 -19
- package/admin/src/components/ItemModeration/index.js +0 -84
- package/admin/src/components/List/Container.js +0 -20
- package/admin/src/components/List/index.js +0 -21
- package/admin/src/components/Search/index.js +0 -83
- package/admin/src/containers/App/Wrapper.js +0 -14
- package/admin/src/containers/App/index.js +0 -47
- package/admin/src/containers/DataManagerProvider/actions.js +0 -15
- package/admin/src/containers/DataManagerProvider/index.js +0 -332
- package/admin/src/containers/DataManagerProvider/init.js +0 -5
- package/admin/src/containers/DataManagerProvider/reducer.js +0 -59
- package/admin/src/containers/DetailsView/Wrapper.js +0 -18
- package/admin/src/containers/DetailsView/index.js +0 -106
- package/admin/src/containers/ListView/FadedWrapper.js +0 -42
- package/admin/src/containers/ListView/Footer.js +0 -53
- package/admin/src/containers/ListView/Wrapper.js +0 -17
- package/admin/src/containers/ListView/components.js +0 -138
- package/admin/src/containers/ListView/index.js +0 -45
- package/admin/src/containers/Panel/actions.js +0 -5
- package/admin/src/containers/Panel/index.js +0 -191
- package/admin/src/containers/Panel/index.styled.js +0 -6
- package/admin/src/containers/Panel/reducer.js +0 -44
- package/admin/src/contexts/DataManagerContext.js +0 -5
- package/admin/src/hooks/useDataManager.js +0 -6
- package/admin/src/lifecycles.js +0 -3
- package/admin/src/translations/index.js +0 -7
- package/admin/src/utils/getTrad.js +0 -5
- package/admin/src/utils/getTradId.js +0 -5
- package/config/functions/bootstrap.js +0 -62
- package/config/routes.json +0 -142
- package/config/schema.graphql.js +0 -268
- package/controllers/comments.js +0 -228
- package/models/comment.js +0 -8
- package/models/comment.settings.json +0 -102
- package/models/report.js +0 -8
- package/models/report.settings.json +0 -49
- package/services/comments.js +0 -539
- package/services/utils/constants.js +0 -9
package/services/comments.js
DELETED
|
@@ -1,539 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { first, isEmpty, isNil, map, uniq, get } = require('lodash');
|
|
4
|
-
const { sanitizeEntity } = require('strapi-utils');
|
|
5
|
-
const PluginError = require('./utils/error');
|
|
6
|
-
const {
|
|
7
|
-
isEqualEntity,
|
|
8
|
-
extractMeta,
|
|
9
|
-
buildNestedStructure,
|
|
10
|
-
checkBadWords,
|
|
11
|
-
filterOurResolvedReports,
|
|
12
|
-
convertContentTypeNameToSlug,
|
|
13
|
-
isValidUserContext,
|
|
14
|
-
resolveUserContextError,
|
|
15
|
-
} = require('./utils/functions');
|
|
16
|
-
const { APPROVAL_STATUS } = require('./utils/constants')
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* comments.js service
|
|
20
|
-
*
|
|
21
|
-
* @description: A set of functions similar to controller's actions to avoid code duplication.
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
module.exports = {
|
|
25
|
-
getAssociationModel(ormModel, related) {
|
|
26
|
-
const relatedAssociation = ormModel.associations.find(_ => _.alias === 'related');
|
|
27
|
-
if (relatedAssociation) {
|
|
28
|
-
return relatedAssociation.related.find(rel => rel.collectionName === related);
|
|
29
|
-
}
|
|
30
|
-
return null;
|
|
31
|
-
},
|
|
32
|
-
|
|
33
|
-
getMorphModel() {
|
|
34
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
35
|
-
const ormModel = strapi.query(model.modelName, pluginName);
|
|
36
|
-
return ormModel.model.morph;
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
isMongoDB(){
|
|
40
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
41
|
-
const orm = strapi.query(model.modelName, pluginName).model.orm;
|
|
42
|
-
return orm === 'mongoose';
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
getMorphData(query) {
|
|
46
|
-
return this
|
|
47
|
-
.getMorphModel()
|
|
48
|
-
.query(function () {
|
|
49
|
-
this.where(query);
|
|
50
|
-
})
|
|
51
|
-
.fetchAll()
|
|
52
|
-
.then((entities) => entities.map(entity => entity.toJSON()));
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
// Find all comments
|
|
56
|
-
async findAll({ related, entity, ...query }) {
|
|
57
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
58
|
-
const paginationEnabled = !isNil(query._start);
|
|
59
|
-
const ormModel = strapi.query(model.modelName, pluginName);
|
|
60
|
-
let relatedCommentIds = [];
|
|
61
|
-
if (related) {
|
|
62
|
-
const associationModel = this.getAssociationModel(ormModel, related);
|
|
63
|
-
if (associationModel) {
|
|
64
|
-
if (this.isMongoDB()) {
|
|
65
|
-
const [fieldName] = Object.entries(associationModel.allAttributes).find(([, value]) => value.plugin === 'comments' && value.collection === 'comment') || [];
|
|
66
|
-
if (!fieldName){
|
|
67
|
-
throw strapi.errors.badRequest();
|
|
68
|
-
}
|
|
69
|
-
const query = { [fieldName]: { $exists: true, $not: { $size: 0 } } };
|
|
70
|
-
if (entity){
|
|
71
|
-
query._id = entity;
|
|
72
|
-
}
|
|
73
|
-
relatedCommentIds = await associationModel
|
|
74
|
-
.find(query)
|
|
75
|
-
.then((entry) => entry.map(_ => _.toJSON()[fieldName]))
|
|
76
|
-
.then(entry => entry.flatMap(comments => comments.map(comment => comment.id)));
|
|
77
|
-
} else {
|
|
78
|
-
const morphQuery = entity
|
|
79
|
-
? { related_type: related, related_id: entity }
|
|
80
|
-
: { related_type: related };
|
|
81
|
-
relatedCommentIds = await this
|
|
82
|
-
.getMorphData(morphQuery)
|
|
83
|
-
.then(_ => _.map(entity => entity.comments_id));
|
|
84
|
-
}
|
|
85
|
-
} else {
|
|
86
|
-
throw strapi.errors.badRequest();
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
// we not find any content to related data
|
|
90
|
-
if (related && relatedCommentIds.length === 0) {
|
|
91
|
-
return {
|
|
92
|
-
items: [],
|
|
93
|
-
total: 0,
|
|
94
|
-
page: 1,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const params = {
|
|
99
|
-
...query,
|
|
100
|
-
_sort: paginationEnabled ? query._sort || 'created_at:desc' : 'created_at:desc',
|
|
101
|
-
};
|
|
102
|
-
if (relatedCommentIds.length) {
|
|
103
|
-
params.id = relatedCommentIds;
|
|
104
|
-
}
|
|
105
|
-
const entities = query && query._q ?
|
|
106
|
-
await ormModel.search(params, ['authorUser', 'related', 'reports']) :
|
|
107
|
-
await ormModel.find(params, ['authorUser', 'related', 'reports']);
|
|
108
|
-
const items = entities.map(_ => filterOurResolvedReports(this.sanitizeCommentEntity(_)));
|
|
109
|
-
const total = paginationEnabled ?
|
|
110
|
-
query && query._q ?
|
|
111
|
-
await ormModel.countSearch(params) :
|
|
112
|
-
await ormModel.count(params) :
|
|
113
|
-
items.length;
|
|
114
|
-
return {
|
|
115
|
-
items,
|
|
116
|
-
total,
|
|
117
|
-
page: paginationEnabled ? query._start / query._limit : undefined,
|
|
118
|
-
};
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
// Find comments in the flat structure
|
|
122
|
-
async findAllFlat(relation, query, isAdmin = false) {
|
|
123
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
124
|
-
let baseCriteria = {};
|
|
125
|
-
if (relation) {
|
|
126
|
-
baseCriteria = {
|
|
127
|
-
...baseCriteria,
|
|
128
|
-
_or: [{ removed_null: true }, { removed: false }],
|
|
129
|
-
relatedSlug: relation,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
let criteria = {
|
|
133
|
-
...baseCriteria,
|
|
134
|
-
_or: [{ removed_null: true }, { removed: false }],
|
|
135
|
-
threadOf_null: true,
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
if (isAdmin){
|
|
139
|
-
delete baseCriteria._or;
|
|
140
|
-
delete criteria._or;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (query) {
|
|
144
|
-
criteria = {
|
|
145
|
-
...criteria,
|
|
146
|
-
...query,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const entitiesRoot = query && query._q ?
|
|
151
|
-
await strapi.query( model.modelName, pluginName)
|
|
152
|
-
.search(criteria, ['authorUser', 'related', 'reports']) :
|
|
153
|
-
await strapi.query( model.modelName, pluginName)
|
|
154
|
-
.find(criteria, ['authorUser', 'related', 'reports']);
|
|
155
|
-
const entitiesNested = await strapi.query( model.modelName, pluginName)
|
|
156
|
-
.find({
|
|
157
|
-
...baseCriteria,
|
|
158
|
-
threadOf_null: false,
|
|
159
|
-
}, ['authorUser', 'related', 'reports']);
|
|
160
|
-
return [...entitiesRoot, ...entitiesNested].map(_ => filterOurResolvedReports(this.sanitizeCommentEntity(_)));
|
|
161
|
-
},
|
|
162
|
-
|
|
163
|
-
// Find comments and create relations tree structure
|
|
164
|
-
async findAllInHierarchy ({
|
|
165
|
-
relation,
|
|
166
|
-
query,
|
|
167
|
-
startingFromId = null,
|
|
168
|
-
dropBlockedThreads = false,
|
|
169
|
-
isAdmin = false,
|
|
170
|
-
}) {
|
|
171
|
-
const entities = await this.findAllFlat(relation, query, isAdmin);
|
|
172
|
-
return buildNestedStructure(entities, startingFromId, 'threadOf', dropBlockedThreads, false, this.isMongoDB());
|
|
173
|
-
},
|
|
174
|
-
|
|
175
|
-
// Find single comment
|
|
176
|
-
async findOne(id, relation) {
|
|
177
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
178
|
-
let criteria = { id };
|
|
179
|
-
if (relation) {
|
|
180
|
-
criteria = {
|
|
181
|
-
...criteria,
|
|
182
|
-
relatedSlug: relation,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
const entity = await strapi.query( model.modelName, pluginName)
|
|
186
|
-
.findOne(criteria, ['related', 'reports']);
|
|
187
|
-
if (!entity){
|
|
188
|
-
throw strapi.errors.notFound();
|
|
189
|
-
}
|
|
190
|
-
return filterOurResolvedReports(this.sanitizeCommentEntity(entity));
|
|
191
|
-
},
|
|
192
|
-
|
|
193
|
-
checkEntityRelation(parsedRelation) {
|
|
194
|
-
const result = parsedRelation.some(ref => {
|
|
195
|
-
const model = strapi.query(ref);
|
|
196
|
-
return model && model.associations.length &&
|
|
197
|
-
model.associations.some(_ => _.plugin === 'comments' && _.collection === 'comment');
|
|
198
|
-
});
|
|
199
|
-
if (!result) {
|
|
200
|
-
throw new PluginError(400, 'Comment not have relation to this type content type.');
|
|
201
|
-
}
|
|
202
|
-
},
|
|
203
|
-
|
|
204
|
-
// Create a comment
|
|
205
|
-
async create(data, user = undefined) {
|
|
206
|
-
const { content, related } = data;
|
|
207
|
-
const parsedRelation = related && related instanceof Array ? related : [related];
|
|
208
|
-
const singleRelationFulfilled = related && (parsedRelation.length === 1);
|
|
209
|
-
if(!singleRelationFulfilled){
|
|
210
|
-
throw new PluginError(400, 'Can relate only one content type');
|
|
211
|
-
}
|
|
212
|
-
const relation = parsedRelation.map(({refId, ref})=> `${ref}:${refId}`).toString();
|
|
213
|
-
this.checkEntityRelation(map(parsedRelation, 'ref'));
|
|
214
|
-
|
|
215
|
-
const linkToThread = !isNil(data.threadOf) ? !!await this.findOne(data.threadOf,relation) : true;
|
|
216
|
-
const validContext = isValidUserContext(user);
|
|
217
|
-
|
|
218
|
-
if (!linkToThread) {
|
|
219
|
-
throw new PluginError(400, 'Thread does not exist');
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (!validContext) {
|
|
223
|
-
throw resolveUserContextError(user);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (checkBadWords(content) && singleRelationFulfilled) {
|
|
227
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
228
|
-
const { authorId, authorEmail, authorName, authorUser, ...rest } = data;
|
|
229
|
-
let authorData = {};
|
|
230
|
-
if (validContext && user) {
|
|
231
|
-
authorData = {
|
|
232
|
-
authorUser: user.id ? user.id : authorUser,
|
|
233
|
-
};
|
|
234
|
-
} else if (authorUser) {
|
|
235
|
-
authorData = {
|
|
236
|
-
authorUser,
|
|
237
|
-
};
|
|
238
|
-
} else {
|
|
239
|
-
const emailValidation = new RegExp(/\S+@\S+\.\S+/);
|
|
240
|
-
if (authorEmail && !emailValidation.test(authorEmail)) {
|
|
241
|
-
throw new PluginError(400, 'Author e-mail is not valid value. Key: \'authorEmail\'');
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
authorData = {
|
|
245
|
-
authorId,
|
|
246
|
-
authorName,
|
|
247
|
-
authorEmail,
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const relationEntity = await Promise
|
|
252
|
-
.all(parsedRelation.map(rel => strapi.query(rel.ref).findOne({ id: rel.refId })));
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
relationEntity.forEach((rel) => {
|
|
256
|
-
if (!rel) {
|
|
257
|
-
throw new PluginError(404, 'Related entity not found');
|
|
258
|
-
}
|
|
259
|
-
if (
|
|
260
|
-
rel.requireCommentsApproval &&
|
|
261
|
-
data.approvalStatus !== APPROVAL_STATUS.PENDING
|
|
262
|
-
) {
|
|
263
|
-
throw new PluginError(400, 'Invalid approval status');
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
const entity = await strapi.query( model.modelName, pluginName).create({
|
|
268
|
-
...rest,
|
|
269
|
-
...authorData,
|
|
270
|
-
relatedSlug: relation,
|
|
271
|
-
related: parsedRelation
|
|
272
|
-
});
|
|
273
|
-
return this.sanitizeCommentEntity(entity);
|
|
274
|
-
}
|
|
275
|
-
throw new PluginError(400, 'No content received');
|
|
276
|
-
},
|
|
277
|
-
|
|
278
|
-
// Update a comment
|
|
279
|
-
async update(id, data, user) {
|
|
280
|
-
const { content, related } = data;
|
|
281
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
282
|
-
const parsedRelation = related && related instanceof Array ? related : [related];
|
|
283
|
-
const singleRelationFulfilled = related && (parsedRelation.length === 1);
|
|
284
|
-
if(!singleRelationFulfilled){
|
|
285
|
-
throw new PluginError(400, 'Can relate only one content type');
|
|
286
|
-
}
|
|
287
|
-
const relation = parsedRelation.map(({refId, ref})=> `${ref}:${refId}`).toString();
|
|
288
|
-
const existingEntity = await this.findOne(id, relation);
|
|
289
|
-
const validContext = isValidUserContext(user);
|
|
290
|
-
|
|
291
|
-
if (!validContext) {
|
|
292
|
-
throw resolveUserContextError(user);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (isEqualEntity(existingEntity, data, user) && content) {
|
|
296
|
-
if (checkBadWords(content)) {
|
|
297
|
-
const entity = await strapi.query( model.modelName, pluginName).update(
|
|
298
|
-
{ id },
|
|
299
|
-
{ content }
|
|
300
|
-
);
|
|
301
|
-
return this.sanitizeCommentEntity(entity);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
throw new PluginError(409, 'Action on that entity is not allowed');
|
|
305
|
-
},
|
|
306
|
-
|
|
307
|
-
// Points up for comment
|
|
308
|
-
async pointsUp(id, relation, user) {
|
|
309
|
-
if (!isValidUserContext(user)) {
|
|
310
|
-
throw resolveUserContextError(user);
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
314
|
-
const existingEntity = await this.findOne(id, relation);
|
|
315
|
-
if (existingEntity) {
|
|
316
|
-
const entity = await strapi.query( model.modelName, pluginName).update(
|
|
317
|
-
{ id },
|
|
318
|
-
{
|
|
319
|
-
points: (existingEntity.points || 0) + 1,
|
|
320
|
-
}
|
|
321
|
-
);
|
|
322
|
-
return this.sanitizeCommentEntity(entity);
|
|
323
|
-
}
|
|
324
|
-
throw new PluginError(409, 'Action on that entity is not allowed');
|
|
325
|
-
},
|
|
326
|
-
|
|
327
|
-
// Report abuse in comment
|
|
328
|
-
async reportAbuse(id, relation, payload, user) {
|
|
329
|
-
if (!isValidUserContext(user)) {
|
|
330
|
-
throw resolveUserContextError(user);
|
|
331
|
-
}
|
|
332
|
-
const { pluginName, plugin } = extractMeta(strapi.plugins);
|
|
333
|
-
const { report: reportModel } = plugin.models;
|
|
334
|
-
const existingEntity = await this.findOne(id, relation);
|
|
335
|
-
if (existingEntity) {
|
|
336
|
-
await this.sendAbuseReportEmail(payload.reason, payload.content); // Could also add some info about relation
|
|
337
|
-
return strapi.query(reportModel.modelName, pluginName).create({
|
|
338
|
-
...payload,
|
|
339
|
-
resolved: false,
|
|
340
|
-
related: id,
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
throw new PluginError(409, 'Action on that entity is not allowed');
|
|
344
|
-
},
|
|
345
|
-
|
|
346
|
-
async markAsRemoved(relationId, commentId, authorId){
|
|
347
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
348
|
-
const entity = await this.findOne(
|
|
349
|
-
commentId,
|
|
350
|
-
relationId,
|
|
351
|
-
{ _or: [{ authorUser: authorId }, { authorId }] },
|
|
352
|
-
);
|
|
353
|
-
if (!entity){
|
|
354
|
-
throw strapi.errors.notFound('Entity not exist');
|
|
355
|
-
}
|
|
356
|
-
return strapi
|
|
357
|
-
.query(model.modelName, pluginName)
|
|
358
|
-
.update({ id: commentId, relatedSlug: relationId }, { removed: true })
|
|
359
|
-
.then(({ id }) => this.markAsRemovedNested(id, true))
|
|
360
|
-
.then(() => ({ id: commentId }));
|
|
361
|
-
},
|
|
362
|
-
|
|
363
|
-
//
|
|
364
|
-
// Moderation
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
// Find single comment
|
|
368
|
-
async findOneAndThread(id) {
|
|
369
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
370
|
-
const entity = await strapi.query( model.modelName, pluginName).findOne({ id }, ['threadOf', 'threadOf.reports', 'authorUser', 'related', 'reports']);
|
|
371
|
-
if (!entity){
|
|
372
|
-
throw new PluginError(404, 'Not found');
|
|
373
|
-
}
|
|
374
|
-
const relatedEntity = !isEmpty(entity.related) ? first(entity.related) : null;
|
|
375
|
-
const relation = relatedEntity ? `${convertContentTypeNameToSlug(relatedEntity.__contentType).toLowerCase()}:${relatedEntity.id}` : null;
|
|
376
|
-
const entitiesOnSameLevel = await this.findAllInHierarchy({
|
|
377
|
-
relation,
|
|
378
|
-
startingFromId: entity.threadOf ? entity.threadOf.id : null,
|
|
379
|
-
isAdmin: true
|
|
380
|
-
})
|
|
381
|
-
const selectedEntity = filterOurResolvedReports(this.sanitizeCommentEntity(entity));
|
|
382
|
-
return {
|
|
383
|
-
selected: {
|
|
384
|
-
...selectedEntity,
|
|
385
|
-
threadOf: selectedEntity.threadOf ? filterOurResolvedReports(selectedEntity.threadOf) : null,
|
|
386
|
-
},
|
|
387
|
-
level: entitiesOnSameLevel.map(_ => filterOurResolvedReports(_))
|
|
388
|
-
};
|
|
389
|
-
},
|
|
390
|
-
|
|
391
|
-
// Block / Unblock a comment
|
|
392
|
-
async blockComment(id) {
|
|
393
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
394
|
-
const existingEntity = await this.findOne(id);
|
|
395
|
-
const changedEntity = await strapi.query( model.modelName, pluginName).update(
|
|
396
|
-
{ id },
|
|
397
|
-
{ blocked: !existingEntity.blocked }
|
|
398
|
-
);
|
|
399
|
-
return this.sanitizeCommentEntity(changedEntity);
|
|
400
|
-
},
|
|
401
|
-
|
|
402
|
-
// Block / Unblock a comment thread
|
|
403
|
-
async blockCommentThread(id) {
|
|
404
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
405
|
-
const existingEntity = await this.findOne(id);
|
|
406
|
-
const changedEntity = await strapi.query( model.modelName, pluginName).update(
|
|
407
|
-
{ id },
|
|
408
|
-
{ blockedThread: !existingEntity.blockedThread }
|
|
409
|
-
);
|
|
410
|
-
await this.blockNestedThreads(id, changedEntity.blockedThread)
|
|
411
|
-
return this.sanitizeCommentEntity(changedEntity);
|
|
412
|
-
},
|
|
413
|
-
|
|
414
|
-
// Approve comment
|
|
415
|
-
async approveComment(id) {
|
|
416
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
417
|
-
const changedEntity = await strapi
|
|
418
|
-
.query(model.modelName, pluginName)
|
|
419
|
-
.update({ id }, { approvalStatus: APPROVAL_STATUS.APPROVED });
|
|
420
|
-
|
|
421
|
-
return this.sanitizeCommentEntity(changedEntity);
|
|
422
|
-
},
|
|
423
|
-
|
|
424
|
-
async rejectComment(id) {
|
|
425
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
426
|
-
const changedEntity = await strapi
|
|
427
|
-
.query(model.modelName, pluginName)
|
|
428
|
-
.update({ id }, { approvalStatus: APPROVAL_STATUS.REJECTED });
|
|
429
|
-
|
|
430
|
-
return this.sanitizeCommentEntity(changedEntity);
|
|
431
|
-
},
|
|
432
|
-
|
|
433
|
-
markAsRemovedNested(id, status){
|
|
434
|
-
return this.modifiedNestedNestedComments(id, 'removed', status);
|
|
435
|
-
},
|
|
436
|
-
|
|
437
|
-
blockNestedThreads(id, blockStatus){
|
|
438
|
-
return this.modifiedNestedNestedComments(id, 'blockedThread', blockStatus)
|
|
439
|
-
},
|
|
440
|
-
|
|
441
|
-
async modifiedNestedNestedComments(id, fieldName, value) {
|
|
442
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
443
|
-
try {
|
|
444
|
-
const entitiesToChange = await strapi.query(model.modelName, pluginName).find({ threadOf: id });
|
|
445
|
-
const changedEntities = await Promise.all(entitiesToChange.map(item => strapi.query(model.modelName, pluginName).update(
|
|
446
|
-
{ id: item.id },
|
|
447
|
-
{ [fieldName]: value }
|
|
448
|
-
)));
|
|
449
|
-
if (changedEntities.length) {
|
|
450
|
-
const nestedTransactions = await Promise.all(
|
|
451
|
-
changedEntities.map(item => this.modifiedNestedNestedComments(item.id, fieldName, value))
|
|
452
|
-
);
|
|
453
|
-
return nestedTransactions.length === changedEntities.length;
|
|
454
|
-
}
|
|
455
|
-
return true;
|
|
456
|
-
} catch (e) {
|
|
457
|
-
return false;
|
|
458
|
-
}
|
|
459
|
-
},
|
|
460
|
-
|
|
461
|
-
async contentTypeName(contentTypeName) {
|
|
462
|
-
const { pluginName, model } = extractMeta(strapi.plugins);
|
|
463
|
-
const ormModel = strapi.query(model.modelName, pluginName);
|
|
464
|
-
const associationModel = this.getAssociationModel(ormModel, contentTypeName);
|
|
465
|
-
const config = get(strapi.config, ['plugins', 'comments', 'relatedContentTypes', contentTypeName], []);
|
|
466
|
-
if (isEmpty(config)) {
|
|
467
|
-
return [];
|
|
468
|
-
}
|
|
469
|
-
if (associationModel) {
|
|
470
|
-
let relatedIds = [];
|
|
471
|
-
let result = [];
|
|
472
|
-
if (this.isMongoDB()) {
|
|
473
|
-
const [fieldName] = Object.entries(associationModel.allAttributes).find(([, value]) => value.plugin === 'comments' && value.collection === 'comment') || [];
|
|
474
|
-
if (!fieldName){
|
|
475
|
-
throw strapi.errors.badRequest();
|
|
476
|
-
}
|
|
477
|
-
result = await associationModel
|
|
478
|
-
.find({ [fieldName]: { $exists: true, $not: { $size: 0 } } })
|
|
479
|
-
.then((entities) => entities.map(_ => _.toJSON()));
|
|
480
|
-
} else {
|
|
481
|
-
relatedIds = await this
|
|
482
|
-
.getMorphData({ related_type: contentTypeName })
|
|
483
|
-
.then(_ => uniq(_.map(entity => entity.related_id)));
|
|
484
|
-
result = await strapi.query(associationModel.uid).find({ id: relatedIds });
|
|
485
|
-
}
|
|
486
|
-
const entries = Object.entries(config);
|
|
487
|
-
return result
|
|
488
|
-
.map(_ =>
|
|
489
|
-
entries
|
|
490
|
-
.reduce((acc, [key, val]) => ({ ...acc, [key]: _[val] }), {}),
|
|
491
|
-
);
|
|
492
|
-
|
|
493
|
-
}
|
|
494
|
-
throw strapi.errors.badRequest();
|
|
495
|
-
},
|
|
496
|
-
|
|
497
|
-
// Resolve reported abuse for comment
|
|
498
|
-
async resolveAbuseReport(id, commentId) {
|
|
499
|
-
const { pluginName, plugin } = extractMeta(strapi.plugins);
|
|
500
|
-
const { report: reportModel } = plugin.models;
|
|
501
|
-
return strapi.query(reportModel.modelName, pluginName).update({
|
|
502
|
-
id,
|
|
503
|
-
related: commentId,
|
|
504
|
-
}, {
|
|
505
|
-
resolved: true,
|
|
506
|
-
});
|
|
507
|
-
},
|
|
508
|
-
|
|
509
|
-
sanitizeCommentEntity: (entity) => ({
|
|
510
|
-
...entity,
|
|
511
|
-
authorUser: sanitizeEntity(entity.authorUser, { model: strapi.plugins['users-permissions'].models.user }),
|
|
512
|
-
}),
|
|
513
|
-
|
|
514
|
-
async sendAbuseReportEmail(reason, content) {
|
|
515
|
-
const pluginName = 'users-permissions';
|
|
516
|
-
const userModel = 'user';
|
|
517
|
-
const rolesToBeNotified = get(strapi.config, "plugins.comments.moderatorRoles", []);
|
|
518
|
-
|
|
519
|
-
const ormModel = strapi.query('user', pluginName);
|
|
520
|
-
const query = { 'role.name': rolesToBeNotified }
|
|
521
|
-
const users = await ormModel.find(query);
|
|
522
|
-
|
|
523
|
-
const moderatorsEmails = users.map(user => user.email);
|
|
524
|
-
const superAdmins = await strapi.query('user', 'admin').find({'roles.id': 1})
|
|
525
|
-
|
|
526
|
-
if (moderatorsEmails.length > 0) {
|
|
527
|
-
strapi.plugins['email'].services.email.send({
|
|
528
|
-
to: moderatorsEmails,
|
|
529
|
-
from: superAdmins[0].email,
|
|
530
|
-
subject: 'New abuse report on comment',
|
|
531
|
-
text: `
|
|
532
|
-
There was a new abuse report on your app.
|
|
533
|
-
Reason: ${reason}
|
|
534
|
-
Message: ${content}
|
|
535
|
-
`,
|
|
536
|
-
}).catch(err => strapi.log(err));
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
};
|