strapi-plugin-comments 2.0.0-beta.4 → 2.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +421 -72
  2. package/admin/src/components/DiscussionThread/index.js +9 -2
  3. package/admin/src/components/DiscussionThreadItem/index.js +27 -12
  4. package/admin/src/components/DiscussionThreadItemActions/index.js +61 -50
  5. package/admin/src/components/DiscussionThreadItemActions/styles.js +0 -4
  6. package/admin/src/components/DiscussionThreadItemApprovalFlowActions/index.js +81 -0
  7. package/admin/src/components/DiscussionThreadItemApprovalFlowActions/styles.js +6 -0
  8. package/admin/src/components/DiscussionThreadItemMeta/index.js +16 -5
  9. package/admin/src/components/DiscussionThreadItemWarnings/index.js +16 -10
  10. package/admin/src/components/Nav/index.js +5 -1
  11. package/admin/src/components/ReportsReviewModal/index.js +5 -0
  12. package/admin/src/components/ReportsReviewTable/index.js +60 -52
  13. package/admin/src/components/icons/review.js +2 -2
  14. package/admin/src/index.js +1 -1
  15. package/admin/src/pages/Details/components/DetailsEntity/index.js +66 -0
  16. package/admin/src/pages/Details/components/DetailsFilters/index.js +37 -0
  17. package/admin/src/pages/Details/components/DetailsFilters/styles.js +8 -0
  18. package/admin/src/pages/Details/index.js +75 -42
  19. package/admin/src/pages/Discover/components/DiscoverTableRow/index.js +69 -48
  20. package/admin/src/pages/Discover/components/DiscoverTableRow/styles.js +8 -0
  21. package/admin/src/pages/Discover/index.js +72 -56
  22. package/admin/src/pages/{Details/utils → utils}/api.js +15 -3
  23. package/admin/src/permissions.js +10 -8
  24. package/admin/src/translations/en.json +17 -4
  25. package/admin/src/utils/constants.js +3 -6
  26. package/admin/src/utils/getMessage.js +1 -1
  27. package/admin/src/utils/getRelatedGroups.js +1 -3
  28. package/admin/src/utils/index.js +5 -1
  29. package/admin/src/utils/renderEntryTitle.js +14 -0
  30. package/admin/src/utils/renderInitials.js +5 -0
  31. package/admin/src/utils/resolveCommentStatus.js +30 -0
  32. package/admin/src/utils/resolveCommentStatusColor.js +22 -0
  33. package/package.json +8 -3
  34. package/permissions.js +19 -0
  35. package/public/assets/logo.png +0 -0
  36. package/public/assets/preview.png +0 -0
  37. package/server/bootstrap.js +38 -2
  38. package/server/config/index.js +9 -3
  39. package/server/controllers/admin.js +3 -3
  40. package/server/controllers/client.js +4 -18
  41. package/server/controllers/utils/parsers.js +17 -0
  42. package/server/graphql/index.js +26 -0
  43. package/server/graphql/mutations/createAbuseReport.js +20 -0
  44. package/server/graphql/mutations/createComment.js +20 -0
  45. package/server/graphql/mutations/index.js +21 -0
  46. package/server/graphql/mutations/removeComment.js +20 -0
  47. package/server/graphql/mutations/updateComment.js +20 -0
  48. package/server/graphql/queries/findAllFlat.js +29 -0
  49. package/server/graphql/queries/findAllInHierarchy.js +31 -0
  50. package/server/graphql/queries/index.js +19 -0
  51. package/server/graphql/resolvers-config.js +10 -0
  52. package/server/graphql/types/comment-author-user.js +9 -0
  53. package/server/graphql/types/comment-author.js +9 -0
  54. package/server/graphql/types/comment-nested.js +16 -0
  55. package/server/graphql/types/comment.js +16 -0
  56. package/server/graphql/types/create-comment-author.js +9 -0
  57. package/server/graphql/types/create-comment.js +9 -0
  58. package/server/graphql/types/create-report.js +9 -0
  59. package/server/graphql/types/id.js +24 -0
  60. package/server/graphql/types/identify-comment-author.js +6 -0
  61. package/server/graphql/types/index.js +17 -0
  62. package/server/graphql/types/remove-comment.js +8 -0
  63. package/server/graphql/types/report-reason.js +10 -0
  64. package/server/graphql/types/report.js +11 -0
  65. package/server/graphql/types/update-comment.js +9 -0
  66. package/server/register.js +3 -1
  67. package/server/services/admin.js +42 -15
  68. package/server/services/client.js +54 -38
  69. package/server/services/common.js +38 -67
  70. package/server/services/graphql.js +190 -0
  71. package/server/services/index.js +2 -0
  72. package/server/services/utils/functions.js +24 -17
  73. package/server/utils/constants.js +3 -3
  74. package/strapi-server.js +33 -0
  75. package/admin/src/components/EntityDetails/index.js +0 -53
@@ -2,7 +2,8 @@ import React, { useState } from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import { useIntl } from 'react-intl';
4
4
  import { useMutation, useQueryClient } from 'react-query';
5
- import { first, isEmpty, orderBy } from 'lodash';
5
+ import { isNil, isEmpty, orderBy } from 'lodash';
6
+ import { Avatar, Initials } from '@strapi/design-system/Avatar';
6
7
  import { Badge } from '@strapi/design-system/Badge';
7
8
  import { Box } from '@strapi/design-system/Box';
8
9
  import { Button } from '@strapi/design-system/Button';
@@ -13,17 +14,19 @@ import { Link } from '@strapi/design-system/Link';
13
14
  import { Stack } from '@strapi/design-system/Stack';
14
15
  import { Tr, Td } from '@strapi/design-system/Table';
15
16
  import { Typography } from '@strapi/design-system/Typography';
16
- import { Eye, Lock } from '@strapi/icons';
17
+ import { Eye } from '@strapi/icons';
17
18
  import { useNotification, useOverlayBlocker } from '@strapi/helper-plugin';
18
- import { getMessage, handleAPIError } from '../../../../utils';
19
- import { COMMENT_STATUS } from '../../../../utils/constants';
19
+ import { getMessage, getUrl, handleAPIError, renderInitials, resolveCommentStatus, resolveCommentStatusColor } from '../../../../utils';
20
20
  import ReportsReviewModal from '../../../../components/ReportsReviewModal';
21
21
  import ReportsReviewTable from '../../../../components/ReportsReviewTable';
22
- import { blockItem, blockItemThread, resolveReport } from '../../../Details/utils/api';
22
+ import { blockItem, blockItemThread, resolveReport } from '../../../utils/api';
23
23
  import pluginId from '../../../../pluginId';
24
24
  import { ReviewIcon, LockIcon } from '../../../../components/icons';
25
+ import { TableLink } from './styles';
26
+ import renderEntryTitle from '../../../../utils/renderEntryTitle';
27
+ import DiscussionThreadItemApprovalFlowActions from '../../../../components/DiscussionThreadItemApprovalFlowActions';
25
28
 
26
- const DiscoverTableRow = ({ config, item, onClick }) => {
29
+ const DiscoverTableRow = ({ config, item, allowedActions: { canModerate, canAccessReports, canReviewReports }, onClick }) => {
27
30
 
28
31
  const { id, reports } = item;
29
32
 
@@ -72,14 +75,22 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
72
75
  refetchActive: false,
73
76
  });
74
77
 
75
- const handleReportsReviewClick = () => setReportsReviewVisible(true);
78
+ const handleReportsReviewClick = () => {
79
+ if (canAccessReports) {
80
+ setReportsReviewVisible(true);
81
+ }
82
+ };
76
83
  const handleBlockItemClick = async () => {
77
- lockApp();
78
- blockItemMutation.mutate(id);
84
+ if (canModerate) {
85
+ lockApp();
86
+ blockItemMutation.mutate(id);
87
+ }
79
88
  };
80
89
  const handleBlockItemThreadClick = async () => {
81
- lockApp();
82
- blockItemThreadMutation.mutate(id);
90
+ if (canModerate) {
91
+ lockApp();
92
+ blockItemThreadMutation.mutate(id);
93
+ }
83
94
  };
84
95
  const handleReportsReviewClose = async () => {
85
96
  await queryClient.invalidateQueries('get-data');
@@ -89,59 +100,56 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
89
100
  const isLoading = blockItemMutation.isLoading || blockItemThreadMutation.isLoading || resolveReportMutation.isLoading;
90
101
  const openReports = reports?.filter(_ => !_.resolved);
91
102
  const hasReports = !isEmpty(openReports);
92
- const reviewFlowEnabled = hasReports && !(item.blocked || item.blockedThread);
103
+ const reviewFlowEnabled = (canAccessReports && hasReports) && !(item.blocked || item.blockedThread);
93
104
 
94
105
  const handleClick = () => onClick();
95
106
  const handleBlockButtonsStateChange = disabled => setBlockButtonsDisabled(disabled);
96
107
 
97
- const renderStatus = ({ blocked, blockedThread }) => {
98
- let status = blocked || blockedThread ? COMMENT_STATUS.BLOCKED : COMMENT_STATUS.OPEN;
99
- if (reviewFlowEnabled) {
100
- status = COMMENT_STATUS.TO_REVIEW;
101
- }
102
- let color = 'secondary';
103
- switch (status) {
104
- case COMMENT_STATUS.OPEN:
105
- color = 'success';
106
- break;
107
- case COMMENT_STATUS.TO_REVIEW:
108
- color = 'warning';
109
- break;
110
- case COMMENT_STATUS.BLOCKED:
111
- color = 'danger';
112
- break;
113
- };
108
+ const renderStatus = (props) => {
109
+ const status = resolveCommentStatus({ ...props, reviewFlowEnabled });
110
+ const color = resolveCommentStatusColor(status);
111
+
114
112
  return (<Badge backgroundColor={`${color}100`} textColor={`${color}600`}>{ getMessage({
115
- id: `page.discover.table.header.status.${status}`,
113
+ id: `page.common.item.status.${status}`,
116
114
  props: {
117
115
  count: openReports.length
118
116
  }
119
117
  }, status) }</Badge>);
120
118
  };
121
119
 
122
- const renderEntryTitle = entry => {
123
- const { entryLabel } = config;
124
- const rule = entry.uid in entryLabel ? entryLabel[entry.uid] : entryLabel['*'];
125
- return first(
126
- Object.keys(entry)
127
- .filter(_ => (rule === _) || rule.includes(_))
128
- .map(_ => entry[_])
129
- .filter(_ => _)
130
- );
131
- };
132
-
133
120
  const renderEntryUrl = entry => `/content-manager/collectionType/${entry.uid}/${entry.id}`;
121
+ const renderDetailsUrl = entry => getUrl(`discover/${entry.id}`);
122
+
123
+ const gotApprovalFlow = !isNil(item.approvalStatus);
124
+ const needsApproval = gotApprovalFlow && (item.approvalStatus === 'PENDING');
125
+
126
+ let actionItemsCount = 1;
127
+ if(reviewFlowEnabled || needsApproval) {
128
+ actionItemsCount = 2;
129
+ }
134
130
 
135
131
  return (
136
132
  <Tr key={item.id}>
137
- <Td style={{ maxWidth: '40vw' }}>
138
- <Typography textColor="neutral800" ellipsis>{item.content}</Typography>
133
+ <Td>
134
+ <Typography textColor="neutral800" fontWeight="bold">#{ item.id }</Typography>
139
135
  </Td>
140
136
  <Td>
141
- <Typography textColor="neutral800">{item.threadOf?.id || '-'}</Typography>
137
+ <Stack size={2} horizontal>
138
+ { item.author?.avatar ? <Avatar src={item.author?.avatar} alt={item.author.name} /> : <Initials>{ renderInitials(item.author.name) }</Initials> }
139
+ <Typography textColor="neutral800" variant="pi">{ item.author.name }</Typography>
140
+ </Stack>
141
+ </Td>
142
+ <Td style={{ maxWidth: '30vw' }}>
143
+ <Typography textColor="neutral800" ellipsis>{item.content}</Typography>
142
144
  </Td>
143
145
  <Td>
144
- <Link to={ renderEntryUrl(item.related) }>{ renderEntryTitle(item.related) }</Link>
146
+ { item.threadOf?.id ? (<Link to={ renderDetailsUrl(item.threadOf) }>{ getMessage({
147
+ id: 'page.discover.table.cell.thread',
148
+ props: { id: item.threadOf.id },
149
+ }, '#' + item.threadOf.id) }</Link>) : '-' }
150
+ </Td>
151
+ <Td style={{ maxWidth: '15vw' }}>
152
+ <TableLink to={ renderEntryUrl(item.related) }>{ renderEntryTitle(item.related, config) }</TableLink>
145
153
  </Td>
146
154
  <Td>
147
155
  <Typography textColor="neutral800">{ formatDate(item.updatedAt || item.createdAt, { dateStyle: 'long', timeStyle: 'short' }) }</Typography>
@@ -151,23 +159,30 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
151
159
  </Td>
152
160
  <Td>
153
161
  <Flex direction="column" alignItems="flex-end">
154
- <Stack size={reviewFlowEnabled ? 2 : 1} horizontal>
162
+ <Stack size={actionItemsCount} horizontal>
155
163
  { reviewFlowEnabled && (<IconButton onClick={handleReportsReviewClick} label={ getMessage('page.discover.table.reports.review') } icon={<ReviewIcon />} />)}
164
+ { (canModerate && needsApproval) && (
165
+ <DiscussionThreadItemApprovalFlowActions
166
+ id={item.id}
167
+ allowedActions={{ canModerate }}
168
+ queryToInvalidate="get-data" />
169
+ )}
156
170
  <IconButton onClick={handleClick} label={ getMessage('page.discover.table.action.display') } icon={<Eye />} />
157
171
  </Stack>
158
172
  </Flex>
159
173
  { reviewFlowEnabled && (<ReportsReviewModal
160
174
  isVisible={reportsReviewVisible}
161
175
  isActionAsync={isLoading}
176
+ allowedActions={{ canModerate, canAccessReports, canReviewReports }}
162
177
  onClose={handleReportsReviewClose}
163
- startActions={<>
178
+ startActions={canModerate && (<>
164
179
  <Button onClick={handleBlockItemClick} variant="danger-light" startIcon={<LockIcon />} disabled={blockButtonsDisabled}>
165
180
  { getMessage(`page.details.actions.comment.block`, 'Block comment') }
166
181
  </Button>
167
182
  { item.gotThread && (<Button onClick={handleBlockItemThreadClick} variant="danger" startIcon={<LockIcon />} disabled={blockButtonsDisabled}>
168
183
  { getMessage(`page.details.actions.thread.block`, 'Block thread') }
169
184
  </Button>) }
170
- </>}
185
+ </>)}
171
186
  item={item}>
172
187
  <Typography variant="sigma" textColor="neutral600" id="reports-list">
173
188
  { getMessage('page.details.panel.discussion.warnings.reports.list', 'List') }
@@ -179,6 +194,7 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
179
194
  commentId={item.id}
180
195
  items={orderBy(reports, ['resolved', 'createdAt'], ['DESC', 'DESC'])}
181
196
  mutation={resolveReportMutation}
197
+ allowedActions={{ canAccessReports, canReviewReports }}
182
198
  onBlockButtonsStateChange={handleBlockButtonsStateChange}
183
199
  />
184
200
  </ReportsReviewModal>) }
@@ -190,6 +206,11 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
190
206
  DiscoverTableRow.propTypes = {
191
207
  config: PropTypes.object.isRequired,
192
208
  item: PropTypes.object.isRequired,
209
+ allowedActions: PropTypes.shape({
210
+ canModerate: PropTypes.bool,
211
+ canAccessReports: PropTypes.bool,
212
+ canReviewReports: PropTypes.bool,
213
+ }),
193
214
  onClick: PropTypes.func.isRequired,
194
215
  };
195
216
 
@@ -0,0 +1,8 @@
1
+ import styled from 'styled-components';
2
+ import { Link } from '@strapi/design-system/Link';
3
+
4
+ export const TableLink = styled(Link)`
5
+ display: block;
6
+ text-overflow: ellipsis;
7
+ overflow: hidden;
8
+ `
@@ -51,17 +51,20 @@ const Discover = ({ config }) => {
51
51
  const [{ query: queryParams }] = useQueryParams();
52
52
  const _q = queryParams?._q || '';
53
53
 
54
- const viewPermissions = useMemo(() => {
55
- return { view: pluginPermissions.view };
56
- }, []);
54
+ const viewPermissions = useMemo(() => ({
55
+ access: pluginPermissions.access,
56
+ moderate: pluginPermissions.moderate,
57
+ accessReports: pluginPermissions.reports,
58
+ reviewReports: pluginPermissions.reportsReview,
59
+ }), []);
57
60
 
58
61
  const {
59
62
  isLoading: isLoadingForPermissions,
60
- allowedActions: { canView },
63
+ allowedActions: { canAccess, canModerate, canAccessReports, canReviewReports },
61
64
  } = useRBAC(viewPermissions);
62
65
 
63
66
  const { isLoading: isLoadingForData, data: { result, pagination = {} }, isFetching } = useQuery(
64
- ['get-data', queryParams],
67
+ ['get-data', queryParams, canAccess],
65
68
  () => fetchData(queryParams, toggleNotification),
66
69
  {
67
70
  initialData: { },
@@ -86,59 +89,72 @@ const Discover = ({ config }) => {
86
89
  },
87
90
  };
88
91
 
89
-
90
-
91
92
  const emptyContent = _q ? 'search' : 'comments';
92
93
 
93
- const COL_COUNT = 6;
94
-
95
- return <Box background="neutral100">
96
- <Layout sideNav={<Nav />}>
97
- {isLoading || isLoadingForPermissions ? (<LoadingIndicatorPage />) : (
98
- <>
99
- <HeaderLayout title={ getMessage('page.discover.header') } subtitle={ `${total} ${ getMessage('page.discover.header.count')}` } as="h2" />
100
- <ActionLayout
101
- startActions={<>
102
- <SearchURLQuery
103
- label={ getMessage('search.label', 'Search', false) }
104
- />
105
- <TableFilters displayedFilters={filtersSchema} />
106
- </>} />
107
- <ContentLayout>
108
- { !isEmpty(result) ? (<>
109
- <Table colCount={COL_COUNT} rowCount={result.length}>
110
- <Thead>
111
- <Tr>
112
- <Th>
113
- <Typography variant="sigma">{ getMessage('page.discover.table.header.message') }</Typography>
114
- </Th>
115
- <Th>
116
- <Typography variant="sigma">{ getMessage('page.discover.table.header.thread') }</Typography>
117
- </Th>
118
- <Th>
119
- <Typography variant="sigma">{ getMessage('page.discover.table.header.entry') }</Typography>
120
- </Th>
121
- <Th>
122
- <Typography variant="sigma">{ getMessage('page.discover.table.header.lastUpdate') }</Typography>
123
- </Th>
124
- <Th>
125
- <Typography variant="sigma">{ getMessage('page.discover.table.header.status') }</Typography>
126
- </Th>
127
- <Th>
128
- <VisuallyHidden>{ getMessage('page.discover.table.header.actions') }</VisuallyHidden>
129
- </Th>
130
- </Tr>
131
- </Thead>
132
- <Tbody>
133
- {result.map(entry => (<DiscoverTableRow config={config} item={entry} onClick={() => handleClickDisplay(entry.id)}/>))}
134
- </Tbody>
135
- </Table>
136
- <TablePagination pagination={{ pageCount: pagination?.pageCount || 1 }} />
137
- </>) : (<EmptyStateLayout content={emptyLayout[emptyContent]} />) }
138
- </ContentLayout>
139
- </>) }
140
- </Layout>
141
- </Box>;
94
+ const COL_COUNT = 8;
95
+
96
+ if (canAccess) {
97
+ return <Box background="neutral100">
98
+ <Layout>
99
+ {isLoading || isLoadingForPermissions ? (<LoadingIndicatorPage />) : (
100
+ <>
101
+ <HeaderLayout title={ getMessage('page.discover.header') } subtitle={ `${total} ${ getMessage('page.discover.header.count')}` } as="h2" />
102
+ <ActionLayout
103
+ startActions={<>
104
+ <SearchURLQuery
105
+ label={ getMessage('search.label', 'Search', false) }
106
+ />
107
+ <TableFilters displayedFilters={filtersSchema} />
108
+ </>} />
109
+ <ContentLayout>
110
+ { !isEmpty(result) ? (<>
111
+ <Table colCount={COL_COUNT} rowCount={result.length}>
112
+ <Thead>
113
+ <Tr>
114
+ <Th>
115
+ <Typography variant="sigma">{ getMessage('page.discover.table.header.id') }</Typography>
116
+ </Th>
117
+ <Th>
118
+ <Typography variant="sigma">{ getMessage('page.discover.table.header.author') }</Typography>
119
+ </Th>
120
+ <Th>
121
+ <Typography variant="sigma">{ getMessage('page.discover.table.header.message') }</Typography>
122
+ </Th>
123
+ <Th>
124
+ <Typography variant="sigma">{ getMessage('page.discover.table.header.thread') }</Typography>
125
+ </Th>
126
+ <Th>
127
+ <Typography variant="sigma">{ getMessage('page.discover.table.header.entry') }</Typography>
128
+ </Th>
129
+ <Th>
130
+ <Typography variant="sigma">{ getMessage('page.discover.table.header.lastUpdate') }</Typography>
131
+ </Th>
132
+ <Th>
133
+ <Typography variant="sigma">{ getMessage('page.discover.table.header.status') }</Typography>
134
+ </Th>
135
+ <Th>
136
+ <VisuallyHidden>{ getMessage('page.discover.table.header.actions') }</VisuallyHidden>
137
+ </Th>
138
+ </Tr>
139
+ </Thead>
140
+ <Tbody>
141
+ {result.map(entry => (<DiscoverTableRow
142
+ key={`comment-${entry.id}`}
143
+ config={config}
144
+ item={entry}
145
+ allowedActions={{ canModerate, canAccessReports, canReviewReports }}
146
+ onClick={() => handleClickDisplay(entry.id)}
147
+ />))}
148
+ </Tbody>
149
+ </Table>
150
+ <TablePagination pagination={{ pageCount: pagination?.pageCount || 1 }} />
151
+ </>) : (<EmptyStateLayout content={emptyLayout[emptyContent]} />) }
152
+ </ContentLayout>
153
+ </>) }
154
+ </Layout>
155
+ </Box>;
156
+ }
157
+ return null;
142
158
  }
143
159
 
144
160
  const mapStateToProps = makeAppView();
@@ -1,9 +1,13 @@
1
- import { getApiURL, axiosInstance, handleAPIError } from '../../../utils';
1
+ import { stringify } from 'qs';
2
+ import { isEmpty } from 'lodash';
3
+
4
+ import { getApiURL, axiosInstance, handleAPIError } from '../../utils';
2
5
 
3
6
  // eslint-disable-next-line import/prefer-default-export
4
- export const fetchDetailsData = async (id, toggleNotification) => {
7
+ export const fetchDetailsData = async (id, queryParams, toggleNotification) => {
5
8
  try {
6
- const { data } = await axiosInstance.get(getApiURL(`moderate/single/${id}`));
9
+ const stringifiedProps = !isEmpty(queryParams) ? `?${stringify(queryParams, { encode: false })}` : '';
10
+ const { data } = await axiosInstance.get(getApiURL(`moderate/single/${id}${stringifiedProps}`));
7
11
 
8
12
  return data;
9
13
  } catch (err) {
@@ -28,6 +32,14 @@ export const unblockItem = id => {
28
32
  return axiosInstance.patch(getApiURL(`moderate/single/${id}/unblock`));
29
33
  };
30
34
 
35
+ export const approveItem = id => {
36
+ return axiosInstance.patch(getApiURL(`moderate/single/${id}/approve`));
37
+ };
38
+
39
+ export const rejectItem = id => {
40
+ return axiosInstance.patch(getApiURL(`moderate/single/${id}/reject`));
41
+ };
42
+
31
43
  export const blockItemThread = id => {
32
44
  return axiosInstance.patch(getApiURL(`moderate/thread/${id}/block`));
33
45
  };
@@ -1,14 +1,16 @@
1
+ const permissions = require('./../../permissions');
2
+
1
3
  const pluginPermissions = {
2
4
  access: [
3
- { action: 'plugin::comments.read', subject: null },
4
- { action: 'plugin::comments.reports.read', subject: null },
5
- { action: 'plugin::comments.discovery.read', subject: null },
6
- { action: 'plugin::comments.settings.read', subject: null },
5
+ { action: permissions.render(permissions.comments.read), subject: null },
6
+ { action: permissions.render(permissions.reports.read), subject: null },
7
+ // { action: 'plugin::comments.discover.read', subject: null },
8
+ { action: permissions.render(permissions.settings.read), subject: null },
7
9
  ],
8
- discover: [{ action: 'plugin::comments.read', subject: null }],
9
- moderate: [{ action: 'plugin::comments.moderate', subject: null }],
10
- reports: [{ action: 'plugin::comments.reports.read', subject: null }],
11
- reportsReview: [{ action: 'plugin::comments.reports.action', subject: null }],
10
+ // discover: [{ action: 'plugin::comments.discover.read', subject: null }],
11
+ moderate: [{ action: permissions.render(permissions.comments.moderate), subject: null }],
12
+ reports: [{ action: permissions.render(permissions.reports.read), subject: null }],
13
+ reportsReview: [{ action: permissions.render(permissions.reports.action), subject: null }],
12
14
  };
13
15
 
14
16
  export default pluginPermissions;
@@ -7,18 +7,24 @@
7
7
  "nav.item.discover": "Discover",
8
8
  "nav.item.reports": "Reports",
9
9
  "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",
10
16
  "page.updates.header": "Recent updates",
11
17
  "page.discover.header": "Discover threads",
12
18
  "page.discover.header.count": "entries found",
19
+ "page.discover.table.header.id": "ID",
20
+ "page.discover.table.header.author": "Author",
13
21
  "page.discover.table.header.message": "Message",
14
22
  "page.discover.table.header.thread": "Thread of",
15
23
  "page.discover.table.header.entry": "Entry",
16
24
  "page.discover.table.header.lastUpdate": "Last update",
17
25
  "page.discover.table.header.status": "Status",
18
- "page.discover.table.header.status.OPEN": "OPEN",
19
- "page.discover.table.header.status.BLOCKED": "BLOCKED",
20
- "page.discover.table.header.status.TO_REVIEW": "TO REVIEW ({count, number})",
21
- "page.discover.table.header.actions": "Status",
26
+ "page.discover.table.header.actions": "Actions",
27
+ "page.discover.table.cell.thread": "Comment #{id}",
22
28
  "page.discover.table.action.display": "Display",
23
29
  "page.discover.table.action.reports": "Reports",
24
30
  "page.discover.table.empty": "You don't have any comments yet.",
@@ -52,6 +58,7 @@
52
58
  "page.details.panel.status.open": "OPEN",
53
59
  "page.details.panel.status.blocked": "BLOCKED",
54
60
  "page.details.panel.status.pending": "PENDING",
61
+ "page.details.panel.loading": "Fetching entity...",
55
62
  "page.details.actions.comment.block": "Block comment",
56
63
  "page.details.actions.comment.block.confirmation.header": "Moderation: Block comment",
57
64
  "page.details.actions.comment.block.confirmation.description": "Do you really want to block this comment?",
@@ -67,6 +74,12 @@
67
74
  "page.details.actions.thread.block.confirmation.success": "Thread has been blocked",
68
75
  "page.details.actions.thread.unblock": "Unblock thread",
69
76
  "page.details.actions.thread.unblock.confirmation.success": "Thread has been unblocked",
77
+ "page.details.actions.comment.approve.confirmation.success": "Comment has been approved",
78
+ "page.details.actions.comment.reject.confirmation.success": "Comment has been rejected",
79
+ "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",
70
83
  "page.coming.soon": "Wait for what is coming soon...",
71
84
  "compontents.confirmation.dialog.header": "Confirmation",
72
85
  "compontents.confirmation.dialog.description": "Do you really want to perform this action?",
@@ -16,13 +16,10 @@ export const REPORT_REASON = {
16
16
  };
17
17
 
18
18
  export const COMMENT_STATUS = {
19
+ 'UNKNOWN': 'UNKNOWN',
19
20
  'BLOCKED': 'BLOCKED',
20
21
  'OPEN': 'OPEN',
22
+ 'REMOVED': 'REMOVED',
21
23
  'TO_REVIEW': 'TO_REVIEW',
22
- };
23
-
24
- export const REGEX = {
25
- uid: new RegExp(/^(?<type>[a-z]+)\:{2}(?<api>[a-z]+)\.{1}(?<contentType>[a-z]+)$/gmi),
26
- relatedUid: new RegExp(/^(?<uid>[a-z]+\:{2}[a-z]+\.[a-z]+)\:{1}(?<id>[0-9]+)$/gmi),
27
- email: new RegExp(/\S+@\S+\.\S+/),
24
+ ...APPROVAL_STATUS,
28
25
  };
@@ -4,7 +4,7 @@ import { isString } from 'lodash';
4
4
 
5
5
  import pluginId from '../pluginId';
6
6
 
7
- const getMessage = (input, defaultMessage, inPluginScope = true) => {
7
+ const getMessage = (input, defaultMessage = '', inPluginScope = true) => {
8
8
  const { formatMessage } = useIntl();
9
9
  let formattedId = ''
10
10
  if (isString(input)) {
@@ -1,5 +1,3 @@
1
- import { REGEX } from "./constants";
2
-
3
- const getRelatedGroups = related => related.split(REGEX.relatedUid).filter(s => s && s.length > 0);
1
+ const getRelatedGroups = (related, config) => related.split(config.regex.relatedUid).filter(s => s && s.length > 0);
4
2
 
5
3
  export default getRelatedGroups;
@@ -3,4 +3,8 @@ export { default as getApiURL } from './getApiUrl';
3
3
  export { default as getUrl } from './getUrl';
4
4
  export { default as getMessage } from './getMessage';
5
5
  export { default as handleAPIError } from './handleAPIError';
6
- export { default as getRelatedGroups } from './getRelatedGroups';
6
+ export { default as getRelatedGroups } from './getRelatedGroups';
7
+ export { default as renderEntryTitle } from './renderEntryTitle';
8
+ export { default as renderInitials } from './renderInitials';
9
+ export { default as resolveCommentStatus } from './resolveCommentStatus';
10
+ export { default as resolveCommentStatusColor } from './resolveCommentStatusColor';
@@ -0,0 +1,14 @@
1
+ import { first } from 'lodash';
2
+
3
+ const renderEntryTitle = (entry, config = {}) => {
4
+ const { entryLabel } = config;
5
+ const rule = entry.uid in entryLabel ? entryLabel[entry.uid] : entryLabel['*'];
6
+ return first(
7
+ Object.keys(entry)
8
+ .filter(_ => (rule === _) || rule.includes(_))
9
+ .map(_ => entry[_])
10
+ .filter(_ => _)
11
+ );
12
+ };
13
+
14
+ export default renderEntryTitle;
@@ -0,0 +1,5 @@
1
+ import { first } from 'lodash';
2
+
3
+ const renderInitials = (value = '') => value.split(' ').map(_ => first(_)).join('').toUpperCase();
4
+
5
+ export default renderInitials;
@@ -0,0 +1,30 @@
1
+ import { isNil } from 'lodash';
2
+ import { COMMENT_STATUS } from './constants';
3
+
4
+ const resolveCommentStatus = ({ removed, blocked, blockedThread, approvalStatus, reviewFlowEnabled }) => {
5
+ const gotApprovalFlow = !isNil(approvalStatus);
6
+
7
+ if (reviewFlowEnabled) {
8
+ return COMMENT_STATUS.TO_REVIEW;
9
+ }
10
+
11
+ if (removed) {
12
+ return COMMENT_STATUS.REMOVED;
13
+ }
14
+
15
+ if (blocked || blockedThread) {
16
+ return COMMENT_STATUS.BLOCKED;
17
+ }
18
+
19
+ if (gotApprovalFlow) {
20
+ const status = approvalStatus.toUpperCase();
21
+ if (Object.keys(COMMENT_STATUS).includes(status)) {
22
+ return status;
23
+ }
24
+ return COMMENT_STATUS.UNKNOWN;
25
+ }
26
+
27
+ return COMMENT_STATUS.OPEN;
28
+ }
29
+
30
+ export default resolveCommentStatus;
@@ -0,0 +1,22 @@
1
+ import { isNil } from 'lodash';
2
+ import { COMMENT_STATUS } from './constants';
3
+
4
+ const resolveCommentStatusColor = (status) => {
5
+ if (Object.keys(COMMENT_STATUS).includes(status)) {
6
+ switch (status) {
7
+ case COMMENT_STATUS.REMOVED:
8
+ return 'secondary';
9
+ case COMMENT_STATUS.TO_REVIEW:
10
+ case COMMENT_STATUS.REJECTED:
11
+ return 'warning';
12
+ case COMMENT_STATUS.BLOCKED:
13
+ return 'danger';
14
+ case COMMENT_STATUS.OPEN:
15
+ case COMMENT_STATUS.APPROVED:
16
+ return 'success';
17
+ };
18
+ }
19
+ return 'secondary';
20
+ }
21
+
22
+ export default resolveCommentStatusColor;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-comments",
3
- "version": "2.0.0-beta.4",
3
+ "version": "2.0.0-rc.1",
4
4
  "description": "Strapi - Comments plugin",
5
5
  "strapi": {
6
6
  "name": "comments",
@@ -15,6 +15,8 @@
15
15
  "url": "https://github.com/VirtusLab-Open-Source/strapi-plugin-comments"
16
16
  },
17
17
  "dependencies": {
18
+ "@strapi/utils": "^4.0.5",
19
+ "@strapi/plugin-graphql": "^4.0.5",
18
20
  "bad-words": "^3.0.4"
19
21
  },
20
22
  "devDependencies": {
@@ -23,6 +25,9 @@
23
25
  "jest-cli": "^26.0.1",
24
26
  "jest-styled-components": "^7.0.2"
25
27
  },
28
+ "peerDependencies": {
29
+ "@strapi/strapi": "4.x"
30
+ },
26
31
  "author": {
27
32
  "name": "VirtusLab // Mateusz Ziarko",
28
33
  "email": "mziarko@virtuslab.com",
@@ -41,8 +46,8 @@
41
46
  }
42
47
  ],
43
48
  "engines": {
44
- "node": ">=12.x. <=16.x.x",
45
- "npm": ">=6.0.0"
49
+ "node": ">=14.x.x <=17.x.x",
50
+ "npm": ">=7.x.x"
46
51
  },
47
52
  "keywords": ["strapi", "plugin", "comments"],
48
53
  "license": "MIT"