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.
- package/README.md +421 -72
- package/admin/src/components/DiscussionThread/index.js +9 -2
- package/admin/src/components/DiscussionThreadItem/index.js +27 -12
- package/admin/src/components/DiscussionThreadItemActions/index.js +61 -50
- package/admin/src/components/DiscussionThreadItemActions/styles.js +0 -4
- package/admin/src/components/DiscussionThreadItemApprovalFlowActions/index.js +81 -0
- package/admin/src/components/DiscussionThreadItemApprovalFlowActions/styles.js +6 -0
- package/admin/src/components/DiscussionThreadItemMeta/index.js +16 -5
- package/admin/src/components/DiscussionThreadItemWarnings/index.js +16 -10
- package/admin/src/components/Nav/index.js +5 -1
- package/admin/src/components/ReportsReviewModal/index.js +5 -0
- package/admin/src/components/ReportsReviewTable/index.js +60 -52
- package/admin/src/components/icons/review.js +2 -2
- package/admin/src/index.js +1 -1
- package/admin/src/pages/Details/components/DetailsEntity/index.js +66 -0
- package/admin/src/pages/Details/components/DetailsFilters/index.js +37 -0
- package/admin/src/pages/Details/components/DetailsFilters/styles.js +8 -0
- package/admin/src/pages/Details/index.js +75 -42
- package/admin/src/pages/Discover/components/DiscoverTableRow/index.js +69 -48
- package/admin/src/pages/Discover/components/DiscoverTableRow/styles.js +8 -0
- package/admin/src/pages/Discover/index.js +72 -56
- package/admin/src/pages/{Details/utils → utils}/api.js +15 -3
- package/admin/src/permissions.js +10 -8
- package/admin/src/translations/en.json +17 -4
- package/admin/src/utils/constants.js +3 -6
- package/admin/src/utils/getMessage.js +1 -1
- package/admin/src/utils/getRelatedGroups.js +1 -3
- package/admin/src/utils/index.js +5 -1
- package/admin/src/utils/renderEntryTitle.js +14 -0
- package/admin/src/utils/renderInitials.js +5 -0
- package/admin/src/utils/resolveCommentStatus.js +30 -0
- package/admin/src/utils/resolveCommentStatusColor.js +22 -0
- package/package.json +8 -3
- package/permissions.js +19 -0
- package/public/assets/logo.png +0 -0
- package/public/assets/preview.png +0 -0
- package/server/bootstrap.js +38 -2
- package/server/config/index.js +9 -3
- package/server/controllers/admin.js +3 -3
- package/server/controllers/client.js +4 -18
- package/server/controllers/utils/parsers.js +17 -0
- package/server/graphql/index.js +26 -0
- package/server/graphql/mutations/createAbuseReport.js +20 -0
- package/server/graphql/mutations/createComment.js +20 -0
- package/server/graphql/mutations/index.js +21 -0
- package/server/graphql/mutations/removeComment.js +20 -0
- package/server/graphql/mutations/updateComment.js +20 -0
- package/server/graphql/queries/findAllFlat.js +29 -0
- package/server/graphql/queries/findAllInHierarchy.js +31 -0
- package/server/graphql/queries/index.js +19 -0
- package/server/graphql/resolvers-config.js +10 -0
- package/server/graphql/types/comment-author-user.js +9 -0
- package/server/graphql/types/comment-author.js +9 -0
- package/server/graphql/types/comment-nested.js +16 -0
- package/server/graphql/types/comment.js +16 -0
- package/server/graphql/types/create-comment-author.js +9 -0
- package/server/graphql/types/create-comment.js +9 -0
- package/server/graphql/types/create-report.js +9 -0
- package/server/graphql/types/id.js +24 -0
- package/server/graphql/types/identify-comment-author.js +6 -0
- package/server/graphql/types/index.js +17 -0
- package/server/graphql/types/remove-comment.js +8 -0
- package/server/graphql/types/report-reason.js +10 -0
- package/server/graphql/types/report.js +11 -0
- package/server/graphql/types/update-comment.js +9 -0
- package/server/register.js +3 -1
- package/server/services/admin.js +42 -15
- package/server/services/client.js +54 -38
- package/server/services/common.js +38 -67
- package/server/services/graphql.js +190 -0
- package/server/services/index.js +2 -0
- package/server/services/utils/functions.js +24 -17
- package/server/utils/constants.js +3 -3
- package/strapi-server.js +33 -0
- 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 {
|
|
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
|
|
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 '../../../
|
|
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 = () =>
|
|
78
|
+
const handleReportsReviewClick = () => {
|
|
79
|
+
if (canAccessReports) {
|
|
80
|
+
setReportsReviewVisible(true);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
76
83
|
const handleBlockItemClick = async () => {
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
if (canModerate) {
|
|
85
|
+
lockApp();
|
|
86
|
+
blockItemMutation.mutate(id);
|
|
87
|
+
}
|
|
79
88
|
};
|
|
80
89
|
const handleBlockItemThreadClick = async () => {
|
|
81
|
-
|
|
82
|
-
|
|
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 = (
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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.
|
|
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
|
|
138
|
-
<Typography textColor="neutral800"
|
|
133
|
+
<Td>
|
|
134
|
+
<Typography textColor="neutral800" fontWeight="bold">#{ item.id }</Typography>
|
|
139
135
|
</Td>
|
|
140
136
|
<Td>
|
|
141
|
-
<
|
|
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={
|
|
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={
|
|
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
|
|
|
@@ -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
|
-
|
|
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: {
|
|
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 =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
<
|
|
111
|
-
<
|
|
112
|
-
<
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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 {
|
|
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
|
|
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
|
};
|
package/admin/src/permissions.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
const permissions = require('./../../permissions');
|
|
2
|
+
|
|
1
3
|
const pluginPermissions = {
|
|
2
4
|
access: [
|
|
3
|
-
{ action:
|
|
4
|
-
{ action:
|
|
5
|
-
{ action: 'plugin::comments.
|
|
6
|
-
{ action:
|
|
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:
|
|
10
|
-
reports: [{ action:
|
|
11
|
-
reportsReview: [{ action:
|
|
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.
|
|
19
|
-
"page.discover.table.
|
|
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
|
-
|
|
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;
|
package/admin/src/utils/index.js
CHANGED
|
@@ -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,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-
|
|
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": ">=
|
|
45
|
-
"npm": ">=
|
|
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"
|