strapi-plugin-comments 2.1.5 → 2.1.8

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 CHANGED
@@ -61,7 +61,7 @@ A plugin for [Strapi Headless CMS](https://github.com/strapi/strapi) that provid
61
61
 
62
62
  ## ⏳ Installation
63
63
 
64
- ### Via Strapi Markerplace
64
+ ### Via Strapi Marketplace
65
65
 
66
66
  As a ✅ **verified** plugin by Strapi team we're available on the [**Strapi Marketplace**](https://market.strapi.io/plugins/strapi-plugin-comments) as well as **In-App Marketplace** where you can follow the installation instructions.
67
67
 
@@ -132,7 +132,7 @@ In our minimum support we're following [official Node.js releases timelines](htt
132
132
 
133
133
  **Supported Strapi versions**:
134
134
 
135
- - Strapi v4.1.8 (recently tested)
135
+ - Strapi v4.1.12 (recently tested)
136
136
  - Strapi v4.x
137
137
 
138
138
  > This plugin is designed for **Strapi v4** and is not working with v3.x. To get version for **Strapi v3** install version [v1.x](https://github.com/VirtusLab-Open-Source/strapi-plugin-comments/tree/strapi-v3).
@@ -150,8 +150,6 @@ To start your journey with **Comments plugin** you must first setup it using the
150
150
 
151
151
  Version `2.0.3` introduce the intuitive **Settings** page which you can easly access via `Strapi Settings -> Section: Comments Plugin -> Configuration`. On dedicated page you will be able to setup all crucial properties which drives the plugin and customize each individual collection for which **Comments plugin** should be enabled.
152
152
 
153
- On the dedicated page, you will be able to set up all crucial properties which drive the plugin and customize each individual collection for which **Comments plugin** should be enabled.
154
-
155
153
  <div style="margin: 20px 0" align="center">
156
154
  <img style="width: 100%; height: auto;" src="public/assets/configuration.png" alt="Plugin configuration" />
157
155
  </div>
@@ -14,9 +14,9 @@ const UserAvatar = ({ avatar, name }) => {
14
14
  if ((0, lodash_1.isObject)(avatar)) {
15
15
  image = avatar?.formats?.thumbnail.url || avatar.url;
16
16
  }
17
- return react_1.default.createElement(Avatar_1.Avatar, { src: image, alt: name });
17
+ return image && (react_1.default.createElement(Avatar_1.Avatar, { src: image, alt: name }));
18
18
  }
19
- return react_1.default.createElement(Avatar_1.Initials, null, (0, utils_1.renderInitials)(name));
19
+ return name && (react_1.default.createElement(Avatar_1.Initials, null, (0, utils_1.renderInitials)(name)));
20
20
  };
21
21
  UserAvatar.propTypes = {
22
22
  avatar: prop_types_1.default.oneOfType(prop_types_1.default.string, prop_types_1.default.object).isRequired,
@@ -34,6 +34,7 @@ const Button_1 = require("@strapi/design-system/Button");
34
34
  const IconButton_1 = require("@strapi/design-system/IconButton");
35
35
  const helper_plugin_1 = require("@strapi/helper-plugin");
36
36
  const icons_1 = require("../icons");
37
+ const icons_2 = require("@strapi/icons");
37
38
  const api_1 = require("../../pages/utils/api");
38
39
  const utils_1 = require("../../utils");
39
40
  const ReportsReviewTable_1 = __importDefault(require("../ReportsReviewTable"));
@@ -42,9 +43,14 @@ const pluginId_1 = require("../../pluginId");
42
43
  const DiscussionThreadItemReviewAction = ({ item, isAnyActionLoading: isAnyActionLoading, queryToInvalidate, areBlockButtonsDisabled, allowedActions: { canModerate, canAccessReports, canReviewReports }, blockItemMutation, blockItemThreadMutation, onBlockButtonsStateChange, onBlockActionClick, }) => {
43
44
  const { reports } = item;
44
45
  const [reportsReviewVisible, setReportsReviewVisible] = (0, react_1.useState)(false);
46
+ const [storedItems, setStoredItems] = (0, react_1.useState)([]);
47
+ const [selectedItems, setSelectedItems] = (0, react_1.useState)([]);
45
48
  const toggleNotification = (0, helper_plugin_1.useNotification)();
46
49
  const queryClient = (0, react_query_1.useQueryClient)();
47
- const { unlockApp } = (0, helper_plugin_1.useOverlayBlocker)();
50
+ const { lockApp, unlockApp } = (0, helper_plugin_1.useOverlayBlocker)();
51
+ (0, react_1.useEffect)(() => {
52
+ setStoredItems((0, lodash_1.orderBy)(reports, ["resolved", "createdAt"], ["DESC", "DESC"]));
53
+ }, [reportsReviewVisible]);
48
54
  const onSuccess = (message, stateAction = () => { }, indalidate = true) => async () => {
49
55
  if (indalidate) {
50
56
  await queryClient.invalidateQueries(queryToInvalidate);
@@ -59,11 +65,17 @@ const DiscussionThreadItemReviewAction = ({ item, isAnyActionLoading: isAnyActio
59
65
  const onError = (err) => {
60
66
  (0, utils_1.handleAPIError)(err, toggleNotification);
61
67
  };
68
+ const isNotResolved = entry => !entry.resolved;
62
69
  const resolveReportMutation = (0, react_query_1.useMutation)(api_1.resolveReport, {
63
70
  onSuccess: onSuccess("page.details.panel.discussion.warnings.reports.dialog.confirmation.success", () => { }, false),
64
71
  onError,
65
72
  refetchActive: false,
66
73
  });
74
+ const resolveMultipleReportsMutation = (0, react_query_1.useMutation)(api_1.resolveMultipleReports, {
75
+ onSuccess: onSuccess("page.details.panel.discussion.warnings.reports.selected.dialog.confirmation.success", () => { }, false),
76
+ onError,
77
+ refetchActive: false,
78
+ });
67
79
  const handleBlockActionClick = () => setReportsReviewVisible(false);
68
80
  const handleReportsReviewClick = () => {
69
81
  if (canAccessReports) {
@@ -80,21 +92,47 @@ const DiscussionThreadItemReviewAction = ({ item, isAnyActionLoading: isAnyActio
80
92
  await onBlockActionClick(blockItemThreadMutation, handleBlockActionClick);
81
93
  }
82
94
  };
95
+ const handleClickResolveSelected = async () => {
96
+ if (canReviewReports) {
97
+ lockApp();
98
+ const items = await resolveMultipleReportsMutation.mutateAsync({
99
+ id: item.id,
100
+ reportIds: selectedItems,
101
+ });
102
+ if (!(0, lodash_1.isEmpty)(items)) {
103
+ const updatedItems = storedItems.map((_) => ({
104
+ ..._,
105
+ resolved: selectedItems.includes(_.id) ? true : _.resolved,
106
+ }));
107
+ setStoredItems(updatedItems);
108
+ setSelectedItems([], false);
109
+ onBlockButtonsStateChange(updatedItems.filter(isNotResolved).length === 0);
110
+ }
111
+ }
112
+ };
83
113
  const handleReportsReviewClose = async () => {
84
114
  await queryClient.invalidateQueries(queryToInvalidate);
85
115
  setReportsReviewVisible(false);
86
116
  };
117
+ const onSelectionChange = selection => setSelectedItems(selection);
87
118
  const isLoading = isAnyActionLoading && resolveReportMutation.isLoading;
88
- const openReports = reports?.filter((_) => !_.resolved);
119
+ const openReports = reports?.filter(isNotResolved);
89
120
  const hasReports = !(0, lodash_1.isEmpty)(openReports);
90
121
  const reviewFlowEnabled = canAccessReports && hasReports && !(item.blocked || item.blockedThread);
122
+ const hasAnySelectedItems = selectedItems.length > 0;
91
123
  if (reviewFlowEnabled) {
92
124
  return (react_1.default.createElement(react_1.default.Fragment, null,
93
125
  react_1.default.createElement(IconButton_1.IconButton, { onClick: handleReportsReviewClick, label: (0, utils_1.getMessage)("page.discover.table.reports.review"), icon: react_1.default.createElement(icons_1.ReviewIcon, null) }),
94
126
  react_1.default.createElement(ReportsReviewModal_1.default, { isVisible: reportsReviewVisible, isActionAsync: isLoading, allowedActions: { canModerate, canAccessReports, canReviewReports }, onClose: handleReportsReviewClose, startActions: react_1.default.createElement(Button_1.Button, { onClick: handleReportsReviewClose, variant: "tertiary" }, (0, utils_1.getMessage)("compontents.confirmation.dialog.button.cancel", "Cancel")), endActions: canModerate && (react_1.default.createElement(react_1.default.Fragment, null,
95
127
  react_1.default.createElement(Button_1.Button, { onClick: handleBlockItemClick, variant: "danger-light", startIcon: react_1.default.createElement(icons_1.LockIcon, null), disabled: areBlockButtonsDisabled }, (0, utils_1.getMessage)(`page.details.actions.comment.block`, "Block comment")),
96
- item.gotThread && (react_1.default.createElement(Button_1.Button, { onClick: handleBlockItemThreadClick, variant: "danger", startIcon: react_1.default.createElement(icons_1.LockIcon, null), disabled: areBlockButtonsDisabled }, (0, utils_1.getMessage)(`page.details.actions.thread.block`, "Block thread"))))), item: item },
97
- react_1.default.createElement(ReportsReviewTable_1.default, { commentId: item.id, items: (0, lodash_1.orderBy)(reports, ["resolved", "createdAt"], ["DESC", "DESC"]), mutation: resolveReportMutation, allowedActions: { canAccessReports, canReviewReports }, onBlockButtonsStateChange: onBlockButtonsStateChange }))));
128
+ item.gotThread && (react_1.default.createElement(Button_1.Button, { onClick: handleBlockItemThreadClick, variant: "danger", startIcon: react_1.default.createElement(icons_1.LockIcon, null), disabled: areBlockButtonsDisabled }, (0, utils_1.getMessage)(`page.details.actions.thread.block`, "Block thread"))),
129
+ hasAnySelectedItems && (react_1.default.createElement(Button_1.Button, { onClick: handleClickResolveSelected, variant: "success", startIcon: react_1.default.createElement(icons_2.Check, null) }, (0, utils_1.getMessage)({
130
+ id: `page.details.panel.discussion.warnings.reports.dialog.actions.resolve.selected`,
131
+ props: {
132
+ count: selectedItems.length,
133
+ }
134
+ }, "Resolve selected"))))), item: item },
135
+ react_1.default.createElement(ReportsReviewTable_1.default, { commentId: item.id, items: storedItems, selectedItems: selectedItems, mutation: resolveReportMutation, updateItems: setStoredItems, allowedActions: { canAccessReports, canReviewReports }, onBlockButtonsStateChange: onBlockButtonsStateChange, onSelectionChange: onSelectionChange }))));
98
136
  }
99
137
  return null;
100
138
  };
@@ -1,20 +1,26 @@
1
1
  declare const ReportsReviewTable: {
2
- ({ commentId, items, mutation, allowedActions: { canAccessReports, canReviewReports }, onBlockButtonsStateChange, }: {
2
+ ({ commentId, items, selectedItems, mutation, updateItems, allowedActions: { canAccessReports, canReviewReports }, onBlockButtonsStateChange, onSelectionChange, }: {
3
3
  commentId: any;
4
4
  items: any;
5
+ selectedItems?: never[] | undefined;
5
6
  mutation: any;
7
+ updateItems: any;
6
8
  allowedActions: {
7
9
  canAccessReports: any;
8
10
  canReviewReports: any;
9
11
  };
10
12
  onBlockButtonsStateChange: any;
13
+ onSelectionChange: any;
11
14
  }): any;
12
15
  propTypes: {
13
16
  commentId: any;
14
17
  items: any;
18
+ selectedItems: any;
15
19
  mutation: any;
16
20
  allowedActions: any;
17
21
  onBlockButtonsStateChange: any;
22
+ onSelectionChange: any;
23
+ updateItems: any;
18
24
  };
19
25
  };
20
26
  export default ReportsReviewTable;
@@ -41,8 +41,7 @@ const utils_1 = require("../../utils");
41
41
  const constants_1 = require("../../utils/constants");
42
42
  const StatusBadge_1 = __importDefault(require("../StatusBadge"));
43
43
  const styles_1 = require("../ActionButton/styles");
44
- const ReportsReviewTable = ({ commentId, items, mutation, allowedActions: { canAccessReports, canReviewReports }, onBlockButtonsStateChange, }) => {
45
- const [storedItems, setStoredItems] = (0, react_1.useState)([]);
44
+ const ReportsReviewTable = ({ commentId, items, selectedItems = [], mutation, updateItems, allowedActions: { canAccessReports, canReviewReports }, onBlockButtonsStateChange, onSelectionChange, }) => {
46
45
  const { formatDate } = (0, react_intl_1.useIntl)();
47
46
  const { lockApp } = (0, helper_plugin_1.useOverlayBlocker)();
48
47
  const renderStatus = (resolved) => {
@@ -68,6 +67,20 @@ const ReportsReviewTable = ({ commentId, items, mutation, allowedActions: { canA
68
67
  }
69
68
  return (react_1.default.createElement(StatusBadge_1.default, { backgroundColor: `${color}100`, textColor: `${color}600`, color: color }, (0, utils_1.getMessage)(`page.details.panel.discussion.warnings.reports.dialog.reason.${reason}`, reason)));
70
69
  };
70
+ const handleItemSelectionChange = (selection, value) => {
71
+ if ((0, lodash_1.isArray)(selection)) {
72
+ onSelectionChange(value ? selection : []);
73
+ }
74
+ else {
75
+ onSelectionChange([...selectedItems, selection].filter(item => value || selection !== item));
76
+ }
77
+ };
78
+ const isNotResolved = entry => !entry.resolved;
79
+ const unresolvedItems = items.filter(isNotResolved);
80
+ const areAllItemsSelected = () => !(0, lodash_1.isEmpty)(selectedItems) ?
81
+ selectedItems.length === unresolvedItems.length :
82
+ false;
83
+ const isItemSelected = id => selectedItems.includes(id);
71
84
  const handleClickResolve = async (reportId) => {
72
85
  if (canReviewReports) {
73
86
  lockApp();
@@ -76,28 +89,26 @@ const ReportsReviewTable = ({ commentId, items, mutation, allowedActions: { canA
76
89
  reportId,
77
90
  });
78
91
  if (item) {
79
- const updatedItems = storedItems.map((_) => ({
92
+ const updatedItems = items.map((_) => ({
80
93
  ..._,
81
94
  resolved: reportId === _.id ? true : _.resolved,
82
95
  }));
83
- setStoredItems(updatedItems);
84
- onBlockButtonsStateChange(updatedItems.filter((_) => !_.resolved).length === 0);
96
+ updateItems(updatedItems);
97
+ onSelectionChange(selectedItems.filter(_ => _ !== reportId));
98
+ onBlockButtonsStateChange(updatedItems.filter(isNotResolved).length === 0);
85
99
  }
86
100
  }
87
101
  };
88
- (0, react_1.useEffect)(() => {
89
- setStoredItems(items);
90
- }, []);
91
- if ((0, lodash_1.isEmpty)(storedItems)) {
102
+ if ((0, lodash_1.isEmpty)(items)) {
92
103
  return null;
93
104
  }
94
105
  const COL_COUNT = 6;
95
106
  if (canAccessReports) {
96
- return (react_1.default.createElement(Table_1.Table, { colCount: COL_COUNT, rowCount: storedItems.length },
107
+ return (react_1.default.createElement(Table_1.Table, { colCount: COL_COUNT, rowCount: items.length },
97
108
  react_1.default.createElement(Table_1.Thead, null,
98
109
  react_1.default.createElement(Table_1.Tr, null,
99
110
  react_1.default.createElement(Table_1.Th, null,
100
- react_1.default.createElement(BaseCheckbox_1.BaseCheckbox, { "aria-label": "Select all entries" })),
111
+ react_1.default.createElement(BaseCheckbox_1.BaseCheckbox, { "aria-label": (0, utils_1.getMessage)("page.details.panel.discussion.warnings.reports.dialog.selectAll"), value: areAllItemsSelected(), disabled: (0, lodash_1.isEmpty)(unresolvedItems), onValueChange: (0, react_1.useCallback)((value) => handleItemSelectionChange(unresolvedItems.map(_ => _.id), value), [unresolvedItems]) })),
101
112
  react_1.default.createElement(Table_1.Th, null,
102
113
  react_1.default.createElement(Typography_1.Typography, { variant: "sigma" }, (0, utils_1.getMessage)("page.details.panel.discussion.warnings.reports.dialog.reason"))),
103
114
  react_1.default.createElement(Table_1.Th, null,
@@ -108,9 +119,9 @@ const ReportsReviewTable = ({ commentId, items, mutation, allowedActions: { canA
108
119
  react_1.default.createElement(Typography_1.Typography, { variant: "sigma" }, (0, utils_1.getMessage)("page.details.panel.discussion.warnings.reports.dialog.status"))),
109
120
  canReviewReports && (react_1.default.createElement(Table_1.Th, null,
110
121
  react_1.default.createElement(VisuallyHidden_1.VisuallyHidden, null, (0, utils_1.getMessage)("page.details.panel.discussion.warnings.reports.dialog.actions")))))),
111
- react_1.default.createElement(Table_1.Tbody, null, storedItems.map((entry) => (react_1.default.createElement(Table_1.Tr, { key: entry.id },
122
+ react_1.default.createElement(Table_1.Tbody, null, items.map((entry) => (react_1.default.createElement(Table_1.Tr, { key: entry.id },
112
123
  react_1.default.createElement(Table_1.Td, null,
113
- react_1.default.createElement(BaseCheckbox_1.BaseCheckbox, { "aria-label": `Select report` })),
124
+ react_1.default.createElement(BaseCheckbox_1.BaseCheckbox, { "aria-label": (0, utils_1.getMessage)("page.details.panel.discussion.warnings.reports.dialog.select"), value: isItemSelected(entry.id), disabled: entry.resolved, onValueChange: (value) => handleItemSelectionChange(entry.id, value) })),
114
125
  react_1.default.createElement(Table_1.Td, null,
115
126
  react_1.default.createElement(Typography_1.Typography, { textColor: "neutral800" }, renderReason(entry.reason))),
116
127
  react_1.default.createElement(Table_1.Td, null,
@@ -129,12 +140,15 @@ const ReportsReviewTable = ({ commentId, items, mutation, allowedActions: { canA
129
140
  ReportsReviewTable.propTypes = {
130
141
  commentId: prop_types_1.default.oneOfType(prop_types_1.default.string, prop_types_1.default.number).isRequired,
131
142
  items: prop_types_1.default.array.isRequired,
143
+ selectedItems: prop_types_1.default.array.isRequired,
132
144
  mutation: prop_types_1.default.func.isRequired,
133
145
  allowedActions: prop_types_1.default.shape({
134
146
  canAccessReports: prop_types_1.default.bool,
135
147
  canReviewReports: prop_types_1.default.bool,
136
148
  }),
137
149
  onBlockButtonsStateChange: prop_types_1.default.func.isRequired,
150
+ onSelectionChange: prop_types_1.default.func.isRequired,
151
+ updateItems: prop_types_1.default.func.isRequired,
138
152
  };
139
153
  exports.default = ReportsReviewTable;
140
154
  //# sourceMappingURL=index.js.map
@@ -39,6 +39,7 @@ const Box_1 = require("@strapi/design-system/Box");
39
39
  const Stack_1 = require("@strapi/design-system/Stack");
40
40
  const Typography_1 = require("@strapi/design-system/Typography");
41
41
  const Grid_1 = require("@strapi/design-system/Grid");
42
+ const TextInput_1 = require("@strapi/design-system/TextInput");
42
43
  const ToggleInput_1 = require("@strapi/design-system/ToggleInput");
43
44
  const Select_1 = require("@strapi/design-system/Select");
44
45
  const LiveRegions_1 = require("@strapi/design-system/LiveRegions");
@@ -75,7 +76,7 @@ const Settings = () => {
75
76
  areCollectionsLoading ||
76
77
  areRolesLoading;
77
78
  const isError = configErr || collectionsErr || rolesErr;
78
- const preparePayload = ({ enabledCollections, gqlAuthEnabled, approvalFlow, entryLabel, ...rest }) => ({
79
+ const preparePayload = ({ enabledCollections, gqlAuthEnabled, approvalFlow, entryLabel, clientUrl, clientEmail, ...rest }) => ({
79
80
  ...rest,
80
81
  enabledCollections,
81
82
  approvalFlow: approvalFlow.filter((_) => enabledCollections.includes(_)),
@@ -89,6 +90,10 @@ const Settings = () => {
89
90
  "*": entryLabel["*"],
90
91
  },
91
92
  reportReasons: configData?.reportReasons,
93
+ client: clientEmail || clientUrl ? {
94
+ contactEmail: clientEmail,
95
+ url: clientUrl,
96
+ } : undefined,
92
97
  gql: gqlAuthEnabled ? { auth: true } : undefined,
93
98
  });
94
99
  if (isLoading || isError) {
@@ -109,6 +114,8 @@ const Settings = () => {
109
114
  const isGQLPluginEnabled = configData?.isGQLPluginEnabled;
110
115
  const gqlAuthEnabled = configData?.gql?.auth || undefined;
111
116
  const moderatorRoles = configData?.moderatorRoles?.filter((code) => allRoles.find((_) => _.code === code)) || [];
117
+ const clientUrl = configData?.client?.url;
118
+ const clientEmail = configData?.client?.contactEmail;
112
119
  const changeApprovalFlowFor = (uid, current, value) => {
113
120
  const currentSet = new Set(current);
114
121
  if (value) {
@@ -170,6 +177,8 @@ const Settings = () => {
170
177
  badWords,
171
178
  approvalFlow,
172
179
  entryLabel,
180
+ clientEmail,
181
+ clientUrl,
173
182
  gqlAuthEnabled,
174
183
  }, enableReinitialize: true, onSubmit: handleUpdateConfiguration }, ({ handleSubmit, setFieldValue, values }) => (react_1.default.createElement(helper_plugin_1.Form, { noValidate: true, onSubmit: handleSubmit },
175
184
  react_1.default.createElement(Layout_1.HeaderLayout, { title: (0, utils_1.getMessage)("page.settings.header.title"), subtitle: (0, utils_1.getMessage)("page.settings.header.description"), primaryAction: react_1.default.createElement(helper_plugin_1.CheckPermissions, { permissions: permissions_1.default.settingsChange },
@@ -205,12 +214,20 @@ const Settings = () => {
205
214
  react_1.default.createElement(Stack_1.Stack, { size: 4 },
206
215
  react_1.default.createElement(Typography_1.Typography, { variant: "delta", as: "h2" }, (0, utils_1.getMessage)("page.settings.section.additional")),
207
216
  react_1.default.createElement(Grid_1.Grid, { gap: 4 },
208
- react_1.default.createElement(Grid_1.GridItem, { col: 6, xs: 12 },
209
- react_1.default.createElement(Select_1.Select, { name: "moderatorRoles", label: (0, utils_1.getMessage)("page.settings.form.moderatorRoles.label"), placeholder: (0, utils_1.getMessage)("page.settings.form.moderatorRoles.placeholder"), hint: (0, utils_1.getMessage)("page.settings.form.moderatorRoles.hint"), onClear: () => setFieldValue("moderatorRoles", [], false), value: values.moderatorRoles, onChange: (value) => setFieldValue("moderatorRoles", value, false), disabled: restartRequired, multi: true, withTags: true }, allRoles.map(({ code, name }) => (react_1.default.createElement(Select_1.Option, { key: code, value: code }, name))))),
210
217
  react_1.default.createElement(Grid_1.GridItem, { col: 6, xs: 12 },
211
218
  react_1.default.createElement(ToggleInput_1.ToggleInput, { name: "badWords", label: (0, utils_1.getMessage)("page.settings.form.badWords.label"), hint: (0, utils_1.getMessage)("page.settings.form.badWords.hint"), checked: values.badWords, onChange: ({ target: { checked } }) => setFieldValue("badWords", checked, false), onLabel: (0, utils_1.getMessage)("compontents.toogle.enabled"), offLabel: (0, utils_1.getMessage)("compontents.toogle.disabled"), disabled: restartRequired })),
212
219
  isGQLPluginEnabled && (react_1.default.createElement(Grid_1.GridItem, { col: 6, xs: 12 },
213
- react_1.default.createElement(ToggleInput_1.ToggleInput, { name: "badWords", label: (0, utils_1.getMessage)("page.settings.form.gqlAuth.label"), hint: (0, utils_1.getMessage)("page.settings.form.gqlAuth.hint"), checked: values.gqlAuthEnabled, onChange: ({ target: { checked } }) => setFieldValue("gqlAuthEnabled", checked, false), onLabel: (0, utils_1.getMessage)("compontents.toogle.enabled"), offLabel: (0, utils_1.getMessage)("compontents.toogle.disabled"), disabled: restartRequired })))))),
220
+ react_1.default.createElement(ToggleInput_1.ToggleInput, { name: "gqlAuthEnabled", label: (0, utils_1.getMessage)("page.settings.form.gqlAuth.label"), hint: (0, utils_1.getMessage)("page.settings.form.gqlAuth.hint"), checked: values.gqlAuthEnabled, onChange: ({ target: { checked } }) => setFieldValue("gqlAuthEnabled", checked, false), onLabel: (0, utils_1.getMessage)("compontents.toogle.enabled"), offLabel: (0, utils_1.getMessage)("compontents.toogle.disabled"), disabled: restartRequired })))))),
221
+ react_1.default.createElement(Box_1.Box, { ...boxDefaultProps },
222
+ react_1.default.createElement(Stack_1.Stack, { size: 4 },
223
+ react_1.default.createElement(Typography_1.Typography, { variant: "delta", as: "h2" }, (0, utils_1.getMessage)("page.settings.section.client")),
224
+ react_1.default.createElement(Grid_1.Grid, { gap: 4 },
225
+ react_1.default.createElement(Grid_1.GridItem, { col: 3, xs: 12 },
226
+ react_1.default.createElement(TextInput_1.TextInput, { type: "url", name: "clientUrl", label: (0, utils_1.getMessage)("page.settings.form.client.url.label"), hint: (0, utils_1.getMessage)("page.settings.form.client.url.hint"), value: values.clientUrl, onChange: ({ target: { value } }) => setFieldValue("clientUrl", value, false), disabled: restartRequired })),
227
+ react_1.default.createElement(Grid_1.GridItem, { col: 3, xs: 12 },
228
+ react_1.default.createElement(TextInput_1.TextInput, { type: "email", name: "clientEmail", label: (0, utils_1.getMessage)("page.settings.form.client.email.label"), hint: (0, utils_1.getMessage)("page.settings.form.client.email.hint"), value: values.clientEmail, onChange: ({ target: { value } }) => setFieldValue("clientEmail", value, false), disabled: restartRequired })),
229
+ react_1.default.createElement(Grid_1.GridItem, { col: 6, xs: 12 },
230
+ react_1.default.createElement(Select_1.Select, { name: "moderatorRoles", label: (0, utils_1.getMessage)("page.settings.form.moderatorRoles.label"), placeholder: (0, utils_1.getMessage)("page.settings.form.moderatorRoles.placeholder"), hint: (0, utils_1.getMessage)("page.settings.form.moderatorRoles.hint"), onClear: () => setFieldValue("moderatorRoles", [], false), value: values.moderatorRoles, onChange: (value) => setFieldValue("moderatorRoles", value, false), disabled: restartRequired, multi: true, withTags: true }, allRoles.map(({ code, name }) => (react_1.default.createElement(Select_1.Option, { key: code, value: code }, name)))))))),
214
231
  react_1.default.createElement(helper_plugin_1.CheckPermissions, { permissions: permissions_1.default.settingsChange },
215
232
  react_1.default.createElement(Box_1.Box, { ...boxDefaultProps },
216
233
  react_1.default.createElement(Stack_1.Stack, { size: 4 },
@@ -9,4 +9,8 @@ export declare const rejectItem: (id: Id) => any;
9
9
  export declare const blockItemThread: (id: Id) => any;
10
10
  export declare const unblockItemThread: (id: Id) => any;
11
11
  export declare const resolveReport: ({ id, reportId }: ToBeFixed) => any;
12
+ export declare const resolveMultipleReports: ({ id, reportIds }: {
13
+ id: Id;
14
+ reportIds: Array<Id>;
15
+ }) => any;
12
16
  //# sourceMappingURL=api.d.ts.map
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolveReport = exports.unblockItemThread = exports.blockItemThread = exports.rejectItem = exports.approveItem = exports.unblockItem = exports.blockItem = exports.fetchContentTypeData = exports.fetchDetailsData = void 0;
3
+ exports.resolveMultipleReports = exports.resolveReport = exports.unblockItemThread = exports.blockItemThread = exports.rejectItem = exports.approveItem = exports.unblockItem = exports.blockItem = exports.fetchContentTypeData = exports.fetchDetailsData = void 0;
4
4
  const qs_1 = require("qs");
5
5
  const lodash_1 = require("lodash");
6
6
  const utils_1 = require("../../utils");
@@ -55,4 +55,8 @@ const resolveReport = ({ id, reportId }) => {
55
55
  return utils_1.axiosInstance.patch((0, utils_1.getApiURL)(`moderate/single/${id}/report/${reportId}/resolve`));
56
56
  };
57
57
  exports.resolveReport = resolveReport;
58
+ const resolveMultipleReports = ({ id, reportIds }) => {
59
+ return utils_1.axiosInstance.put((0, utils_1.getApiURL)(`moderate/single/${id}/report/resolve`), reportIds);
60
+ };
61
+ exports.resolveMultipleReports = resolveMultipleReports;
58
62
  //# sourceMappingURL=api.js.map
@@ -43,6 +43,8 @@
43
43
  "page.details.panel.discussion.warnings.reports.review": "Review",
44
44
  "page.details.panel.discussion.warnings.reports.dialog.list": "Reports",
45
45
  "page.details.panel.discussion.warnings.reports.dialog.header": "Moderation: Review open reports",
46
+ "page.details.panel.discussion.warnings.reports.dialog.selectAll": "Select all entries",
47
+ "page.details.panel.discussion.warnings.reports.dialog.select": "Select entity",
46
48
  "page.details.panel.discussion.warnings.reports.dialog.reason": "Reason",
47
49
  "page.details.panel.discussion.warnings.reports.dialog.reason.BAD_LANGUAGE": "Bad language",
48
50
  "page.details.panel.discussion.warnings.reports.dialog.reason.DISCRIMINATION": "Discrimination",
@@ -54,7 +56,9 @@
54
56
  "page.details.panel.discussion.warnings.reports.dialog.status.RESOLVED": "Resolved",
55
57
  "page.details.panel.discussion.warnings.reports.dialog.actions": "Actions",
56
58
  "page.details.panel.discussion.warnings.reports.dialog.actions.resolve": "Resolve",
59
+ "page.details.panel.discussion.warnings.reports.dialog.actions.resolve.selected": "Resolve reports ({count})",
57
60
  "page.details.panel.discussion.warnings.reports.dialog.confirmation.success": "Open report has been resolved",
61
+ "page.details.panel.discussion.warnings.reports.selected.dialog.confirmation.success": "Open reports has been resolved",
58
62
  "page.details.panel.discussion.status.blocked": "Blocked",
59
63
  "page.details.panel.entity": "Entity details",
60
64
  "page.details.panel.status": "Discussion is",
@@ -90,6 +94,7 @@
90
94
  "page.settings.actions.restart": "Restart Strapi",
91
95
  "page.settings.section.general": "General configuration",
92
96
  "page.settings.section.additional": "Additional configuration",
97
+ "page.settings.section.client": "Client portal configuration",
93
98
  "page.settings.section.restore": "Restore default settings",
94
99
  "page.settings.section.restore.subtitle": "Discarding all of applied settings and getting back to plugin default ones. Use reasonable.",
95
100
  "page.settings.form.enabledCollections.label": "Enable comments only for",
@@ -109,6 +114,10 @@
109
114
  "page.settings.form.entryLabel.label": "Title fields",
110
115
  "page.settings.form.entryLabel.placeholder": "Select at least one or leave empty to apply defaults",
111
116
  "page.settings.form.entryLabel.hint": "If left empty title rendering is going to take following ordered fields: \"Title\", \"Subject\" & \"Name\"",
117
+ "page.settings.form.client.url.label": "Portal URL",
118
+ "page.settings.form.client.url.hint": "URL where your client portal is available",
119
+ "page.settings.form.client.email.label": "Communication e-mail",
120
+ "page.settings.form.client.email.hint": "E-mail address used for mailings, communication with users on behalf of portal team",
112
121
  "page.settings.actions.restore.confirmation.header": "Restore default configuration",
113
122
  "page.settings.actions.restore.confirmation.description": "You're about to restore plugin configuration to it default values. It might have destructive impact on already collected content. Do you really want to proceed?",
114
123
  "page.settings.actions.restore.confirmation.button.confirm": "Yes, I want to restore",
@@ -43,6 +43,8 @@
43
43
  "page.details.panel.discussion.warnings.reports.review": "consultation",
44
44
  "page.details.panel.discussion.warnings.reports.dialog.list": "Rapports",
45
45
  "page.details.panel.discussion.warnings.reports.dialog.header": "Modération : Examiner les rapports ouverts",
46
+ "page.details.panel.discussion.warnings.reports.dialog.selectAll": "Sélectionner toutes les entrées",
47
+ "page.details.panel.discussion.warnings.reports.dialog.select": "Sélectionnez l'entité",
46
48
  "page.details.panel.discussion.warnings.reports.dialog.reason": "Raison",
47
49
  "page.details.panel.discussion.warnings.reports.dialog.reason.BAD_LANGUAGE": "Langage inapproprié",
48
50
  "page.details.panel.discussion.warnings.reports.dialog.reason.DISCRIMINATION": "Discrimination",
@@ -54,7 +56,9 @@
54
56
  "page.details.panel.discussion.warnings.reports.dialog.status.RESOLVED": "Résolu",
55
57
  "page.details.panel.discussion.warnings.reports.dialog.actions": "Actions",
56
58
  "page.details.panel.discussion.warnings.reports.dialog.actions.resolve": "Résoudre",
59
+ "page.details.panel.discussion.warnings.reports.dialog.actions.resolve.selected": "Résoudre les rapports ({count})",
57
60
  "page.details.panel.discussion.warnings.reports.dialog.confirmation.success": "Le rapport ouvert a été résolu",
61
+ "page.details.panel.discussion.warnings.reports.selected.dialog.confirmation.success": "Les rapports ouverts ont été résolus",
58
62
  "page.details.panel.discussion.status.blocked": "Bloqué",
59
63
  "page.details.panel.entity": "Détails de l'entité",
60
64
  "page.details.panel.status": "La discussion est",
@@ -90,6 +94,7 @@
90
94
  "page.settings.actions.restart": "Redémarrer Strapi",
91
95
  "page.settings.section.general": "Configuration générale",
92
96
  "page.settings.section.additional": "Configuration supplémentaire",
97
+ "page.settings.section.client": "Configuration du portail client",
93
98
  "page.settings.section.restore": "Restaurer les paramètres par défaut",
94
99
  "page.settings.section.restore.subtitle": "Suppression de tous les paramètres appliqués et retour aux paramètres par défaut du plugin. Utilisez-les raisonnablement",
95
100
  "page.settings.form.enabledCollections.label": "Activer les commentaires uniquement pour",
@@ -109,6 +114,10 @@
109
114
  "page.settings.form.entryLabel.label": "Champs de titre",
110
115
  "page.settings.form.entryLabel.placeholder": "Sélectionnez au moins un champ ou laissez vide pour appliquer les valeurs par défaut",
111
116
  "page.settings.form.entryLabel.hint": "Si vous laissez vide, le rendu du titre sera le suivant : \"Title\", \"Subject\" & \"Name\"",
117
+ "page.settings.form.client.url.label": "Portail URL",
118
+ "page.settings.form.client.url.hint": "URL où votre portail client est disponible",
119
+ "page.settings.form.client.email.label": "E-mail de communication",
120
+ "page.settings.form.client.email.hint": "Adresse e-mail utilisée pour les mailings, la communication avec les utilisateurs au nom de l'équipe du portail",
112
121
  "page.settings.actions.restore.confirmation.header": "Restaurer la configuration par défaut",
113
122
  "page.settings.actions.restore.confirmation.description": "Vous êtes sur le point de restaurer la configuration du plugin à ses valeurs par défaut. Cela pourrait avoir un impact destructeur sur le contenu déjà collecté. Voulez-vous vraiment continuer ?",
114
123
  "page.settings.actions.restore.confirmation.button.confirm": "Oui, je veux restaurer",
@@ -43,6 +43,8 @@
43
43
  "page.details.panel.discussion.warnings.reports.review": "Review",
44
44
  "page.details.panel.discussion.warnings.reports.dialog.list": "Reports",
45
45
  "page.details.panel.discussion.warnings.reports.dialog.header": "Moderation: Review open reports",
46
+ "page.details.panel.discussion.warnings.reports.dialog.selectAll": "Selecione todas as entradas",
47
+ "page.details.panel.discussion.warnings.reports.dialog.select": "Selecionar entidade",
46
48
  "page.details.panel.discussion.warnings.reports.dialog.reason": "Razão",
47
49
  "page.details.panel.discussion.warnings.reports.dialog.reason.BAD_LANGUAGE": "Linguagem imprópria",
48
50
  "page.details.panel.discussion.warnings.reports.dialog.reason.DISCRIMINATION": "Discrimination",
@@ -54,7 +56,9 @@
54
56
  "page.details.panel.discussion.warnings.reports.dialog.status.RESOLVED": "Resolvido",
55
57
  "page.details.panel.discussion.warnings.reports.dialog.actions": "Ações",
56
58
  "page.details.panel.discussion.warnings.reports.dialog.actions.resolve": "Resolver",
57
- "page.details.panel.discussion.warnings.reports.dialog.confirmation.success": "Open report has been resolved",
59
+ "page.details.panel.discussion.warnings.reports.dialog.actions.resolve.selected": "Resolver relatórios ({count})",
60
+ "page.details.panel.discussion.warnings.reports.dialog.confirmation.success": "O relatório aberto foi resolvido",
61
+ "page.details.panel.discussion.warnings.reports.selected.dialog.confirmation.success": "Os relatórios abertos foram resolvidos",
58
62
  "page.details.panel.discussion.status.blocked": "Bloqueado",
59
63
  "page.details.panel.entity": "Entity details",
60
64
  "page.details.panel.status": "Discussion is",
@@ -90,6 +94,7 @@
90
94
  "page.settings.actions.restart": "Restart Strapi",
91
95
  "page.settings.section.general": "Configurações gerais",
92
96
  "page.settings.section.additional": "Configurações adicionais",
97
+ "page.settings.section.client": "Configurações do portal do cliente",
93
98
  "page.settings.section.restore": "Restaurar configurações originais",
94
99
  "page.settings.section.restore.subtitle": "Descartando todas as configurações aplicadas e voltando para as configurações padrão do plugin. Use razoável.",
95
100
  "page.settings.form.enabledCollections.label": "Ativar comentários apenas para",
@@ -109,6 +114,10 @@
109
114
  "page.settings.form.entryLabel.label": "Campos de título",
110
115
  "page.settings.form.entryLabel.placeholder": "Selecione pelo menos um ou deixe em branco para aplicar os padrões",
111
116
  "page.settings.form.entryLabel.hint": "Se deixado em branco, a renderização do título terá os seguintes campos ordenados: \"Title\", \"Subject\" & \"Name\"",
117
+ "page.settings.form.client.url.label": "Portal URL",
118
+ "page.settings.form.client.url.hint": "URL onde seu portal do cliente está disponível",
119
+ "page.settings.form.client.email.label": "Email de comunicação",
120
+ "page.settings.form.client.email.hint": "Endereço de e-mail usado para correspondências, comunicação com usuários em nome da equipe do portal",
112
121
  "page.settings.actions.restore.confirmation.header": "Restaurar a configuração padrão",
113
122
  "page.settings.actions.restore.confirmation.description": "Você está prestes a restaurar a configuração do plug-in para seus valores padrão. Pode ter um impacto destrutivo no conteúdo já coletado. Você realmente quer continuar?",
114
123
  "page.settings.actions.restore.confirmation.button.confirm": "Sim, quero restaurar",
@@ -1,10 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const lodash_1 = require("lodash");
4
- const renderInitials = (value = "") => value
5
- .split(" ")
6
- .map((_) => (0, lodash_1.first)(_))
7
- .join("")
8
- .toUpperCase();
4
+ const renderInitials = (value = "") => (0, lodash_1.isString)(value) ?
5
+ value
6
+ .split(" ")
7
+ .map((_) => (0, lodash_1.first)(_))
8
+ .join("")
9
+ .toUpperCase() : "";
9
10
  exports.default = renderInitials;
10
11
  //# sourceMappingURL=renderInitials.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-plugin-comments",
3
- "version": "2.1.5",
3
+ "version": "2.1.8",
4
4
  "description": "Strapi - Comments plugin",
5
5
  "strapi": {
6
6
  "name": "comments",
@@ -14,8 +14,8 @@
14
14
  "url": "https://github.com/VirtusLab-Open-Source/strapi-plugin-comments"
15
15
  },
16
16
  "dependencies": {
17
- "@strapi/utils": "^4.1.8",
18
- "@strapi/plugin-graphql": "^4.1.8",
17
+ "@strapi/utils": "^4.3.0",
18
+ "@strapi/plugin-graphql": "^4.3.0",
19
19
  "bad-words": "^3.0.4"
20
20
  },
21
21
  "devDependencies": {},
@@ -53,7 +53,7 @@
53
53
  }
54
54
  ],
55
55
  "engines": {
56
- "node": ">=14.x.x <=17.x.x",
56
+ "node": ">=14.19.1 <=17.x.x",
57
57
  "npm": ">=7.x.x"
58
58
  },
59
59
  "keywords": [
@@ -112,6 +112,18 @@ const controllers = {
112
112
  throw (0, functions_2.throwError)(ctx, e);
113
113
  }
114
114
  },
115
+ async resolveMultipleAbuseReports(ctx) {
116
+ const { params = {}, request } = ctx;
117
+ const { body } = request;
118
+ const { id: commentId } = (0, functions_2.parseParams)(params);
119
+ (0, functions_1.assertNotEmpty)(body);
120
+ try {
121
+ return await this.getService().resolveMultipleAbuseReports(body, commentId);
122
+ }
123
+ catch (e) {
124
+ throw (0, functions_2.throwError)(ctx, e);
125
+ }
126
+ },
115
127
  async approveComment(ctx) {
116
128
  const { id } = (0, functions_2.parseParams)(ctx.params || {});
117
129
  try {
@@ -65,6 +65,14 @@ const routes = [
65
65
  policies: [],
66
66
  },
67
67
  },
68
+ {
69
+ method: "PUT",
70
+ path: "/moderate/single/:id/report/resolve",
71
+ handler: "admin.resolveMultipleAbuseReports",
72
+ config: {
73
+ policies: [],
74
+ },
75
+ },
68
76
  {
69
77
  method: "PATCH",
70
78
  path: "/moderate/thread/:id/block",