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.
Files changed (59) hide show
  1. package/README.md +109 -67
  2. package/__mocks__/initSetup.js +53 -0
  3. package/admin/src/components/ActionButton/styles.js +10 -0
  4. package/admin/src/components/DiscussionThread/index.js +3 -7
  5. package/admin/src/components/DiscussionThreadItem/index.js +25 -70
  6. package/admin/src/components/DiscussionThreadItem/styles.js +4 -79
  7. package/admin/src/components/DiscussionThreadItemActions/index.js +82 -40
  8. package/admin/src/components/DiscussionThreadItemActions/styles.js +1 -12
  9. package/admin/src/components/DiscussionThreadItemApprovalFlowActions/index.js +2 -3
  10. package/admin/src/components/DiscussionThreadItemFooter/index.js +47 -0
  11. package/admin/src/components/DiscussionThreadItemFooter/styles.js +13 -0
  12. package/admin/src/components/DiscussionThreadItemReviewAction/index.js +131 -0
  13. package/admin/src/components/IconButton/styles.js +14 -0
  14. package/admin/src/components/ReportsReviewModal/index.js +1 -1
  15. package/admin/src/components/ReportsReviewTable/index.js +12 -6
  16. package/admin/src/components/StatusBadge/index.js +17 -0
  17. package/admin/src/components/StatusBadge/styles.js +17 -0
  18. package/admin/src/pages/Details/components/DetailsEntity/index.js +42 -24
  19. package/admin/src/pages/Details/components/DetailsFilters/index.js +8 -10
  20. package/admin/src/pages/Details/index.js +6 -15
  21. package/admin/src/pages/Discover/components/DiscoverTableRow/index.js +30 -67
  22. package/admin/src/translations/en.json +13 -15
  23. package/admin/src/utils/resolveCommentStatus.js +4 -4
  24. package/content-types/comment.js +0 -4
  25. package/package.json +8 -5
  26. package/server/controllers/client.js +11 -6
  27. package/server/controllers/utils/__tests__/functions.test.js +2 -2
  28. package/server/controllers/utils/parsers.js +6 -2
  29. package/server/graphql/mutations/createAbuseReport.js +8 -3
  30. package/server/graphql/mutations/createComment.js +8 -3
  31. package/server/graphql/mutations/removeComment.js +8 -3
  32. package/server/graphql/mutations/updateComment.js +8 -3
  33. package/server/graphql/queries/findAllFlat.js +4 -2
  34. package/server/graphql/queries/findAllInHierarchy.js +3 -3
  35. package/server/graphql/resolvers-config.js +13 -8
  36. package/server/graphql/types/comment-nested.js +1 -1
  37. package/server/graphql/types/comment.js +0 -1
  38. package/server/graphql/types/create-comment-author.js +1 -1
  39. package/server/graphql/types/identify-comment-author.js +1 -1
  40. package/server/graphql/types/index.js +0 -1
  41. package/server/services/__tests__/common.test.js +165 -0
  42. package/server/services/admin.js +10 -7
  43. package/server/services/client.js +45 -31
  44. package/server/services/common.js +71 -19
  45. package/server/services/utils/__tests__/functions.test.js +9 -73
  46. package/server/services/utils/functions.js +23 -34
  47. package/server/utils/__tests__/functions.test.js +17 -0
  48. package/server/utils/constants.js +1 -0
  49. package/.circleci/config.yml +0 -48
  50. package/.eslintrc +0 -35
  51. package/.github/pull_request_template.md +0 -13
  52. package/.nvmrc +0 -1
  53. package/admin/src/components/DiscussionThreadItemMeta/index.js +0 -42
  54. package/admin/src/components/DiscussionThreadItemWarnings/index.js +0 -147
  55. package/admin/src/components/DiscussionThreadItemWarnings/styles.js +0 -7
  56. package/codecov.yml +0 -4
  57. package/public/assets/logo.png +0 -0
  58. package/public/assets/preview.png +0 -0
  59. 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
- return await getPluginService('client')
17
- .update(id, relation, body);
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
- module.exports = ({ }) => ({
4
- 'Query.findAllFlat': { auth: false },
5
- 'Query.findAllInHierarchy': { auth: false },
6
- 'Mutation.createComment': { auth: false },
7
- 'Mutation.updateComment': { auth: false },
8
- 'Mutation.removeComment': { auth: false },
9
- 'Mutation.createAbuseReport': { auth: false },
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")
@@ -1,7 +1,7 @@
1
1
  module.exports = ({ nexus }) => nexus.inputObjectType({
2
2
  name: "CreateCommentAuthor",
3
3
  definition(t) {
4
- t.nonNull.string("id")
4
+ t.nonNull.id("id")
5
5
  t.nonNull.string("name")
6
6
  t.string("email")
7
7
  t.string("avatar")
@@ -1,6 +1,6 @@
1
1
  module.exports = ({ nexus }) => nexus.inputObjectType({
2
2
  name: "IdentifyCommentAuthor",
3
3
  definition(t) {
4
- t.nonNull.string("id")
4
+ t.nonNull.id("id")
5
5
  }
6
6
  })
@@ -3,7 +3,6 @@ const typesFactories = [
3
3
  require('./comment'),
4
4
  require('./comment-nested'),
5
5
  require('./comment-author'),
6
- require('./comment-author-user'),
7
6
  require('./create-comment'),
8
7
  require('./create-comment-author'),
9
8
  require('./update-comment'),
@@ -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
+ });
@@ -117,19 +117,24 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
117
117
  }
118
118
  };
119
119
 
120
- const entity = await strapi.db.query(getModelUid('comment')).findOne({
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
- populate: {
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, authorUser, ...rest } = data;
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 || authorUser,
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, 'Object: "author" is invalid. Check your payload');
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, `Field "related" got incorrect format, use format like "api::<collection name>.<content type name>:<entity id>"`);
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
- const validContext = isValidUserContext(user);
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(409, 'Action on that entity is not allowed');
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(409, 'Action on that entity is not allowed');
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
- const entity = await this.getCommonService().findOne({
181
- id,
182
- related: relation,
183
- $or: [{ authorUser: authorId }, { authorId }],
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 (!entity) {
187
- throw new PluginError(404, 'Entity not exist');
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 { isArray, isNumber, isObject, first, parseInt } = require('lodash');
4
- // const { sanitizeEntity } = require('strapi-utils');
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
- return strapi.config.get(`plugin.comments${ queryProp ? '.' + queryProp : ''}`) || defaultValue;
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({ query = {}, populate = {} }, relatedEntity = null) {
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
- }, ['related', 'reports']);
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: (entity) => ({
158
- ...buildAuthorModel({
159
- ...entity,
160
- threadOf: isObject(entity.threadOf) ? buildAuthorModel(entity.threadOf) : entity.threadOf,
161
- }),
162
- }),
163
-
164
- // sanitizeCommentEntity: (entity) => ({
165
- // ...entity,
166
- // authorUser: sanitizeEntity(entity.authorUser, { model: strapi.plugins['users-permissions'].models.user }),
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
  });