strapi-plugin-comments 2.0.0-beta.5 → 2.0.0-rc.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 (60) hide show
  1. package/README.md +472 -118
  2. package/admin/src/components/DiscussionThread/index.js +9 -3
  3. package/admin/src/components/DiscussionThreadItem/index.js +24 -11
  4. package/admin/src/components/DiscussionThreadItemActions/index.js +45 -18
  5. package/admin/src/components/DiscussionThreadItemApprovalFlowActions/index.js +20 -11
  6. package/admin/src/components/DiscussionThreadItemMeta/index.js +16 -5
  7. package/admin/src/components/DiscussionThreadItemWarnings/index.js +15 -9
  8. package/admin/src/components/ReportsReviewModal/index.js +5 -0
  9. package/admin/src/components/ReportsReviewTable/index.js +60 -52
  10. package/admin/src/components/icons/review.js +1 -1
  11. package/admin/src/index.js +1 -1
  12. package/admin/src/pages/Details/components/DetailsEntity/index.js +2 -2
  13. package/admin/src/pages/Details/components/DetailsFilters/index.js +1 -0
  14. package/admin/src/pages/Details/index.js +43 -37
  15. package/admin/src/pages/Discover/components/DiscoverTableRow/index.js +38 -11
  16. package/admin/src/pages/Discover/index.js +72 -59
  17. package/admin/src/permissions.js +10 -8
  18. package/admin/src/translations/en.json +2 -0
  19. package/admin/src/utils/index.js +2 -0
  20. package/admin/src/utils/renderInitials.js +5 -0
  21. package/content-types/comment.js +0 -4
  22. package/package.json +4 -2
  23. package/permissions.js +19 -0
  24. package/public/assets/logo.png +0 -0
  25. package/public/assets/preview.png +0 -0
  26. package/server/bootstrap.js +38 -2
  27. package/server/config/index.js +7 -2
  28. package/server/controllers/client.js +11 -21
  29. package/server/controllers/utils/parsers.js +19 -0
  30. package/server/graphql/index.js +26 -0
  31. package/server/graphql/mutations/createAbuseReport.js +25 -0
  32. package/server/graphql/mutations/createComment.js +25 -0
  33. package/server/graphql/mutations/index.js +21 -0
  34. package/server/graphql/mutations/removeComment.js +25 -0
  35. package/server/graphql/mutations/updateComment.js +25 -0
  36. package/server/graphql/queries/findAllFlat.js +29 -0
  37. package/server/graphql/queries/findAllInHierarchy.js +31 -0
  38. package/server/graphql/queries/index.js +19 -0
  39. package/server/graphql/resolvers-config.js +15 -0
  40. package/server/graphql/types/comment-author.js +9 -0
  41. package/server/graphql/types/comment-nested.js +15 -0
  42. package/server/graphql/types/comment.js +15 -0
  43. package/server/graphql/types/create-comment-author.js +9 -0
  44. package/server/graphql/types/create-comment.js +9 -0
  45. package/server/graphql/types/create-report.js +9 -0
  46. package/server/graphql/types/id.js +24 -0
  47. package/server/graphql/types/identify-comment-author.js +6 -0
  48. package/server/graphql/types/index.js +16 -0
  49. package/server/graphql/types/remove-comment.js +8 -0
  50. package/server/graphql/types/report-reason.js +10 -0
  51. package/server/graphql/types/report.js +11 -0
  52. package/server/graphql/types/update-comment.js +9 -0
  53. package/server/register.js +3 -1
  54. package/server/services/admin.js +20 -16
  55. package/server/services/client.js +72 -49
  56. package/server/services/common.js +23 -12
  57. package/server/services/graphql.js +190 -0
  58. package/server/services/index.js +2 -0
  59. package/server/services/utils/functions.js +30 -11
  60. package/strapi-server.js +33 -0
@@ -50,13 +50,16 @@ const Details = ({ config }) => {
50
50
  const trackUsageRef = useRef(trackUsage);
51
51
  const toggleNotification = useNotification();
52
52
 
53
- const viewPermissions = useMemo(() => {
54
- return { view: pluginPermissions.view };
55
- }, []);
53
+ const viewPermissions = useMemo(() => ({
54
+ access: pluginPermissions.access,
55
+ moderate: pluginPermissions.moderate,
56
+ accessReports: pluginPermissions.reports,
57
+ reviewReports: pluginPermissions.reportsReview,
58
+ }), []);
56
59
 
57
60
  const {
58
61
  isLoading: isLoadingForPermissions,
59
- allowedActions: { canView },
62
+ allowedActions: { canAccess, canModerate, canAccessReports, canReviewReports },
60
63
  } = useRBAC(viewPermissions);
61
64
 
62
65
  const [filters, setFilters] = useState({});
@@ -64,7 +67,7 @@ const Details = ({ config }) => {
64
67
  const regexUID = new RegExp(config.regex.uid);
65
68
 
66
69
  const { isLoading: isLoadingForData, data, isFetching } = useQuery(
67
- ['get-details-data', id, filters],
70
+ ['get-details-data', id, filters, canAccess],
68
71
  () => fetchDetailsData(id, filters, toggleNotification),
69
72
  {
70
73
  initialData: { },
@@ -75,7 +78,7 @@ const Details = ({ config }) => {
75
78
  const entityUidValid = entity?.uid && regexUID.test(entity.uid);
76
79
 
77
80
  const { data: contentTypeData } = useQuery(
78
- ['get-additional-data', entity?.uid],
81
+ ['get-additional-data', entity?.uid, canAccess],
79
82
  () => fetchContentTypeData(entity?.uid, toggleNotification),
80
83
  {
81
84
  enabled: !!entity?.uid,
@@ -87,37 +90,40 @@ const Details = ({ config }) => {
87
90
 
88
91
  const isLoading = isLoadingForData || isFetching;
89
92
 
90
- return <Box background="neutral100">
91
- <Layout>
92
- {(isLoading || isLoadingForPermissions) && isEmpty(data) ? (<LoadingIndicatorPage />) : (
93
- <>
94
- <HeaderLayout
95
- navigationAction={
96
- <Link startIcon={<ArrowLeft />} to={getUrl(`discover`)}>
97
- { getMessage('HeaderLayout.link.go-back', 'Back', false) }
98
- </Link>}
99
- title={ getMessage('page.details.header') }
100
- subtitle={ getMessage('page.details.header.description') }
101
- primaryAction={<DetailsFilters data={filters} onChange={handleChangeFilters} />}
102
- secondaryAction={<Box as={Flex}>&nbsp;</Box>}
103
- as="h2" />
104
- <ContentLayout>
105
- <TwoColsLayout
106
- startCol={<DiscussionThread level={level} selected={selected} isReloading={isLoading} />}
107
- endCol={ <Stack size={2}>
108
- <DiscussionStatus item={selected} />
109
- { !contentTypeData?.schema ? (<Box padding={4}>
110
- <Loader small>{ getMessage('page.details.panel.loading', 'Loading...') }</Loader>
111
- </Box>) :
112
- (<DetailsEntity data={entity} schema={contentTypeData?.schema} config={config} />)
113
- }
114
- </Stack>
115
- }
116
- />
117
- </ContentLayout>
118
- </>) }
119
- </Layout>
120
- </Box>;
93
+ if (canAccess) {
94
+ return <Box background="neutral100">
95
+ <Layout>
96
+ {(isLoading || isLoadingForPermissions) && isEmpty(data) ? (<LoadingIndicatorPage />) : (
97
+ <>
98
+ <HeaderLayout
99
+ navigationAction={
100
+ <Link startIcon={<ArrowLeft />} to={getUrl(`discover`)}>
101
+ { getMessage('HeaderLayout.link.go-back', 'Back', false) }
102
+ </Link>}
103
+ title={ getMessage('page.details.header') }
104
+ subtitle={ getMessage('page.details.header.description') }
105
+ primaryAction={<DetailsFilters data={filters} onChange={handleChangeFilters} />}
106
+ secondaryAction={<Box as={Flex}>&nbsp;</Box>}
107
+ as="h2" />
108
+ <ContentLayout>
109
+ <TwoColsLayout
110
+ startCol={<DiscussionThread level={level} selected={selected} isReloading={isLoading} allowedActions={{ canModerate, canAccessReports, canReviewReports }} />}
111
+ endCol={ <Stack size={2}>
112
+ <DiscussionStatus item={selected} />
113
+ { !contentTypeData?.schema ? (<Box padding={4}>
114
+ <Loader small>{ getMessage('page.details.panel.loading', 'Loading...') }</Loader>
115
+ </Box>) :
116
+ (<DetailsEntity data={entity} schema={contentTypeData?.schema} config={config} />)
117
+ }
118
+ </Stack>
119
+ }
120
+ />
121
+ </ContentLayout>
122
+ </>) }
123
+ </Layout>
124
+ </Box>;
125
+ }
126
+ return null;
121
127
  }
122
128
 
123
129
  const mapStateToProps = makeAppView();
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import { useIntl } from 'react-intl';
4
4
  import { useMutation, useQueryClient } from 'react-query';
5
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';
@@ -15,7 +16,7 @@ import { Tr, Td } from '@strapi/design-system/Table';
15
16
  import { Typography } from '@strapi/design-system/Typography';
16
17
  import { Eye } from '@strapi/icons';
17
18
  import { useNotification, useOverlayBlocker } from '@strapi/helper-plugin';
18
- import { getMessage, getUrl, handleAPIError, resolveCommentStatus, resolveCommentStatusColor } from '../../../../utils';
19
+ import { getMessage, getUrl, handleAPIError, renderInitials, resolveCommentStatus, resolveCommentStatusColor } from '../../../../utils';
19
20
  import ReportsReviewModal from '../../../../components/ReportsReviewModal';
20
21
  import ReportsReviewTable from '../../../../components/ReportsReviewTable';
21
22
  import { blockItem, blockItemThread, resolveReport } from '../../../utils/api';
@@ -25,7 +26,7 @@ import { TableLink } from './styles';
25
26
  import renderEntryTitle from '../../../../utils/renderEntryTitle';
26
27
  import DiscussionThreadItemApprovalFlowActions from '../../../../components/DiscussionThreadItemApprovalFlowActions';
27
28
 
28
- const DiscoverTableRow = ({ config, item, onClick }) => {
29
+ const DiscoverTableRow = ({ config, item, allowedActions: { canModerate, canAccessReports, canReviewReports }, onClick }) => {
29
30
 
30
31
  const { id, reports } = item;
31
32
 
@@ -74,14 +75,22 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
74
75
  refetchActive: false,
75
76
  });
76
77
 
77
- const handleReportsReviewClick = () => setReportsReviewVisible(true);
78
+ const handleReportsReviewClick = () => {
79
+ if (canAccessReports) {
80
+ setReportsReviewVisible(true);
81
+ }
82
+ };
78
83
  const handleBlockItemClick = async () => {
79
- lockApp();
80
- blockItemMutation.mutate(id);
84
+ if (canModerate) {
85
+ lockApp();
86
+ blockItemMutation.mutate(id);
87
+ }
81
88
  };
82
89
  const handleBlockItemThreadClick = async () => {
83
- lockApp();
84
- blockItemThreadMutation.mutate(id);
90
+ if (canModerate) {
91
+ lockApp();
92
+ blockItemThreadMutation.mutate(id);
93
+ }
85
94
  };
86
95
  const handleReportsReviewClose = async () => {
87
96
  await queryClient.invalidateQueries('get-data');
@@ -91,7 +100,7 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
91
100
  const isLoading = blockItemMutation.isLoading || blockItemThreadMutation.isLoading || resolveReportMutation.isLoading;
92
101
  const openReports = reports?.filter(_ => !_.resolved);
93
102
  const hasReports = !isEmpty(openReports);
94
- const reviewFlowEnabled = hasReports && !(item.blocked || item.blockedThread);
103
+ const reviewFlowEnabled = (canAccessReports && hasReports) && !(item.blocked || item.blockedThread);
95
104
 
96
105
  const handleClick = () => onClick();
97
106
  const handleBlockButtonsStateChange = disabled => setBlockButtonsDisabled(disabled);
@@ -124,6 +133,12 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
124
133
  <Td>
125
134
  <Typography textColor="neutral800" fontWeight="bold">#{ item.id }</Typography>
126
135
  </Td>
136
+ <Td>
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>
127
142
  <Td style={{ maxWidth: '30vw' }}>
128
143
  <Typography textColor="neutral800" ellipsis>{item.content}</Typography>
129
144
  </Td>
@@ -146,22 +161,28 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
146
161
  <Flex direction="column" alignItems="flex-end">
147
162
  <Stack size={actionItemsCount} horizontal>
148
163
  { reviewFlowEnabled && (<IconButton onClick={handleReportsReviewClick} label={ getMessage('page.discover.table.reports.review') } icon={<ReviewIcon />} />)}
149
- { needsApproval && (<DiscussionThreadItemApprovalFlowActions id={item.id} queryToInvalidate="get-data" />)}
164
+ { (canModerate && needsApproval) && (
165
+ <DiscussionThreadItemApprovalFlowActions
166
+ id={item.id}
167
+ allowedActions={{ canModerate }}
168
+ queryToInvalidate="get-data" />
169
+ )}
150
170
  <IconButton onClick={handleClick} label={ getMessage('page.discover.table.action.display') } icon={<Eye />} />
151
171
  </Stack>
152
172
  </Flex>
153
173
  { reviewFlowEnabled && (<ReportsReviewModal
154
174
  isVisible={reportsReviewVisible}
155
175
  isActionAsync={isLoading}
176
+ allowedActions={{ canModerate, canAccessReports, canReviewReports }}
156
177
  onClose={handleReportsReviewClose}
157
- startActions={<>
178
+ startActions={canModerate && (<>
158
179
  <Button onClick={handleBlockItemClick} variant="danger-light" startIcon={<LockIcon />} disabled={blockButtonsDisabled}>
159
180
  { getMessage(`page.details.actions.comment.block`, 'Block comment') }
160
181
  </Button>
161
182
  { item.gotThread && (<Button onClick={handleBlockItemThreadClick} variant="danger" startIcon={<LockIcon />} disabled={blockButtonsDisabled}>
162
183
  { getMessage(`page.details.actions.thread.block`, 'Block thread') }
163
184
  </Button>) }
164
- </>}
185
+ </>)}
165
186
  item={item}>
166
187
  <Typography variant="sigma" textColor="neutral600" id="reports-list">
167
188
  { getMessage('page.details.panel.discussion.warnings.reports.list', 'List') }
@@ -173,6 +194,7 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
173
194
  commentId={item.id}
174
195
  items={orderBy(reports, ['resolved', 'createdAt'], ['DESC', 'DESC'])}
175
196
  mutation={resolveReportMutation}
197
+ allowedActions={{ canAccessReports, canReviewReports }}
176
198
  onBlockButtonsStateChange={handleBlockButtonsStateChange}
177
199
  />
178
200
  </ReportsReviewModal>) }
@@ -184,6 +206,11 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
184
206
  DiscoverTableRow.propTypes = {
185
207
  config: PropTypes.object.isRequired,
186
208
  item: PropTypes.object.isRequired,
209
+ allowedActions: PropTypes.shape({
210
+ canModerate: PropTypes.bool,
211
+ canAccessReports: PropTypes.bool,
212
+ canReviewReports: PropTypes.bool,
213
+ }),
187
214
  onClick: PropTypes.func.isRequired,
188
215
  };
189
216
 
@@ -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,62 +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 = 7;
94
-
95
- return <Box background="neutral100">
96
- <Layout>
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.id') }</Typography>
114
- </Th>
115
- <Th>
116
- <Typography variant="sigma">{ getMessage('page.discover.table.header.message') }</Typography>
117
- </Th>
118
- <Th>
119
- <Typography variant="sigma">{ getMessage('page.discover.table.header.thread') }</Typography>
120
- </Th>
121
- <Th>
122
- <Typography variant="sigma">{ getMessage('page.discover.table.header.entry') }</Typography>
123
- </Th>
124
- <Th>
125
- <Typography variant="sigma">{ getMessage('page.discover.table.header.lastUpdate') }</Typography>
126
- </Th>
127
- <Th>
128
- <Typography variant="sigma">{ getMessage('page.discover.table.header.status') }</Typography>
129
- </Th>
130
- <Th>
131
- <VisuallyHidden>{ getMessage('page.discover.table.header.actions') }</VisuallyHidden>
132
- </Th>
133
- </Tr>
134
- </Thead>
135
- <Tbody>
136
- {result.map(entry => (<DiscoverTableRow key={`comment-${entry.id}`} config={config} item={entry} onClick={() => handleClickDisplay(entry.id)}/>))}
137
- </Tbody>
138
- </Table>
139
- <TablePagination pagination={{ pageCount: pagination?.pageCount || 1 }} />
140
- </>) : (<EmptyStateLayout content={emptyLayout[emptyContent]} />) }
141
- </ContentLayout>
142
- </>) }
143
- </Layout>
144
- </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;
145
158
  }
146
159
 
147
160
  const mapStateToProps = makeAppView();
@@ -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;
@@ -17,6 +17,7 @@
17
17
  "page.discover.header": "Discover threads",
18
18
  "page.discover.header.count": "entries found",
19
19
  "page.discover.table.header.id": "ID",
20
+ "page.discover.table.header.author": "Author",
20
21
  "page.discover.table.header.message": "Message",
21
22
  "page.discover.table.header.thread": "Thread of",
22
23
  "page.discover.table.header.entry": "Entry",
@@ -76,6 +77,7 @@
76
77
  "page.details.actions.comment.approve.confirmation.success": "Comment has been approved",
77
78
  "page.details.actions.comment.reject.confirmation.success": "Comment has been rejected",
78
79
  "page.details.filters.label": "View",
80
+ "page.details.filters.removed.visibility": "Comments visibility",
79
81
  "page.details.filters.removed.show": "Show removed",
80
82
  "page.details.filters.removed.hide": "Hide removed",
81
83
  "page.coming.soon": "Wait for what is coming soon...",
@@ -4,5 +4,7 @@ export { default as getUrl } from './getUrl';
4
4
  export { default as getMessage } from './getMessage';
5
5
  export { default as handleAPIError } from './handleAPIError';
6
6
  export { default as getRelatedGroups } from './getRelatedGroups';
7
+ export { default as renderEntryTitle } from './renderEntryTitle';
8
+ export { default as renderInitials } from './renderInitials';
7
9
  export { default as resolveCommentStatus } from './resolveCommentStatus';
8
10
  export { default as resolveCommentStatusColor } from './resolveCommentStatusColor';
@@ -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;
@@ -48,10 +48,6 @@ module.exports = {
48
48
  target: 'plugin::users-permissions.user',
49
49
  configurable: false,
50
50
  },
51
- authorType: {
52
- type: 'string',
53
- configurable: false
54
- },
55
51
  authorId: {
56
52
  type: 'string',
57
53
  configurable: false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-comments",
3
- "version": "2.0.0-beta.5",
3
+ "version": "2.0.0-rc.2",
4
4
  "description": "Strapi - Comments plugin",
5
5
  "strapi": {
6
6
  "name": "comments",
@@ -8,6 +8,7 @@
8
8
  "kind": "plugin"
9
9
  },
10
10
  "scripts": {
11
+ "publish": "npm publish --tag latest",
11
12
  "test:unit": "jest --verbose --coverage"
12
13
  },
13
14
  "repository": {
@@ -15,7 +16,8 @@
15
16
  "url": "https://github.com/VirtusLab-Open-Source/strapi-plugin-comments"
16
17
  },
17
18
  "dependencies": {
18
- "@strapi/utils": "^4.0.5",
19
+ "@strapi/utils": "^4.0.6",
20
+ "@strapi/plugin-graphql": "^4.0.6",
19
21
  "bad-words": "^3.0.4"
20
22
  },
21
23
  "devDependencies": {
package/permissions.js ADDED
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ render: function(uid) {
5
+ return `plugin::comments.${uid}`;
6
+ },
7
+ comments: {
8
+ read: 'read',
9
+ moderate: 'moderate',
10
+ },
11
+ reports: {
12
+ read: 'reports.read',
13
+ action: 'reports.action',
14
+ },
15
+ settings: {
16
+ read: 'settings.read',
17
+ change: 'settings.change',
18
+ },
19
+ };
Binary file
Binary file
@@ -1,5 +1,41 @@
1
1
  'use strict';
2
2
 
3
- module.exports = ({ strapi }) => {
4
- // bootstrap phase
3
+ const permissions = require('./../permissions');
4
+
5
+ module.exports = async ({ strapi }) => {
6
+ // Check if the plugin users-permissions is installed because the navigation needs it
7
+ if (Object.keys(strapi.plugins).indexOf("users-permissions") === -1) {
8
+ throw new Error(
9
+ "In order to make the comments plugin work the users-permissions plugin is required",
10
+ );
11
+ }
12
+ // Add permissions
13
+ const actions = [
14
+ {
15
+ section: "plugins",
16
+ displayName: "Comments: Read",
17
+ uid: permissions.comments.read,
18
+ pluginName: "comments",
19
+ },
20
+ {
21
+ section: "plugins",
22
+ displayName: "Comments: Moderate",
23
+ uid: permissions.comments.moderate,
24
+ pluginName: "comments",
25
+ },
26
+ {
27
+ section: "plugins",
28
+ displayName: "Reports: Read",
29
+ uid: permissions.reports.read,
30
+ pluginName: "comments",
31
+ },
32
+ {
33
+ section: "plugins",
34
+ displayName: "Reports: Moderate",
35
+ uid: permissions.reports.action,
36
+ pluginName: "comments",
37
+ },
38
+ ];
39
+
40
+ await strapi.admin.services.permission.actionProvider.registerMany(actions);
5
41
  };
@@ -2,10 +2,15 @@
2
2
 
3
3
  module.exports = {
4
4
  default: {
5
+ moderatorRoles: [],
5
6
  approvalFlow: [],
6
7
  entryLabel: {
7
8
  '*': ['Title', 'title', 'Name', 'name', 'Subject', 'subject']
8
9
  },
9
- },
10
- validator() {},
10
+ reportReasons: {
11
+ 'BAD_LANGUAGE': 'BAD_LANGUAGE',
12
+ 'DISCRIMINATION': 'DISCRIMINATION',
13
+ 'OTHER': 'OTHER',
14
+ },
15
+ }
11
16
  };
@@ -2,7 +2,7 @@
2
2
 
3
3
  const { getPluginService } = require('./../utils/functions');
4
4
  const { parseParams, throwError } = require('./utils/functions');
5
- const _ = require('lodash');
5
+ const { flatInput } = require('./utils/parsers');
6
6
 
7
7
  module.exports = {
8
8
 
@@ -15,16 +15,7 @@ module.exports = {
15
15
  const { relation } = parseParams(params);
16
16
  try {
17
17
  return this.getService('common')
18
- .findAllFlat({
19
- query: {
20
- ...query,
21
- $or: [{ removed: { $null: true } }, { removed: false }],
22
- related: relation,
23
- },
24
- populate: {
25
- threadOf: true,
26
- },
27
- });
18
+ .findAllFlat(flatInput(relation, query));
28
19
  } catch (e) {
29
20
  throwError(ctx, e);
30
21
  }
@@ -36,13 +27,7 @@ module.exports = {
36
27
  try {
37
28
  return await this.getService('common')
38
29
  .findAllInHierarchy({
39
- query: {
40
- ...query,
41
- related: relation,
42
- },
43
- populate: {
44
- threadOf: true,
45
- },
30
+ ...flatInput(relation, query),
46
31
  dropBlockedThreads: true,
47
32
  });
48
33
  } catch (e) {
@@ -97,14 +82,19 @@ module.exports = {
97
82
  },
98
83
 
99
84
  async removeComment(ctx) {
100
- const { params: { relationId, commentId }, query: { authorId } } = ctx;
101
- if (authorId) {
85
+ const {
86
+ params: { relationId, commentId },
87
+ query: { authorId },
88
+ state: { user },
89
+ } = ctx;
90
+ if (authorId || user?.id) {
102
91
  try {
103
92
  return await this.getService()
104
93
  .markAsRemoved(
105
- relationId,
106
94
  commentId,
95
+ relationId,
107
96
  authorId,
97
+ user
108
98
  );
109
99
  } catch (e) {
110
100
  if (!e.isBoom) {