apostrophe 3.16.0 → 3.17.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/.eslintignore +3 -1
- package/CHANGELOG.md +23 -1
- package/index.js +5 -4
- package/lib/moog.js +14 -1
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +3 -1
- package/modules/@apostrophecms/asset/index.js +213 -101
- package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.config.js +3 -3
- package/modules/@apostrophecms/asset/lib/webpack/src/webpack.config.js +30 -12
- package/modules/@apostrophecms/asset/lib/webpack/src/webpack.es5.js +1 -1
- package/modules/@apostrophecms/asset/lib/webpack/src/webpack.scss.js +6 -2
- package/modules/@apostrophecms/asset/lib/webpack/utils.js +266 -0
- package/modules/@apostrophecms/asset/views/scripts.html +1 -0
- package/modules/@apostrophecms/asset/views/stylesheets.html +1 -0
- package/modules/@apostrophecms/doc/index.js +64 -0
- package/modules/@apostrophecms/doc-type/index.js +35 -0
- package/modules/@apostrophecms/i18n/i18n/en.json +2 -0
- package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +7 -0
- package/modules/@apostrophecms/login/index.js +4 -0
- package/modules/@apostrophecms/schema/index.js +40 -49
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +67 -0
- package/modules/@apostrophecms/template/index.js +14 -5
- package/modules/@apostrophecms/template/lib/bundlesLoader.js +158 -0
- package/modules/@apostrophecms/template/views/outerLayoutBase.html +6 -0
- package/modules/@apostrophecms/widget-type/index.js +21 -0
- package/package.json +1 -1
- package/test/areas.js +2 -1
- package/test/assets.js +307 -3
- package/test/docs.js +67 -2
- package/test/modules/bundle/index.js +7 -0
- package/test/modules/bundle-page/index.js +32 -0
- package/test/modules/bundle-page/ui/src/extra.js +1 -0
- package/test/modules/bundle-page/ui/src/extra.scss +0 -0
- package/test/modules/bundle-page/views/index.html +9 -0
- package/test/modules/bundle-page/views/show.html +10 -0
- package/test/modules/bundle-widget/index.js +27 -0
- package/test/modules/bundle-widget/ui/src/extra2.js +1 -0
- package/test/modules/bundle-widget/views/widget.html +1 -0
- package/test/pieces.js +38 -12
- package/test/public/static-test.txt +0 -1
|
@@ -2,6 +2,7 @@ const path = require('path');
|
|
|
2
2
|
const merge = require('webpack-merge').merge;
|
|
3
3
|
const scssTask = require('./webpack.scss');
|
|
4
4
|
const es5Task = require('./webpack.es5');
|
|
5
|
+
const srcBuildNames = [ 'src-build', 'src-es5-build' ];
|
|
5
6
|
|
|
6
7
|
let BundleAnalyzerPlugin;
|
|
7
8
|
|
|
@@ -10,9 +11,11 @@ if (process.env.APOS_BUNDLE_ANALYZER) {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
module.exports = ({
|
|
13
|
-
importFile, modulesDir, outputPath, outputFilename, es5
|
|
14
|
+
importFile, modulesDir, outputPath, outputFilename, bundles = {}, es5
|
|
14
15
|
}, apos) => {
|
|
16
|
+
const mainBundleName = outputFilename.replace('.js', '');
|
|
15
17
|
const taskFns = [ scssTask ];
|
|
18
|
+
|
|
16
19
|
if (es5) {
|
|
17
20
|
taskFns.push(es5Task);
|
|
18
21
|
}
|
|
@@ -20,16 +23,20 @@ module.exports = ({
|
|
|
20
23
|
task(
|
|
21
24
|
{
|
|
22
25
|
importFile,
|
|
23
|
-
modulesDir
|
|
24
|
-
outputFilename
|
|
26
|
+
modulesDir
|
|
25
27
|
},
|
|
26
|
-
apos
|
|
28
|
+
apos,
|
|
29
|
+
srcBuildNames
|
|
27
30
|
)
|
|
28
31
|
);
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const moduleName = es5 ? 'nomodule' : 'module';
|
|
34
|
+
const config = {
|
|
35
|
+
entry: {
|
|
36
|
+
[mainBundleName]: importFile,
|
|
37
|
+
...bundles
|
|
38
|
+
},
|
|
39
|
+
target: es5 ? [ 'web', 'es5' ] : 'web',
|
|
33
40
|
mode: process.env.NODE_ENV || 'development',
|
|
34
41
|
optimization: {
|
|
35
42
|
minimize: process.env.NODE_ENV === 'production'
|
|
@@ -37,11 +44,17 @@ module.exports = ({
|
|
|
37
44
|
devtool: 'source-map',
|
|
38
45
|
output: {
|
|
39
46
|
path: outputPath,
|
|
40
|
-
filename:
|
|
47
|
+
filename: ({ chunk }) => {
|
|
48
|
+
return srcBuildNames.includes(chunk.name)
|
|
49
|
+
? '[name].js'
|
|
50
|
+
: `[name]-${moduleName}-bundle.js`;
|
|
51
|
+
}
|
|
41
52
|
},
|
|
42
53
|
resolveLoader: {
|
|
43
54
|
extensions: [ '*', '.js' ],
|
|
44
|
-
|
|
55
|
+
// Make sure css-loader and postcss-loader can always be found, even
|
|
56
|
+
// if npm didn't hoist them
|
|
57
|
+
modules: [ 'node_modules', 'node_modules/apostrophe/node_modules' ]
|
|
45
58
|
},
|
|
46
59
|
resolve: {
|
|
47
60
|
extensions: [ '*', '.js' ],
|
|
@@ -50,16 +63,21 @@ module.exports = ({
|
|
|
50
63
|
Modules: path.resolve(modulesDir)
|
|
51
64
|
},
|
|
52
65
|
modules: [
|
|
66
|
+
'node_modules',
|
|
53
67
|
`${apos.npmRootDir}/node_modules`,
|
|
54
68
|
// Make sure core-js and regenerator-runtime can always be found, even
|
|
55
69
|
// if npm didn't hoist them
|
|
56
70
|
`${apos.npmRootDir}/node_modules/apostrophe/node_modules`
|
|
57
|
-
]
|
|
71
|
+
],
|
|
72
|
+
symlinks: false
|
|
58
73
|
},
|
|
59
74
|
stats: 'verbose',
|
|
60
75
|
plugins: process.env.APOS_BUNDLE_ANALYZER ? [ new BundleAnalyzerPlugin() ] : []
|
|
61
76
|
};
|
|
62
77
|
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
if (es5) {
|
|
79
|
+
config.output.chunkFormat = 'array-push';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return merge(config, ...tasks);
|
|
65
83
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
|
2
2
|
|
|
3
|
-
module.exports = (options, apos) => {
|
|
3
|
+
module.exports = (options, apos, srcBuildNames) => {
|
|
4
4
|
return {
|
|
5
5
|
module: {
|
|
6
6
|
rules: [
|
|
@@ -40,7 +40,11 @@ module.exports = (options, apos) => {
|
|
|
40
40
|
plugins: [
|
|
41
41
|
new MiniCssExtractPlugin({
|
|
42
42
|
// Should be automatic but we wind up with main.css if we try to go with that
|
|
43
|
-
filename:
|
|
43
|
+
filename: ({ chunk }) => {
|
|
44
|
+
return srcBuildNames.includes(chunk.name)
|
|
45
|
+
? '[name].css'
|
|
46
|
+
: '[name]-bundle.css';
|
|
47
|
+
}
|
|
44
48
|
})
|
|
45
49
|
]
|
|
46
50
|
};
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
checkModulesWebpackConfig(modules, t) {
|
|
5
|
+
const allowedProperties = [ 'extensions', 'bundles' ];
|
|
6
|
+
for (const mod of Object.values(modules)) {
|
|
7
|
+
const webpackConfig = mod.__meta.webpack[mod.__meta.name];
|
|
8
|
+
|
|
9
|
+
if (!webpackConfig) {
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (
|
|
14
|
+
typeof webpackConfig !== 'object' ||
|
|
15
|
+
webpackConfig === null ||
|
|
16
|
+
Array.isArray(webpackConfig) ||
|
|
17
|
+
Object.keys(webpackConfig).some((prop) => !allowedProperties.includes(prop))
|
|
18
|
+
) {
|
|
19
|
+
const error = t('apostrophe:assetWebpackConfigWarning', {
|
|
20
|
+
module: mod.__meta.name
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
throw new Error(error);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (webpackConfig && webpackConfig.bundles) {
|
|
27
|
+
const bundles = Object.values(webpackConfig.bundles);
|
|
28
|
+
|
|
29
|
+
bundles.forEach(bundle => {
|
|
30
|
+
const bundleProps = Object.keys(bundle);
|
|
31
|
+
if (
|
|
32
|
+
bundleProps.length > 1 ||
|
|
33
|
+
(bundleProps.length === 1 && !bundle.templates) ||
|
|
34
|
+
(bundle.templates && !Array.isArray(bundle.templates))
|
|
35
|
+
) {
|
|
36
|
+
const error = t('apostrophe:assetWebpackBundlesWarning', {
|
|
37
|
+
module: mod.__meta.name
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
throw new Error(error);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
async getWebpackExtensions ({
|
|
48
|
+
getMetadata, modulesToInstantiate
|
|
49
|
+
}) {
|
|
50
|
+
const modulesMeta = modulesToInstantiate
|
|
51
|
+
.map((name) => getMetadata(name));
|
|
52
|
+
|
|
53
|
+
const { extensions, foundBundles } = getModulesWebpackConfigs(
|
|
54
|
+
modulesMeta
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const verifiedBundles = await verifyBundlesEntryPoints(foundBundles);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
extensions,
|
|
61
|
+
verifiedBundles
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
fillExtraBundles (verifiedBundles = {}) {
|
|
66
|
+
return Object.entries(verifiedBundles).reduce((acc, [ bundleName, { js, scss } ]) => {
|
|
67
|
+
return {
|
|
68
|
+
js: [
|
|
69
|
+
...acc.js,
|
|
70
|
+
...(js.length && !acc.js.includes(bundleName)) ? [ bundleName ] : []
|
|
71
|
+
],
|
|
72
|
+
css: [
|
|
73
|
+
...acc.css,
|
|
74
|
+
...(scss.length && !acc.css.includes(bundleName)) ? [ bundleName ] : []
|
|
75
|
+
]
|
|
76
|
+
};
|
|
77
|
+
}, {
|
|
78
|
+
js: [],
|
|
79
|
+
css: []
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
getBundlesNames (bundles, es5 = false) {
|
|
84
|
+
return Object.entries(bundles).reduce((acc, [ ext, bundlesNames ]) => {
|
|
85
|
+
const nameExtension = ext === 'css'
|
|
86
|
+
? '-bundle'
|
|
87
|
+
: '-module-bundle';
|
|
88
|
+
|
|
89
|
+
const es5Bundles = es5 && ext === 'js'
|
|
90
|
+
? bundlesNames.map((name) => `${name}-nomodule-bundle.${ext}`)
|
|
91
|
+
: [];
|
|
92
|
+
|
|
93
|
+
return [
|
|
94
|
+
...acc,
|
|
95
|
+
...bundlesNames.map((name) => `${name}${nameExtension}.${ext}`),
|
|
96
|
+
...es5Bundles
|
|
97
|
+
];
|
|
98
|
+
}, []);
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
writeBundlesImportFiles ({
|
|
102
|
+
name,
|
|
103
|
+
buildDir,
|
|
104
|
+
mainBundleName,
|
|
105
|
+
verifiedBundles,
|
|
106
|
+
getImportFileOutput,
|
|
107
|
+
writeImportFile
|
|
108
|
+
}) {
|
|
109
|
+
if (!name.includes('src')) {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const bundlesOutputs = Object.entries(verifiedBundles)
|
|
114
|
+
.map(([ bundleName, paths ]) => {
|
|
115
|
+
return {
|
|
116
|
+
bundleName,
|
|
117
|
+
importFile: `${buildDir}/${bundleName}-import.js`,
|
|
118
|
+
js: getImportFileOutput(paths.js, {
|
|
119
|
+
invokeApps: true,
|
|
120
|
+
enumerateImports: true,
|
|
121
|
+
requireDefaultExport: true
|
|
122
|
+
}),
|
|
123
|
+
scss: getImportFileOutput(paths.scss, {
|
|
124
|
+
enumerateImports: true,
|
|
125
|
+
importSuffix: 'Stylesheet'
|
|
126
|
+
})
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
for (const output of bundlesOutputs) {
|
|
131
|
+
writeImportFile({
|
|
132
|
+
importFile: output.importFile,
|
|
133
|
+
indexJs: output.js,
|
|
134
|
+
indexSass: output.scss
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return bundlesOutputs.reduce((acc, { bundleName, importFile }) => {
|
|
139
|
+
return {
|
|
140
|
+
...acc,
|
|
141
|
+
[bundleName]: {
|
|
142
|
+
import: importFile,
|
|
143
|
+
dependOn: mainBundleName
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}, {});
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
function getModulesWebpackConfigs (modulesMeta) {
|
|
151
|
+
const { extensions, bundles } = modulesMeta.reduce((acc, meta) => {
|
|
152
|
+
const { webpack, __meta } = meta;
|
|
153
|
+
|
|
154
|
+
const configs = formatConfigs(__meta.chain, webpack);
|
|
155
|
+
|
|
156
|
+
if (!configs.length) {
|
|
157
|
+
return acc;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const moduleBundles = configs.reduce((acc, conf) => {
|
|
161
|
+
return {
|
|
162
|
+
...acc,
|
|
163
|
+
...conf.bundles
|
|
164
|
+
};
|
|
165
|
+
}, {});
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
extensions: {
|
|
169
|
+
...acc.extensions,
|
|
170
|
+
...configs.reduce((acc, config) => ({
|
|
171
|
+
...acc,
|
|
172
|
+
...config.extensions
|
|
173
|
+
}), {})
|
|
174
|
+
},
|
|
175
|
+
bundles: {
|
|
176
|
+
...acc.bundles,
|
|
177
|
+
...moduleBundles
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}, {
|
|
181
|
+
extensions: {},
|
|
182
|
+
bundles: {}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
extensions,
|
|
187
|
+
foundBundles: flattenBundles(bundles)
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
async function verifyBundlesEntryPoints (bundles) {
|
|
192
|
+
const checkPathsPromises = bundles.map(async ({ bundleName, modulePath }) => {
|
|
193
|
+
const jsPath = `${modulePath}/ui/src/${bundleName}.js`;
|
|
194
|
+
const scssPath = `${modulePath}/ui/src/${bundleName}.scss`;
|
|
195
|
+
|
|
196
|
+
const jsFileExists = await fs.pathExists(jsPath);
|
|
197
|
+
const scssFileExists = await fs.pathExists(scssPath);
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
bundleName,
|
|
201
|
+
...jsFileExists && { jsPath: jsPath },
|
|
202
|
+
...scssFileExists && { scssPath: scssPath }
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const bundlesPaths = await Promise.all(checkPathsPromises);
|
|
207
|
+
|
|
208
|
+
const packedFilesByBundle = bundlesPaths.reduce((acc, {
|
|
209
|
+
bundleName, jsPath, scssPath
|
|
210
|
+
}) => {
|
|
211
|
+
if (!jsPath && !scssPath) {
|
|
212
|
+
return acc;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
...acc,
|
|
217
|
+
[bundleName]: {
|
|
218
|
+
js: [
|
|
219
|
+
...acc[bundleName] ? acc[bundleName].js : [],
|
|
220
|
+
...jsPath ? [ jsPath ] : []
|
|
221
|
+
],
|
|
222
|
+
scss: [
|
|
223
|
+
...acc[bundleName] ? acc[bundleName].scss : [],
|
|
224
|
+
...scssPath ? [ scssPath ] : []
|
|
225
|
+
]
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}, {});
|
|
229
|
+
|
|
230
|
+
return packedFilesByBundle;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
function formatConfigs (chain, webpackConfigs) {
|
|
234
|
+
return Object.entries(webpackConfigs)
|
|
235
|
+
.map(([ name, config ], i) => {
|
|
236
|
+
|
|
237
|
+
if (!config) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const { bundles = {}, extensions = {} } = config;
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
extensions,
|
|
245
|
+
bundles: {
|
|
246
|
+
[name]: {
|
|
247
|
+
bundleNames: Object.keys(bundles),
|
|
248
|
+
modulePath: chain[i].dirname
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}).filter((config) => config);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function flattenBundles (bundles) {
|
|
256
|
+
return Object.values(bundles)
|
|
257
|
+
.reduce((acc, { bundleNames, modulePath }) => {
|
|
258
|
+
return [
|
|
259
|
+
...acc,
|
|
260
|
+
...bundleNames.map((bundleName) => ({
|
|
261
|
+
bundleName,
|
|
262
|
+
modulePath
|
|
263
|
+
}))
|
|
264
|
+
];
|
|
265
|
+
}, []);
|
|
266
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{ data.placeholder }}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{ data.placeholder }}
|
|
@@ -329,6 +329,10 @@ module.exports = {
|
|
|
329
329
|
updatedAt: -1,
|
|
330
330
|
aposLocale: 1
|
|
331
331
|
}, {});
|
|
332
|
+
await self.db.createIndex({
|
|
333
|
+
relatedReverseIds: 1,
|
|
334
|
+
aposLocale: 1
|
|
335
|
+
}, {});
|
|
332
336
|
await self.db.createIndex({ 'advisoryLock._id': 1 }, {});
|
|
333
337
|
await self.createTextIndex();
|
|
334
338
|
await self.db.createIndex({ parkedId: 1 }, {});
|
|
@@ -1093,6 +1097,66 @@ module.exports = {
|
|
|
1093
1097
|
}
|
|
1094
1098
|
});
|
|
1095
1099
|
},
|
|
1100
|
+
|
|
1101
|
+
// Iterate through the document fields and call the provided handlers
|
|
1102
|
+
// for each item of an array, object and relationship field type.
|
|
1103
|
+
walkByMetaType(doc, handlers) {
|
|
1104
|
+
const defaultHandlers = {
|
|
1105
|
+
arrayItem: () => {},
|
|
1106
|
+
object: () => {},
|
|
1107
|
+
relationship: () => {}
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
handlers = {
|
|
1111
|
+
...defaultHandlers,
|
|
1112
|
+
...handlers
|
|
1113
|
+
};
|
|
1114
|
+
|
|
1115
|
+
if (doc.metaType === 'doc') {
|
|
1116
|
+
const manager = self.getManager(doc.type);
|
|
1117
|
+
if (!manager) {
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
forSchema(manager.schema, doc);
|
|
1121
|
+
} else if (doc.metaType === 'widget') {
|
|
1122
|
+
const manager = self.apos.area.getWidgetManager(doc.type);
|
|
1123
|
+
if (!manager) {
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
forSchema(manager.schema, doc);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
function forSchema(schema, doc) {
|
|
1130
|
+
for (const field of schema) {
|
|
1131
|
+
if (field.type === 'area' && doc[field.name] && doc[field.name].items) {
|
|
1132
|
+
for (const widget of doc[field.name].items) {
|
|
1133
|
+
self.walkByMetaType(widget, {
|
|
1134
|
+
arrayItem: handlers.arrayItem,
|
|
1135
|
+
object: handlers.object,
|
|
1136
|
+
relationship: handlers.relationship
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
} else if (field.type === 'array') {
|
|
1140
|
+
if (doc[field.name]) {
|
|
1141
|
+
doc[field.name].forEach(item => {
|
|
1142
|
+
handlers.arrayItem(field, item);
|
|
1143
|
+
forSchema(field.schema, item);
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
} else if (field.type === 'object') {
|
|
1147
|
+
const value = doc[field.name];
|
|
1148
|
+
if (value) {
|
|
1149
|
+
handlers.object(field, value);
|
|
1150
|
+
forSchema(field.schema, value);
|
|
1151
|
+
}
|
|
1152
|
+
} else if (field.type === 'relationship') {
|
|
1153
|
+
if (Array.isArray(doc[field.name])) {
|
|
1154
|
+
handlers.relationship(field, doc);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
},
|
|
1096
1160
|
...require('./lib/legacy-migrations')(self)
|
|
1097
1161
|
};
|
|
1098
1162
|
}
|
|
@@ -94,6 +94,29 @@ module.exports = {
|
|
|
94
94
|
handlers(self) {
|
|
95
95
|
return {
|
|
96
96
|
beforeSave: {
|
|
97
|
+
async updateBacklinks(req, doc) {
|
|
98
|
+
const relatedDocsIds = self.getRelatedDocsIds(req, doc);
|
|
99
|
+
|
|
100
|
+
// Remove all references to the doc
|
|
101
|
+
await self.apos.doc.db.updateMany({
|
|
102
|
+
relatedReverseIds: { $in: [ doc.aposDocId ] },
|
|
103
|
+
aposLocale: { $in: [ doc.aposLocale, null ] }
|
|
104
|
+
}, {
|
|
105
|
+
$pull: { relatedReverseIds: doc.aposDocId }
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
if (!relatedDocsIds.length) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Add doc reference to all related docs
|
|
113
|
+
await self.apos.doc.db.updateMany({
|
|
114
|
+
aposDocId: { $in: relatedDocsIds },
|
|
115
|
+
aposLocale: { $in: [ doc.aposLocale, null ] }
|
|
116
|
+
}, {
|
|
117
|
+
$push: { relatedReverseIds: doc.aposDocId }
|
|
118
|
+
});
|
|
119
|
+
},
|
|
97
120
|
prepareForStorage(req, doc) {
|
|
98
121
|
self.apos.schema.prepareForStorage(req, doc);
|
|
99
122
|
},
|
|
@@ -253,6 +276,18 @@ module.exports = {
|
|
|
253
276
|
|
|
254
277
|
methods(self) {
|
|
255
278
|
return {
|
|
279
|
+
getRelatedDocsIds(req, doc) {
|
|
280
|
+
const relatedDocsIds = [];
|
|
281
|
+
const handlers = {
|
|
282
|
+
relationship: (field, doc) => {
|
|
283
|
+
relatedDocsIds.push(...doc[field.name].map(relatedDoc => self.apos.doc.toAposDocId(relatedDoc)));
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
self.apos.doc.walkByMetaType(doc, handlers);
|
|
288
|
+
|
|
289
|
+
return relatedDocsIds;
|
|
290
|
+
},
|
|
256
291
|
sanitizeFieldList(choices) {
|
|
257
292
|
if ((typeof choices) === 'string') {
|
|
258
293
|
return choices.split(/\s*,\s*/);
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
"areYouSure": "Are You Sure?",
|
|
31
31
|
"assetTypeBuildComplete": "👍 {{ label }} is complete!",
|
|
32
32
|
"assetTypeBuilding": "🧑💻 Building the {{ label }}...",
|
|
33
|
+
"assetWebpackConfigWarning": "⚠️ In the module {{ module }}, your webpack config is incorrect. It must be an object and should contain only two properties extensions and bundles.",
|
|
34
|
+
"assetWebpackBundlesWarning": "⚠️ In the module {{ module }} your webpack config is incorrect. Each bundle can only have one property 'templates' that must be an array of strings.",
|
|
33
35
|
"back": "Back",
|
|
34
36
|
"backToHome": "Back to Home",
|
|
35
37
|
"basics": "Basics",
|
|
@@ -717,6 +717,13 @@ export default {
|
|
|
717
717
|
...getRelatedBySchema(value, field.schema)
|
|
718
718
|
];
|
|
719
719
|
}
|
|
720
|
+
} else if (field.type === 'object') {
|
|
721
|
+
if (object[field.name]) {
|
|
722
|
+
related = [
|
|
723
|
+
...related,
|
|
724
|
+
...getRelatedBySchema(object[field.name], field.schema)
|
|
725
|
+
];
|
|
726
|
+
}
|
|
720
727
|
} else if (field.type === 'area') {
|
|
721
728
|
for (const widget of (object[field.name]?.items || [])) {
|
|
722
729
|
related = [
|
|
@@ -682,6 +682,10 @@ module.exports = {
|
|
|
682
682
|
|
|
683
683
|
// Awaitable wrapper for req.login. An implementation detail of the login route
|
|
684
684
|
async passportLogin(req, user) {
|
|
685
|
+
const cookieName = `${self.apos.shortName}.${loggedInCookieName}`;
|
|
686
|
+
if (req.cookies[cookieName] !== 'true') {
|
|
687
|
+
req.res.cookie(cookieName, 'true');
|
|
688
|
+
}
|
|
685
689
|
const passportLogin = (user) => {
|
|
686
690
|
return require('util').promisify(function(user, callback) {
|
|
687
691
|
return req.login(user, callback);
|
|
@@ -790,6 +790,7 @@ module.exports = {
|
|
|
790
790
|
self.addFieldType({
|
|
791
791
|
name: 'object',
|
|
792
792
|
async convert(req, field, data, destination) {
|
|
793
|
+
data = data[field.name];
|
|
793
794
|
const schema = field.schema;
|
|
794
795
|
const errors = [];
|
|
795
796
|
const result = {
|
|
@@ -808,6 +809,8 @@ module.exports = {
|
|
|
808
809
|
});
|
|
809
810
|
}
|
|
810
811
|
}
|
|
812
|
+
result.metaType = 'objectItem';
|
|
813
|
+
result.scopedObjectName = field.scopedObjectName;
|
|
811
814
|
destination[field.name] = result;
|
|
812
815
|
if (errors.length) {
|
|
813
816
|
throw errors;
|
|
@@ -821,6 +824,11 @@ module.exports = {
|
|
|
821
824
|
};
|
|
822
825
|
self.register(metaType, type, field.schema);
|
|
823
826
|
},
|
|
827
|
+
validate: function (field, options, warn, fail) {
|
|
828
|
+
for (const subField of field.schema || field.fields.add) {
|
|
829
|
+
self.validateField(subField, options);
|
|
830
|
+
}
|
|
831
|
+
},
|
|
824
832
|
isEqual(req, field, one, two) {
|
|
825
833
|
if (one && (!two)) {
|
|
826
834
|
return false;
|
|
@@ -831,6 +839,15 @@ module.exports = {
|
|
|
831
839
|
if (!(one || two)) {
|
|
832
840
|
return true;
|
|
833
841
|
}
|
|
842
|
+
if (one[field.name] && (!two[field.name])) {
|
|
843
|
+
return false;
|
|
844
|
+
}
|
|
845
|
+
if (two[field.name] && (!one[field.name])) {
|
|
846
|
+
return false;
|
|
847
|
+
}
|
|
848
|
+
if (!(one[field.name] || two[field.name])) {
|
|
849
|
+
return true;
|
|
850
|
+
}
|
|
834
851
|
return self.isEqual(req, field.schema, one[field.name], two[field.name]);
|
|
835
852
|
},
|
|
836
853
|
def: {}
|
|
@@ -1952,59 +1969,31 @@ module.exports = {
|
|
|
1952
1969
|
// Currently `req` does not impact this, but that may change.
|
|
1953
1970
|
|
|
1954
1971
|
prepareForStorage(req, doc) {
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
for (const widget of doc[field.name].items) {
|
|
1973
|
-
self.prepareForStorage(req, widget);
|
|
1974
|
-
}
|
|
1975
|
-
}
|
|
1976
|
-
} else if (field.type === 'array') {
|
|
1977
|
-
if (doc[field.name]) {
|
|
1978
|
-
doc[field.name].forEach(item => {
|
|
1979
|
-
item._id = item._id || self.apos.util.generateId();
|
|
1980
|
-
item.metaType = 'arrayItem';
|
|
1981
|
-
item.scopedArrayName = field.scopedArrayName;
|
|
1982
|
-
forSchema(field.schema, item);
|
|
1983
|
-
});
|
|
1984
|
-
}
|
|
1985
|
-
} else if (field.type === 'object') {
|
|
1986
|
-
const value = doc[field.name];
|
|
1987
|
-
if (value) {
|
|
1988
|
-
value.metaType = 'object';
|
|
1989
|
-
value.scopedObjectName = field.scopedObjectName;
|
|
1990
|
-
forSchema(field.schema, value);
|
|
1991
|
-
}
|
|
1992
|
-
} else if (field.type === 'relationship') {
|
|
1993
|
-
if (Array.isArray(doc[field.name])) {
|
|
1994
|
-
doc[field.idsStorage] = doc[field.name].map(relatedDoc => self.apos.doc.toAposDocId(relatedDoc));
|
|
1995
|
-
if (field.fieldsStorage) {
|
|
1996
|
-
const fieldsById = doc[field.fieldsStorage] || {};
|
|
1997
|
-
for (const relatedDoc of doc[field.name]) {
|
|
1998
|
-
if (relatedDoc._fields) {
|
|
1999
|
-
fieldsById[self.apos.doc.toAposDocId(relatedDoc)] = relatedDoc._fields;
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
doc[field.fieldsStorage] = fieldsById;
|
|
1972
|
+
const handlers = {
|
|
1973
|
+
arrayItem: (field, object) => {
|
|
1974
|
+
object._id = object._id || self.apos.util.generateId();
|
|
1975
|
+
object.metaType = 'arrayItem';
|
|
1976
|
+
object.scopedArrayName = field.scopedArrayName;
|
|
1977
|
+
},
|
|
1978
|
+
object: (field, object) => {
|
|
1979
|
+
object.metaType = 'object';
|
|
1980
|
+
object.scopedObjectName = field.scopedObjectName;
|
|
1981
|
+
},
|
|
1982
|
+
relationship: (field, doc) => {
|
|
1983
|
+
doc[field.idsStorage] = doc[field.name].map(relatedDoc => self.apos.doc.toAposDocId(relatedDoc));
|
|
1984
|
+
if (field.fieldsStorage) {
|
|
1985
|
+
const fieldsById = doc[field.fieldsStorage] || {};
|
|
1986
|
+
for (const relatedDoc of doc[field.name]) {
|
|
1987
|
+
if (relatedDoc._fields) {
|
|
1988
|
+
fieldsById[self.apos.doc.toAposDocId(relatedDoc)] = relatedDoc._fields;
|
|
2003
1989
|
}
|
|
2004
1990
|
}
|
|
1991
|
+
doc[field.fieldsStorage] = fieldsById;
|
|
2005
1992
|
}
|
|
2006
1993
|
}
|
|
2007
|
-
}
|
|
1994
|
+
};
|
|
1995
|
+
|
|
1996
|
+
self.apos.doc.walkByMetaType(doc, handlers);
|
|
2008
1997
|
},
|
|
2009
1998
|
|
|
2010
1999
|
// Add a new field type. The `type` object may contain the following properties:
|
|
@@ -2540,6 +2529,8 @@ module.exports = {
|
|
|
2540
2529
|
item._id = self.apos.util.generateId();
|
|
2541
2530
|
self.regenerateIds(req, field.schema, item);
|
|
2542
2531
|
}
|
|
2532
|
+
} else if (field.type === 'object') {
|
|
2533
|
+
this.regenerateIds(req, field.schema, doc[field.name] || {});
|
|
2543
2534
|
} else if (field.type === 'area') {
|
|
2544
2535
|
if (doc[field.name]) {
|
|
2545
2536
|
doc[field.name]._id = self.apos.util.generateId();
|