strapi-plugin-comments 2.0.0-beta.6 → 2.0.0
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 +1 -1
- package/README.md +474 -119
- package/__mocks__/initSetup.js +53 -0
- package/admin/src/components/ActionButton/styles.js +10 -0
- package/admin/src/components/DiscussionThread/index.js +3 -7
- package/admin/src/components/DiscussionThreadItem/index.js +25 -74
- package/admin/src/components/DiscussionThreadItem/styles.js +4 -79
- package/admin/src/components/DiscussionThreadItemActions/index.js +82 -40
- package/admin/src/components/DiscussionThreadItemActions/styles.js +1 -12
- package/admin/src/components/DiscussionThreadItemApprovalFlowActions/index.js +2 -3
- package/admin/src/components/DiscussionThreadItemFooter/index.js +47 -0
- package/admin/src/components/DiscussionThreadItemFooter/styles.js +13 -0
- package/admin/src/components/DiscussionThreadItemReviewAction/index.js +131 -0
- package/admin/src/components/IconButton/styles.js +14 -0
- package/admin/src/components/ReportsReviewModal/index.js +1 -1
- package/admin/src/components/ReportsReviewTable/index.js +12 -6
- package/admin/src/components/StatusBadge/index.js +17 -0
- package/admin/src/components/StatusBadge/styles.js +17 -0
- package/admin/src/pages/Details/components/DetailsEntity/index.js +40 -24
- package/admin/src/pages/Details/components/DetailsFilters/index.js +8 -10
- package/admin/src/pages/Details/index.js +9 -15
- package/admin/src/pages/Discover/components/DiscoverTableRow/index.js +36 -66
- package/admin/src/pages/Discover/index.js +4 -1
- package/admin/src/translations/en.json +14 -15
- package/admin/src/utils/index.js +2 -0
- package/admin/src/utils/renderInitials.js +5 -0
- package/admin/src/utils/resolveCommentStatus.js +4 -4
- package/content-types/comment.js +0 -4
- package/package.json +8 -4
- package/public/assets/logo.png +0 -0
- package/public/assets/preview.png +0 -0
- package/server/config/index.js +5 -0
- package/server/controllers/client.js +11 -21
- package/server/controllers/utils/__tests__/functions.test.js +2 -2
- package/server/controllers/utils/parsers.js +19 -0
- package/server/graphql/index.js +26 -0
- package/server/graphql/mutations/createAbuseReport.js +25 -0
- package/server/graphql/mutations/createComment.js +25 -0
- package/server/graphql/mutations/index.js +21 -0
- package/server/graphql/mutations/removeComment.js +25 -0
- package/server/graphql/mutations/updateComment.js +25 -0
- package/server/graphql/queries/findAllFlat.js +29 -0
- package/server/graphql/queries/findAllInHierarchy.js +31 -0
- package/server/graphql/queries/index.js +19 -0
- package/server/graphql/resolvers-config.js +15 -0
- package/server/graphql/types/comment-author.js +9 -0
- package/server/graphql/types/comment-nested.js +16 -0
- package/server/graphql/types/comment.js +15 -0
- package/server/graphql/types/create-comment-author.js +9 -0
- package/server/graphql/types/create-comment.js +9 -0
- package/server/graphql/types/create-report.js +9 -0
- package/server/graphql/types/id.js +24 -0
- package/server/graphql/types/identify-comment-author.js +6 -0
- package/server/graphql/types/index.js +16 -0
- package/server/graphql/types/remove-comment.js +8 -0
- package/server/graphql/types/report-reason.js +10 -0
- package/server/graphql/types/report.js +11 -0
- package/server/graphql/types/update-comment.js +9 -0
- package/server/register.js +3 -1
- package/server/services/__tests__/common.test.js +165 -0
- package/server/services/admin.js +18 -15
- package/server/services/client.js +73 -51
- package/server/services/common.js +42 -14
- package/server/services/graphql.js +190 -0
- package/server/services/index.js +2 -0
- package/server/services/utils/__tests__/functions.test.js +9 -73
- package/server/services/utils/functions.js +30 -27
- package/server/utils/__tests__/functions.test.js +17 -0
- package/admin/src/components/DiscussionThreadItemMeta/index.js +0 -31
- package/admin/src/components/DiscussionThreadItemWarnings/index.js +0 -147
- package/admin/src/components/DiscussionThreadItemWarnings/styles.js +0 -7
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module.exports = ({ nexus }) => nexus.objectType({
|
|
2
|
+
name: "Report",
|
|
3
|
+
definition(t) {
|
|
4
|
+
t.id("id")
|
|
5
|
+
t.nonNull.string("content")
|
|
6
|
+
t.field("reason", { type: 'ReportReason' })
|
|
7
|
+
t.field("related", { type: 'CommentSingle' })
|
|
8
|
+
t.string("createdAt")
|
|
9
|
+
t.string("updatedAt")
|
|
10
|
+
}
|
|
11
|
+
})
|
package/server/register.js
CHANGED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
const PluginError = require('../../utils/error');
|
|
2
|
+
const { getPluginService } = require('../../utils/functions');
|
|
3
|
+
|
|
4
|
+
jest.mock
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const setup = function(config) {
|
|
8
|
+
Object.defineProperty(global, 'strapi', {
|
|
9
|
+
value: require('../../../__mocks__/initSetup')(config),
|
|
10
|
+
writable: true,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
Object.defineProperty(global, 'strapi', {});
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe('Test Comments service functions utils', () => {
|
|
19
|
+
|
|
20
|
+
describe('Get plugin config', () => {
|
|
21
|
+
beforeEach(() => setup({
|
|
22
|
+
test: 'sample',
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
test('Should return config value', () => {
|
|
26
|
+
expect(getPluginService('common').getConfig('test')).toEqual('sample');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('Should return default config prop value if not set', () => {
|
|
30
|
+
expect(getPluginService('common').getConfig('another', 'defaultValue')).toEqual('defaultValue');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('Validate user context', () => {
|
|
35
|
+
beforeEach(() => setup());
|
|
36
|
+
|
|
37
|
+
test('Should context be valid', () => {
|
|
38
|
+
expect(getPluginService('common').isValidUserContext({ id: 1 })).toEqual(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('Should context be invalid', () => {
|
|
42
|
+
expect(getPluginService('common').isValidUserContext({ })).toEqual(false);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('Should use fallback', () => {
|
|
46
|
+
expect(getPluginService('common').isValidUserContext()).toEqual(true);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('Sanitize comments entity', () => {
|
|
51
|
+
beforeEach(() => setup());
|
|
52
|
+
|
|
53
|
+
const sample = {
|
|
54
|
+
id: 1,
|
|
55
|
+
content: 'Test text',
|
|
56
|
+
blocked: false,
|
|
57
|
+
removed: true,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const authorSample = {
|
|
61
|
+
id: 1,
|
|
62
|
+
username: 'Joe Doe',
|
|
63
|
+
email: 'test@example.com',
|
|
64
|
+
password: 'xxx',
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const authorGenericSample = {
|
|
68
|
+
authorId: 1,
|
|
69
|
+
authorName: 'Joe Doe',
|
|
70
|
+
authorEmail: 'test@example.com',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const outputAuthorSample = {
|
|
74
|
+
id: 1,
|
|
75
|
+
name: 'Joe Doe',
|
|
76
|
+
email: 'test@example.com',
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
test('Should return exact the same value', () => {
|
|
80
|
+
const input = { ...sample };
|
|
81
|
+
const output = getPluginService('common').sanitizeCommentEntity(input);
|
|
82
|
+
expect(output).toHaveProperty('id', input.id);
|
|
83
|
+
expect(output).toHaveProperty('content', input.content);
|
|
84
|
+
expect(output).toHaveProperty('blocked', input.blocked);
|
|
85
|
+
expect(output).toHaveProperty('removed', input.removed);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('Should return entity with \'author\' property for generic author', () => {
|
|
89
|
+
const input = {
|
|
90
|
+
...sample,
|
|
91
|
+
...authorGenericSample,
|
|
92
|
+
};
|
|
93
|
+
const output = getPluginService('common').sanitizeCommentEntity(input);
|
|
94
|
+
|
|
95
|
+
expect(output).toHaveProperty('author');
|
|
96
|
+
expect(output).toHaveProperty('author.id', input.authorId);
|
|
97
|
+
expect(output).toHaveProperty('author.id', outputAuthorSample.id);
|
|
98
|
+
expect(output).toHaveProperty('author.name', input.authorName);
|
|
99
|
+
expect(output).toHaveProperty('author.name', outputAuthorSample.name);
|
|
100
|
+
expect(output).toHaveProperty('author.email', input.authorEmail);
|
|
101
|
+
expect(output).toHaveProperty('author.email', outputAuthorSample.email);
|
|
102
|
+
|
|
103
|
+
expect(output).not.toHaveProperty('authorId');
|
|
104
|
+
expect(output).not.toHaveProperty('authorName');
|
|
105
|
+
expect(output).not.toHaveProperty('authorEmail');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('Should return entity with \'author\' property for Strapi author', () => {
|
|
109
|
+
const input = {
|
|
110
|
+
...sample,
|
|
111
|
+
authorUser: authorSample,
|
|
112
|
+
};
|
|
113
|
+
const output = getPluginService('common').sanitizeCommentEntity(input);
|
|
114
|
+
|
|
115
|
+
expect(output).toHaveProperty('author');
|
|
116
|
+
expect(output).toHaveProperty('author.id', input.authorUser.id);
|
|
117
|
+
expect(output).toHaveProperty('author.id', outputAuthorSample.id);
|
|
118
|
+
expect(output).toHaveProperty('author.name', input.authorUser.username);
|
|
119
|
+
expect(output).toHaveProperty('author.name', outputAuthorSample.name);
|
|
120
|
+
expect(output).toHaveProperty('author.email', input.authorUser.email);
|
|
121
|
+
expect(output).toHaveProperty('author.email', outputAuthorSample.email);
|
|
122
|
+
|
|
123
|
+
expect(output).not.toHaveProperty('authorUser');
|
|
124
|
+
|
|
125
|
+
expect(output).not.toHaveProperty('author.password');
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('Bad Words filtering', () => {
|
|
130
|
+
beforeEach(() => setup());
|
|
131
|
+
|
|
132
|
+
const text = 'Lorem ipsum dolor sit fuck amet';
|
|
133
|
+
|
|
134
|
+
test('Should find bad words usage and throw error PluginError', () => {
|
|
135
|
+
expect.assertions(7);
|
|
136
|
+
try {
|
|
137
|
+
getPluginService('common').checkBadWords(text);
|
|
138
|
+
} catch (e) {
|
|
139
|
+
expect(getPluginService('common').getConfig('badWords')).toEqual(undefined);
|
|
140
|
+
expect(e).toBeInstanceOf(PluginError);
|
|
141
|
+
expect(e).toHaveProperty('status', 400);
|
|
142
|
+
expect(e).toHaveProperty('name', 'Strapi:Plugin:Comments');
|
|
143
|
+
expect(e).toHaveProperty('message', 'Bad language used! Please polite your comment...');
|
|
144
|
+
expect(e).toHaveProperty('payload');
|
|
145
|
+
expect(e.payload).toEqual(expect.objectContaining({
|
|
146
|
+
content: {
|
|
147
|
+
original: text,
|
|
148
|
+
filtered: text.replace('fuck', '****'),
|
|
149
|
+
}
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('Bad Words filtering', () => {
|
|
156
|
+
beforeEach(() => setup({ badWords: false }));
|
|
157
|
+
|
|
158
|
+
const text = 'Lorem ipsum dolor sit fuck amet';
|
|
159
|
+
|
|
160
|
+
test('Should skip bad words filtering because of configuration change', () => {
|
|
161
|
+
expect(getPluginService('common').getConfig('badWords')).toEqual(false);
|
|
162
|
+
expect(getPluginService('common').checkBadWords(text)).toEqual(text);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
});
|
package/server/services/admin.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { getPluginService, parseParams } = require('./../utils/functions');
|
|
4
|
-
const { isEmpty, isNil } = require('lodash');
|
|
4
|
+
const { isEmpty, isNil, isNumber, parseInt } = require('lodash');
|
|
5
5
|
const PluginError = require('./../utils/error');
|
|
6
6
|
const {
|
|
7
7
|
getModelUid,
|
|
@@ -117,19 +117,24 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
|
117
117
|
}
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
-
const
|
|
121
|
-
where: {
|
|
122
|
-
id,
|
|
123
|
-
},
|
|
120
|
+
const defaultPopulate = {
|
|
124
121
|
populate: {
|
|
125
122
|
authorUser: true,
|
|
126
123
|
threadOf: {
|
|
127
124
|
populate: {
|
|
125
|
+
authorUser: true,
|
|
128
126
|
...reportsPopulation
|
|
129
127
|
},
|
|
130
128
|
},
|
|
131
129
|
...reportsPopulation,
|
|
132
130
|
},
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const entity = await strapi.db.query(getModelUid('comment')).findOne({
|
|
134
|
+
where: {
|
|
135
|
+
id,
|
|
136
|
+
},
|
|
137
|
+
...defaultPopulate,
|
|
133
138
|
});
|
|
134
139
|
|
|
135
140
|
if (!entity){
|
|
@@ -137,7 +142,8 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
|
137
142
|
}
|
|
138
143
|
|
|
139
144
|
const [relatedUid, relatedStringId] = getRelatedGroups(entity.related);
|
|
140
|
-
const
|
|
145
|
+
const parsedRelatedId = parseInt(relatedStringId);
|
|
146
|
+
const relatedId = isNumber(parsedRelatedId) ? parsedRelatedId : relatedStringId;
|
|
141
147
|
const relatedEntity = await strapi.db.query(relatedUid).findOne({
|
|
142
148
|
where: { id: relatedId }
|
|
143
149
|
}).then(_ => ({
|
|
@@ -158,21 +164,18 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
|
158
164
|
threadOf: levelThreadId,
|
|
159
165
|
related: entity.related,
|
|
160
166
|
},
|
|
161
|
-
|
|
162
|
-
...reportsPopulation,
|
|
163
|
-
},
|
|
167
|
+
...defaultPopulate,
|
|
164
168
|
startingFromId: levelThreadId,
|
|
165
169
|
isAdmin: true
|
|
166
170
|
}, false);
|
|
167
|
-
|
|
168
|
-
|
|
171
|
+
const selectedEntity = this.getCommonService().sanitizeCommentEntity({
|
|
172
|
+
...entity,
|
|
173
|
+
threadOf: entity.threadOf || null,
|
|
174
|
+
});
|
|
169
175
|
|
|
170
176
|
return {
|
|
171
177
|
entity: relatedEntity,
|
|
172
|
-
selected:
|
|
173
|
-
...selectedEntity,
|
|
174
|
-
threadOf: selectedEntity.threadOf ? selectedEntity.threadOf : null,
|
|
175
|
-
},
|
|
178
|
+
selected: selectedEntity,
|
|
176
179
|
level: entitiesOnSameLevel,
|
|
177
180
|
};
|
|
178
181
|
},
|
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const config = require('../config');
|
|
4
4
|
const { getPluginService } = require('./../utils/functions');
|
|
5
|
-
const { isNil, isEmpty } = require('lodash');
|
|
5
|
+
const { isNil, isEmpty, isNumber, parseInt } = require('lodash');
|
|
6
6
|
const PluginError = require('./../utils/error');
|
|
7
7
|
const {
|
|
8
8
|
isEqualEntity,
|
|
9
9
|
getModelUid,
|
|
10
10
|
getRelatedGroups,
|
|
11
|
-
checkBadWords,
|
|
12
|
-
isValidUserContext,
|
|
13
11
|
resolveUserContextError,
|
|
14
12
|
} = require('./utils/functions');
|
|
15
13
|
const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
@@ -34,7 +32,8 @@ module.exports = ({ strapi }) => ({
|
|
|
34
32
|
}
|
|
35
33
|
|
|
36
34
|
const [ uid, relatedStringId ] = getRelatedGroups(relation);
|
|
37
|
-
const
|
|
35
|
+
const parsedRelatedId = parseInt(relatedStringId);
|
|
36
|
+
const relatedId = isNumber(parsedRelatedId) ? parsedRelatedId : relatedStringId;
|
|
38
37
|
|
|
39
38
|
const relatedEntity = await strapi.db.query(uid).findOne(relatedId);
|
|
40
39
|
if (!relatedEntity) {
|
|
@@ -44,11 +43,11 @@ module.exports = ({ strapi }) => ({
|
|
|
44
43
|
const isApprovalFlowEnabled = this.getCommonService().getConfig('approvalFlow', []).includes(uid) ||
|
|
45
44
|
relatedEntity.requireCommentsApproval;
|
|
46
45
|
|
|
47
|
-
const linkToThread = !isNil(threadOf) ?
|
|
46
|
+
const linkToThread = !isNil(threadOf) ? await this.getCommonService().findOne({
|
|
48
47
|
id: threadOf,
|
|
49
48
|
related: relation,
|
|
50
49
|
}) : true;
|
|
51
|
-
const validContext = isValidUserContext(user);
|
|
50
|
+
const validContext = this.getCommonService().isValidUserContext(user);
|
|
52
51
|
|
|
53
52
|
if (!linkToThread) {
|
|
54
53
|
throw new PluginError(400, 'Thread does not exist');
|
|
@@ -58,16 +57,12 @@ module.exports = ({ strapi }) => ({
|
|
|
58
57
|
throw resolveUserContextError(user);
|
|
59
58
|
}
|
|
60
59
|
|
|
61
|
-
if (checkBadWords(content) && singleRelationFulfilled) {
|
|
62
|
-
const { author
|
|
60
|
+
if (this.getCommonService().checkBadWords(content) && singleRelationFulfilled) {
|
|
61
|
+
const { author = {}, ...rest } = data;
|
|
63
62
|
let authorData = {};
|
|
64
63
|
if (validContext && user) {
|
|
65
64
|
authorData = {
|
|
66
|
-
authorUser: user?.id
|
|
67
|
-
};
|
|
68
|
-
} else if (authorUser) {
|
|
69
|
-
authorData = {
|
|
70
|
-
authorUser,
|
|
65
|
+
authorUser: user?.id,
|
|
71
66
|
};
|
|
72
67
|
} else {
|
|
73
68
|
if (author.email && !REGEX.email.test(author.email)) {
|
|
@@ -82,8 +77,8 @@ module.exports = ({ strapi }) => ({
|
|
|
82
77
|
};
|
|
83
78
|
}
|
|
84
79
|
|
|
85
|
-
if (!isEmpty(authorData) && !authorData.authorId) {
|
|
86
|
-
throw new PluginError(400, '
|
|
80
|
+
if (!isEmpty(authorData) && !(authorData.authorId || authorData.authorUser)) {
|
|
81
|
+
throw new PluginError(400, `Not able to recognise author of a comment. Make sure you've provided "author" property in a payload or authenticated your request properly.`);
|
|
87
82
|
}
|
|
88
83
|
|
|
89
84
|
if (isApprovalFlowEnabled &&
|
|
@@ -97,59 +92,63 @@ module.exports = ({ strapi }) => ({
|
|
|
97
92
|
...authorData,
|
|
98
93
|
related: relation,
|
|
99
94
|
approvalStatus: isApprovalFlowEnabled ? APPROVAL_STATUS.PENDING : null,
|
|
100
|
-
}
|
|
95
|
+
},
|
|
96
|
+
populate: {
|
|
97
|
+
authorUser: true,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
return this.getCommonService().sanitizeCommentEntity({
|
|
101
|
+
...entity,
|
|
102
|
+
threadOf: !isNil(threadOf) ? linkToThread : null,
|
|
101
103
|
});
|
|
102
|
-
return this.getCommonService().sanitizeCommentEntity(entity);
|
|
103
104
|
}
|
|
104
105
|
throw new PluginError(400, 'No content received');
|
|
105
106
|
},
|
|
106
107
|
|
|
107
108
|
// Update a comment
|
|
108
|
-
async update(id, relation, data, user) {
|
|
109
|
-
const { content
|
|
109
|
+
async update(id, relation, data, user = undefined) {
|
|
110
|
+
const { content } = data;
|
|
110
111
|
|
|
111
|
-
const singleRelationFulfilled =
|
|
112
|
-
|
|
113
|
-
if (relation !== related) {
|
|
114
|
-
throw new PluginError(400, `Insonsistent relation uid`);
|
|
115
|
-
}
|
|
112
|
+
const singleRelationFulfilled = relation && REGEX.relatedUid.test(relation)
|
|
116
113
|
|
|
117
114
|
if(!singleRelationFulfilled) {
|
|
118
|
-
throw new PluginError(400, `
|
|
115
|
+
throw new PluginError(400, `Request property "relation" got incorrect format, use format like "api::<collection name>.<content type name>:<entity id>"`);
|
|
119
116
|
}
|
|
120
117
|
|
|
121
118
|
const existingEntity = await this.getCommonService().findOne({
|
|
122
119
|
id,
|
|
123
|
-
related,
|
|
120
|
+
related: relation,
|
|
124
121
|
});
|
|
125
|
-
|
|
122
|
+
|
|
123
|
+
const validContext = this.getCommonService().isValidUserContext(user);
|
|
126
124
|
|
|
127
125
|
if (!validContext) {
|
|
128
126
|
throw resolveUserContextError(user);
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
if (isEqualEntity(existingEntity, data, user) && content) {
|
|
132
|
-
if (checkBadWords(content)) {
|
|
130
|
+
if (this.getCommonService().checkBadWords(content)) {
|
|
133
131
|
const entity = await strapi.db.query(getModelUid('comment')).update({
|
|
134
132
|
where: { id },
|
|
135
133
|
data: { content },
|
|
134
|
+
populate: { threadOf: true, authorUser: true },
|
|
136
135
|
});
|
|
137
136
|
return this.getCommonService().sanitizeCommentEntity(entity);
|
|
138
137
|
}
|
|
139
138
|
}
|
|
140
|
-
throw new PluginError(
|
|
139
|
+
throw new PluginError(403, `You're not allowed to take an action on that entity. Make sure you've provided "author" property in a payload or authenticated your request properly.`);
|
|
141
140
|
},
|
|
142
141
|
|
|
143
142
|
// Report abuse in comment
|
|
144
|
-
async reportAbuse(id, relation, payload, user) {
|
|
145
|
-
if (!isValidUserContext(user)) {
|
|
143
|
+
async reportAbuse(id, relation, payload, user = undefined) {
|
|
144
|
+
if (!this.getCommonService().isValidUserContext(user)) {
|
|
146
145
|
throw resolveUserContextError(user);
|
|
147
146
|
}
|
|
148
|
-
const
|
|
147
|
+
const reportAgainstEntity = await this.getCommonService().findOne({
|
|
149
148
|
id,
|
|
150
149
|
related: relation,
|
|
151
150
|
});
|
|
152
|
-
if (
|
|
151
|
+
if (reportAgainstEntity) {
|
|
153
152
|
const entity = await strapi.db.query(getModelUid('comment-report')).create({
|
|
154
153
|
data: {
|
|
155
154
|
...payload,
|
|
@@ -158,39 +157,62 @@ module.exports = ({ strapi }) => ({
|
|
|
158
157
|
}
|
|
159
158
|
});
|
|
160
159
|
if (entity) {
|
|
160
|
+
const response = {
|
|
161
|
+
...entity,
|
|
162
|
+
related: reportAgainstEntity,
|
|
163
|
+
};
|
|
161
164
|
try {
|
|
162
165
|
await this.sendAbuseReportEmail(entity.reason, entity.content); // Could also add some info about relation
|
|
166
|
+
return response
|
|
163
167
|
} catch (err) {
|
|
164
|
-
return
|
|
168
|
+
return response;
|
|
165
169
|
}
|
|
166
170
|
} else {
|
|
167
171
|
throw new PluginError(500, 'Report cannot be created');
|
|
168
172
|
}
|
|
169
|
-
return entity;
|
|
170
173
|
}
|
|
171
|
-
throw new PluginError(
|
|
174
|
+
throw new PluginError(403, `You're not allowed to take an action on that entity. Make sure that comment exist or you've authenticated your request properly.`);
|
|
172
175
|
},
|
|
173
176
|
|
|
174
|
-
async markAsRemoved(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
177
|
+
async markAsRemoved(id, relation, authorId, user = undefined){
|
|
178
|
+
if (!this.getCommonService().isValidUserContext(user)) {
|
|
179
|
+
throw resolveUserContextError(user);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const author = user?.id || authorId;
|
|
183
|
+
|
|
184
|
+
if (!author) {
|
|
185
|
+
throw new PluginError(403, `You're not allowed to take an action on that entity. Make sure that you've provided proper "authorId" or authenticated your request properly.`);
|
|
182
186
|
}
|
|
183
187
|
|
|
184
|
-
|
|
188
|
+
let entity;
|
|
189
|
+
try {
|
|
190
|
+
const byAuthor = user?.id ? {
|
|
191
|
+
authorUser: author
|
|
192
|
+
} : {
|
|
193
|
+
authorId: author
|
|
194
|
+
}
|
|
195
|
+
entity = await this.getCommonService().findOne({
|
|
196
|
+
id,
|
|
197
|
+
related: relation,
|
|
198
|
+
...byAuthor,
|
|
199
|
+
});
|
|
200
|
+
} catch(e) {
|
|
201
|
+
throw new PluginError(404, `Entity does not exist or you're not allowed to take an action on it`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const removedEntity = await strapi.db.query(getModelUid('comment'))
|
|
185
205
|
.update({
|
|
186
206
|
where: {
|
|
187
|
-
id
|
|
188
|
-
related:
|
|
207
|
+
id,
|
|
208
|
+
related: relation
|
|
189
209
|
},
|
|
190
|
-
data: { removed: true }
|
|
210
|
+
data: { removed: true },
|
|
211
|
+
populate: { threadOf: true, authorUser: true },
|
|
191
212
|
})
|
|
192
|
-
|
|
193
|
-
|
|
213
|
+
await this.markAsRemovedNested(id, true);
|
|
214
|
+
|
|
215
|
+
return this.getCommonService().sanitizeCommentEntity(removedEntity);
|
|
194
216
|
},
|
|
195
217
|
|
|
196
218
|
async sendAbuseReportEmail(reason, content) {
|
|
@@ -229,7 +251,7 @@ module.exports = ({ strapi }) => ({
|
|
|
229
251
|
}
|
|
230
252
|
},
|
|
231
253
|
|
|
232
|
-
markAsRemovedNested(id, status){
|
|
254
|
+
async markAsRemovedNested(id, status){
|
|
233
255
|
return this.getCommonService().modifiedNestedNestedComments(id, 'removed', status);
|
|
234
256
|
},
|
|
235
257
|
});
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const BadWordsFilter = require('bad-words');
|
|
4
|
+
const { isArray, isNumber, isObject, isNil, first, parseInt } = require('lodash');
|
|
5
5
|
const PluginError = require('./../utils/error');
|
|
6
6
|
const {
|
|
7
7
|
getModelUid,
|
|
8
8
|
getRelatedGroups,
|
|
9
9
|
buildNestedStructure,
|
|
10
|
-
filterOurResolvedReports
|
|
10
|
+
filterOurResolvedReports,
|
|
11
|
+
buildAuthorModel,
|
|
11
12
|
} = require('./utils/functions');
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -21,7 +22,8 @@ module.exports = ({ strapi }) => ({
|
|
|
21
22
|
if (prop && isArray(prop)) {
|
|
22
23
|
queryProp = prop.join('.');
|
|
23
24
|
}
|
|
24
|
-
|
|
25
|
+
const result = strapi.config.get(`plugin.comments${ queryProp ? '.' + queryProp : ''}`);
|
|
26
|
+
return isNil(result) ? defaultValue : result;
|
|
25
27
|
},
|
|
26
28
|
|
|
27
29
|
// Find comments in the flat structure
|
|
@@ -85,8 +87,12 @@ module.exports = ({ strapi }) => ({
|
|
|
85
87
|
async findOne(criteria) {
|
|
86
88
|
const entity = await strapi.db.query(getModelUid('comment'))
|
|
87
89
|
.findOne({
|
|
88
|
-
where: criteria
|
|
89
|
-
|
|
90
|
+
where: criteria,
|
|
91
|
+
populate: {
|
|
92
|
+
reports: true,
|
|
93
|
+
authorUser: true,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
90
96
|
if (!entity){
|
|
91
97
|
throw new PluginError(404, 'Not found');
|
|
92
98
|
}
|
|
@@ -97,9 +103,11 @@ module.exports = ({ strapi }) => ({
|
|
|
97
103
|
async findRelatedEntitiesFor(entities = []) {
|
|
98
104
|
const data = entities.reduce((acc, cur) => {
|
|
99
105
|
const [relatedUid, relatedStringId] = getRelatedGroups(cur.related);
|
|
106
|
+
const parsedRelatedId = parseInt(relatedStringId);
|
|
107
|
+
const relatedId = isNumber(parsedRelatedId) ? parsedRelatedId : relatedStringId;
|
|
100
108
|
return {
|
|
101
109
|
...acc,
|
|
102
|
-
[relatedUid]: [...(acc[relatedUid] || []),
|
|
110
|
+
[relatedUid]: [...(acc[relatedUid] || []), relatedId]
|
|
103
111
|
};
|
|
104
112
|
},
|
|
105
113
|
{}
|
|
@@ -151,12 +159,32 @@ module.exports = ({ strapi }) => ({
|
|
|
151
159
|
}
|
|
152
160
|
},
|
|
153
161
|
|
|
154
|
-
sanitizeCommentEntity
|
|
155
|
-
|
|
156
|
-
|
|
162
|
+
sanitizeCommentEntity(entity) {
|
|
163
|
+
return {
|
|
164
|
+
...buildAuthorModel({
|
|
165
|
+
...entity,
|
|
166
|
+
threadOf: isObject(entity.threadOf) ? buildAuthorModel(entity.threadOf) : entity.threadOf,
|
|
167
|
+
}),
|
|
168
|
+
};
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
isValidUserContext(user) {
|
|
172
|
+
return user ? !isNil(user?.id) : true;
|
|
173
|
+
},
|
|
157
174
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
175
|
+
checkBadWords(content) {
|
|
176
|
+
const config = this.getConfig('badWords', true);
|
|
177
|
+
if (config) {
|
|
178
|
+
const filter = new BadWordsFilter(isObject(config) ? config : undefined);
|
|
179
|
+
if (content && filter.isProfane(content)) {
|
|
180
|
+
throw new PluginError(400, 'Bad language used! Please polite your comment...', {
|
|
181
|
+
content: {
|
|
182
|
+
original: content,
|
|
183
|
+
filtered: content && filter.clean(content),
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return content;
|
|
189
|
+
},
|
|
162
190
|
});
|