strapi-plugin-comments 2.0.0-beta.4 → 2.0.0-beta.5
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 +10 -4
- package/admin/src/components/DiscussionThread/index.js +1 -0
- package/admin/src/components/DiscussionThreadItem/index.js +3 -3
- package/admin/src/components/DiscussionThreadItemActions/index.js +22 -38
- package/admin/src/components/DiscussionThreadItemActions/styles.js +0 -4
- package/admin/src/components/DiscussionThreadItemApprovalFlowActions/index.js +72 -0
- package/admin/src/components/DiscussionThreadItemApprovalFlowActions/styles.js +6 -0
- package/admin/src/components/DiscussionThreadItemWarnings/index.js +1 -1
- package/admin/src/components/Nav/index.js +5 -1
- package/admin/src/components/icons/review.js +2 -2
- package/admin/src/pages/Details/components/DetailsEntity/index.js +66 -0
- package/admin/src/pages/Details/components/DetailsFilters/index.js +36 -0
- package/admin/src/pages/Details/components/DetailsFilters/styles.js +8 -0
- package/admin/src/pages/Details/index.js +40 -13
- package/admin/src/pages/Discover/components/DiscoverTableRow/index.js +33 -39
- package/admin/src/pages/Discover/components/DiscoverTableRow/styles.js +8 -0
- package/admin/src/pages/Discover/index.js +6 -3
- package/admin/src/pages/{Details/utils → utils}/api.js +15 -3
- package/admin/src/translations/en.json +15 -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 +3 -1
- package/admin/src/utils/renderEntryTitle.js +14 -0
- package/admin/src/utils/resolveCommentStatus.js +30 -0
- package/admin/src/utils/resolveCommentStatusColor.js +22 -0
- package/package.json +7 -3
- package/server/config/index.js +2 -1
- package/server/controllers/admin.js +3 -3
- package/server/controllers/client.js +1 -0
- package/server/services/admin.js +33 -7
- package/server/services/client.js +21 -13
- package/server/services/common.js +30 -65
- package/server/services/utils/functions.js +7 -14
- package/server/utils/constants.js +3 -3
- package/admin/src/components/EntityDetails/index.js +0 -53
|
@@ -2,7 +2,7 @@ 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
6
|
import { Badge } from '@strapi/design-system/Badge';
|
|
7
7
|
import { Box } from '@strapi/design-system/Box';
|
|
8
8
|
import { Button } from '@strapi/design-system/Button';
|
|
@@ -13,15 +13,17 @@ import { Link } from '@strapi/design-system/Link';
|
|
|
13
13
|
import { Stack } from '@strapi/design-system/Stack';
|
|
14
14
|
import { Tr, Td } from '@strapi/design-system/Table';
|
|
15
15
|
import { Typography } from '@strapi/design-system/Typography';
|
|
16
|
-
import { Eye
|
|
16
|
+
import { Eye } from '@strapi/icons';
|
|
17
17
|
import { useNotification, useOverlayBlocker } from '@strapi/helper-plugin';
|
|
18
|
-
import { getMessage, handleAPIError } from '../../../../utils';
|
|
19
|
-
import { COMMENT_STATUS } from '../../../../utils/constants';
|
|
18
|
+
import { getMessage, getUrl, handleAPIError, resolveCommentStatus, resolveCommentStatusColor } from '../../../../utils';
|
|
20
19
|
import ReportsReviewModal from '../../../../components/ReportsReviewModal';
|
|
21
20
|
import ReportsReviewTable from '../../../../components/ReportsReviewTable';
|
|
22
|
-
import { blockItem, blockItemThread, resolveReport } from '../../../
|
|
21
|
+
import { blockItem, blockItemThread, resolveReport } from '../../../utils/api';
|
|
23
22
|
import pluginId from '../../../../pluginId';
|
|
24
23
|
import { ReviewIcon, LockIcon } from '../../../../components/icons';
|
|
24
|
+
import { TableLink } from './styles';
|
|
25
|
+
import renderEntryTitle from '../../../../utils/renderEntryTitle';
|
|
26
|
+
import DiscussionThreadItemApprovalFlowActions from '../../../../components/DiscussionThreadItemApprovalFlowActions';
|
|
25
27
|
|
|
26
28
|
const DiscoverTableRow = ({ config, item, onClick }) => {
|
|
27
29
|
|
|
@@ -94,54 +96,45 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
|
|
|
94
96
|
const handleClick = () => onClick();
|
|
95
97
|
const handleBlockButtonsStateChange = disabled => setBlockButtonsDisabled(disabled);
|
|
96
98
|
|
|
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
|
-
};
|
|
99
|
+
const renderStatus = (props) => {
|
|
100
|
+
const status = resolveCommentStatus({ ...props, reviewFlowEnabled });
|
|
101
|
+
const color = resolveCommentStatusColor(status);
|
|
102
|
+
|
|
114
103
|
return (<Badge backgroundColor={`${color}100`} textColor={`${color}600`}>{ getMessage({
|
|
115
|
-
id: `page.
|
|
104
|
+
id: `page.common.item.status.${status}`,
|
|
116
105
|
props: {
|
|
117
106
|
count: openReports.length
|
|
118
107
|
}
|
|
119
108
|
}, status) }</Badge>);
|
|
120
109
|
};
|
|
121
110
|
|
|
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
111
|
const renderEntryUrl = entry => `/content-manager/collectionType/${entry.uid}/${entry.id}`;
|
|
112
|
+
const renderDetailsUrl = entry => getUrl(`discover/${entry.id}`);
|
|
113
|
+
|
|
114
|
+
const gotApprovalFlow = !isNil(item.approvalStatus);
|
|
115
|
+
const needsApproval = gotApprovalFlow && (item.approvalStatus === 'PENDING');
|
|
116
|
+
|
|
117
|
+
let actionItemsCount = 1;
|
|
118
|
+
if(reviewFlowEnabled || needsApproval) {
|
|
119
|
+
actionItemsCount = 2;
|
|
120
|
+
}
|
|
134
121
|
|
|
135
122
|
return (
|
|
136
123
|
<Tr key={item.id}>
|
|
137
|
-
<Td
|
|
124
|
+
<Td>
|
|
125
|
+
<Typography textColor="neutral800" fontWeight="bold">#{ item.id }</Typography>
|
|
126
|
+
</Td>
|
|
127
|
+
<Td style={{ maxWidth: '30vw' }}>
|
|
138
128
|
<Typography textColor="neutral800" ellipsis>{item.content}</Typography>
|
|
139
129
|
</Td>
|
|
140
130
|
<Td>
|
|
141
|
-
<
|
|
131
|
+
{ item.threadOf?.id ? (<Link to={ renderDetailsUrl(item.threadOf) }>{ getMessage({
|
|
132
|
+
id: 'page.discover.table.cell.thread',
|
|
133
|
+
props: { id: item.threadOf.id },
|
|
134
|
+
}, '#' + item.threadOf.id) }</Link>) : '-' }
|
|
142
135
|
</Td>
|
|
143
|
-
<Td>
|
|
144
|
-
<
|
|
136
|
+
<Td style={{ maxWidth: '15vw' }}>
|
|
137
|
+
<TableLink to={ renderEntryUrl(item.related) }>{ renderEntryTitle(item.related, config) }</TableLink>
|
|
145
138
|
</Td>
|
|
146
139
|
<Td>
|
|
147
140
|
<Typography textColor="neutral800">{ formatDate(item.updatedAt || item.createdAt, { dateStyle: 'long', timeStyle: 'short' }) }</Typography>
|
|
@@ -151,8 +144,9 @@ const DiscoverTableRow = ({ config, item, onClick }) => {
|
|
|
151
144
|
</Td>
|
|
152
145
|
<Td>
|
|
153
146
|
<Flex direction="column" alignItems="flex-end">
|
|
154
|
-
<Stack size={
|
|
147
|
+
<Stack size={actionItemsCount} horizontal>
|
|
155
148
|
{ reviewFlowEnabled && (<IconButton onClick={handleReportsReviewClick} label={ getMessage('page.discover.table.reports.review') } icon={<ReviewIcon />} />)}
|
|
149
|
+
{ needsApproval && (<DiscussionThreadItemApprovalFlowActions id={item.id} queryToInvalidate="get-data" />)}
|
|
156
150
|
<IconButton onClick={handleClick} label={ getMessage('page.discover.table.action.display') } icon={<Eye />} />
|
|
157
151
|
</Stack>
|
|
158
152
|
</Flex>
|
|
@@ -90,10 +90,10 @@ const Discover = ({ config }) => {
|
|
|
90
90
|
|
|
91
91
|
const emptyContent = _q ? 'search' : 'comments';
|
|
92
92
|
|
|
93
|
-
const COL_COUNT =
|
|
93
|
+
const COL_COUNT = 7;
|
|
94
94
|
|
|
95
95
|
return <Box background="neutral100">
|
|
96
|
-
<Layout
|
|
96
|
+
<Layout>
|
|
97
97
|
{isLoading || isLoadingForPermissions ? (<LoadingIndicatorPage />) : (
|
|
98
98
|
<>
|
|
99
99
|
<HeaderLayout title={ getMessage('page.discover.header') } subtitle={ `${total} ${ getMessage('page.discover.header.count')}` } as="h2" />
|
|
@@ -109,6 +109,9 @@ const Discover = ({ config }) => {
|
|
|
109
109
|
<Table colCount={COL_COUNT} rowCount={result.length}>
|
|
110
110
|
<Thead>
|
|
111
111
|
<Tr>
|
|
112
|
+
<Th>
|
|
113
|
+
<Typography variant="sigma">{ getMessage('page.discover.table.header.id') }</Typography>
|
|
114
|
+
</Th>
|
|
112
115
|
<Th>
|
|
113
116
|
<Typography variant="sigma">{ getMessage('page.discover.table.header.message') }</Typography>
|
|
114
117
|
</Th>
|
|
@@ -130,7 +133,7 @@ const Discover = ({ config }) => {
|
|
|
130
133
|
</Tr>
|
|
131
134
|
</Thead>
|
|
132
135
|
<Tbody>
|
|
133
|
-
{result.map(entry => (<DiscoverTableRow config={config} item={entry} onClick={() => handleClickDisplay(entry.id)}/>))}
|
|
136
|
+
{result.map(entry => (<DiscoverTableRow key={`comment-${entry.id}`} config={config} item={entry} onClick={() => handleClickDisplay(entry.id)}/>))}
|
|
134
137
|
</Tbody>
|
|
135
138
|
</Table>
|
|
136
139
|
<TablePagination pagination={{ pageCount: pagination?.pageCount || 1 }} />
|
|
@@ -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
|
};
|
|
@@ -7,18 +7,23 @@
|
|
|
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",
|
|
13
20
|
"page.discover.table.header.message": "Message",
|
|
14
21
|
"page.discover.table.header.thread": "Thread of",
|
|
15
22
|
"page.discover.table.header.entry": "Entry",
|
|
16
23
|
"page.discover.table.header.lastUpdate": "Last update",
|
|
17
24
|
"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",
|
|
25
|
+
"page.discover.table.header.actions": "Actions",
|
|
26
|
+
"page.discover.table.cell.thread": "Comment #{id}",
|
|
22
27
|
"page.discover.table.action.display": "Display",
|
|
23
28
|
"page.discover.table.action.reports": "Reports",
|
|
24
29
|
"page.discover.table.empty": "You don't have any comments yet.",
|
|
@@ -52,6 +57,7 @@
|
|
|
52
57
|
"page.details.panel.status.open": "OPEN",
|
|
53
58
|
"page.details.panel.status.blocked": "BLOCKED",
|
|
54
59
|
"page.details.panel.status.pending": "PENDING",
|
|
60
|
+
"page.details.panel.loading": "Fetching entity...",
|
|
55
61
|
"page.details.actions.comment.block": "Block comment",
|
|
56
62
|
"page.details.actions.comment.block.confirmation.header": "Moderation: Block comment",
|
|
57
63
|
"page.details.actions.comment.block.confirmation.description": "Do you really want to block this comment?",
|
|
@@ -67,6 +73,11 @@
|
|
|
67
73
|
"page.details.actions.thread.block.confirmation.success": "Thread has been blocked",
|
|
68
74
|
"page.details.actions.thread.unblock": "Unblock thread",
|
|
69
75
|
"page.details.actions.thread.unblock.confirmation.success": "Thread has been unblocked",
|
|
76
|
+
"page.details.actions.comment.approve.confirmation.success": "Comment has been approved",
|
|
77
|
+
"page.details.actions.comment.reject.confirmation.success": "Comment has been rejected",
|
|
78
|
+
"page.details.filters.label": "View",
|
|
79
|
+
"page.details.filters.removed.show": "Show removed",
|
|
80
|
+
"page.details.filters.removed.hide": "Hide removed",
|
|
70
81
|
"page.coming.soon": "Wait for what is coming soon...",
|
|
71
82
|
"compontents.confirmation.dialog.header": "Confirmation",
|
|
72
83
|
"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,6 @@ 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 resolveCommentStatus } from './resolveCommentStatus';
|
|
8
|
+
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-beta.
|
|
3
|
+
"version": "2.0.0-beta.5",
|
|
4
4
|
"description": "Strapi - Comments plugin",
|
|
5
5
|
"strapi": {
|
|
6
6
|
"name": "comments",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"url": "https://github.com/VirtusLab-Open-Source/strapi-plugin-comments"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
+
"@strapi/utils": "^4.0.5",
|
|
18
19
|
"bad-words": "^3.0.4"
|
|
19
20
|
},
|
|
20
21
|
"devDependencies": {
|
|
@@ -23,6 +24,9 @@
|
|
|
23
24
|
"jest-cli": "^26.0.1",
|
|
24
25
|
"jest-styled-components": "^7.0.2"
|
|
25
26
|
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@strapi/strapi": "4.x"
|
|
29
|
+
},
|
|
26
30
|
"author": {
|
|
27
31
|
"name": "VirtusLab // Mateusz Ziarko",
|
|
28
32
|
"email": "mziarko@virtuslab.com",
|
|
@@ -41,8 +45,8 @@
|
|
|
41
45
|
}
|
|
42
46
|
],
|
|
43
47
|
"engines": {
|
|
44
|
-
"node": ">=
|
|
45
|
-
"npm": ">=
|
|
48
|
+
"node": ">=14.x.x <=17.x.x",
|
|
49
|
+
"npm": ">=7.x.x"
|
|
46
50
|
},
|
|
47
51
|
"keywords": ["strapi", "plugin", "comments"],
|
|
48
52
|
"license": "MIT"
|
package/server/config/index.js
CHANGED
|
@@ -16,11 +16,11 @@ module.exports = {
|
|
|
16
16
|
},
|
|
17
17
|
|
|
18
18
|
async findOne(ctx) {
|
|
19
|
-
const { params = {} } = ctx;
|
|
19
|
+
const { params = {}, query } = ctx;
|
|
20
20
|
const { id } = parseParams(params);
|
|
21
21
|
try {
|
|
22
22
|
return await this.getService()
|
|
23
|
-
.findOneAndThread(id);
|
|
23
|
+
.findOneAndThread(id, query);
|
|
24
24
|
} catch (e) {
|
|
25
25
|
throwError(ctx, e);
|
|
26
26
|
}
|
|
@@ -101,7 +101,7 @@ module.exports = {
|
|
|
101
101
|
}
|
|
102
102
|
},
|
|
103
103
|
|
|
104
|
-
async config() {
|
|
104
|
+
async config(ctx) {
|
|
105
105
|
try {
|
|
106
106
|
return await this.getService().config();
|
|
107
107
|
} catch (e) {
|
package/server/services/admin.js
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { getPluginService, parseParams } = require('./../utils/functions');
|
|
4
|
-
const {
|
|
5
|
-
const { first, isEmpty, isNil, uniq, get } = require('lodash');
|
|
4
|
+
const { isEmpty, isNil } = require('lodash');
|
|
6
5
|
const PluginError = require('./../utils/error');
|
|
7
6
|
const {
|
|
8
7
|
getModelUid,
|
|
9
8
|
getRelatedGroups,
|
|
10
9
|
filterOurResolvedReports,
|
|
11
|
-
convertContentTypeNameToSlug
|
|
12
10
|
} = require('./utils/functions');
|
|
13
11
|
const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
14
12
|
|
|
@@ -25,8 +23,14 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
|
25
23
|
// Config
|
|
26
24
|
async config() {
|
|
27
25
|
const entryLabel = this.getCommonService().getConfig('entryLabel');
|
|
26
|
+
const approvalFlow = this.getCommonService().getConfig('approvalFlow');
|
|
28
27
|
return {
|
|
29
28
|
entryLabel,
|
|
29
|
+
approvalFlow,
|
|
30
|
+
regex: Object.keys(REGEX).reduce((prev, curr) => ({
|
|
31
|
+
...prev,
|
|
32
|
+
[curr]: REGEX[curr].toString(),
|
|
33
|
+
}), {}),
|
|
30
34
|
};
|
|
31
35
|
},
|
|
32
36
|
|
|
@@ -34,10 +38,18 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
|
34
38
|
async findAll({ related, entity, ...query }) {
|
|
35
39
|
const { _q, orderBy, pageSize = 10, page = 1, filters, ...rest } = parseParams(query);
|
|
36
40
|
|
|
41
|
+
const defaultWhere = {
|
|
42
|
+
$or: [
|
|
43
|
+
{ removed: false },
|
|
44
|
+
{ removed: null },
|
|
45
|
+
],
|
|
46
|
+
};
|
|
47
|
+
|
|
37
48
|
let params = {
|
|
38
49
|
where: !isEmpty(filters) ? {
|
|
50
|
+
...defaultWhere,
|
|
39
51
|
...filters,
|
|
40
|
-
} :
|
|
52
|
+
} : { ...defaultWhere },
|
|
41
53
|
offset: (page - 1)*pageSize,
|
|
42
54
|
limit: pageSize,
|
|
43
55
|
orderBy: orderBy || [{ createdAt: 'desc' }],
|
|
@@ -59,6 +71,7 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
|
59
71
|
...params,
|
|
60
72
|
populate: {
|
|
61
73
|
authorUser: true,
|
|
74
|
+
threadOf: true,
|
|
62
75
|
reports: {
|
|
63
76
|
where: {
|
|
64
77
|
resolved: false
|
|
@@ -87,7 +100,14 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
|
87
100
|
},
|
|
88
101
|
|
|
89
102
|
// Find single comment
|
|
90
|
-
async findOneAndThread(id) {
|
|
103
|
+
async findOneAndThread(id, { removed, ...query }) {
|
|
104
|
+
const defaultWhere = !removed ? {
|
|
105
|
+
$or: [
|
|
106
|
+
{ removed: false },
|
|
107
|
+
{ removed: null },
|
|
108
|
+
],
|
|
109
|
+
} : {};
|
|
110
|
+
|
|
91
111
|
const reportsPopulation = {
|
|
92
112
|
reports: {
|
|
93
113
|
where: {
|
|
@@ -98,7 +118,7 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
|
98
118
|
|
|
99
119
|
const entity = await strapi.db.query(getModelUid('comment')).findOne({
|
|
100
120
|
where: {
|
|
101
|
-
id
|
|
121
|
+
id,
|
|
102
122
|
},
|
|
103
123
|
populate: {
|
|
104
124
|
authorUser: true,
|
|
@@ -132,6 +152,8 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
|
132
152
|
const entitiesOnSameLevel = await this.getCommonService()
|
|
133
153
|
.findAllInHierarchy({
|
|
134
154
|
query: {
|
|
155
|
+
...defaultWhere,
|
|
156
|
+
...query,
|
|
135
157
|
threadOf: levelThreadId,
|
|
136
158
|
related: entity.related,
|
|
137
159
|
},
|
|
@@ -167,9 +189,13 @@ const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
|
|
|
167
189
|
// Block / Unblock a comment thread
|
|
168
190
|
async blockCommentThread(id, forceStatus) {
|
|
169
191
|
const existingEntity = await this.getCommonService().findOne({ id });
|
|
192
|
+
const status = !isNil(forceStatus) ? forceStatus : !existingEntity.blockedThread;
|
|
170
193
|
const changedEntity = await strapi.db.query(getModelUid('comment')).update({
|
|
171
194
|
where: { id },
|
|
172
|
-
data: {
|
|
195
|
+
data: {
|
|
196
|
+
blocked: status,
|
|
197
|
+
blockedThread: status,
|
|
198
|
+
},
|
|
173
199
|
});
|
|
174
200
|
await this.blockNestedThreads(id, changedEntity.blockedThread);
|
|
175
201
|
|
|
@@ -26,18 +26,14 @@ module.exports = ({ strapi }) => ({
|
|
|
26
26
|
|
|
27
27
|
// Create a comment
|
|
28
28
|
async create(relation, data, user = undefined) {
|
|
29
|
-
const { content, threadOf
|
|
30
|
-
const singleRelationFulfilled =
|
|
31
|
-
|
|
32
|
-
if (relation !== related) {
|
|
33
|
-
throw new PluginError(400, `Insonsistent relation uid`);
|
|
34
|
-
}
|
|
35
|
-
|
|
29
|
+
const { content, threadOf } = data;
|
|
30
|
+
const singleRelationFulfilled = relation && REGEX.relatedUid.test(relation);
|
|
31
|
+
|
|
36
32
|
if(!singleRelationFulfilled) {
|
|
37
33
|
throw new PluginError(400, `Field "related" got incorrect format, use format like "api::<collection name>.<content type name>:<entity id>"`);
|
|
38
34
|
}
|
|
39
35
|
|
|
40
|
-
const [ uid, relatedStringId ] = getRelatedGroups(
|
|
36
|
+
const [ uid, relatedStringId ] = getRelatedGroups(relation);
|
|
41
37
|
const relatedId = parseInt(relatedStringId, 10);
|
|
42
38
|
|
|
43
39
|
const relatedEntity = await strapi.db.query(uid).findOne(relatedId);
|
|
@@ -45,9 +41,12 @@ module.exports = ({ strapi }) => ({
|
|
|
45
41
|
throw new PluginError(400, `Relation for field "related" does not exist. Check your payload please.`);
|
|
46
42
|
}
|
|
47
43
|
|
|
44
|
+
const isApprovalFlowEnabled = this.getCommonService().getConfig('approvalFlow', []).includes(uid) ||
|
|
45
|
+
relatedEntity.requireCommentsApproval;
|
|
46
|
+
|
|
48
47
|
const linkToThread = !isNil(threadOf) ? !!await this.getCommonService().findOne({
|
|
49
48
|
id: threadOf,
|
|
50
|
-
related,
|
|
49
|
+
related: relation,
|
|
51
50
|
}) : true;
|
|
52
51
|
const validContext = isValidUserContext(user);
|
|
53
52
|
|
|
@@ -87,7 +86,8 @@ module.exports = ({ strapi }) => ({
|
|
|
87
86
|
throw new PluginError(400, 'Object: "author" is invalid. Check your payload');
|
|
88
87
|
}
|
|
89
88
|
|
|
90
|
-
if (
|
|
89
|
+
if (isApprovalFlowEnabled &&
|
|
90
|
+
(!isNil(data.approvalStatus) && (data.approvalStatus !== APPROVAL_STATUS.PENDING))) {
|
|
91
91
|
throw new PluginError(400, 'Invalid approval status');
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -95,7 +95,8 @@ module.exports = ({ strapi }) => ({
|
|
|
95
95
|
data: {
|
|
96
96
|
...rest,
|
|
97
97
|
...authorData,
|
|
98
|
-
related,
|
|
98
|
+
related: relation,
|
|
99
|
+
approvalStatus: isApprovalFlowEnabled ? APPROVAL_STATUS.PENDING : null,
|
|
99
100
|
}
|
|
100
101
|
});
|
|
101
102
|
return this.getCommonService().sanitizeCommentEntity(entity);
|
|
@@ -177,10 +178,17 @@ module.exports = ({ strapi }) => ({
|
|
|
177
178
|
$or: [{ authorUser: authorId }, { authorId }],
|
|
178
179
|
});
|
|
179
180
|
if (!entity){
|
|
180
|
-
throw
|
|
181
|
+
throw new PluginError(404, 'Entity not exist');
|
|
181
182
|
}
|
|
183
|
+
|
|
182
184
|
return strapi.db.query(getModelUid('comment'))
|
|
183
|
-
.update({
|
|
185
|
+
.update({
|
|
186
|
+
where: {
|
|
187
|
+
id: commentId,
|
|
188
|
+
related: relationId
|
|
189
|
+
},
|
|
190
|
+
data: { removed: true }
|
|
191
|
+
})
|
|
184
192
|
.then(({ id }) => this.markAsRemovedNested(id, true))
|
|
185
193
|
.then(() => ({ id: commentId }));
|
|
186
194
|
},
|