strapi-plugin-comments 2.0.0-rc.2 → 2.0.3

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 (65) hide show
  1. package/.husky/pre-commit +4 -0
  2. package/README.md +53 -9
  3. package/__mocks__/initSetup.js +82 -0
  4. package/admin/src/components/ActionButton/styles.js +10 -0
  5. package/admin/src/components/DiscussionThread/index.js +2 -6
  6. package/admin/src/components/DiscussionThreadItem/index.js +25 -68
  7. package/admin/src/components/DiscussionThreadItem/styles.js +4 -79
  8. package/admin/src/components/DiscussionThreadItemActions/index.js +82 -40
  9. package/admin/src/components/DiscussionThreadItemActions/styles.js +1 -12
  10. package/admin/src/components/DiscussionThreadItemApprovalFlowActions/index.js +2 -3
  11. package/admin/src/components/DiscussionThreadItemFooter/index.js +47 -0
  12. package/admin/src/components/DiscussionThreadItemFooter/styles.js +13 -0
  13. package/admin/src/components/DiscussionThreadItemReviewAction/index.js +131 -0
  14. package/admin/src/components/IconButton/styles.js +14 -0
  15. package/admin/src/components/ReportsReviewModal/index.js +1 -1
  16. package/admin/src/components/ReportsReviewTable/index.js +12 -6
  17. package/admin/src/components/StatusBadge/index.js +17 -0
  18. package/admin/src/components/StatusBadge/styles.js +17 -0
  19. package/admin/src/hooks/useConfig.js +43 -0
  20. package/admin/src/index.js +31 -2
  21. package/admin/src/pages/Details/components/DetailsEntity/index.js +42 -24
  22. package/admin/src/pages/Details/components/DetailsFilters/index.js +8 -10
  23. package/admin/src/pages/Details/index.js +11 -17
  24. package/admin/src/pages/Discover/components/DiscoverTableRow/index.js +30 -67
  25. package/admin/src/pages/Settings/index.js +342 -0
  26. package/admin/src/pages/Settings/utils/api.js +50 -0
  27. package/admin/src/permissions.js +3 -0
  28. package/admin/src/translations/en.json +50 -16
  29. package/admin/src/utils/index.js +2 -1
  30. package/admin/src/utils/parseRegExp.js +10 -0
  31. package/admin/src/utils/resolveCommentStatus.js +4 -4
  32. package/package.json +12 -7
  33. package/server/bootstrap.js +24 -0
  34. package/server/config/index.js +1 -0
  35. package/server/controllers/admin.js +33 -8
  36. package/server/controllers/client.js +4 -4
  37. package/server/controllers/utils/__tests__/functions.test.js +2 -2
  38. package/server/controllers/utils/parsers.js +3 -1
  39. package/server/graphql/index.js +7 -8
  40. package/server/graphql/queries/findAllFlat.js +4 -2
  41. package/server/graphql/queries/findAllInHierarchy.js +3 -3
  42. package/server/graphql/resolvers-config.js +2 -4
  43. package/server/graphql/types/comment-nested.js +1 -0
  44. package/server/graphql/types/report-reason.js +10 -10
  45. package/server/register.js +1 -3
  46. package/server/routes/admin.js +24 -0
  47. package/server/services/__tests__/common.test.js +246 -0
  48. package/server/services/admin.js +60 -6
  49. package/server/services/client.js +13 -9
  50. package/server/services/common.js +86 -11
  51. package/server/services/utils/__tests__/functions.test.js +20 -73
  52. package/server/services/utils/functions.js +9 -17
  53. package/server/utils/__tests__/functions.test.js +17 -0
  54. package/server/utils/constants.js +10 -0
  55. package/strapi-server.js +12 -5
  56. package/.circleci/config.yml +0 -48
  57. package/.eslintrc +0 -35
  58. package/.github/pull_request_template.md +0 -13
  59. package/.nvmrc +0 -1
  60. package/admin/src/components/DiscussionThreadItemMeta/index.js +0 -42
  61. package/admin/src/components/DiscussionThreadItemWarnings/index.js +0 -147
  62. package/admin/src/components/DiscussionThreadItemWarnings/styles.js +0 -7
  63. package/codecov.yml +0 -4
  64. package/public/assets/logo.png +0 -0
  65. package/public/assets/preview.png +0 -0
@@ -0,0 +1,342 @@
1
+ import React, { useRef, useMemo, useState } from 'react';
2
+ import { useQuery } from 'react-query';
3
+ import { Formik } from 'formik';
4
+ import { capitalize, first, orderBy, isEmpty } from 'lodash';
5
+ import {
6
+ CheckPermissions,
7
+ LoadingIndicatorPage,
8
+ Form,
9
+ useTracking,
10
+ useNotification,
11
+ useRBAC,
12
+ useFocusWhenNavigate,
13
+ useOverlayBlocker,
14
+ } from '@strapi/helper-plugin';
15
+ import { Main } from '@strapi/design-system/Main';
16
+ import { ContentLayout, HeaderLayout } from '@strapi/design-system/Layout';
17
+ import { Button } from '@strapi/design-system/Button';
18
+ import { Box } from '@strapi/design-system/Box';
19
+ import { Stack } from '@strapi/design-system/Stack';
20
+ import { Typography } from '@strapi/design-system/Typography';
21
+ import { Grid, GridItem } from '@strapi/design-system/Grid';
22
+ import { ToggleInput } from '@strapi/design-system/ToggleInput';
23
+ import { Select, Option } from '@strapi/design-system/Select';
24
+ import { useNotifyAT } from '@strapi/design-system/LiveRegions';
25
+ import {
26
+ Card,
27
+ CardBody,
28
+ CardContent,
29
+ } from '@strapi/design-system/Card';
30
+ import { Check, Refresh } from '@strapi/icons';
31
+
32
+ import pluginPermissions from '../../permissions';
33
+ import useConfig from '../../hooks/useConfig';
34
+ import { fetchAllContentTypes, fetchRoles } from './utils/api';
35
+ import { getMessage, parseRegExp } from '../../utils';
36
+ import ConfirmationDialog from '../../components/ConfirmationDialog';
37
+
38
+
39
+ const Settings = () => {
40
+ useFocusWhenNavigate();
41
+
42
+ const { notifyStatus } = useNotifyAT();
43
+ const { trackUsage } = useTracking();
44
+ const trackUsageRef = useRef(trackUsage);
45
+ const toggleNotification = useNotification();
46
+ const { lockApp, unlockApp } = useOverlayBlocker();
47
+
48
+ const viewPermissions = useMemo(() => ({
49
+ access: pluginPermissions.settings.read,
50
+ change: pluginPermissions.settings.change,
51
+ }), []);
52
+
53
+ const {
54
+ isLoading: isLoadingForPermissions,
55
+ allowedActions: { canAccess, canChange },
56
+ } = useRBAC(viewPermissions);
57
+
58
+ const [restoreConfigmationVisible, setRestoreConfigmationVisible] = useState(false);
59
+
60
+ const { fetch, submitMutation, restoreMutation } = useConfig(toggleNotification);
61
+ const { data: configData, isLoading: isConfigLoading, err: configErr } = fetch;
62
+
63
+ const { data: allCollectionsData, isLoading: areCollectionsLoading, err: collectionsErr } = useQuery(
64
+ ['get-all-content-types', canAccess],
65
+ () => fetchAllContentTypes(toggleNotification)
66
+ );
67
+
68
+ const { data: allRolesData, isLoading: areRolesLoading, err: rolesErr } = useQuery(
69
+ ['get-all-roles', canAccess],
70
+ () => fetchRoles(toggleNotification)
71
+ );
72
+
73
+ const isLoading = isLoadingForPermissions || isConfigLoading || areCollectionsLoading || areRolesLoading;
74
+ const isError = configErr || collectionsErr || rolesErr;
75
+
76
+ const preparePayload = ({ enabledCollections, gqlAuthEnabled, approvalFlow, entryLabel, ...rest }) => ({
77
+ ...rest,
78
+ enabledCollections,
79
+ approvalFlow: approvalFlow.filter(_ => enabledCollections.includes(_)),
80
+ entryLabel: {
81
+ ...Object.keys(entryLabel).reduce((prev, curr) => ({
82
+ ...prev,
83
+ [curr]: enabledCollections.includes(curr) ? entryLabel[curr] : undefined,
84
+ }), {}),
85
+ '*': entryLabel['*'],
86
+ },
87
+ reportReasons: configData?.reportReasons,
88
+ gql: gqlAuthEnabled ? { auth: true } : undefined,
89
+ });
90
+
91
+ if (isLoading || isError) {
92
+ return (<LoadingIndicatorPage>
93
+ { getMessage('page.settings.loading')}
94
+ </LoadingIndicatorPage>);
95
+ }
96
+
97
+ const regexUID = !isLoading ? new RegExp(
98
+ parseRegExp(fetch.data.regex?.uid).value,
99
+ parseRegExp(fetch.data.regex?.uid).flags
100
+ ) : null;
101
+
102
+ const allRoles = allRolesData?.data || [];
103
+ const allCollections = !isLoading && allCollectionsData
104
+ .filter(({ uid }) => first(uid.split(regexUID).filter(s => s && s.length > 0)) === 'api');
105
+ const enabledCollections = configData?.enabledCollections
106
+ ?.map(uid => allCollections.find(_ => _.uid === uid) ? uid : undefined)
107
+ .filter(_ => _) || [];
108
+ const entryLabel = configData?.entryLabel || {};
109
+ const approvalFlow = configData?.approvalFlow || [];
110
+ const badWords = configData?.badWords || undefined;
111
+ const isGQLPluginEnabled = configData?.isGQLPluginEnabled;
112
+ const gqlAuthEnabled = configData?.gql?.auth || undefined;
113
+ const moderatorRoles = configData?.moderatorRoles
114
+ ?.filter(code => allRoles.find(_ => _.code === code)) || [];
115
+
116
+ const changeApprovalFlowFor = (uid, current, value) => {
117
+ const currentSet = new Set(current);
118
+ if (value) {
119
+ currentSet.add(uid);
120
+ } else {
121
+ currentSet.delete(uid);
122
+ }
123
+ return Array.from(currentSet);
124
+ };
125
+
126
+ const changeEntryLabelFor = (uid, current, value) => ({
127
+ ...current,
128
+ [uid]: value && !isEmpty(value) ? [...value] : undefined,
129
+ });
130
+
131
+ const handleUpdateConfiguration = async (form) => {
132
+ if (canChange) {
133
+ lockApp();
134
+ await submitMutation.mutateAsync(preparePayload(form));
135
+ unlockApp();
136
+ }
137
+ };
138
+
139
+ const handleRestoreConfirmation = () => setRestoreConfigmationVisible(true);
140
+ const handleRestoreConfiguration = async () => {
141
+ if (canChange) {
142
+ lockApp();
143
+ await restoreMutation.mutateAsync();
144
+ unlockApp();
145
+ setRestoreConfigmationVisible(false);
146
+ }
147
+ };
148
+ const handleRestoreCancel = () => setRestoreConfigmationVisible(false);
149
+
150
+ const boxDefaultProps = {
151
+ background: "neutral0",
152
+ hasRadius: true,
153
+ shadow: "filterShadow",
154
+ padding: 6,
155
+ };
156
+
157
+ return (
158
+ <Main>
159
+ <Formik
160
+ initialValues={{
161
+ enabledCollections,
162
+ moderatorRoles,
163
+ badWords,
164
+ approvalFlow,
165
+ entryLabel,
166
+ gqlAuthEnabled,
167
+ }}
168
+ enableReinitialize={true}
169
+ onSubmit={handleUpdateConfiguration}
170
+ >
171
+ {({ handleSubmit, setFieldValue, values }) => (
172
+ <Form noValidate onSubmit={handleSubmit}>
173
+ <HeaderLayout
174
+ title={getMessage('page.settings.header.title')}
175
+ subtitle={getMessage('page.settings.header.description')}
176
+ primaryAction={
177
+ <CheckPermissions permissions={pluginPermissions.settingsChange}>
178
+ <Button type="submit" startIcon={<Check />} >
179
+ {getMessage('page.settings.actions.submit')}
180
+ </Button>
181
+ </CheckPermissions>
182
+ }
183
+ />
184
+ <ContentLayout>
185
+ <Stack size={6}>
186
+ <Box {...boxDefaultProps}>
187
+ <Stack size={4}>
188
+ <Typography variant="delta" as="h2">
189
+ {getMessage('page.settings.section.general')}
190
+ </Typography>
191
+ <Grid gap={4}>
192
+ <GridItem col={12}>
193
+ <Select
194
+ name="enabledCollections"
195
+ label={getMessage('page.settings.form.enabledCollections.label')}
196
+ placeholder={getMessage('page.settings.form.enabledCollections.placeholder')}
197
+ hint={getMessage('page.settings.form.enabledCollections.hint')}
198
+ onClear={() => setFieldValue('enabledCollections', [], false)}
199
+ value={values.enabledCollections}
200
+ onChange={(value) => setFieldValue('enabledCollections', value, false)}
201
+ multi
202
+ withTags
203
+ >
204
+ { allCollections.map(({ uid, schema: { displayName } }) =>
205
+ (<Option key={uid} value={uid}>{displayName}</Option>))}
206
+ </Select>
207
+ </GridItem>
208
+ <GridItem col={6} xs={12}>
209
+ <Select
210
+ name="moderatorRoles"
211
+ label={getMessage('page.settings.form.moderatorRoles.label')}
212
+ placeholder={getMessage('page.settings.form.moderatorRoles.placeholder')}
213
+ hint={getMessage('page.settings.form.moderatorRoles.hint')}
214
+ onClear={() => setFieldValue('moderatorRoles', [], false)}
215
+ value={values.moderatorRoles}
216
+ onChange={(value) => setFieldValue('moderatorRoles', value, false)}
217
+ multi
218
+ withTags
219
+ >
220
+ { allRoles.map(({ code, name }) =>
221
+ (<Option key={code} value={code}>{name}</Option>))}
222
+ </Select>
223
+ </GridItem>
224
+ <GridItem col={6} xs={12}>
225
+ <ToggleInput
226
+ name="badWords"
227
+ label={getMessage('page.settings.form.badWords.label')}
228
+ hint={getMessage('page.settings.form.badWords.hint')}
229
+ checked={values.badWords}
230
+ onChange={({ target: { checked } }) => setFieldValue('badWords', checked, false)}
231
+ onLabel={getMessage('compontents.toogle.enabled')}
232
+ offLabel={getMessage('compontents.toogle.disabled')}
233
+ />
234
+ </GridItem>
235
+ { isGQLPluginEnabled && (<GridItem col={6} xs={12}>
236
+ <ToggleInput
237
+ name="badWords"
238
+ label={getMessage('page.settings.form.gqlAuth.label')}
239
+ hint={getMessage('page.settings.form.gqlAuth.hint')}
240
+ checked={values.gqlAuthEnabled}
241
+ onChange={({ target: { checked } }) => setFieldValue('gqlAuthEnabled', checked, false)}
242
+ onLabel={getMessage('compontents.toogle.enabled')}
243
+ offLabel={getMessage('compontents.toogle.disabled')}
244
+ />
245
+ </GridItem>) }
246
+ </Grid>
247
+ </Stack>
248
+ </Box>
249
+ { !isEmpty(values.enabledCollections) && (<Box {...boxDefaultProps}>
250
+ <Stack size={4}>
251
+ <Typography variant="delta" as="h2">
252
+ {getMessage('page.settings.section.collections')}
253
+ </Typography>
254
+ <Grid gap={4}>
255
+ { orderBy(values.enabledCollections).map(uid => {
256
+ const { schema: { displayName, attributes = {} } } = allCollections.find(_ => _.uid === uid);
257
+ const stringAttributes = Object.keys(attributes).filter(_ => attributes[_].type === 'string');
258
+ return (<GridItem key={`collectionSettings-${uid}`} col={6} s={12} xs={12}>
259
+ <Card background="primary100" borderColor="primary200">
260
+ <CardBody>
261
+ <CardContent style={{ width: '100%' }}>
262
+ <Stack size={4}>
263
+ <Typography variant="epsilon" fontWeight="semibold" as="h3">{ displayName }</Typography>
264
+ <ToggleInput
265
+ name={`collectionSettings-${uid}-approvalFlow`}
266
+ label={getMessage('page.settings.form.approvalFlow.label')}
267
+ hind={getMessage({
268
+ id: 'page.settings.form.approvalFlow.hint',
269
+ params: { name: displayName },
270
+ })}
271
+ checked={values.approvalFlow.includes(uid)}
272
+ onChange={({ target: { checked } }) => setFieldValue('approvalFlow', changeApprovalFlowFor(uid, values.approvalFlow, checked), [])}
273
+ onLabel={getMessage('compontents.toogle.enabled')}
274
+ offLabel={getMessage('compontents.toogle.disabled')}
275
+ />
276
+ { !isEmpty(stringAttributes) && (<Select
277
+ name={`collectionSettings-${uid}-entryLabel`}
278
+ label={getMessage('page.settings.form.entryLabel.label')}
279
+ placeholder={getMessage('page.settings.form.entryLabel.placeholder')}
280
+ hint={getMessage('page.settings.form.entryLabel.hint')}
281
+ onClear={() => setFieldValue('entryLabel', changeEntryLabelFor(uid, values.entryLabel))}
282
+ value={values.entryLabel[uid] || []}
283
+ onChange={(value) => setFieldValue('entryLabel', changeEntryLabelFor(uid, values.entryLabel, value))}
284
+ multi
285
+ withTags
286
+ >
287
+ { stringAttributes.map(key =>
288
+ (<Option key={`collectionSettings-${uid}-entryLabel-${key}`} value={key}>{ capitalize(key.split('_').join(' ')) }</Option>))}
289
+ </Select>) }
290
+ </Stack>
291
+ </CardContent>
292
+ </CardBody>
293
+ </Card>
294
+ </GridItem>);
295
+ })}
296
+ </Grid>
297
+ </Stack>
298
+ </Box>)}
299
+
300
+ <CheckPermissions permissions={pluginPermissions.settingsChange}>
301
+ <Box {...boxDefaultProps}>
302
+ <Stack size={4}>
303
+ <Stack size={2}>
304
+ <Typography variant="delta" textColor="danger700" as="h2">
305
+ {getMessage('page.settings.section.restore')}
306
+ </Typography>
307
+ <Typography variant="pi"as="h4">
308
+ {getMessage('page.settings.section.restore.subtitle')}
309
+ </Typography>
310
+ </Stack>
311
+ <Grid gap={4}>
312
+ <GridItem col={6}>
313
+ <Button variant="danger-light" startIcon={<Refresh />} onClick={handleRestoreConfirmation}>
314
+ {getMessage('page.settings.actions.restore')}
315
+ </Button>
316
+
317
+ <ConfirmationDialog
318
+ isVisible={restoreConfigmationVisible}
319
+ isActionAsync={restoreMutation.isLoading}
320
+ header={getMessage('page.settings.actions.restore.confirmation.header')}
321
+ labelConfirm={getMessage('page.settings.actions.restore.confirmation.button.confirm')}
322
+ iconConfirm={<Refresh />}
323
+ onConfirm={handleRestoreConfiguration}
324
+ onCancel={handleRestoreCancel}>
325
+ { getMessage('page.settings.actions.restore.confirmation.description') }
326
+ </ConfirmationDialog>
327
+ </GridItem>
328
+ </Grid>
329
+ </Stack>
330
+ </Box>
331
+ </CheckPermissions>
332
+ </Stack>
333
+ </ContentLayout>
334
+ </Form>
335
+ )}
336
+ </Formik>
337
+ </Main>
338
+ );
339
+ }
340
+
341
+
342
+ export default Settings;
@@ -0,0 +1,50 @@
1
+ import { getApiURL, axiosInstance, handleAPIError } from '../../../utils';
2
+
3
+ // eslint-disable-next-line import/prefer-default-export
4
+ export const fetchConfig = async (toggleNotification) => {
5
+ try {
6
+ const { data } = await axiosInstance.get(getApiURL(`settings/config`));
7
+
8
+ return data;
9
+ } catch (err) {
10
+ handleAPIError(err, toggleNotification);
11
+ }
12
+ };
13
+
14
+ export const updateConfig = async (body, toggleNotification) => {
15
+ try {
16
+ const { data } = await axiosInstance.put(getApiURL(`settings/config`), body);
17
+
18
+ return data;
19
+ } catch (err) {
20
+ handleAPIError(err, toggleNotification);
21
+ }
22
+ };
23
+
24
+ export const restoreConfig = async (toggleNotification) => {
25
+ try {
26
+ const { data } = await axiosInstance.delete(getApiURL(`settings/config`));
27
+
28
+ return data;
29
+ } catch (err) {
30
+ handleAPIError(err, toggleNotification);
31
+ }
32
+ };
33
+
34
+ export const fetchAllContentTypes = async (toggleNotification) => {
35
+ try {
36
+ const { data } = await axiosInstance.get(`/content-type-builder/content-types`);
37
+ return data?.data;
38
+ } catch (err) {
39
+ handleAPIError(err, toggleNotification);
40
+ }
41
+ };
42
+
43
+ export const fetchRoles = async (toggleNotification) => {
44
+ try {
45
+ const { data } = await axiosInstance.get(`/admin/roles`);
46
+ return data;
47
+ } catch (err) {
48
+ handleAPIError(err, toggleNotification);
49
+ }
50
+ };
@@ -6,11 +6,14 @@ const pluginPermissions = {
6
6
  { action: permissions.render(permissions.reports.read), subject: null },
7
7
  // { action: 'plugin::comments.discover.read', subject: null },
8
8
  { action: permissions.render(permissions.settings.read), subject: null },
9
+ { action: permissions.render(permissions.settings.read), subject: null },
9
10
  ],
10
11
  // discover: [{ action: 'plugin::comments.discover.read', subject: null }],
11
12
  moderate: [{ action: permissions.render(permissions.comments.moderate), subject: null }],
12
13
  reports: [{ action: permissions.render(permissions.reports.read), subject: null }],
13
14
  reportsReview: [{ action: permissions.render(permissions.reports.action), subject: null }],
15
+ settings: [{ action: permissions.render(permissions.settings.read), subject: null }],
16
+ settingsChange: [{ action: permissions.render(permissions.settings.change), subject: null }],
14
17
  };
15
18
 
16
19
  export default pluginPermissions;
@@ -1,5 +1,7 @@
1
1
  {
2
2
  "plugin.name": "Comments",
3
+ "plugin.section": "Comments plugin",
4
+ "plugin.section.item": "Configuration",
3
5
  "common.button.back": "Go back",
4
6
  "nav.header.moderation": "Moderation",
5
7
  "nav.header.settings": "Settings",
@@ -7,12 +9,12 @@
7
9
  "nav.item.discover": "Discover",
8
10
  "nav.item.reports": "Reports",
9
11
  "nav.item.settings": "Settings",
10
- "page.common.item.status.OPEN": "OPEN",
11
- "page.common.item.status.BLOCKED": "BLOCKED",
12
- "page.common.item.status.APPROVED": "APPROVED",
13
- "page.common.item.status.TO_REVIEW": "TO REVIEW ({count, number})",
14
- "page.common.item.status.REJECTED": "REJECTED",
15
- "page.common.item.status.REMOVED": "REMOVED BY AUTHOR",
12
+ "page.common.item.status.OPEN": "Open",
13
+ "page.common.item.status.BLOCKED": "Blocked",
14
+ "page.common.item.status.APPROVED": "Approved",
15
+ "page.common.item.status.TO_REVIEW": "To review ({count, number})",
16
+ "page.common.item.status.REJECTED": "Rejected",
17
+ "page.common.item.status.REMOVED": "Removed by author",
16
18
  "page.updates.header": "Recent updates",
17
19
  "page.discover.header": "Discover threads",
18
20
  "page.discover.header.count": "entries found",
@@ -47,17 +49,17 @@
47
49
  "page.details.panel.discussion.warnings.reports.dialog.content": "Content",
48
50
  "page.details.panel.discussion.warnings.reports.dialog.createdAt": "Issue date",
49
51
  "page.details.panel.discussion.warnings.reports.dialog.status": "Status",
50
- "page.details.panel.discussion.warnings.reports.dialog.status.OPEN": "OPEN",
51
- "page.details.panel.discussion.warnings.reports.dialog.status.RESOLVED": "RESOLVED",
52
+ "page.details.panel.discussion.warnings.reports.dialog.status.OPEN": "Open",
53
+ "page.details.panel.discussion.warnings.reports.dialog.status.RESOLVED": "Resolved",
52
54
  "page.details.panel.discussion.warnings.reports.dialog.actions": "Actions",
53
55
  "page.details.panel.discussion.warnings.reports.dialog.actions.resolve": "Resolve",
54
56
  "page.details.panel.discussion.warnings.reports.dialog.confirmation.success": "Open report has been resolved",
55
- "page.details.panel.discussion.status.blocked": "BLOCKED",
57
+ "page.details.panel.discussion.status.blocked": "Blocked",
56
58
  "page.details.panel.entity": "Entity details",
57
59
  "page.details.panel.status": "Discussion is",
58
- "page.details.panel.status.open": "OPEN",
59
- "page.details.panel.status.blocked": "BLOCKED",
60
- "page.details.panel.status.pending": "PENDING",
60
+ "page.details.panel.status.open": "Open",
61
+ "page.details.panel.status.blocked": "Blocked",
62
+ "page.details.panel.status.pending": "Pending",
61
63
  "page.details.panel.loading": "Fetching entity...",
62
64
  "page.details.actions.comment.block": "Block comment",
63
65
  "page.details.actions.comment.block.confirmation.header": "Moderation: Block comment",
@@ -77,12 +79,44 @@
77
79
  "page.details.actions.comment.approve.confirmation.success": "Comment has been approved",
78
80
  "page.details.actions.comment.reject.confirmation.success": "Comment has been rejected",
79
81
  "page.details.filters.label": "View",
80
- "page.details.filters.removed.visibility": "Comments visibility",
81
- "page.details.filters.removed.show": "Show removed",
82
- "page.details.filters.removed.hide": "Hide removed",
82
+ "page.details.filters.removed.visibility": "Show removed comments",
83
83
  "page.coming.soon": "Wait for what is coming soon...",
84
+ "page.settings": "Settings",
85
+ "page.settings.header.title": "Comments",
86
+ "page.settings.header.description": "Configure your comments moderation capabilities",
87
+ "page.settings.actions.submit": "Save",
88
+ "page.settings.actions.restore": "Restore",
89
+ "page.settings.section.general": "General configuration",
90
+ "page.settings.section.collections": "Collection(s) configuration",
91
+ "page.settings.section.restore": "Restore default configuration",
92
+ "page.settings.section.restore.subtitle": "Discarding all of applied settings and getting back to plugin default ones. Use reasonable.",
93
+ "page.settings.form.enabledCollections.label": "Enable Comments for Collection(s)",
94
+ "page.settings.form.enabledCollections.placeholder": "Select one or more collection",
95
+ "page.settings.form.enabledCollections.hint": "If none of collection is selected, feature are enabled for any of exsiting",
96
+ "page.settings.form.moderatorRoles.label": "Send significant notifications to",
97
+ "page.settings.form.moderatorRoles.placeholder": "Select one or more roles",
98
+ "page.settings.form.moderatorRoles.hint": "Roles which are going to be notified by the plugin about significant actions to perform",
99
+ "page.settings.form.badWords.label": "Bad words filtering",
100
+ "page.settings.form.badWords.hint": "If enabled, every post / update of comment is going to be checked against bad wording",
101
+ "page.settings.form.gqlAuth.label": "GraphQL queries authorization",
102
+ "page.settings.form.gqlAuth.hint": "If enabled, GraphQL API queries & mutations can be triggered only by Authenticated Strapi users. Otherwise API remains open.",
103
+ "page.settings.form.approvalFlow.label": "Enable approval flow",
104
+ "page.settings.form.approvalFlow.hint": "Comments for \"{name}\" are going to be taken through manual approval flow",
105
+ "page.settings.form.entryLabel.label": "Title fields",
106
+ "page.settings.form.entryLabel.placeholder": "Select at least one or leave empty to apply defaults",
107
+ "page.settings.form.entryLabel.hint": "If left empty title rendering is going to take following ordered fields: \"Title\", \"Subject\" & \"Name\"",
108
+ "page.settings.actions.restore.confirmation.header": "Restore default configuration",
109
+ "page.settings.actions.restore.confirmation.description": "You're about to restore plugin configuration to it default values. It might have destructive impact on already collected content. Do you really want to proceed?",
110
+ "page.settings.actions.restore.confirmation.button.confirm": "Yes, I want to restore",
111
+ "page.settings.notification.submit.success": "Configuration has been saved successfully",
112
+ "page.settings.notification.submit.error": "Failed to save configuration. Try again.",
113
+ "page.settings.notification.restore.success": "Configuration has been restored successfully",
114
+ "page.settings.notification.restore.error": "Failed to restore configuration. Try again.",
115
+ "page.settings.loading": "Fetching configuration...",
84
116
  "compontents.confirmation.dialog.header": "Confirmation",
85
117
  "compontents.confirmation.dialog.description": "Do you really want to perform this action?",
86
118
  "compontents.confirmation.dialog.button.confirm": "Yes, I do",
87
- "compontents.confirmation.dialog.button.cancel": "Cancel"
119
+ "compontents.confirmation.dialog.button.cancel": "Cancel",
120
+ "compontents.toogle.enabled": "Enabled",
121
+ "compontents.toogle.disabled": "Disabled"
88
122
  }
@@ -7,4 +7,5 @@ export { default as getRelatedGroups } from './getRelatedGroups';
7
7
  export { default as renderEntryTitle } from './renderEntryTitle';
8
8
  export { default as renderInitials } from './renderInitials';
9
9
  export { default as resolveCommentStatus } from './resolveCommentStatus';
10
- export { default as resolveCommentStatusColor } from './resolveCommentStatusColor';
10
+ export { default as resolveCommentStatusColor } from './resolveCommentStatusColor';
11
+ export { default as parseRegExp } from './parseRegExp';
@@ -0,0 +1,10 @@
1
+
2
+ const parseRegExp = (regexpString) => {
3
+ const [value, flags] = regexpString.split('/').filter(_ => _.length > 0);
4
+ return {
5
+ value,
6
+ flags,
7
+ };
8
+ }
9
+
10
+ export default parseRegExp;
@@ -4,10 +4,6 @@ import { COMMENT_STATUS } from './constants';
4
4
  const resolveCommentStatus = ({ removed, blocked, blockedThread, approvalStatus, reviewFlowEnabled }) => {
5
5
  const gotApprovalFlow = !isNil(approvalStatus);
6
6
 
7
- if (reviewFlowEnabled) {
8
- return COMMENT_STATUS.TO_REVIEW;
9
- }
10
-
11
7
  if (removed) {
12
8
  return COMMENT_STATUS.REMOVED;
13
9
  }
@@ -15,6 +11,10 @@ const resolveCommentStatus = ({ removed, blocked, blockedThread, approvalStatus,
15
11
  if (blocked || blockedThread) {
16
12
  return COMMENT_STATUS.BLOCKED;
17
13
  }
14
+
15
+ if (reviewFlowEnabled) {
16
+ return COMMENT_STATUS.TO_REVIEW;
17
+ }
18
18
 
19
19
  if (gotApprovalFlow) {
20
20
  const status = approvalStatus.toUpperCase();
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "strapi-plugin-comments",
3
- "version": "2.0.0-rc.2",
3
+ "version": "2.0.3",
4
4
  "description": "Strapi - Comments plugin",
5
5
  "strapi": {
6
6
  "name": "comments",
7
- "description": "Comments & audience engagement",
7
+ "displayName": "Comments",
8
+ "description": "Powerful Strapi based comments moderation tool for you and your users",
8
9
  "kind": "plugin"
9
10
  },
10
11
  "scripts": {
12
+ "prepare": "husky install",
11
13
  "publish": "npm publish --tag latest",
12
14
  "test:unit": "jest --verbose --coverage"
13
15
  },
@@ -16,22 +18,25 @@
16
18
  "url": "https://github.com/VirtusLab-Open-Source/strapi-plugin-comments"
17
19
  },
18
20
  "dependencies": {
19
- "@strapi/utils": "^4.0.6",
20
- "@strapi/plugin-graphql": "^4.0.6",
21
+ "@strapi/utils": "^4.0.7",
22
+ "@strapi/plugin-graphql": "^4.0.7",
21
23
  "bad-words": "^3.0.4"
22
24
  },
23
25
  "devDependencies": {
24
26
  "codecov": "^3.7.2",
25
27
  "jest": "^26.0.1",
26
28
  "jest-cli": "^26.0.1",
27
- "jest-styled-components": "^7.0.2"
29
+ "jest-styled-components": "^7.0.2",
30
+ "koa": "2.13.3",
31
+ "husky": "7.0.4",
32
+ "prettier": "2.5.1"
28
33
  },
29
34
  "peerDependencies": {
30
35
  "@strapi/strapi": "4.x"
31
36
  },
32
37
  "author": {
33
- "name": "VirtusLab // Mateusz Ziarko",
34
- "email": "mziarko@virtuslab.com",
38
+ "name": "VirtusLab",
39
+ "email": "strapi@virtuslab.com",
35
40
  "url": "https://virtuslab.com"
36
41
  },
37
42
  "maintainers": [
@@ -1,8 +1,20 @@
1
1
  'use strict';
2
2
 
3
+ const { isEmpty } = require('lodash');
3
4
  const permissions = require('./../permissions');
5
+ const { getPluginService } = require('./utils/functions');
4
6
 
5
7
  module.exports = async ({ strapi }) => {
8
+
9
+ // Provide GQL support
10
+ if (strapi.plugin('graphql')) {
11
+ const config = await getPluginService('common').getConfig();
12
+ const { enabledCollections } = config;
13
+ if (!isEmpty(enabledCollections)) {
14
+ await require('./graphql')({ strapi, config });
15
+ }
16
+ }
17
+
6
18
  // Check if the plugin users-permissions is installed because the navigation needs it
7
19
  if (Object.keys(strapi.plugins).indexOf("users-permissions") === -1) {
8
20
  throw new Error(
@@ -35,6 +47,18 @@ module.exports = async ({ strapi }) => {
35
47
  uid: permissions.reports.action,
36
48
  pluginName: "comments",
37
49
  },
50
+ {
51
+ section: "plugins",
52
+ displayName: "Settings: Read",
53
+ uid: permissions.settings.read,
54
+ pluginName: "comments",
55
+ },
56
+ {
57
+ section: "plugins",
58
+ displayName: "Settings: Change",
59
+ uid: permissions.settings.change,
60
+ pluginName: "comments",
61
+ },
38
62
  ];
39
63
 
40
64
  await strapi.admin.services.permission.actionProvider.registerMany(actions);
@@ -2,6 +2,7 @@
2
2
 
3
3
  module.exports = {
4
4
  default: {
5
+ enabledCollections: [],
5
6
  moderatorRoles: [],
6
7
  approvalFlow: [],
7
8
  entryLabel: {