strapi-plugin-comments 2.0.0-rc.1 → 2.0.2
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/README.md +109 -67
- 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 -70
- 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 +42 -24
- package/admin/src/pages/Details/components/DetailsFilters/index.js +8 -10
- package/admin/src/pages/Details/index.js +6 -15
- package/admin/src/pages/Discover/components/DiscoverTableRow/index.js +30 -67
- package/admin/src/translations/en.json +13 -15
- package/admin/src/utils/resolveCommentStatus.js +4 -4
- package/content-types/comment.js +0 -4
- package/package.json +8 -5
- package/server/controllers/client.js +11 -6
- package/server/controllers/utils/__tests__/functions.test.js +2 -2
- package/server/controllers/utils/parsers.js +6 -2
- package/server/graphql/mutations/createAbuseReport.js +8 -3
- package/server/graphql/mutations/createComment.js +8 -3
- package/server/graphql/mutations/removeComment.js +8 -3
- package/server/graphql/mutations/updateComment.js +8 -3
- package/server/graphql/queries/findAllFlat.js +4 -2
- package/server/graphql/queries/findAllInHierarchy.js +3 -3
- package/server/graphql/resolvers-config.js +13 -8
- package/server/graphql/types/comment-nested.js +1 -1
- package/server/graphql/types/comment.js +0 -1
- package/server/graphql/types/create-comment-author.js +1 -1
- package/server/graphql/types/identify-comment-author.js +1 -1
- package/server/graphql/types/index.js +0 -1
- package/server/services/__tests__/common.test.js +165 -0
- package/server/services/admin.js +10 -7
- package/server/services/client.js +45 -31
- package/server/services/common.js +71 -19
- package/server/services/utils/__tests__/functions.test.js +9 -73
- package/server/services/utils/functions.js +23 -34
- package/server/utils/__tests__/functions.test.js +17 -0
- package/server/utils/constants.js +1 -0
- package/.circleci/config.yml +0 -48
- package/.eslintrc +0 -35
- package/.github/pull_request_template.md +0 -13
- package/.nvmrc +0 -1
- package/admin/src/components/DiscussionThreadItemMeta/index.js +0 -42
- package/admin/src/components/DiscussionThreadItemWarnings/index.js +0 -147
- package/admin/src/components/DiscussionThreadItemWarnings/styles.js +0 -7
- package/codecov.yml +0 -4
- package/public/assets/logo.png +0 -0
- package/public/assets/preview.png +0 -0
- package/server/graphql/types/comment-author-user.js +0 -9
|
@@ -10,11 +10,16 @@ module.exports = ({ nexus }) => {
|
|
|
10
10
|
args: {
|
|
11
11
|
input: nonNull('UpdateComment'),
|
|
12
12
|
},
|
|
13
|
-
async resolve(obj, args) {
|
|
13
|
+
async resolve(obj, args, ctx) {
|
|
14
14
|
const { input } = args;
|
|
15
|
+
const { state: { user } = {} } = ctx;
|
|
15
16
|
const { id, relation, ...body } = input;
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
try {
|
|
18
|
+
return await getPluginService('client')
|
|
19
|
+
.update(id, relation, body, user);
|
|
20
|
+
} catch(e) {
|
|
21
|
+
throw new Error(e);
|
|
22
|
+
}
|
|
18
23
|
},
|
|
19
24
|
};
|
|
20
25
|
}
|
|
@@ -18,11 +18,13 @@ module.exports = ({ strapi, nexus }) => {
|
|
|
18
18
|
sort: args.SortArg,
|
|
19
19
|
},
|
|
20
20
|
async resolve(obj, args) {
|
|
21
|
-
const { relation, filters } = args;
|
|
21
|
+
const { relation, filters, sort, pagination } = args;
|
|
22
22
|
return await getPluginService('common')
|
|
23
23
|
.findAllFlat(flatInput(
|
|
24
24
|
relation,
|
|
25
|
-
getPluginService('gql').graphQLFiltersToStrapiQuery(filters, contentType)
|
|
25
|
+
getPluginService('gql').graphQLFiltersToStrapiQuery(filters, contentType),
|
|
26
|
+
sort,
|
|
27
|
+
pagination
|
|
26
28
|
), undefined);
|
|
27
29
|
},
|
|
28
30
|
};
|
|
@@ -13,16 +13,16 @@ module.exports = ({ strapi, nexus }) => {
|
|
|
13
13
|
type: nonNull(list('CommentNested')),
|
|
14
14
|
args: {
|
|
15
15
|
relation: nonNull(stringArg()),
|
|
16
|
-
// filters: filtersArg,
|
|
17
16
|
sort: args.SortArg,
|
|
18
17
|
},
|
|
19
18
|
async resolve(obj, args) {
|
|
20
|
-
const { relation, filters } = args;
|
|
19
|
+
const { relation, filters, sort } = args;
|
|
21
20
|
return await getPluginService('common')
|
|
22
21
|
.findAllInHierarchy({
|
|
23
22
|
...flatInput(
|
|
24
23
|
relation,
|
|
25
|
-
getPluginService('gql').graphQLFiltersToStrapiQuery(filters, contentType)
|
|
24
|
+
getPluginService('gql').graphQLFiltersToStrapiQuery(filters, contentType),
|
|
25
|
+
sort
|
|
26
26
|
),
|
|
27
27
|
dropBlockedThreads: true,
|
|
28
28
|
});
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
'
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
3
|
+
const { getPluginService } = require("../utils/functions");
|
|
4
|
+
|
|
5
|
+
module.exports = () => {
|
|
6
|
+
const auth = getPluginService('common').getConfig('gql.auth', false);
|
|
7
|
+
return {
|
|
8
|
+
'Query.findAllFlat': { auth },
|
|
9
|
+
'Query.findAllInHierarchy': { auth },
|
|
10
|
+
'Mutation.createComment': { auth },
|
|
11
|
+
'Mutation.updateComment': { auth },
|
|
12
|
+
'Mutation.removeComment': { auth },
|
|
13
|
+
'Mutation.createAbuseReport': { auth },
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -6,9 +6,9 @@ module.exports = ({ nexus }) => nexus.objectType({
|
|
|
6
6
|
t.boolean("blocked")
|
|
7
7
|
t.boolean("blockedThread")
|
|
8
8
|
t.string("approvalStatus")
|
|
9
|
+
t.boolean("removed")
|
|
9
10
|
t.field("threadOf", { type: 'CommentSingle' })
|
|
10
11
|
t.list.field("children", { type: 'CommentNested' })
|
|
11
|
-
t.field("authorUser", { type: 'CommentAuthorUser' })
|
|
12
12
|
t.field("author", { type: 'CommentAuthor' })
|
|
13
13
|
t.string("createdAt")
|
|
14
14
|
t.string("updatedAt")
|
|
@@ -8,7 +8,6 @@ module.exports = ({ nexus }) => nexus.objectType({
|
|
|
8
8
|
t.boolean("removed")
|
|
9
9
|
t.string("approvalStatus")
|
|
10
10
|
t.field("threadOf", { type: 'CommentSingle' })
|
|
11
|
-
t.field("authorUser", { type: 'CommentAuthorUser' })
|
|
12
11
|
t.field("author", { type: 'CommentAuthor' })
|
|
13
12
|
t.string("createdAt")
|
|
14
13
|
t.string("updatedAt")
|
|
@@ -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
|
@@ -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){
|
|
@@ -159,9 +164,7 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
|
159
164
|
threadOf: levelThreadId,
|
|
160
165
|
related: entity.related,
|
|
161
166
|
},
|
|
162
|
-
|
|
163
|
-
...reportsPopulation,
|
|
164
|
-
},
|
|
167
|
+
...defaultPopulate,
|
|
165
168
|
startingFromId: levelThreadId,
|
|
166
169
|
isAdmin: true
|
|
167
170
|
}, false);
|
|
@@ -8,8 +8,6 @@ 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')
|
|
@@ -49,7 +47,7 @@ module.exports = ({ strapi }) => ({
|
|
|
49
47
|
id: threadOf,
|
|
50
48
|
related: relation,
|
|
51
49
|
}) : true;
|
|
52
|
-
const validContext = isValidUserContext(user);
|
|
50
|
+
const validContext = this.getCommonService().isValidUserContext(user);
|
|
53
51
|
|
|
54
52
|
if (!linkToThread) {
|
|
55
53
|
throw new PluginError(400, 'Thread does not exist');
|
|
@@ -59,16 +57,12 @@ module.exports = ({ strapi }) => ({
|
|
|
59
57
|
throw resolveUserContextError(user);
|
|
60
58
|
}
|
|
61
59
|
|
|
62
|
-
if (checkBadWords(content) && singleRelationFulfilled) {
|
|
63
|
-
const { author
|
|
60
|
+
if (this.getCommonService().checkBadWords(content) && singleRelationFulfilled) {
|
|
61
|
+
const { author = {}, ...rest } = data;
|
|
64
62
|
let authorData = {};
|
|
65
63
|
if (validContext && user) {
|
|
66
64
|
authorData = {
|
|
67
|
-
authorUser: user?.id
|
|
68
|
-
};
|
|
69
|
-
} else if (authorUser) {
|
|
70
|
-
authorData = {
|
|
71
|
-
authorUser,
|
|
65
|
+
authorUser: user?.id,
|
|
72
66
|
};
|
|
73
67
|
} else {
|
|
74
68
|
if (author.email && !REGEX.email.test(author.email)) {
|
|
@@ -83,8 +77,8 @@ module.exports = ({ strapi }) => ({
|
|
|
83
77
|
};
|
|
84
78
|
}
|
|
85
79
|
|
|
86
|
-
if (!isEmpty(authorData) && !authorData.authorId) {
|
|
87
|
-
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.`);
|
|
88
82
|
}
|
|
89
83
|
|
|
90
84
|
if (isApprovalFlowEnabled &&
|
|
@@ -98,7 +92,10 @@ module.exports = ({ strapi }) => ({
|
|
|
98
92
|
...authorData,
|
|
99
93
|
related: relation,
|
|
100
94
|
approvalStatus: isApprovalFlowEnabled ? APPROVAL_STATUS.PENDING : null,
|
|
101
|
-
}
|
|
95
|
+
},
|
|
96
|
+
populate: {
|
|
97
|
+
authorUser: true,
|
|
98
|
+
},
|
|
102
99
|
});
|
|
103
100
|
return this.getCommonService().sanitizeCommentEntity({
|
|
104
101
|
...entity,
|
|
@@ -109,41 +106,42 @@ module.exports = ({ strapi }) => ({
|
|
|
109
106
|
},
|
|
110
107
|
|
|
111
108
|
// Update a comment
|
|
112
|
-
async update(id, relation, data, user) {
|
|
109
|
+
async update(id, relation, data, user = undefined) {
|
|
113
110
|
const { content } = data;
|
|
114
111
|
|
|
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
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 },
|
|
136
|
-
populate: { threadOf: true },
|
|
134
|
+
populate: { threadOf: true, authorUser: true },
|
|
137
135
|
});
|
|
138
136
|
return this.getCommonService().sanitizeCommentEntity(entity);
|
|
139
137
|
}
|
|
140
138
|
}
|
|
141
|
-
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.`);
|
|
142
140
|
},
|
|
143
141
|
|
|
144
142
|
// Report abuse in comment
|
|
145
|
-
async reportAbuse(id, relation, payload, user) {
|
|
146
|
-
if (!isValidUserContext(user)) {
|
|
143
|
+
async reportAbuse(id, relation, payload, user = undefined) {
|
|
144
|
+
if (!this.getCommonService().isValidUserContext(user)) {
|
|
147
145
|
throw resolveUserContextError(user);
|
|
148
146
|
}
|
|
149
147
|
const reportAgainstEntity = await this.getCommonService().findOne({
|
|
@@ -173,18 +171,34 @@ module.exports = ({ strapi }) => ({
|
|
|
173
171
|
throw new PluginError(500, 'Report cannot be created');
|
|
174
172
|
}
|
|
175
173
|
}
|
|
176
|
-
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.`);
|
|
177
175
|
},
|
|
178
176
|
|
|
179
|
-
async markAsRemoved(id, relation, authorId){
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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;
|
|
185
183
|
|
|
186
|
-
if (!
|
|
187
|
-
throw new PluginError(
|
|
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.`);
|
|
186
|
+
}
|
|
187
|
+
|
|
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`);
|
|
188
202
|
}
|
|
189
203
|
|
|
190
204
|
const removedEntity = await strapi.db.query(getModelUid('comment'))
|
|
@@ -194,7 +208,7 @@ module.exports = ({ strapi }) => ({
|
|
|
194
208
|
related: relation
|
|
195
209
|
},
|
|
196
210
|
data: { removed: true },
|
|
197
|
-
populate: { threadOf: true },
|
|
211
|
+
populate: { threadOf: true, authorUser: true },
|
|
198
212
|
})
|
|
199
213
|
await this.markAsRemovedNested(id, true);
|
|
200
214
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
3
|
+
const BadWordsFilter = require('bad-words');
|
|
4
|
+
const { isArray, isNumber, isObject, isNil, isString, first, parseInt, set } = require('lodash');
|
|
5
|
+
const { REGEX } = require('../utils/constants');
|
|
5
6
|
const PluginError = require('./../utils/error');
|
|
6
7
|
const {
|
|
7
8
|
getModelUid,
|
|
@@ -22,16 +23,44 @@ module.exports = ({ strapi }) => ({
|
|
|
22
23
|
if (prop && isArray(prop)) {
|
|
23
24
|
queryProp = prop.join('.');
|
|
24
25
|
}
|
|
25
|
-
|
|
26
|
+
const result = strapi.config.get(`plugin.comments${ queryProp ? '.' + queryProp : ''}`);
|
|
27
|
+
return isNil(result) ? defaultValue : result;
|
|
26
28
|
},
|
|
27
29
|
|
|
28
30
|
// Find comments in the flat structure
|
|
29
|
-
async findAllFlat({
|
|
31
|
+
async findAllFlat({
|
|
32
|
+
query = {},
|
|
33
|
+
populate = {},
|
|
34
|
+
sort,
|
|
35
|
+
pagination }, relatedEntity = null) {
|
|
30
36
|
|
|
31
37
|
const defaultPopulate = {
|
|
32
38
|
authorUser: true,
|
|
33
39
|
};
|
|
34
40
|
|
|
41
|
+
let queryExtension = {};
|
|
42
|
+
|
|
43
|
+
if (sort && (isString(sort) || isArray(sort))) {
|
|
44
|
+
queryExtension = {
|
|
45
|
+
...queryExtension,
|
|
46
|
+
orderBy: (isString(sort) ? [sort] : sort)
|
|
47
|
+
.map(_ => REGEX.sorting.test(_) ? _ : `${_}:asc`)
|
|
48
|
+
.reduce((prev, curr) => {
|
|
49
|
+
const [type = 'asc', ...parts] = curr.split(':').reverse();
|
|
50
|
+
return { ...set(prev, parts.reverse().join('.'), type) };
|
|
51
|
+
}, {})
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (pagination && isObject(pagination)) {
|
|
56
|
+
const { page = 1, pageSize = 10 } = pagination;
|
|
57
|
+
queryExtension = {
|
|
58
|
+
...queryExtension,
|
|
59
|
+
offset: (page - 1) * pageSize,
|
|
60
|
+
limit: pageSize,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
35
64
|
const entries = await strapi.db.query(getModelUid('comment'))
|
|
36
65
|
.findMany({
|
|
37
66
|
where: {
|
|
@@ -41,6 +70,7 @@ module.exports = ({ strapi }) => ({
|
|
|
41
70
|
...defaultPopulate,
|
|
42
71
|
...populate,
|
|
43
72
|
},
|
|
73
|
+
...queryExtension,
|
|
44
74
|
});
|
|
45
75
|
|
|
46
76
|
const entriesWithThreads = await Promise.all(entries.map(async _ => {
|
|
@@ -75,10 +105,11 @@ module.exports = ({ strapi }) => ({
|
|
|
75
105
|
async findAllInHierarchy ({
|
|
76
106
|
query,
|
|
77
107
|
populate = {},
|
|
108
|
+
sort,
|
|
78
109
|
startingFromId = null,
|
|
79
110
|
dropBlockedThreads = false,
|
|
80
111
|
}, relatedEntity) {
|
|
81
|
-
const entities = await this.findAllFlat({ query, populate }, relatedEntity);
|
|
112
|
+
const entities = await this.findAllFlat({ query, populate, sort }, relatedEntity);
|
|
82
113
|
return buildNestedStructure(entities, startingFromId, 'threadOf', dropBlockedThreads, false);
|
|
83
114
|
},
|
|
84
115
|
|
|
@@ -86,8 +117,12 @@ module.exports = ({ strapi }) => ({
|
|
|
86
117
|
async findOne(criteria) {
|
|
87
118
|
const entity = await strapi.db.query(getModelUid('comment'))
|
|
88
119
|
.findOne({
|
|
89
|
-
where: criteria
|
|
90
|
-
|
|
120
|
+
where: criteria,
|
|
121
|
+
populate: {
|
|
122
|
+
reports: true,
|
|
123
|
+
authorUser: true,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
91
126
|
if (!entity){
|
|
92
127
|
throw new PluginError(404, 'Not found');
|
|
93
128
|
}
|
|
@@ -111,7 +146,7 @@ module.exports = ({ strapi }) => ({
|
|
|
111
146
|
Object.entries(data)
|
|
112
147
|
.map(async ([relatedUid, relatedStringIds]) =>
|
|
113
148
|
strapi.db.query(relatedUid).findMany({
|
|
114
|
-
where: { id: Array.from(new Set(relatedStringIds)) }
|
|
149
|
+
where: { id: Array.from(new Set(relatedStringIds)) },
|
|
115
150
|
}).then(relatedEntities => relatedEntities.map(_ =>
|
|
116
151
|
({
|
|
117
152
|
..._,
|
|
@@ -154,15 +189,32 @@ module.exports = ({ strapi }) => ({
|
|
|
154
189
|
}
|
|
155
190
|
},
|
|
156
191
|
|
|
157
|
-
sanitizeCommentEntity
|
|
158
|
-
|
|
159
|
-
...
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
192
|
+
sanitizeCommentEntity(entity) {
|
|
193
|
+
return {
|
|
194
|
+
...buildAuthorModel({
|
|
195
|
+
...entity,
|
|
196
|
+
threadOf: isObject(entity.threadOf) ? buildAuthorModel(entity.threadOf) : entity.threadOf,
|
|
197
|
+
}),
|
|
198
|
+
};
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
isValidUserContext(user) {
|
|
202
|
+
return user ? !isNil(user?.id) : true;
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
checkBadWords(content) {
|
|
206
|
+
const config = this.getConfig('badWords', true);
|
|
207
|
+
if (config) {
|
|
208
|
+
const filter = new BadWordsFilter(isObject(config) ? config : undefined);
|
|
209
|
+
if (content && filter.isProfane(content)) {
|
|
210
|
+
throw new PluginError(400, 'Bad language used! Please polite your comment...', {
|
|
211
|
+
content: {
|
|
212
|
+
original: content,
|
|
213
|
+
filtered: content && filter.clean(content),
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return content;
|
|
219
|
+
},
|
|
168
220
|
});
|