strapi-plugin-comments 2.0.0-beta.3 → 2.0.0-beta.7
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 +335 -11
- 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 +6 -2
- 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/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
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { isArray,
|
|
3
|
+
const { isArray, isNumber, isObject, first, parseInt } = require('lodash');
|
|
4
4
|
// const { sanitizeEntity } = require('strapi-utils');
|
|
5
5
|
const PluginError = require('./../utils/error');
|
|
6
6
|
const {
|
|
7
|
-
extractMeta,
|
|
8
7
|
getModelUid,
|
|
9
8
|
getRelatedGroups,
|
|
10
9
|
buildNestedStructure,
|
|
11
|
-
filterOurResolvedReports
|
|
10
|
+
filterOurResolvedReports,
|
|
11
|
+
buildAuthorModel,
|
|
12
12
|
} = require('./utils/functions');
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -16,62 +16,17 @@ const {
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
module.exports = ({ strapi }) => ({
|
|
19
|
-
getAssociationModel(ormModel, related) {
|
|
20
|
-
const relatedAssociation = ormModel.associations.find(_ => _.alias === 'related');
|
|
21
|
-
if (relatedAssociation) {
|
|
22
|
-
return relatedAssociation.related.find(rel => rel.collectionName === related);
|
|
23
|
-
}
|
|
24
|
-
return null;
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
getMorphModel() {
|
|
28
|
-
const ormModel = strapi.query(getModelUid('comment'));
|
|
29
|
-
return ormModel.model.morph;
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
isMongoDB(){
|
|
33
|
-
return strapi.query(getModelUid('comment'))?.model?.orm === 'mongoose';
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
getMorphData(query) {
|
|
37
|
-
return this
|
|
38
|
-
.getMorphModel()
|
|
39
|
-
.query(function () {
|
|
40
|
-
this.where(query);
|
|
41
|
-
})
|
|
42
|
-
.fetchAll()
|
|
43
|
-
.then((entities) => entities.map(entity => entity.toJSON()));
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
checkEntityRelation(uid, entityId) {
|
|
47
|
-
const result = parsedRelation.some(ref => {
|
|
48
|
-
const model = strapi.query(ref);
|
|
49
|
-
return model && model.associations.length &&
|
|
50
|
-
model.associations.some(_ => _.plugin === 'comments' && _.collection === 'comment');
|
|
51
|
-
});
|
|
52
|
-
if (!result) {
|
|
53
|
-
throw new PluginError(400, 'Comment not have relation to this type content type.');
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
19
|
|
|
57
|
-
getConfig(prop) {
|
|
20
|
+
getConfig(prop, defaultValue) {
|
|
58
21
|
let queryProp = prop;
|
|
59
22
|
if (prop && isArray(prop)) {
|
|
60
23
|
queryProp = prop.join('.');
|
|
61
24
|
}
|
|
62
|
-
return strapi.config.get(`plugin.comments${ queryProp ? '.' + queryProp : ''}`);
|
|
25
|
+
return strapi.config.get(`plugin.comments${ queryProp ? '.' + queryProp : ''}`) || defaultValue;
|
|
63
26
|
},
|
|
64
27
|
|
|
65
28
|
// Find comments in the flat structure
|
|
66
|
-
async findAllFlat({ query, populate = {} },
|
|
67
|
-
const criteria = {
|
|
68
|
-
...query,
|
|
69
|
-
$or: [{ removed: { $null: true } }, { removed: false }],
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (isAdmin){
|
|
73
|
-
delete criteria.$or;
|
|
74
|
-
}
|
|
29
|
+
async findAllFlat({ query = {}, populate = {} }, relatedEntity = null) {
|
|
75
30
|
|
|
76
31
|
const defaultPopulate = {
|
|
77
32
|
authorUser: true,
|
|
@@ -80,7 +35,7 @@ module.exports = ({ strapi }) => ({
|
|
|
80
35
|
const entries = await strapi.db.query(getModelUid('comment'))
|
|
81
36
|
.findMany({
|
|
82
37
|
where: {
|
|
83
|
-
...
|
|
38
|
+
...query,
|
|
84
39
|
},
|
|
85
40
|
populate: {
|
|
86
41
|
...defaultPopulate,
|
|
@@ -122,9 +77,8 @@ module.exports = ({ strapi }) => ({
|
|
|
122
77
|
populate = {},
|
|
123
78
|
startingFromId = null,
|
|
124
79
|
dropBlockedThreads = false,
|
|
125
|
-
isAdmin = false,
|
|
126
80
|
}, relatedEntity) {
|
|
127
|
-
const entities = await this.findAllFlat({ query, populate },
|
|
81
|
+
const entities = await this.findAllFlat({ query, populate }, relatedEntity);
|
|
128
82
|
return buildNestedStructure(entities, startingFromId, 'threadOf', dropBlockedThreads, false);
|
|
129
83
|
},
|
|
130
84
|
|
|
@@ -135,24 +89,38 @@ module.exports = ({ strapi }) => ({
|
|
|
135
89
|
where: criteria
|
|
136
90
|
}, ['related', 'reports']);
|
|
137
91
|
if (!entity){
|
|
138
|
-
throw
|
|
92
|
+
throw new PluginError(404, 'Not found');
|
|
139
93
|
}
|
|
140
94
|
return filterOurResolvedReports(this.sanitizeCommentEntity(entity));
|
|
141
95
|
},
|
|
142
96
|
|
|
143
97
|
// Find all related entiries
|
|
144
98
|
async findRelatedEntitiesFor(entities = []) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
99
|
+
const data = entities.reduce((acc, cur) => {
|
|
100
|
+
const [relatedUid, relatedStringId] = getRelatedGroups(cur.related);
|
|
101
|
+
const parsedRelatedId = parseInt(relatedStringId);
|
|
102
|
+
const relatedId = isNumber(parsedRelatedId) ? parsedRelatedId : relatedStringId;
|
|
103
|
+
return {
|
|
104
|
+
...acc,
|
|
105
|
+
[relatedUid]: [...(acc[relatedUid] || []), relatedId]
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
{}
|
|
109
|
+
);
|
|
110
|
+
return Promise.all(
|
|
111
|
+
Object.entries(data)
|
|
112
|
+
.map(async ([relatedUid, relatedStringIds]) =>
|
|
113
|
+
strapi.db.query(relatedUid).findMany({
|
|
114
|
+
where: { id: Array.from(new Set(relatedStringIds)) }
|
|
115
|
+
}).then(relatedEntities => relatedEntities.map(_ =>
|
|
116
|
+
({
|
|
117
|
+
..._,
|
|
118
|
+
uid: relatedUid,
|
|
119
|
+
})
|
|
120
|
+
))
|
|
121
|
+
)
|
|
122
|
+
)
|
|
123
|
+
.then(result => result.flat(2));
|
|
156
124
|
},
|
|
157
125
|
|
|
158
126
|
// Merge related entity with comment
|
|
@@ -187,7 +155,10 @@ module.exports = ({ strapi }) => ({
|
|
|
187
155
|
},
|
|
188
156
|
|
|
189
157
|
sanitizeCommentEntity: (entity) => ({
|
|
190
|
-
...
|
|
158
|
+
...buildAuthorModel({
|
|
159
|
+
...entity,
|
|
160
|
+
threadOf: isObject(entity.threadOf) ? buildAuthorModel(entity.threadOf) : entity.threadOf,
|
|
161
|
+
}),
|
|
191
162
|
}),
|
|
192
163
|
|
|
193
164
|
// sanitizeCommentEntity: (entity) => ({
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { has, propEq, isNil, isDate, isObject } = require('lodash/fp');
|
|
4
|
+
const { inputObjectType } = require('nexus');
|
|
5
|
+
|
|
6
|
+
const virtualScalarAttributes = ['id'];
|
|
7
|
+
|
|
8
|
+
module.exports = ({ strapi }) => {
|
|
9
|
+
const { service: getService } = strapi.plugin('graphql');
|
|
10
|
+
|
|
11
|
+
const rootLevelOperators = () => {
|
|
12
|
+
const { operators } = strapi.plugin('graphql').service('builders').filters;
|
|
13
|
+
|
|
14
|
+
return [operators.and, operators.or, operators.not];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const buildContentTypeFilters = contentType => {
|
|
18
|
+
const utils = strapi.plugin('graphql').service('utils');
|
|
19
|
+
const extension = strapi.plugin('graphql').service('extension');
|
|
20
|
+
|
|
21
|
+
const { getFiltersInputTypeName, getScalarFilterInputTypeName } = utils.naming;
|
|
22
|
+
const { isStrapiScalar, isRelation } = utils.attributes;
|
|
23
|
+
|
|
24
|
+
const { attributes } = contentType;
|
|
25
|
+
|
|
26
|
+
const filtersTypeName = getFiltersInputTypeName(contentType);
|
|
27
|
+
|
|
28
|
+
return inputObjectType({
|
|
29
|
+
name: filtersTypeName,
|
|
30
|
+
|
|
31
|
+
definition(t) {
|
|
32
|
+
const validAttributes = Object.entries(attributes).filter(([attributeName]) =>
|
|
33
|
+
extension
|
|
34
|
+
.shadowCRUD(contentType.uid)
|
|
35
|
+
.field(attributeName)
|
|
36
|
+
.hasFiltersEnabeld()
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const isIDFilterEnabled = extension
|
|
40
|
+
.shadowCRUD(contentType.uid)
|
|
41
|
+
.field('id')
|
|
42
|
+
.hasFiltersEnabeld();
|
|
43
|
+
// Add an ID filter to the collection types
|
|
44
|
+
if (contentType.kind === 'collectionType' && isIDFilterEnabled) {
|
|
45
|
+
t.field('id', { type: getScalarFilterInputTypeName('ID') });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Add every defined attribute
|
|
49
|
+
for (const [attributeName, attribute] of validAttributes) {
|
|
50
|
+
// Handle scalars
|
|
51
|
+
if (isStrapiScalar(attribute)) {
|
|
52
|
+
addScalarAttribute(t, attributeName, attribute);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Handle relations
|
|
56
|
+
else if (isRelation(attribute)) {
|
|
57
|
+
addRelationalAttribute(t, attributeName, attribute);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Conditional clauses
|
|
62
|
+
for (const operator of rootLevelOperators()) {
|
|
63
|
+
operator.add(t, filtersTypeName);
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const addScalarAttribute = (builder, attributeName, attribute) => {
|
|
70
|
+
const { naming, mappers } = strapi.plugin('graphql').service('utils');
|
|
71
|
+
|
|
72
|
+
const gqlType = mappers.strapiScalarToGraphQLScalar(attribute.type);
|
|
73
|
+
|
|
74
|
+
builder.field(attributeName, { type: naming.getScalarFilterInputTypeName(gqlType) });
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const addRelationalAttribute = (builder, attributeName, attribute) => {
|
|
78
|
+
const utils = strapi.plugin('graphql').service('utils');
|
|
79
|
+
const extension = strapi.plugin('graphql').service('extension');
|
|
80
|
+
const { getFiltersInputTypeName } = utils.naming;
|
|
81
|
+
const { isMorphRelation } = utils.attributes;
|
|
82
|
+
|
|
83
|
+
const model = strapi.getModel(attribute.target);
|
|
84
|
+
|
|
85
|
+
// If there is no model corresponding to the attribute configuration
|
|
86
|
+
// or if the attribute is a polymorphic relation, then ignore it
|
|
87
|
+
if (!model || isMorphRelation(attribute)) return;
|
|
88
|
+
|
|
89
|
+
// If the target model is disabled, then ignore it too
|
|
90
|
+
if (extension.shadowCRUD(model.uid).isDisabled()) return;
|
|
91
|
+
|
|
92
|
+
builder.field(attributeName, { type: getFiltersInputTypeName(model) });
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const recursivelyReplaceScalarOperators = data => {
|
|
96
|
+
const { operators } = getService('builders').filters;
|
|
97
|
+
|
|
98
|
+
if (Array.isArray(data)) {
|
|
99
|
+
return data.map(recursivelyReplaceScalarOperators);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Note: We need to make an exception for date since GraphQL
|
|
103
|
+
// automatically cast date strings to date instances in args
|
|
104
|
+
if (isDate(data) || !isObject(data)) {
|
|
105
|
+
return data;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const result = {};
|
|
109
|
+
|
|
110
|
+
for (const [key, value] of Object.entries(data)) {
|
|
111
|
+
const isOperator = !!operators[key];
|
|
112
|
+
|
|
113
|
+
const newKey = isOperator ? operators[key].strapiOperator : key;
|
|
114
|
+
|
|
115
|
+
result[newKey] = recursivelyReplaceScalarOperators(value);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return result;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const graphQLFiltersToStrapiQuery = (filters, contentType = {}) => {
|
|
122
|
+
const { isStrapiScalar, isMedia, isRelation } = getService('utils').attributes;
|
|
123
|
+
const { operators } = getService('builders').filters;
|
|
124
|
+
|
|
125
|
+
const ROOT_LEVEL_OPERATORS = [operators.and, operators.or, operators.not];
|
|
126
|
+
|
|
127
|
+
// Handle unwanted scenario where there is no filters defined
|
|
128
|
+
if (isNil(filters)) {
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// If filters is a collection, then apply the transformation to every item of the list
|
|
133
|
+
if (Array.isArray(filters)) {
|
|
134
|
+
return filters.map(filtersItem =>
|
|
135
|
+
graphQLFiltersToStrapiQuery(filtersItem, contentType)
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const resultMap = {};
|
|
140
|
+
const { attributes } = contentType;
|
|
141
|
+
|
|
142
|
+
const isAttribute = attributeName => {
|
|
143
|
+
return virtualScalarAttributes.includes(attributeName) || has(attributeName, attributes);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
147
|
+
// If the key is an attribute, update the value
|
|
148
|
+
if (isAttribute(key)) {
|
|
149
|
+
const attribute = attributes[key];
|
|
150
|
+
|
|
151
|
+
// If it's a scalar attribute
|
|
152
|
+
if (virtualScalarAttributes.includes(key) || isStrapiScalar(attribute)) {
|
|
153
|
+
// Replace (recursively) every GraphQL scalar operator with the associated Strapi operator
|
|
154
|
+
resultMap[key] = recursivelyReplaceScalarOperators(value);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// If it's a deep filter on a relation
|
|
158
|
+
else if (isRelation(attribute) || isMedia(attribute)) {
|
|
159
|
+
// Fetch the model from the relation
|
|
160
|
+
const relModel = strapi.getModel(attribute.target);
|
|
161
|
+
|
|
162
|
+
// Recursively apply the mapping to the value using the fetched model,
|
|
163
|
+
// and update the value within `resultMap`
|
|
164
|
+
resultMap[key] = graphQLFiltersToStrapiQuery(value, relModel);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Handle the case where the key is not an attribute (operator, ...)
|
|
169
|
+
else {
|
|
170
|
+
const rootLevelOperator = ROOT_LEVEL_OPERATORS.find(propEq('fieldName', key));
|
|
171
|
+
|
|
172
|
+
// If it's a root level operator (AND, NOT, OR, ...)
|
|
173
|
+
if (rootLevelOperator) {
|
|
174
|
+
const { strapiOperator } = rootLevelOperator;
|
|
175
|
+
|
|
176
|
+
// Transform the current value recursively and add it to the resultMap
|
|
177
|
+
// object using the strapiOperator equivalent of the GraphQL key
|
|
178
|
+
resultMap[strapiOperator] = graphQLFiltersToStrapiQuery(value, contentType);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return resultMap;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
buildContentTypeFilters,
|
|
188
|
+
graphQLFiltersToStrapiQuery,
|
|
189
|
+
};
|
|
190
|
+
};
|
package/server/services/index.js
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
const adminServices = require('./admin');
|
|
4
4
|
const clientServices = require('./client');
|
|
5
5
|
const commonServices = require('./common');
|
|
6
|
+
const graphQLService = require('./graphql');
|
|
6
7
|
|
|
7
8
|
module.exports = {
|
|
8
9
|
admin: adminServices,
|
|
9
10
|
client: clientServices,
|
|
10
11
|
common: commonServices,
|
|
12
|
+
gql: graphQLService,
|
|
11
13
|
};
|
|
@@ -6,7 +6,7 @@ const { first, get, isObject, isNil } = require('lodash');
|
|
|
6
6
|
const buildNestedStructure = (
|
|
7
7
|
entities,
|
|
8
8
|
id = null,
|
|
9
|
-
field = '
|
|
9
|
+
field = 'threadOf',
|
|
10
10
|
dropBlockedThreads = false,
|
|
11
11
|
blockNestedThreads = false,
|
|
12
12
|
) =>
|
|
@@ -33,28 +33,21 @@ const buildNestedStructure = (
|
|
|
33
33
|
|
|
34
34
|
module.exports = {
|
|
35
35
|
isEqualEntity: (existing, data, user) => {
|
|
36
|
-
const { authorUser,
|
|
37
|
-
console.log('data', data);
|
|
38
|
-
console.log(existing);
|
|
36
|
+
const { authorUser, author: existingAuthor } = existing;
|
|
39
37
|
const { author } = data;
|
|
38
|
+
|
|
39
|
+
// Disallow approval status change by Client
|
|
40
|
+
if (data.approvalStatus && (existing.approvalStatus !== data.approvalStatus)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Make sure that author is exact the same
|
|
40
45
|
if (authorUser) {
|
|
41
46
|
const existingUserId = authorUser?.id || authorUser;
|
|
42
47
|
const receivedUserId = user?.id || author;
|
|
43
48
|
return existingUserId === receivedUserId;
|
|
44
49
|
}
|
|
45
|
-
return
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
extractMeta: plugins => {
|
|
49
|
-
const { comments: plugin } = plugins;
|
|
50
|
-
const { comments: service } = plugin.services;
|
|
51
|
-
const { comment: model} = plugin.models;
|
|
52
|
-
return {
|
|
53
|
-
model,
|
|
54
|
-
service,
|
|
55
|
-
plugin,
|
|
56
|
-
pluginName: plugin.package.strapi.name.toLowerCase()
|
|
57
|
-
};
|
|
50
|
+
return existingAuthor.id === author?.id;
|
|
58
51
|
},
|
|
59
52
|
|
|
60
53
|
getRelatedGroups: related => related.split(REGEX.relatedUid).filter(s => s && s.length > 0),
|
|
@@ -97,6 +90,20 @@ module.exports = {
|
|
|
97
90
|
return builtInContextEnabled ? !isNil(user.id) : true;
|
|
98
91
|
},
|
|
99
92
|
|
|
93
|
+
buildAuthorModel: (item) => {
|
|
94
|
+
const { authorUser, authorId, authorName, authorEmail, authorAvatar, ...rest } = item;
|
|
95
|
+
const author = authorUser || {
|
|
96
|
+
id: authorId,
|
|
97
|
+
name: authorName,
|
|
98
|
+
email: authorEmail,
|
|
99
|
+
avatar: authorAvatar,
|
|
100
|
+
};
|
|
101
|
+
return {
|
|
102
|
+
...rest,
|
|
103
|
+
author,
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
|
|
100
107
|
resolveUserContextError: user => {
|
|
101
108
|
if (user) {
|
|
102
109
|
throw new PluginError(401, 'Not authenticated');
|
|
@@ -5,9 +5,9 @@ const APPROVAL_STATUS = {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
const REGEX = {
|
|
8
|
-
uid:
|
|
9
|
-
relatedUid:
|
|
10
|
-
email:
|
|
8
|
+
uid: /^(?<type>[a-z0-9-]+)\:{2}(?<api>[a-z0-9-]+)\.{1}(?<contentType>[a-z0-9-]+)$/i,
|
|
9
|
+
relatedUid: /^(?<uid>[a-z0-9-]+\:{2}[a-z0-9-]+\.[a-z0-9-]+)\:{1}(?<id>[a-z0-9-]+)$/i,
|
|
10
|
+
email: /\S+@\S+\.\S+/,
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
module.exports = {
|
package/strapi-server.js
CHANGED
|
@@ -1,9 +1,42 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { isArray, isEmpty, isString } = require('lodash');
|
|
4
|
+
|
|
3
5
|
const server = require('./server');
|
|
4
6
|
const contentTypes = require('./content-types');
|
|
7
|
+
const { REGEX } = require('./server/utils/constants');
|
|
5
8
|
|
|
6
9
|
module.exports = () => ({
|
|
7
10
|
...server,
|
|
8
11
|
contentTypes,
|
|
12
|
+
config: {
|
|
13
|
+
...server.config,
|
|
14
|
+
default: ({ env }) => ({
|
|
15
|
+
...server.config.default,
|
|
16
|
+
}),
|
|
17
|
+
validator: (config) => {
|
|
18
|
+
// Check moderatorRoles values
|
|
19
|
+
const moderatorRolesValid = config.moderatorRoles
|
|
20
|
+
.filter(_ => !isString(_)).length === 0;
|
|
21
|
+
if (!moderatorRolesValid) {
|
|
22
|
+
throw new Error(`'moderatorRoles' must roles keys`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Check approvalFlow values
|
|
26
|
+
const approvalFlowValid = config.approvalFlow
|
|
27
|
+
.filter(_ => !REGEX.uid.test(_)).length === 0;
|
|
28
|
+
if (!approvalFlowValid) {
|
|
29
|
+
throw new Error(`'approvalFlow' must contain Content Types identifiers in the format like 'api::<collection name>.<content type name>'`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check entryLabel keys and values
|
|
33
|
+
const entryLabelKeysValid = Object.keys(config.entryLabel)
|
|
34
|
+
.filter(_ => _ !== '*')
|
|
35
|
+
.filter(_ => !(REGEX.uid.test(_) && isArray(config.entryLabel[_]) && !isEmpty(config.entryLabel[_]))).length === 0;
|
|
36
|
+
|
|
37
|
+
if (!entryLabelKeysValid) {
|
|
38
|
+
throw new Error(`'entryLabel' must contain records in the format like 'api::<collection name>.<content type name>': ['Field1', 'field_2']`);
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
},
|
|
9
42
|
});
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
* Entity Details
|
|
4
|
-
*
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import React from 'react';
|
|
8
|
-
import PropTypes from 'prop-types';
|
|
9
|
-
import { isNil } from 'lodash';
|
|
10
|
-
import { Box } from '@strapi/design-system/Box';
|
|
11
|
-
import { Divider } from '@strapi/design-system/Divider';
|
|
12
|
-
import { Flex } from '@strapi/design-system/Flex';
|
|
13
|
-
import { Stack } from '@strapi/design-system/Stack';
|
|
14
|
-
import { Typography } from '@strapi/design-system/Typography';
|
|
15
|
-
import { getMessage } from '../../utils';
|
|
16
|
-
|
|
17
|
-
const EntityDetails = ({ data = {}, schema = {} }) => {
|
|
18
|
-
const { attributes = {} } = schema;
|
|
19
|
-
const keys = Object.keys(attributes);
|
|
20
|
-
|
|
21
|
-
if (keys.length === 0) {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const itemKeys = keys.filter(_ => (attributes[_].type === 'string') && !isNil(data[_]));
|
|
26
|
-
|
|
27
|
-
return (<Box padding={4}>
|
|
28
|
-
<Typography variant="sigma" textColor="neutral600" id="entity-details">
|
|
29
|
-
{ getMessage('page.details.panel.entity', 'Details') }
|
|
30
|
-
</Typography>
|
|
31
|
-
<Box paddingTop={2} paddingBottom={6}>
|
|
32
|
-
<Divider />
|
|
33
|
-
</Box>
|
|
34
|
-
<Stack size={itemKeys.length}>
|
|
35
|
-
{ itemKeys.map(_ =>
|
|
36
|
-
(<Flex justifyContent="space-between">
|
|
37
|
-
<Typography fontWeight="bold">
|
|
38
|
-
{ _ }
|
|
39
|
-
</Typography>
|
|
40
|
-
<Typography>{ data[_] }</Typography>
|
|
41
|
-
</Flex>))
|
|
42
|
-
}
|
|
43
|
-
</Stack>
|
|
44
|
-
</Box>);
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
EntityDetails.propTypes = {
|
|
48
|
-
data: PropTypes.object.isRequired,
|
|
49
|
-
schema: PropTypes.object.isRequired,
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export default EntityDetails;
|
|
53
|
-
|