apostrophe 3.4.1 → 3.8.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/.eslintrc +4 -0
- package/.scratch.md +2 -0
- package/CHANGELOG.md +114 -2
- package/README.md +1 -1
- package/deploy-test-count +1 -1
- package/index.js +125 -5
- package/lib/moog-require.js +41 -3
- package/lib/moog.js +20 -8
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +42 -23
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +30 -14
- package/modules/@apostrophecms/area/index.js +9 -0
- package/modules/@apostrophecms/area/lib/custom-tags/area.js +1 -1
- package/modules/@apostrophecms/area/lib/custom-tags/widget.js +1 -1
- package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +3 -0
- package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +6 -6
- package/modules/@apostrophecms/asset/index.js +85 -21
- package/modules/@apostrophecms/asset/lib/globalIcons.js +2 -0
- package/modules/@apostrophecms/asset/lib/webpack/src/webpack.scss.js +5 -2
- package/modules/@apostrophecms/attachment/index.js +1 -0
- package/modules/@apostrophecms/db/index.js +5 -6
- package/modules/@apostrophecms/doc/index.js +13 -3
- package/modules/@apostrophecms/doc-type/index.js +24 -4
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +13 -1
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +3 -0
- package/modules/@apostrophecms/i18n/i18n/en.json +26 -6
- package/modules/@apostrophecms/i18n/i18n/es.json +382 -0
- package/modules/@apostrophecms/i18n/i18n/pt-BR.json +379 -0
- package/modules/@apostrophecms/i18n/i18n/sk.json +380 -0
- package/modules/@apostrophecms/i18n/index.js +10 -1
- package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +153 -121
- package/modules/@apostrophecms/image/index.js +2 -1
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +6 -3
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +24 -13
- package/modules/@apostrophecms/image-widget/index.js +2 -1
- package/modules/@apostrophecms/image-widget/views/widget.html +12 -2
- package/modules/@apostrophecms/job/index.js +164 -212
- package/modules/@apostrophecms/login/index.js +36 -17
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +8 -0
- package/modules/@apostrophecms/migration/index.js +1 -1
- package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +151 -61
- package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +6 -2
- package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +9 -7
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +12 -15
- package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +6 -0
- package/modules/@apostrophecms/module/index.js +1 -1
- package/modules/@apostrophecms/notification/index.js +116 -8
- package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +89 -11
- package/modules/@apostrophecms/notification/ui/apos/components/TheAposNotifications.vue +1 -1
- package/modules/@apostrophecms/page/index.js +37 -30
- package/modules/@apostrophecms/permission/index.js +1 -1
- package/modules/@apostrophecms/permission/ui/apos/components/AposInputRole.vue +4 -2
- package/modules/@apostrophecms/piece-type/index.js +178 -61
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +179 -47
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +1 -3
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +138 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +42 -10
- package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +3 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Classes.js +6 -10
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Default.js +64 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Document.js +15 -0
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Heading.js +23 -0
- package/modules/@apostrophecms/schema/index.js +97 -20
- package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +1 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +4 -1
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +11 -160
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +8 -5
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +24 -2
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +24 -6
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +0 -4
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +0 -7
- package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +25 -3
- package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +10 -2
- package/modules/@apostrophecms/task/index.js +2 -2
- package/modules/@apostrophecms/template/index.js +63 -36
- package/modules/@apostrophecms/template/lib/custom-tags/component.js +1 -1
- package/modules/@apostrophecms/template/lib/custom-tags/render.js +6 -2
- package/modules/@apostrophecms/ui/index.js +6 -2
- package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +21 -3
- package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +1 -1
- package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +205 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposIndicator.vue +5 -0
- package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +16 -2
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +4 -3
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +3 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_widgets.scss +3 -0
- package/modules/@apostrophecms/ui/ui/apos/scss/global/import-all.scss +2 -1
- package/modules/@apostrophecms/user/index.js +21 -0
- package/modules/@apostrophecms/util/index.js +2 -2
- package/modules/@apostrophecms/util/ui/src/http.js +12 -8
- package/modules/@apostrophecms/util/ui/src/util.js +15 -0
- package/modules/@apostrophecms/widget-type/index.js +1 -1
- package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +1 -0
- package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +15 -7
- package/package.json +4 -4
- package/test/extra_node_modules/improve-global/index.js +7 -0
- package/test/extra_node_modules/improve-piece-type/index.js +7 -0
- package/test/improve-overrides.js +30 -0
- package/test/job.js +224 -0
- package/test/login.js +183 -0
- package/test/modules/@apostrophecms/global/index.js +8 -0
- package/test/modules/fragment-all/views/aux-test.html +7 -0
- package/test/modules/fragment-all/views/fragment.html +5 -0
- package/test/moog.js +47 -0
- package/test/package.json +5 -4
- package/test/pieces.js +17 -0
- package/test/reverse-relationship.js +170 -0
- package/test/subdir-project/app.js +3 -0
- package/test/subdir-project.js +26 -0
- package/test/templates.js +7 -1
- package/test-lib/test.js +23 -12
- package/test-lib/util.js +33 -0
|
@@ -259,20 +259,32 @@ export default {
|
|
|
259
259
|
// So that areas revert to being editable
|
|
260
260
|
await this.refresh();
|
|
261
261
|
},
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
262
|
+
// Accept a hint that a user is actively typing and/or manipulating controls
|
|
263
|
+
// and it would best not to enable a save button or a "...Saved" indication yet
|
|
264
|
+
// to avoid a frenetic display and/or a situation where not everything is ready
|
|
265
|
+
// to be saved yet.
|
|
266
|
+
//
|
|
267
|
+
// If the event is emitted with a boolean value of `true`, the emitter takes
|
|
268
|
+
// responsibility for later emitting `false` to indicate active typing/manipulating
|
|
269
|
+
// is no longer in progress. If the event is emitted with no value then there is a
|
|
270
|
+
// 1100-millisecond, debounced timeout.
|
|
271
|
+
|
|
272
|
+
async onContextEditing(state) {
|
|
273
|
+
if ((typeof state) === 'boolean') {
|
|
274
|
+
this.editing = state;
|
|
275
|
+
} else {
|
|
276
|
+
if (!this.editing) {
|
|
277
|
+
this.editing = true;
|
|
278
|
+
}
|
|
279
|
+
if (this.editingTimeout) {
|
|
280
|
+
clearTimeout(this.editingTimeout);
|
|
281
|
+
}
|
|
282
|
+
this.editingTimeout = setTimeout(() => {
|
|
283
|
+
this.editing = false;
|
|
284
|
+
// Wait slightly longer than the rich text editor does
|
|
285
|
+
// before sending us a context-edited event
|
|
286
|
+
}, 1100);
|
|
270
287
|
}
|
|
271
|
-
this.editingTimeout = setTimeout(() => {
|
|
272
|
-
this.editing = false;
|
|
273
|
-
// Wait slightly longer than the rich text editor does
|
|
274
|
-
// before sending us a context-edited event
|
|
275
|
-
}, 1100);
|
|
276
288
|
},
|
|
277
289
|
async onPublish(e) {
|
|
278
290
|
if (!this.canPublish) {
|
|
@@ -451,7 +463,11 @@ export default {
|
|
|
451
463
|
}
|
|
452
464
|
},
|
|
453
465
|
async onContentChanged(e) {
|
|
454
|
-
|
|
466
|
+
|
|
467
|
+
if (
|
|
468
|
+
(e.doc && (e.doc._id === this.context._id)) ||
|
|
469
|
+
(e.docIds && e.docIds.includes(this.context._id))
|
|
470
|
+
) {
|
|
455
471
|
if (e.action === 'delete') {
|
|
456
472
|
if (!this.contextStack.length) {
|
|
457
473
|
// With the current page gone, we need to move to safe ground
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const _ = require('lodash');
|
|
2
2
|
const deep = require('deep-get-set');
|
|
3
|
+
const { stripIndent } = require('common-tags');
|
|
3
4
|
|
|
4
5
|
// An area is a series of zero or more widgets, in which users can add
|
|
5
6
|
// and remove widgets and drag them to reorder them. This module implements
|
|
@@ -131,6 +132,14 @@ module.exports = {
|
|
|
131
132
|
const field = self.apos.schema.getFieldById(area._fieldId);
|
|
132
133
|
|
|
133
134
|
const options = field.options;
|
|
135
|
+
if (!options) {
|
|
136
|
+
throw new Error(stripIndent`
|
|
137
|
+
The area field ${field.name} has no options property.
|
|
138
|
+
|
|
139
|
+
You probably forgot to nest the widgets property
|
|
140
|
+
in an options property.
|
|
141
|
+
`);
|
|
142
|
+
}
|
|
134
143
|
_.each(options.widgets, function (options, name) {
|
|
135
144
|
const manager = self.widgetManagers[name];
|
|
136
145
|
if (manager) {
|
|
@@ -46,7 +46,7 @@ module.exports = function(self) {
|
|
|
46
46
|
return { args };
|
|
47
47
|
},
|
|
48
48
|
async run(context, doc, name, _with) {
|
|
49
|
-
const req = context.
|
|
49
|
+
const req = context.ctx.__req;
|
|
50
50
|
let area;
|
|
51
51
|
if ((!doc) || ((typeof doc) !== 'object')) {
|
|
52
52
|
throw usage('You must pass an existing doc or widget as the first argument.');
|
|
@@ -40,7 +40,7 @@ module.exports = function(self) {
|
|
|
40
40
|
return { args };
|
|
41
41
|
},
|
|
42
42
|
async run(context, item, options, _with) {
|
|
43
|
-
const req = context.
|
|
43
|
+
const req = context.ctx.__req;
|
|
44
44
|
if (!item) {
|
|
45
45
|
self.apos.util.warn('a null widget was encountered.');
|
|
46
46
|
return '';
|
|
@@ -384,9 +384,9 @@ export default {
|
|
|
384
384
|
}
|
|
385
385
|
if (!this.focused) {
|
|
386
386
|
this.state.labels.show = false;
|
|
387
|
+
this.state.add.top.show = false;
|
|
388
|
+
this.state.add.bottom.show = false;
|
|
387
389
|
}
|
|
388
|
-
this.state.add.top.show = false;
|
|
389
|
-
this.state.add.bottom.show = false;
|
|
390
390
|
},
|
|
391
391
|
|
|
392
392
|
focus(e) {
|
|
@@ -396,8 +396,8 @@ export default {
|
|
|
396
396
|
this.focused = true;
|
|
397
397
|
this.state.container.focus = true;
|
|
398
398
|
this.state.controls.show = true;
|
|
399
|
-
this.state.add.top.show =
|
|
400
|
-
this.state.add.bottom.show =
|
|
399
|
+
this.state.add.top.show = true;
|
|
400
|
+
this.state.add.bottom.show = true;
|
|
401
401
|
this.state.labels.show = true;
|
|
402
402
|
document.addEventListener('click', this.unfocus);
|
|
403
403
|
},
|
|
@@ -615,8 +615,8 @@ export default {
|
|
|
615
615
|
}
|
|
616
616
|
|
|
617
617
|
.apos-area-widget-wrapper--foreign .apos-area-widget-inner .apos-area-widget__breadcrumbs {
|
|
618
|
-
background-color: var(--a-
|
|
619
|
-
& ::v-deep .apos-
|
|
618
|
+
background-color: var(--a-background-inverted);
|
|
619
|
+
& ::v-deep .apos-button__content {
|
|
620
620
|
color: var(--a-text-inverted);
|
|
621
621
|
}
|
|
622
622
|
}
|
|
@@ -73,6 +73,12 @@ module.exports = {
|
|
|
73
73
|
await fs.remove(buildDir);
|
|
74
74
|
await fs.mkdirp(buildDir);
|
|
75
75
|
|
|
76
|
+
// Static asset files in `public` subdirs of each module are copied
|
|
77
|
+
// to the same relative path `/public/apos-frontend/namespace/modules/modulename`.
|
|
78
|
+
// Inherited files are also copied, with the deepest subclass overriding in the
|
|
79
|
+
// event of a conflict
|
|
80
|
+
await moduleOverrides(`${bundleDir}/modules`, 'public');
|
|
81
|
+
|
|
76
82
|
for (const [ name, options ] of Object.entries(self.builds)) {
|
|
77
83
|
// If the option is not present always rebuild everything
|
|
78
84
|
let rebuild = argv && !argv['check-apos-build'];
|
|
@@ -122,12 +128,17 @@ module.exports = {
|
|
|
122
128
|
// just `public` and `apos`) by examining those specified as
|
|
123
129
|
// targets for the various builds
|
|
124
130
|
const scenes = [ ...new Set(Object.values(self.builds).map(options => options.scenes).flat()) ];
|
|
125
|
-
let
|
|
131
|
+
let deployFiles = [];
|
|
126
132
|
for (const scene of scenes) {
|
|
127
|
-
|
|
133
|
+
deployFiles = [ ...deployFiles, ...merge(scene) ];
|
|
128
134
|
}
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
// enumerate public assets and include them in deployment if appropriate
|
|
136
|
+
const publicAssets = glob.sync('modules/**/*', {
|
|
137
|
+
cwd: bundleDir,
|
|
138
|
+
mark: true
|
|
139
|
+
}).filter(match => !match.endsWith('/'));
|
|
140
|
+
deployFiles = [ ...deployFiles, ...publicAssets ];
|
|
141
|
+
await deploy(deployFiles);
|
|
131
142
|
|
|
132
143
|
if (process.env.APOS_BUNDLE_ANALYZER) {
|
|
133
144
|
return new Promise((resolve, reject) => {
|
|
@@ -162,7 +173,7 @@ module.exports = {
|
|
|
162
173
|
for (const name of names) {
|
|
163
174
|
const moduleDir = `${modulesDir}/${name}`;
|
|
164
175
|
for (const dir of directories[name]) {
|
|
165
|
-
const srcDir = `${dir}
|
|
176
|
+
const srcDir = `${dir}/${source}`;
|
|
166
177
|
if (fs.existsSync(srcDir)) {
|
|
167
178
|
await fs.copy(srcDir, moduleDir);
|
|
168
179
|
}
|
|
@@ -176,7 +187,7 @@ module.exports = {
|
|
|
176
187
|
}));
|
|
177
188
|
const modulesDir = `${buildDir}/${name}/modules`;
|
|
178
189
|
const source = options.source || name;
|
|
179
|
-
await moduleOverrides(modulesDir, source);
|
|
190
|
+
await moduleOverrides(modulesDir, `ui/${source}`);
|
|
180
191
|
|
|
181
192
|
let iconImports, componentImports, tiptapExtensionImports, appImports, indexJsImports, indexSassImports;
|
|
182
193
|
if (options.apos) {
|
|
@@ -233,7 +244,8 @@ module.exports = {
|
|
|
233
244
|
// Remove previous build artifacts, as some pipelines won't build all artifacts
|
|
234
245
|
// if there is no input, and we don't want stale output in the bundle
|
|
235
246
|
fs.removeSync(`${bundleDir}/${outputFilename}`);
|
|
236
|
-
|
|
247
|
+
const cssPath = `${bundleDir}/${outputFilename}`.replace(/\.js$/, '.css');
|
|
248
|
+
fs.removeSync(cssPath);
|
|
237
249
|
await Promise.promisify(webpackModule)(require(`./lib/webpack/${name}/webpack.config`)(
|
|
238
250
|
{
|
|
239
251
|
importFile,
|
|
@@ -243,6 +255,11 @@ module.exports = {
|
|
|
243
255
|
},
|
|
244
256
|
self.apos
|
|
245
257
|
));
|
|
258
|
+
if (fs.existsSync(cssPath)) {
|
|
259
|
+
fs.writeFileSync(cssPath, self.filterCss(fs.readFileSync(cssPath, 'utf8'), {
|
|
260
|
+
modulesPrefix: `${self.getAssetBaseUrl()}/modules`
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
246
263
|
if (options.apos) {
|
|
247
264
|
const now = Date.now().toString();
|
|
248
265
|
fs.writeFileSync(`${bundleDir}/${name}-build-timestamp.txt`, now);
|
|
@@ -269,7 +286,9 @@ module.exports = {
|
|
|
269
286
|
const publicImports = getImports(name, '*.css', { });
|
|
270
287
|
fs.writeFileSync(`${bundleDir}/${name}-build.css`,
|
|
271
288
|
publicImports.paths.map(path => {
|
|
272
|
-
return fs.readFileSync(path, 'utf8')
|
|
289
|
+
return self.filterCss(fs.readFileSync(path, 'utf8'), {
|
|
290
|
+
modulesPrefix: `${self.getAssetBaseUrl()}/modules`
|
|
291
|
+
});
|
|
273
292
|
}).join('\n')
|
|
274
293
|
);
|
|
275
294
|
}
|
|
@@ -358,7 +377,19 @@ module.exports = {
|
|
|
358
377
|
return [ jsModules, jsNoModules, css ];
|
|
359
378
|
}
|
|
360
379
|
|
|
361
|
-
|
|
380
|
+
// If NODE_ENV is production, this function will copy the given
|
|
381
|
+
// array of asset files from `${bundleDir}/${file}` to
|
|
382
|
+
// the same relative location in the appropriate release subdirectory in
|
|
383
|
+
// `/public/apos-frontend/releases`, or in `/apos-frontend/releases` in
|
|
384
|
+
// uploadfs if `APOS_UPLOADFS_ASSETS` is present.
|
|
385
|
+
//
|
|
386
|
+
// If NODE_ENV is not production this function does nothing and
|
|
387
|
+
// the assets are served directly from `/public/apos-frontend/${file}`.
|
|
388
|
+
//
|
|
389
|
+
// The namespace (e.g. default) should be part of each filename given.
|
|
390
|
+
// A leading slash should NOT be passed.
|
|
391
|
+
|
|
392
|
+
async function deploy(files) {
|
|
362
393
|
if (process.env.NODE_ENV !== 'production') {
|
|
363
394
|
return;
|
|
364
395
|
}
|
|
@@ -373,17 +404,23 @@ module.exports = {
|
|
|
373
404
|
} else {
|
|
374
405
|
// The right choice with Docker if uploadfs is just the local filesystem
|
|
375
406
|
// mapped to a volume (a Docker build step can't access that)
|
|
376
|
-
copyIn =
|
|
407
|
+
copyIn = fsCopyIn;
|
|
377
408
|
releaseDir = `${self.apos.rootDir}/public/apos-frontend/releases/${releaseId}/${namespace}`;
|
|
378
409
|
await fs.mkdirp(releaseDir);
|
|
379
410
|
}
|
|
380
|
-
for (const
|
|
381
|
-
const src = `${bundleDir}/${
|
|
382
|
-
await copyIn(src, `${releaseDir}/${
|
|
411
|
+
for (const file of files) {
|
|
412
|
+
const src = `${bundleDir}/${file}`;
|
|
413
|
+
await copyIn(src, `${releaseDir}/${file}`);
|
|
383
414
|
await fs.remove(src);
|
|
384
415
|
}
|
|
385
416
|
}
|
|
386
417
|
|
|
418
|
+
async function fsCopyIn(from, to) {
|
|
419
|
+
const base = path.dirname(to);
|
|
420
|
+
await fs.mkdirp(base);
|
|
421
|
+
return fs.copyFile(from, to);
|
|
422
|
+
}
|
|
423
|
+
|
|
387
424
|
function getImports(folder, pattern, options) {
|
|
388
425
|
let components = [];
|
|
389
426
|
const seen = {};
|
|
@@ -580,7 +617,14 @@ module.exports = {
|
|
|
580
617
|
if (data) {
|
|
581
618
|
document.body.removeAttribute('data-apos');
|
|
582
619
|
}
|
|
583
|
-
|
|
620
|
+
if (window.apos.modules) {
|
|
621
|
+
for (const module of Object.values(window.apos.modules)) {
|
|
622
|
+
if (module.alias) {
|
|
623
|
+
window.apos[module.alias] = module;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
})();
|
|
584
628
|
`;
|
|
585
629
|
self.builds = {
|
|
586
630
|
src: {
|
|
@@ -632,13 +676,6 @@ module.exports = {
|
|
|
632
676
|
prologue: stripIndent`
|
|
633
677
|
import 'Modules/@apostrophecms/ui/scss/global/import-all.scss';
|
|
634
678
|
import Vue from 'Modules/@apostrophecms/ui/lib/vue';
|
|
635
|
-
if (window.apos.modules) {
|
|
636
|
-
for (const module of Object.values(window.apos.modules)) {
|
|
637
|
-
if (module.alias) {
|
|
638
|
-
window.apos[module.alias] = module;
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
679
|
window.apos.bus = new Vue();
|
|
643
680
|
`,
|
|
644
681
|
// Load only in browsers that support ES6 modules
|
|
@@ -650,6 +687,30 @@ module.exports = {
|
|
|
650
687
|
if (!self.options.es5) {
|
|
651
688
|
delete self.builds['src-es5'];
|
|
652
689
|
}
|
|
690
|
+
},
|
|
691
|
+
// Filter the given css performing any necessary transformations,
|
|
692
|
+
// such as support for the /modules path regardless of where
|
|
693
|
+
// static assets are actually deployed
|
|
694
|
+
filterCss(css, { modulesPrefix }) {
|
|
695
|
+
return self.filterCssUrls(css, url => {
|
|
696
|
+
if (url.startsWith('/modules')) {
|
|
697
|
+
return url.replace('/modules', modulesPrefix);
|
|
698
|
+
}
|
|
699
|
+
return url;
|
|
700
|
+
});
|
|
701
|
+
},
|
|
702
|
+
// Run all URLs in CSS through a filter function
|
|
703
|
+
filterCssUrls(css, filter) {
|
|
704
|
+
css = css.replace(/url\(([^'"].*?)\)/g, function(s, url) {
|
|
705
|
+
return 'url(' + filter(url) + ')';
|
|
706
|
+
});
|
|
707
|
+
css = css.replace(/url\("([^"]+?)"\)/g, function(s, url) {
|
|
708
|
+
return 'url("' + filter(url) + '")';
|
|
709
|
+
});
|
|
710
|
+
css = css.replace(/url\('([^']+?)'\)/g, function(s, url) {
|
|
711
|
+
return 'url(\'' + filter(url) + '\')';
|
|
712
|
+
});
|
|
713
|
+
return css;
|
|
653
714
|
}
|
|
654
715
|
};
|
|
655
716
|
},
|
|
@@ -667,6 +728,9 @@ module.exports = {
|
|
|
667
728
|
}
|
|
668
729
|
const script = fs.readFileSync(path.join(__dirname, '/lib/refresh-on-restart.js'), 'utf8');
|
|
669
730
|
return self.apos.template.safe(`<script data-apos-refresh-on-restart="${self.action}/restart-id">\n${script}</script>`);
|
|
731
|
+
},
|
|
732
|
+
url(path) {
|
|
733
|
+
return `${self.getAssetBaseUrl()}${path}`;
|
|
670
734
|
}
|
|
671
735
|
};
|
|
672
736
|
},
|
|
@@ -56,9 +56,11 @@ module.exports = {
|
|
|
56
56
|
'play-box-icon': 'PlayBox',
|
|
57
57
|
'instagram-icon': 'Instagram',
|
|
58
58
|
'label-icon': 'Label',
|
|
59
|
+
'lightbulb-on-icon': 'LightbulbOn',
|
|
59
60
|
'link-icon': 'Link',
|
|
60
61
|
'list-status-icon': 'ListStatus',
|
|
61
62
|
'lock-icon': 'Lock',
|
|
63
|
+
'map-marker-icon': 'MapMarker',
|
|
62
64
|
'magnify-icon': 'Magnify',
|
|
63
65
|
'menu-down-icon': 'MenuDown',
|
|
64
66
|
'minus-icon': 'Minus',
|
|
@@ -9,8 +9,11 @@ module.exports = (options, apos) => {
|
|
|
9
9
|
use: [
|
|
10
10
|
// Instead of style-loader, to avoid FOUC
|
|
11
11
|
MiniCssExtractPlugin.loader,
|
|
12
|
-
// Parses CSS imports
|
|
13
|
-
|
|
12
|
+
// Parses CSS imports and make css-loader ignore urls. Urls will still be handled by webpack
|
|
13
|
+
{
|
|
14
|
+
loader: 'css-loader',
|
|
15
|
+
options: { url: false }
|
|
16
|
+
},
|
|
14
17
|
// Provides autoprefixing
|
|
15
18
|
{
|
|
16
19
|
loader: 'postcss-loader',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// This module establishes `apos.db`, the
|
|
1
|
+
// This module establishes `apos.db`, the MongoDB database object.
|
|
2
2
|
//
|
|
3
3
|
// ## Options
|
|
4
4
|
//
|
|
@@ -20,8 +20,7 @@
|
|
|
20
20
|
//
|
|
21
21
|
// ### `client`
|
|
22
22
|
//
|
|
23
|
-
// An existing MongoDB connection (MongoClient) object. If present,
|
|
24
|
-
// connection instance is created that reuses the same sockets,
|
|
23
|
+
// An existing MongoDB connection (MongoClient) object. If present, it is used
|
|
25
24
|
// and `uri`, `host`, `connect`, etc. are ignored.
|
|
26
25
|
//
|
|
27
26
|
// ### `versionCheck`
|
|
@@ -40,7 +39,7 @@
|
|
|
40
39
|
// allow other modules to drop related non-MongoDB resources at the
|
|
41
40
|
// same time, if desired.
|
|
42
41
|
//
|
|
43
|
-
// Note that `apos.db` is the
|
|
42
|
+
// Note that `apos.db` is the MongoDB database object, not this module.
|
|
44
43
|
// You shouldn't need to talk to this module after startup, but you can
|
|
45
44
|
// access it as `apos.modules['@apostrophecms/db']` if you wish. You can
|
|
46
45
|
// also access `apos.dbClient` if you need the MongoClient object.
|
|
@@ -81,8 +80,8 @@ module.exports = {
|
|
|
81
80
|
},
|
|
82
81
|
methods(self) {
|
|
83
82
|
return {
|
|
84
|
-
// Open the database connection. Always
|
|
85
|
-
// sensible defaults.
|
|
83
|
+
// Open the database connection. Always uses MongoClient with its
|
|
84
|
+
// sensible defaults. Builds a URI if necessary, so we can call it
|
|
86
85
|
// in a consistent way.
|
|
87
86
|
//
|
|
88
87
|
// One default we override: if the connection is lost, we keep
|
|
@@ -38,11 +38,19 @@ module.exports = {
|
|
|
38
38
|
restApiRoutes(self) {
|
|
39
39
|
return {
|
|
40
40
|
// GET /api/v1/@apostrophecms/doc/_id supports only the universal query
|
|
41
|
-
// features, but works for any document type
|
|
42
|
-
// logic for redirects to foreign documents
|
|
41
|
+
// features, but works for any document type. Simplifies browser-side
|
|
42
|
+
// logic for redirects to foreign documents. The frontend only has to
|
|
43
43
|
// know the doc _id.
|
|
44
|
+
//
|
|
45
|
+
// Since this API is solely for editing purposes you will receive
|
|
46
|
+
// a 404 if you request a document you cannot edit.
|
|
44
47
|
async getOne(req, _id) {
|
|
45
|
-
|
|
48
|
+
_id = self.apos.i18n.inferIdLocaleAndMode(req, _id);
|
|
49
|
+
const doc = await self.find(req, { _id }).permission('edit').toObject();
|
|
50
|
+
if (!doc) {
|
|
51
|
+
throw self.apos.error('notfound');
|
|
52
|
+
}
|
|
53
|
+
return doc;
|
|
46
54
|
}
|
|
47
55
|
};
|
|
48
56
|
},
|
|
@@ -431,6 +439,7 @@ module.exports = {
|
|
|
431
439
|
await self.insertBody(req, doc, options);
|
|
432
440
|
await m.emit('afterInsert', req, doc, options);
|
|
433
441
|
await m.emit('afterSave', req, doc, options);
|
|
442
|
+
// TODO: Remove `afterLoad` in next major version. Deprecated.
|
|
434
443
|
await m.emit('afterLoad', req, [ doc ]);
|
|
435
444
|
return doc;
|
|
436
445
|
},
|
|
@@ -466,6 +475,7 @@ module.exports = {
|
|
|
466
475
|
await self.updateBody(req, doc, options);
|
|
467
476
|
await m.emit('afterUpdate', req, doc, options);
|
|
468
477
|
await m.emit('afterSave', req, doc, options);
|
|
478
|
+
// TODO: Remove `afterLoad` in next major version. Deprecated.
|
|
469
479
|
await m.emit('afterLoad', req, [ doc ]);
|
|
470
480
|
return doc;
|
|
471
481
|
},
|
|
@@ -389,7 +389,7 @@ module.exports = {
|
|
|
389
389
|
self.schema = self.apos.schema.compose({
|
|
390
390
|
addFields: self.apos.schema.fieldsToArray(`Module ${self.__meta.name}`, self.fields),
|
|
391
391
|
arrangeFields: self.apos.schema.groupsToArray(self.fieldsGroups)
|
|
392
|
-
});
|
|
392
|
+
}, self);
|
|
393
393
|
if (self.options.slugPrefix) {
|
|
394
394
|
if (self.options.slugPrefix === 'deduplicate-') {
|
|
395
395
|
const req = self.apos.task.getReq();
|
|
@@ -738,7 +738,7 @@ module.exports = {
|
|
|
738
738
|
},
|
|
739
739
|
// Localize (export) the given draft to another locale, creating the document in the
|
|
740
740
|
// other locale if necessary. By default, if the document already exists in the
|
|
741
|
-
// other locale, it is not
|
|
741
|
+
// other locale, it is not overwritten. Use the `update: true` option to change that.
|
|
742
742
|
// You can localize starting from either draft or published content. Either way what
|
|
743
743
|
// gets created or updated in the other locale is a draft.
|
|
744
744
|
async localize(req, draft, toLocale, options = { update: false }) {
|
|
@@ -1217,6 +1217,21 @@ module.exports = {
|
|
|
1217
1217
|
// cursors.
|
|
1218
1218
|
|
|
1219
1219
|
project: {
|
|
1220
|
+
launder (p) {
|
|
1221
|
+
// check that project is an object
|
|
1222
|
+
if (!p || typeof p !== 'object' || Array.isArray(p)) {
|
|
1223
|
+
return {};
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
const projection = Object.entries(p).reduce((acc, [ key, val ]) => {
|
|
1227
|
+
return {
|
|
1228
|
+
...acc,
|
|
1229
|
+
[key]: self.apos.launder.boolean(val)
|
|
1230
|
+
};
|
|
1231
|
+
}, {});
|
|
1232
|
+
|
|
1233
|
+
return projection;
|
|
1234
|
+
},
|
|
1220
1235
|
finalize() {
|
|
1221
1236
|
let projection = query.get('project') || {};
|
|
1222
1237
|
// Keys beginning with `_` are computed values
|
|
@@ -1442,9 +1457,14 @@ module.exports = {
|
|
|
1442
1457
|
attachments: {
|
|
1443
1458
|
def: false,
|
|
1444
1459
|
after(results) {
|
|
1445
|
-
|
|
1446
|
-
|
|
1460
|
+
const attachments = query.get('attachments');
|
|
1461
|
+
|
|
1462
|
+
if (attachments) {
|
|
1463
|
+
self.apos.attachment.all(results, { annotate: true });
|
|
1447
1464
|
}
|
|
1465
|
+
},
|
|
1466
|
+
launder(b) {
|
|
1467
|
+
return self.apos.launder.boolean(b);
|
|
1448
1468
|
}
|
|
1449
1469
|
},
|
|
1450
1470
|
|
|
@@ -286,9 +286,21 @@ export default {
|
|
|
286
286
|
apos.bus.$off('content-changed', this.onContentChanged);
|
|
287
287
|
},
|
|
288
288
|
methods: {
|
|
289
|
-
onContentChanged(e) {
|
|
289
|
+
async onContentChanged(e) {
|
|
290
290
|
if (e.doc && (e.doc._id === this.context._id)) {
|
|
291
291
|
this.context = e.doc;
|
|
292
|
+
} else if (e.docIds && e.docIds.includes(this.context._id)) {
|
|
293
|
+
try {
|
|
294
|
+
this.context = await apos.http.get(`${this.moduleOptions.action}/${this.context._id}`, {
|
|
295
|
+
busy: true
|
|
296
|
+
});
|
|
297
|
+
} catch (error) {
|
|
298
|
+
// If not found it is likely that there was an archiving or restoring
|
|
299
|
+
// batch operation.
|
|
300
|
+
if (error.name !== 'notfound') {
|
|
301
|
+
console.error(error);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
292
304
|
}
|
|
293
305
|
},
|
|
294
306
|
menuHandler(action) {
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
:doc-id="docId"
|
|
69
69
|
:value="docFields"
|
|
70
70
|
@input="updateDocFields"
|
|
71
|
+
@validate="triggerValidate"
|
|
71
72
|
:server-errors="serverErrors"
|
|
72
73
|
:ref="tab.name"
|
|
73
74
|
/>
|
|
@@ -90,6 +91,7 @@
|
|
|
90
91
|
:doc-id="docId"
|
|
91
92
|
:value="docFields"
|
|
92
93
|
@input="updateDocFields"
|
|
94
|
+
@validate="triggerValidate"
|
|
93
95
|
:modifiers="['small', 'inverted']"
|
|
94
96
|
ref="utilitySchema"
|
|
95
97
|
:server-errors="serverErrors"
|
|
@@ -444,6 +446,7 @@ export default {
|
|
|
444
446
|
if (!this.errorCount) {
|
|
445
447
|
this[action]();
|
|
446
448
|
} else {
|
|
449
|
+
this.triggerValidation = false;
|
|
447
450
|
await apos.notify('apostrophe:resolveErrorsBeforeSaving', {
|
|
448
451
|
type: 'warning',
|
|
449
452
|
icon: 'alert-circle-icon',
|