@strapi/admin 4.13.3 → 4.14.0-alpha.0
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/admin/src/hooks/useAdminRoles/index.js +17 -7
- package/admin/src/hooks/useAdminUsers/useAdminUsers.js +16 -7
- package/admin/src/hooks/useContentTypes/useContentTypes.js +18 -7
- package/build/1227.ec336799.chunk.js +1 -0
- package/build/{3483.19381b40.chunk.js → 3483.f6b2439f.chunk.js} +1 -1
- package/build/4174.4587c7f6.chunk.js +1 -0
- package/build/6266.53be9ea3.chunk.js +124 -0
- package/build/7897.eac204a4.chunk.js +6 -0
- package/build/{Admin-authenticatedApp.796792a8.chunk.js → Admin-authenticatedApp.d200a4ee.chunk.js} +1 -1
- package/build/{admin-app.2a8615ab.chunk.js → admin-app.582877a3.chunk.js} +11 -11
- package/build/admin-edit-roles-page.0aa65505.chunk.js +267 -0
- package/build/admin-edit-users.9215912a.chunk.js +10 -0
- package/build/admin-roles-list.824a50de.chunk.js +22 -0
- package/build/admin-users.f6b3c643.chunk.js +11 -0
- package/build/audit-logs-settings-page.be2cb4dd.chunk.js +1 -0
- package/build/{content-manager.f448efdf.chunk.js → content-manager.06a2f7ec.chunk.js} +39 -39
- package/build/index.html +1 -1
- package/build/review-workflows-settings-create-view.604cffa0.chunk.js +1 -0
- package/build/review-workflows-settings-edit-view.73c57f07.chunk.js +1 -0
- package/build/review-workflows-settings-list-view.7e300ecb.chunk.js +56 -0
- package/build/{runtime~main.d2b8d4a1.js → runtime~main.9de029f4.js} +2 -2
- package/build/sso-settings-page.94373f78.chunk.js +1 -0
- package/ee/admin/content-manager/pages/EditView/InformationBox/components/StageSelect/StageSelect.js +65 -53
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/actions/index.js +50 -5
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js +227 -19
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/WorkflowAttributes.js +8 -23
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/constants.js +6 -0
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows.js +17 -7
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflowsStages.js +36 -0
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js +68 -19
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js +105 -35
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/index.js +68 -27
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/selectors.js +45 -0
- package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/utils/validateWorkflow.js +20 -0
- package/ee/server/config/admin-actions.js +6 -0
- package/ee/server/constants/workflows.js +13 -0
- package/ee/server/content-types/workflow-stage/index.js +6 -0
- package/ee/server/controllers/workflows/index.js +41 -16
- package/ee/server/controllers/workflows/stages/index.js +93 -6
- package/ee/server/routes/review-workflows.js +10 -9
- package/ee/server/services/index.js +1 -0
- package/ee/server/services/review-workflows/stage-permissions.js +60 -0
- package/ee/server/services/review-workflows/stages.js +83 -12
- package/ee/server/services/review-workflows/workflows/index.js +20 -7
- package/ee/server/validation/review-workflows.js +11 -0
- package/package.json +8 -8
- package/scripts/build.js +2 -3
- package/scripts/create-dev-plugins-file.js +2 -3
- package/server/content-types/Permission.js +6 -0
- package/server/domain/permission/index.js +11 -2
- package/server/services/role.js +12 -4
- package/server/validation/action-provider.js +1 -1
- package/server/validation/common-validators.js +92 -100
- package/server/validation/permission.js +0 -3
- package/utils/create-cache-dir.js +5 -102
- package/utils/plugins.js +217 -0
- package/webpack.config.js +2 -2
- package/build/1227.9f37e1dc.chunk.js +0 -1
- package/build/2237.b832ae6e.chunk.js +0 -114
- package/build/4174.f1f39e40.chunk.js +0 -1
- package/build/4724.aea5c8c1.chunk.js +0 -6
- package/build/admin-edit-roles-page.38a6c863.chunk.js +0 -267
- package/build/admin-edit-users.545fc882.chunk.js +0 -10
- package/build/admin-roles-list.1e2e814d.chunk.js +0 -22
- package/build/admin-users.b8ea5677.chunk.js +0 -11
- package/build/audit-logs-settings-page.96f9d608.chunk.js +0 -1
- package/build/review-workflows-settings-create-view.4a156a19.chunk.js +0 -1
- package/build/review-workflows-settings-edit-view.ce984d1f.chunk.js +0 -1
- package/build/review-workflows-settings-list-view.419b8deb.chunk.js +0 -56
- package/build/sso-settings-page.45153df5.chunk.js +0 -1
- package/utils/create-plugins-exclude-path.js +0 -20
- package/utils/get-plugins.js +0 -110
|
@@ -89,112 +89,103 @@ const fieldsPropertyValidation = (action) =>
|
|
|
89
89
|
checkNilFields(action)
|
|
90
90
|
);
|
|
91
91
|
|
|
92
|
+
const permission = yup
|
|
93
|
+
.object()
|
|
94
|
+
.shape({
|
|
95
|
+
action: yup
|
|
96
|
+
.string()
|
|
97
|
+
.required()
|
|
98
|
+
.test('action-validity', 'action is not an existing permission action', function (actionId) {
|
|
99
|
+
// If the action field is Nil, ignore the test and let the required check handle the error
|
|
100
|
+
if (isNil(actionId)) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return !!getActionFromProvider(actionId);
|
|
105
|
+
}),
|
|
106
|
+
actionParameters: yup.object().nullable(),
|
|
107
|
+
subject: yup
|
|
108
|
+
.string()
|
|
109
|
+
.nullable()
|
|
110
|
+
.test('subject-validity', 'Invalid subject submitted', function (subject) {
|
|
111
|
+
const action = getActionFromProvider(this.options.parent.action);
|
|
112
|
+
|
|
113
|
+
if (!action) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (isNil(action.subjects)) {
|
|
118
|
+
return isNil(subject);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (isArray(action.subjects)) {
|
|
122
|
+
return action.subjects.includes(subject);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return false;
|
|
126
|
+
}),
|
|
127
|
+
properties: yup
|
|
128
|
+
.object()
|
|
129
|
+
.test('properties-structure', 'Invalid property set at ${path}', function (properties) {
|
|
130
|
+
const action = getActionFromProvider(this.options.parent.action);
|
|
131
|
+
const hasNoProperties = isEmpty(properties) || isNil(properties);
|
|
132
|
+
|
|
133
|
+
if (!has('options.applyToProperties', action)) {
|
|
134
|
+
return hasNoProperties;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (hasNoProperties) {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const { applyToProperties } = action.options;
|
|
142
|
+
|
|
143
|
+
if (!isArray(applyToProperties)) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return Object.keys(properties).every((property) => applyToProperties.includes(property));
|
|
148
|
+
})
|
|
149
|
+
.test(
|
|
150
|
+
'fields-property',
|
|
151
|
+
'Invalid fields property at ${path}',
|
|
152
|
+
async function (properties = {}) {
|
|
153
|
+
const action = getActionFromProvider(this.options.parent.action);
|
|
154
|
+
|
|
155
|
+
if (!action || !properties) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (!actionDomain.appliesToProperty('fields', action)) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
await fieldsPropertyValidation(action).validate(properties.fields, {
|
|
165
|
+
strict: true,
|
|
166
|
+
abortEarly: false,
|
|
167
|
+
});
|
|
168
|
+
return true;
|
|
169
|
+
} catch (e) {
|
|
170
|
+
// Propagate fieldsPropertyValidation error with updated path
|
|
171
|
+
throw this.createError({
|
|
172
|
+
message: e.message,
|
|
173
|
+
path: `${this.path}.fields`,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
),
|
|
178
|
+
conditions: yup.array().of(yup.string()),
|
|
179
|
+
})
|
|
180
|
+
.noUnknown();
|
|
181
|
+
|
|
92
182
|
const updatePermissions = yup
|
|
93
183
|
.object()
|
|
94
184
|
.shape({
|
|
95
185
|
permissions: yup
|
|
96
186
|
.array()
|
|
97
187
|
.required()
|
|
98
|
-
.of(
|
|
99
|
-
yup
|
|
100
|
-
.object()
|
|
101
|
-
.shape({
|
|
102
|
-
action: yup
|
|
103
|
-
.string()
|
|
104
|
-
.required()
|
|
105
|
-
.test(
|
|
106
|
-
'action-validity',
|
|
107
|
-
'action is not an existing permission action',
|
|
108
|
-
function (actionId) {
|
|
109
|
-
// If the action field is Nil, ignore the test and let the required check handle the error
|
|
110
|
-
if (isNil(actionId)) {
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return !!getActionFromProvider(actionId);
|
|
115
|
-
}
|
|
116
|
-
),
|
|
117
|
-
subject: yup
|
|
118
|
-
.string()
|
|
119
|
-
.nullable()
|
|
120
|
-
.test('subject-validity', 'Invalid subject submitted', function (subject) {
|
|
121
|
-
const action = getActionFromProvider(this.options.parent.action);
|
|
122
|
-
|
|
123
|
-
if (!action) {
|
|
124
|
-
return true;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (isNil(action.subjects)) {
|
|
128
|
-
return isNil(subject);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (isArray(action.subjects)) {
|
|
132
|
-
return action.subjects.includes(subject);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return false;
|
|
136
|
-
}),
|
|
137
|
-
properties: yup
|
|
138
|
-
.object()
|
|
139
|
-
.test(
|
|
140
|
-
'properties-structure',
|
|
141
|
-
'Invalid property set at ${path}',
|
|
142
|
-
function (properties) {
|
|
143
|
-
const action = getActionFromProvider(this.options.parent.action);
|
|
144
|
-
const hasNoProperties = isEmpty(properties) || isNil(properties);
|
|
145
|
-
|
|
146
|
-
if (!has('options.applyToProperties', action)) {
|
|
147
|
-
return hasNoProperties;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (hasNoProperties) {
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const { applyToProperties } = action.options;
|
|
155
|
-
|
|
156
|
-
if (!isArray(applyToProperties)) {
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return Object.keys(properties).every((property) =>
|
|
161
|
-
applyToProperties.includes(property)
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
)
|
|
165
|
-
.test(
|
|
166
|
-
'fields-property',
|
|
167
|
-
'Invalid fields property at ${path}',
|
|
168
|
-
async function (properties = {}) {
|
|
169
|
-
const action = getActionFromProvider(this.options.parent.action);
|
|
170
|
-
|
|
171
|
-
if (!action || !properties) {
|
|
172
|
-
return true;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (!actionDomain.appliesToProperty('fields', action)) {
|
|
176
|
-
return true;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
try {
|
|
180
|
-
await fieldsPropertyValidation(action).validate(properties.fields, {
|
|
181
|
-
strict: true,
|
|
182
|
-
abortEarly: false,
|
|
183
|
-
});
|
|
184
|
-
return true;
|
|
185
|
-
} catch (e) {
|
|
186
|
-
// Propagate fieldsPropertyValidation error with updated path
|
|
187
|
-
throw this.createError({
|
|
188
|
-
message: e.message,
|
|
189
|
-
path: `${this.path}.fields`,
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
),
|
|
194
|
-
conditions: yup.array().of(yup.string()),
|
|
195
|
-
})
|
|
196
|
-
.noUnknown()
|
|
197
|
-
)
|
|
188
|
+
.of(permission)
|
|
198
189
|
.test(
|
|
199
190
|
'duplicated-permissions',
|
|
200
191
|
'Some permissions are duplicated (same action and subject)',
|
|
@@ -213,5 +204,6 @@ module.exports = {
|
|
|
213
204
|
roles,
|
|
214
205
|
isAPluginName,
|
|
215
206
|
arrayOfConditionNames,
|
|
207
|
+
permission,
|
|
216
208
|
updatePermissions,
|
|
217
209
|
};
|
|
@@ -17,8 +17,6 @@ const checkPermissionsSchema = yup.object().shape({
|
|
|
17
17
|
),
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
// validatePermissionsExist
|
|
21
|
-
|
|
22
20
|
const checkPermissionsExist = function (permissions) {
|
|
23
21
|
const existingActions = getService('permission').actionProvider.values();
|
|
24
22
|
const failIndex = permissions.findIndex(
|
|
@@ -48,7 +46,6 @@ const actionsExistSchema = yup
|
|
|
48
46
|
.test('actions-exist', '', checkPermissionsExist);
|
|
49
47
|
|
|
50
48
|
// exports
|
|
51
|
-
|
|
52
49
|
module.exports = {
|
|
53
50
|
validatedUpdatePermissionsInput: validateYupSchema(validators.updatePermissions),
|
|
54
51
|
validatePermissionsExist: validateYupSchema(actionsExistSchema),
|
|
@@ -1,69 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const _ = require('lodash');
|
|
5
4
|
const fs = require('fs-extra');
|
|
6
5
|
const tsUtils = require('@strapi/typescript-utils');
|
|
7
6
|
const getCustomAppConfigFile = require('./get-custom-app-config-file');
|
|
7
|
+
const { filterPluginsByAdminEntry, createPluginFile } = require('./plugins');
|
|
8
8
|
|
|
9
9
|
const getPkgPath = (name) => path.dirname(require.resolve(`${name}/package.json`));
|
|
10
10
|
|
|
11
|
-
async function createPluginsJs(plugins, dest) {
|
|
12
|
-
const pluginsArray = plugins.map(({ pathToPlugin, name, info }) => {
|
|
13
|
-
const shortName = _.camelCase(name);
|
|
14
|
-
|
|
15
|
-
let realPath = '';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* We're using a module here so we want to keep using the module resolution procedure.
|
|
19
|
-
*/
|
|
20
|
-
if (info?.packageName || info?.required) {
|
|
21
|
-
/**
|
|
22
|
-
* path.join, on windows, it uses backslashes to resolve path.
|
|
23
|
-
* The problem is that Webpack does not windows paths
|
|
24
|
-
* With this tool, we need to rely on "/" and not "\".
|
|
25
|
-
* This is the reason why '..\\..\\..\\node_modules\\@strapi\\plugin-content-type-builder/strapi-admin.js' was not working.
|
|
26
|
-
* The regexp at line 105 aims to replace the windows backslashes by standard slash so that webpack can deal with them.
|
|
27
|
-
* Backslash looks to work only for absolute paths with webpack => https://webpack.js.org/concepts/module-resolution/#absolute-paths
|
|
28
|
-
*/
|
|
29
|
-
realPath = path.join(pathToPlugin, 'strapi-admin').replace(/\\/g, '/');
|
|
30
|
-
} else {
|
|
31
|
-
realPath = path
|
|
32
|
-
.join(path.relative(path.resolve(dest, 'admin', 'src'), pathToPlugin), 'strapi-admin')
|
|
33
|
-
.replace(/\\/g, '/');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
name,
|
|
38
|
-
pathToPlugin: realPath,
|
|
39
|
-
shortName,
|
|
40
|
-
};
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const content = `
|
|
44
|
-
${pluginsArray
|
|
45
|
-
.map(({ pathToPlugin, shortName }) => {
|
|
46
|
-
const req = `'${pathToPlugin}'`;
|
|
47
|
-
|
|
48
|
-
return `import ${shortName} from ${req};`;
|
|
49
|
-
})
|
|
50
|
-
.join('\n')}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const plugins = {
|
|
54
|
-
${[...pluginsArray]
|
|
55
|
-
.map(({ name, shortName }) => {
|
|
56
|
-
return ` '${name}': ${shortName},`;
|
|
57
|
-
})
|
|
58
|
-
.join('\n')}
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export default plugins;
|
|
62
|
-
`;
|
|
63
|
-
|
|
64
|
-
return fs.writeFile(path.resolve(dest, 'admin', 'src', 'plugins.js'), content);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
11
|
async function copyAdmin(dest) {
|
|
68
12
|
const adminPath = getPkgPath('@strapi/admin');
|
|
69
13
|
|
|
@@ -86,49 +30,8 @@ async function createCacheDir({ appDir, plugins }) {
|
|
|
86
30
|
);
|
|
87
31
|
|
|
88
32
|
const pluginsWithFront = Object.entries(plugins)
|
|
89
|
-
.
|
|
90
|
-
|
|
91
|
-
* There are two ways a plugin should be imported, either it's local to the strapi app,
|
|
92
|
-
* or it's an actual npm module that's installed and resolved via node_modules.
|
|
93
|
-
*
|
|
94
|
-
* We first check if the plugin is local to the strapi app, using a regular `resolve` because
|
|
95
|
-
* the pathToPlugin will be relative i.e. `/Users/my-name/strapi-app/src/plugins/my-plugin`.
|
|
96
|
-
*
|
|
97
|
-
* If the file doesn't exist well then it's probably a node_module, so instead we use `require.resolve`
|
|
98
|
-
* which will resolve the path to the module in node_modules. If it fails with the specific code `MODULE_NOT_FOUND`
|
|
99
|
-
* then it doesn't have an admin part to the package.
|
|
100
|
-
*
|
|
101
|
-
* NOTE: we should try to move to `./package.json[exports]` map with bundling of our own plugins,
|
|
102
|
-
* because these entry files are written in commonjs restricting features e.g. tree-shaking.
|
|
103
|
-
*/
|
|
104
|
-
try {
|
|
105
|
-
const isLocalPluginWithLegacyAdminFile = fs.existsSync(
|
|
106
|
-
path.resolve(`${plugin.pathToPlugin}/strapi-admin.js`)
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
if (!isLocalPluginWithLegacyAdminFile) {
|
|
110
|
-
const isModulewithLegacyAdminFile = require.resolve(
|
|
111
|
-
`${plugin.pathToPlugin}/strapi-admin.js`
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
return isModulewithLegacyAdminFile;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return isLocalPluginWithLegacyAdminFile;
|
|
118
|
-
} catch (err) {
|
|
119
|
-
if (err.code === 'MODULE_NOT_FOUND') {
|
|
120
|
-
/**
|
|
121
|
-
* the plugin does not contain FE code, so we
|
|
122
|
-
* don't want to import it anyway
|
|
123
|
-
*/
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
throw err;
|
|
128
|
-
}
|
|
129
|
-
})
|
|
130
|
-
.map(([name, plugin]) => ({ name, ...plugin }));
|
|
131
|
-
|
|
33
|
+
.map(([name, plugin]) => ({ name, ...plugin }))
|
|
34
|
+
.filter(filterPluginsByAdminEntry);
|
|
132
35
|
// create .cache dir
|
|
133
36
|
await fs.emptyDir(cacheDir);
|
|
134
37
|
|
|
@@ -166,7 +69,7 @@ async function createCacheDir({ appDir, plugins }) {
|
|
|
166
69
|
}
|
|
167
70
|
|
|
168
71
|
// create plugins.js with plugins requires
|
|
169
|
-
await
|
|
72
|
+
await createPluginFile(pluginsWithFront, cacheDir);
|
|
170
73
|
|
|
171
74
|
// create the tsconfig.json file so we can develop plugins in ts while being in a JS project
|
|
172
75
|
if (!useTypeScript) {
|
|
@@ -174,4 +77,4 @@ async function createCacheDir({ appDir, plugins }) {
|
|
|
174
77
|
}
|
|
175
78
|
}
|
|
176
79
|
|
|
177
|
-
module.exports = { createCacheDir
|
|
80
|
+
module.exports = { createCacheDir };
|
package/utils/plugins.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const asyncFs = require('fs/promises');
|
|
6
|
+
const { camelCase } = require('lodash');
|
|
7
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
8
|
+
const glob = require('glob');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {Object} PluginInfo
|
|
12
|
+
* @property {string} packageName
|
|
13
|
+
* @property {string} description
|
|
14
|
+
* @property {boolean=} required
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {Object} Plugin
|
|
19
|
+
* @property {string} pathToPlugin
|
|
20
|
+
* @property {string} name
|
|
21
|
+
* @property {PluginInfo} info
|
|
22
|
+
* @property {string=} directory
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string[]} pluginsAllowlist
|
|
27
|
+
* @returns {Plugin[]}
|
|
28
|
+
*/
|
|
29
|
+
const getPlugins = (pluginsAllowlist) => {
|
|
30
|
+
const rootPath = path.resolve(__dirname, '..', path.join('..', '..', '..', 'packages'));
|
|
31
|
+
/**
|
|
32
|
+
* So `glob` only supports '/' as a path separator, so we need to replace
|
|
33
|
+
* the path separator for the current OS with '/'. e.g. on windows it's `\`.
|
|
34
|
+
*
|
|
35
|
+
* see https://github.com/isaacs/node-glob/#windows for more information
|
|
36
|
+
*
|
|
37
|
+
* and see https://github.com/isaacs/node-glob/issues/467#issuecomment-1114240501 for the recommended fix.
|
|
38
|
+
*/
|
|
39
|
+
let corePath = path.join(rootPath, 'core', '*');
|
|
40
|
+
let pluginsPath = path.join(rootPath, 'plugins', '*');
|
|
41
|
+
|
|
42
|
+
if (process.platform === 'win32') {
|
|
43
|
+
corePath = corePath.split(path.sep).join(path.posix.sep);
|
|
44
|
+
pluginsPath = pluginsPath.split(path.sep).join(path.posix.sep);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const corePackageDirs = glob.sync(corePath);
|
|
48
|
+
const pluginsPackageDirs = glob.sync(pluginsPath);
|
|
49
|
+
|
|
50
|
+
const plugins = [...corePackageDirs, ...pluginsPackageDirs]
|
|
51
|
+
.map((directory) => {
|
|
52
|
+
const isCoreAdmin = directory.includes('packages/core/admin');
|
|
53
|
+
|
|
54
|
+
if (isCoreAdmin) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const { name, strapi } = require(path.join(directory, 'package.json'));
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* this will remove any of our packages that are
|
|
62
|
+
* not actually plugins for the application
|
|
63
|
+
*/
|
|
64
|
+
if (!strapi || strapi.kind !== 'plugin') {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* we want the name of the node_module
|
|
70
|
+
*/
|
|
71
|
+
return {
|
|
72
|
+
pathToPlugin: name,
|
|
73
|
+
name: strapi.name,
|
|
74
|
+
info: { ...strapi, packageName: name },
|
|
75
|
+
directory,
|
|
76
|
+
};
|
|
77
|
+
})
|
|
78
|
+
.filter(filterPluginsByAdminEntry);
|
|
79
|
+
|
|
80
|
+
if (Array.isArray(pluginsAllowlist)) {
|
|
81
|
+
return plugins.filter((plugin) => pluginsAllowlist.includes(plugin.pathToPlugin));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return plugins;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* @type {(plugin: Plugin) => boolean}
|
|
89
|
+
*/
|
|
90
|
+
const filterPluginsByAdminEntry = (plugin) => {
|
|
91
|
+
if (!plugin) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* There are two ways a plugin should be imported, either it's local to the strapi app,
|
|
97
|
+
* or it's an actual npm module that's installed and resolved via node_modules.
|
|
98
|
+
*
|
|
99
|
+
* We first check if the plugin is local to the strapi app, using a regular `resolve` because
|
|
100
|
+
* the pathToPlugin will be relative i.e. `/Users/my-name/strapi-app/src/plugins/my-plugin`.
|
|
101
|
+
*
|
|
102
|
+
* If the file doesn't exist well then it's probably a node_module, so instead we use `require.resolve`
|
|
103
|
+
* which will resolve the path to the module in node_modules. If it fails with the specific code `MODULE_NOT_FOUND`
|
|
104
|
+
* then it doesn't have an admin part to the package.
|
|
105
|
+
*
|
|
106
|
+
* NOTE: we should try to move to `./package.json[exports]` map with bundling of our own plugins,
|
|
107
|
+
* because these entry files are written in commonjs restricting features e.g. tree-shaking.
|
|
108
|
+
*/
|
|
109
|
+
try {
|
|
110
|
+
const isLocalPluginWithLegacyAdminFile = fs.existsSync(
|
|
111
|
+
path.resolve(`${plugin.pathToPlugin}/strapi-admin.js`)
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
if (!isLocalPluginWithLegacyAdminFile) {
|
|
115
|
+
const isModuleWithFE = require.resolve(`${plugin.pathToPlugin}/strapi-admin`);
|
|
116
|
+
|
|
117
|
+
return isModuleWithFE;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return isLocalPluginWithLegacyAdminFile;
|
|
121
|
+
} catch (err) {
|
|
122
|
+
if (err.code === 'MODULE_NOT_FOUND') {
|
|
123
|
+
/**
|
|
124
|
+
* the plugin does not contain FE code, so we
|
|
125
|
+
* don't want to import it anyway
|
|
126
|
+
*/
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
*
|
|
136
|
+
* @param {Plugin[]} plugins
|
|
137
|
+
* @param {string} dest
|
|
138
|
+
* @returns {void}
|
|
139
|
+
*/
|
|
140
|
+
async function createPluginFile(plugins, dest) {
|
|
141
|
+
const pluginsArray = plugins.map(({ pathToPlugin, name, info }) => {
|
|
142
|
+
const shortName = camelCase(name);
|
|
143
|
+
|
|
144
|
+
let realPath = '';
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* We're using a module here so we want to keep using the module resolution procedure.
|
|
148
|
+
*/
|
|
149
|
+
if (info?.packageName || info?.required) {
|
|
150
|
+
/**
|
|
151
|
+
* path.join, on windows, it uses backslashes to resolve path.
|
|
152
|
+
* The problem is that Webpack does not windows paths
|
|
153
|
+
* With this tool, we need to rely on "/" and not "\".
|
|
154
|
+
* This is the reason why '..\\..\\..\\node_modules\\@strapi\\plugin-content-type-builder/strapi-admin.js' was not working.
|
|
155
|
+
* The regexp at line 105 aims to replace the windows backslashes by standard slash so that webpack can deal with them.
|
|
156
|
+
* Backslash looks to work only for absolute paths with webpack => https://webpack.js.org/concepts/module-resolution/#absolute-paths
|
|
157
|
+
*/
|
|
158
|
+
realPath = path.join(pathToPlugin, 'strapi-admin').replace(/\\/g, '/');
|
|
159
|
+
} else {
|
|
160
|
+
realPath = path
|
|
161
|
+
.join(path.relative(path.resolve(dest, 'admin', 'src'), pathToPlugin), 'strapi-admin')
|
|
162
|
+
.replace(/\\/g, '/');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
name,
|
|
167
|
+
pathToPlugin: realPath,
|
|
168
|
+
shortName,
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const content = `
|
|
173
|
+
${pluginsArray
|
|
174
|
+
.map(({ pathToPlugin, shortName }) => {
|
|
175
|
+
const req = `'${pathToPlugin}'`;
|
|
176
|
+
|
|
177
|
+
return `import ${shortName} from ${req};`;
|
|
178
|
+
})
|
|
179
|
+
.join('\n')}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
const plugins = {
|
|
183
|
+
${[...pluginsArray]
|
|
184
|
+
.map(({ name, shortName }) => {
|
|
185
|
+
return ` '${name}': ${shortName},`;
|
|
186
|
+
})
|
|
187
|
+
.join('\n')}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export default plugins;
|
|
191
|
+
`;
|
|
192
|
+
|
|
193
|
+
return asyncFs.writeFile(path.resolve(dest, 'admin', 'src', 'plugins.js'), content);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @param {string[]} pluginsPath – an array of paths to the plugins from the user's directory
|
|
198
|
+
* @returns {RegExp} a regex that will exclude _all_ node_modules except for the plugins in the pluginsPath array.
|
|
199
|
+
*/
|
|
200
|
+
const createPluginsExcludePath = (pluginsPath = []) => {
|
|
201
|
+
/**
|
|
202
|
+
* If there aren't any plugins in the node_modules array, just return the node_modules regex
|
|
203
|
+
* without complicating it.
|
|
204
|
+
*/
|
|
205
|
+
if (pluginsPath.length === 0) {
|
|
206
|
+
return /node_modules/;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return new RegExp(`node_modules/(?!(${pluginsPath.join('|')}))`);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
module.exports = {
|
|
213
|
+
getPlugins,
|
|
214
|
+
filterPluginsByAdminEntry,
|
|
215
|
+
createPluginFile,
|
|
216
|
+
createPluginsExcludePath,
|
|
217
|
+
};
|
package/webpack.config.js
CHANGED
|
@@ -13,7 +13,7 @@ const browserslistToEsbuild = require('browserslist-to-esbuild');
|
|
|
13
13
|
|
|
14
14
|
const alias = require('./webpack.alias');
|
|
15
15
|
const getClientEnvironment = require('./env');
|
|
16
|
-
const { createPluginsExcludePath } = require('./utils/
|
|
16
|
+
const { createPluginsExcludePath } = require('./utils/plugins');
|
|
17
17
|
|
|
18
18
|
module.exports = ({
|
|
19
19
|
dest,
|
|
@@ -85,7 +85,7 @@ module.exports = ({
|
|
|
85
85
|
module: {
|
|
86
86
|
rules: [
|
|
87
87
|
{
|
|
88
|
-
test: /\.tsx
|
|
88
|
+
test: /\.(ts|tsx)$/,
|
|
89
89
|
loader: require.resolve('esbuild-loader'),
|
|
90
90
|
exclude: excludeRegex,
|
|
91
91
|
options: {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";(self.webpackChunk_strapi_admin=self.webpackChunk_strapi_admin||[]).push([[1227],{21227:function(g,E,t){t.d(E,{ReviewWorkflowsAssigneeEE:function(){return O},ReviewWorkflowsStageEE:function(){return f}});var e=t(32735),s=t(87933),c=t(72850),i=t(49372),r=t(45509),l=t(60216),n=t.n(l),o=t(2121),u=t(68886);function f({color:a,name:A}){const{themeColorName:R}=(0,u.k)(a);return e.createElement(s.k,{alignItems:"center",gap:2,maxWidth:(0,r.Q1)(300)},e.createElement(c.x,{height:2,background:a,borderColor:R==="neutral0"?"neutral150":"transparent",hasRadius:!0,shrink:0,width:2}),e.createElement(i.Z,{fontWeight:"regular",textColor:"neutral700",ellipsis:!0},A))}f.defaultProps={color:o.FT},f.propTypes={color:n().string,name:n().string.isRequired};var T=t(67879),W=t(69997);function O({user:a}){const{formatMessage:A}=(0,T.Z)();return e.createElement(i.Z,{textColor:"neutral800"},(0,W.Gf)(a,A))}O.propTypes={user:n().shape({firstname:n().string,lastname:n().string,username:n().string}).isRequired}},2121:function(g,E,t){t.d(E,{$k:function(){return o},Ef:function(){return O},FT:function(){return T},Nj:function(){return n},Ot:function(){return l},VS:function(){return u},_X:function(){return a},gu:function(){return c},lv:function(){return f},qZ:function(){return i},sN:function(){return s},uL:function(){return W},x4:function(){return r}});var e=t(12301);const s="settings_review-workflows",c="Settings/Review_Workflows/RESET_WORKFLOW",i="Settings/Review_Workflows/SET_WORKFLOW",r="Settings/Review_Workflows/WORKFLOW_DELETE_STAGE",l="Settings/Review_Workflows/WORKFLOW_ADD_STAGE",n="Settings/Review_Workflows/WORKFLOW_UPDATE_STAGE",o="Settings/Review_Workflows/WORKFLOW_UPDATE_STAGE_POSITION",u="Settings/Review_Workflows/WORKFLOW_UPDATE",f={primary600:"Blue",primary200:"Lilac",alternative600:"Violet",alternative200:"Lavender",success600:"Green",success200:"Pale Green",danger500:"Cherry",danger200:"Pink",warning600:"Orange",warning200:"Yellow",secondary600:"Teal",secondary200:"Baby Blue",neutral400:"Gray",neutral0:"White"},T=e.W.colors.primary600,W={STAGE:"stage"},O="numberOfWorkflows",a="stagesPerWorkflow"},68886:function(g,E,t){t.d(E,{k:function(){return c},s:function(){return i}});var e=t(12301),s=t(2121);function c(r){if(!r)return null;const n=Object.entries(e.W.colors).filter(([,o])=>o.toUpperCase()===r.toUpperCase()).reduce((o,[u])=>(s.lv?.[u]&&(o=u),o),null);return n?{themeColorName:n,name:s.lv[n]}:null}function i(){return Object.entries(s.lv).map(([r,l])=>({hex:e.W.colors[r].toUpperCase(),name:l}))}}}]);
|