apostrophe 3.5.0 → 3.8.1
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 +96 -3
- package/README.md +1 -1
- package/index.js +108 -3
- package/lib/moog-require.js +23 -0
- package/lib/moog.js +1 -0
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +30 -14
- 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/components/AposAreaWidget.vue +2 -2
- package/modules/@apostrophecms/asset/index.js +77 -13
- package/modules/@apostrophecms/attachment/index.js +1 -0
- package/modules/@apostrophecms/db/index.js +5 -6
- package/modules/@apostrophecms/doc/index.js +2 -0
- package/modules/@apostrophecms/doc-type/index.js +39 -16
- 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 +19 -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/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 +2 -1
- 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 +1 -16
- package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +5 -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 +8 -6
- 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/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 +35 -6
- package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Classes.js +1 -3
- 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/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/ui/apos/components/AposButton.vue +5 -2
- 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/lib/i18next.js +16 -2
- package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +4 -3
- 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 +3 -3
- 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/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/package.json +5 -4
- package/test/pieces.js +34 -0
- package/test/reverse-relationship.js +170 -0
- package/test/templates.js +7 -1
- package/test-lib/test.js +23 -12
- package/test-lib/util.js +33 -0
|
@@ -219,8 +219,8 @@ module.exports = {
|
|
|
219
219
|
};
|
|
220
220
|
addCloneMethod(req);
|
|
221
221
|
req.res.__ = req.__;
|
|
222
|
-
const {
|
|
223
|
-
Object.assign(req,
|
|
222
|
+
const { _role, ...properties } = options || {};
|
|
223
|
+
Object.assign(req, properties);
|
|
224
224
|
self.apos.i18n.setPrefixUrls(req);
|
|
225
225
|
return req;
|
|
226
226
|
|
|
@@ -61,11 +61,14 @@ module.exports = {
|
|
|
61
61
|
prefix: self.apos.prefix
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
+
self.envs = {};
|
|
65
|
+
|
|
64
66
|
self.filters = {};
|
|
65
67
|
|
|
66
68
|
self.nunjucks = self.options.language || require('nunjucks');
|
|
67
69
|
|
|
68
70
|
self.insertions = {};
|
|
71
|
+
|
|
69
72
|
},
|
|
70
73
|
handlers(self) {
|
|
71
74
|
return {
|
|
@@ -248,6 +251,34 @@ module.exports = {
|
|
|
248
251
|
|
|
249
252
|
let result;
|
|
250
253
|
|
|
254
|
+
const args = self.getRenderArgs(req, data, module);
|
|
255
|
+
|
|
256
|
+
const env = self.getEnv(req, module);
|
|
257
|
+
|
|
258
|
+
if (type === 'file') {
|
|
259
|
+
let finalName = s;
|
|
260
|
+
if (!finalName.match(/\.\w+$/)) {
|
|
261
|
+
finalName += '.html';
|
|
262
|
+
}
|
|
263
|
+
result = await Promise.promisify(function (finalName, args, callback) {
|
|
264
|
+
return env.getTemplate(finalName).render(args, callback);
|
|
265
|
+
})(finalName, args);
|
|
266
|
+
} else if (type === 'string') {
|
|
267
|
+
result = await Promise.promisify(function (s, args, callback) {
|
|
268
|
+
return env.renderString(s, args, callback);
|
|
269
|
+
})(s, args);
|
|
270
|
+
} else {
|
|
271
|
+
throw new Error('renderBody does not support the type ' + type);
|
|
272
|
+
}
|
|
273
|
+
return result;
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
// Implementation detail of `renderBody` responsible for
|
|
277
|
+
// creating the input object passed to Nunjucks for rendering,
|
|
278
|
+
// with `data` merged into the `.data` property,
|
|
279
|
+
// `apos` available separately, `__req` available separately, etc.
|
|
280
|
+
|
|
281
|
+
getRenderArgs(req, data, module) {
|
|
251
282
|
const merged = {};
|
|
252
283
|
|
|
253
284
|
if (data) {
|
|
@@ -278,8 +309,6 @@ module.exports = {
|
|
|
278
309
|
|
|
279
310
|
args.data.locale = args.data.locale || req.locale;
|
|
280
311
|
|
|
281
|
-
const env = self.getEnv(req, module);
|
|
282
|
-
|
|
283
312
|
args.apos = self.templateApos;
|
|
284
313
|
args.__t = req.t;
|
|
285
314
|
args.__ = key => {
|
|
@@ -289,39 +318,39 @@ module.exports = {
|
|
|
289
318
|
`);
|
|
290
319
|
return key;
|
|
291
320
|
};
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
321
|
+
args.__req = req;
|
|
322
|
+
args.getOption = (key, def) => {
|
|
323
|
+
const colonAt = key.indexOf(':');
|
|
324
|
+
let optionModule = self.apos.modules[module.__meta.name];
|
|
325
|
+
if (colonAt !== -1) {
|
|
326
|
+
const name = key.substring(0, colonAt);
|
|
327
|
+
key = key.substring(colonAt + 1);
|
|
328
|
+
optionModule = self.apos.modules[name];
|
|
296
329
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
} else if (type === 'string') {
|
|
301
|
-
result = await Promise.promisify(function (s, args, callback) {
|
|
302
|
-
return env.renderString(s, args, callback);
|
|
303
|
-
})(s, args);
|
|
304
|
-
} else {
|
|
305
|
-
throw new Error('renderBody does not support the type ' + type);
|
|
306
|
-
}
|
|
307
|
-
return result;
|
|
330
|
+
return optionModule.getOption(req, key, def);
|
|
331
|
+
};
|
|
332
|
+
return args;
|
|
308
333
|
},
|
|
309
334
|
|
|
310
335
|
// Fetch a nunjucks environment in which `include`, `extends`, etc. search
|
|
311
336
|
// the views directories of the specified module and its ancestors.
|
|
312
337
|
// Typically you will call `self.render` or `self.partial` on your module
|
|
313
338
|
// object rather than calling this directly.
|
|
339
|
+
//
|
|
340
|
+
// `req` is effectively here for bc purposes only. This method
|
|
341
|
+
// does NOT always pass `req` to `newEnv` for every new release, as
|
|
342
|
+
// `req` is separately supplied to each request to fix a memory leak
|
|
343
|
+
// that occurs when Nunjucks environments are created for every request.
|
|
314
344
|
|
|
315
345
|
getEnv(req, module) {
|
|
316
346
|
const name = module.__meta.name;
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
347
|
+
if (!_.has(self.envs, name)) {
|
|
348
|
+
// Pass the original req for bc purposes only,
|
|
349
|
+
// note that due to the reuse of envs there is
|
|
350
|
+
// no guarantee newEnv will be called for every req
|
|
351
|
+
self.envs[name] = self.newEnv(req, name, self.getViewFolders(module));
|
|
322
352
|
}
|
|
323
|
-
|
|
324
|
-
return req.envs[name];
|
|
353
|
+
return self.envs[name];
|
|
325
354
|
},
|
|
326
355
|
|
|
327
356
|
getViewFolders(module) {
|
|
@@ -343,7 +372,14 @@ module.exports = {
|
|
|
343
372
|
// specified directories are searched for includes,
|
|
344
373
|
// etc. Don't call this directly, use:
|
|
345
374
|
//
|
|
346
|
-
// apos.template.getEnv(module)
|
|
375
|
+
// apos.template.getEnv(req, module)
|
|
376
|
+
//
|
|
377
|
+
// `req` is effectively here for bc purposes only. Apostrophe
|
|
378
|
+
// does NOT always pass `req` to `newEnv` for every new release, as
|
|
379
|
+
// `req` is separately supplied to each request to fix a memory leak
|
|
380
|
+
// that occurs when Nunjucks environments are created for every request.
|
|
381
|
+
// If you must access `req` in a custom Nunjucks tag use
|
|
382
|
+
// `context.ctx.__req`, NOT `env.opts.req` which is no longer provided.
|
|
347
383
|
|
|
348
384
|
newEnv(req, moduleName, dirs) {
|
|
349
385
|
|
|
@@ -351,22 +387,11 @@ module.exports = {
|
|
|
351
387
|
|
|
352
388
|
const env = new self.nunjucks.Environment(loader, {
|
|
353
389
|
autoescape: true,
|
|
354
|
-
req,
|
|
355
390
|
module: self.apos.modules[moduleName]
|
|
356
391
|
});
|
|
357
392
|
|
|
358
393
|
env.addGlobal('apos', self.templateApos);
|
|
359
394
|
env.addGlobal('module', self.templateApos.modules[moduleName]);
|
|
360
|
-
env.addGlobal('getOption', function(key, def) {
|
|
361
|
-
const colonAt = key.indexOf(':');
|
|
362
|
-
let optionModule = self.apos.modules[moduleName];
|
|
363
|
-
if (colonAt !== -1) {
|
|
364
|
-
const name = key.substring(0, colonAt);
|
|
365
|
-
key = key.substring(colonAt + 1);
|
|
366
|
-
optionModule = self.apos.modules[name];
|
|
367
|
-
}
|
|
368
|
-
return optionModule.getOption(req, key, def);
|
|
369
|
-
});
|
|
370
395
|
|
|
371
396
|
self.addStandardFilters(env);
|
|
372
397
|
|
|
@@ -627,6 +652,8 @@ module.exports = {
|
|
|
627
652
|
locale: req.locale,
|
|
628
653
|
csrfCookieName: self.apos.csrfCookieName,
|
|
629
654
|
tabId: self.apos.util.generateId(),
|
|
655
|
+
uploadsUrl: self.apos.attachment.uploadfs.getUrl(),
|
|
656
|
+
assetBaseUrl: self.apos.asset.getAssetBaseUrl(),
|
|
630
657
|
scene
|
|
631
658
|
};
|
|
632
659
|
if (req.user) {
|
|
@@ -98,9 +98,8 @@ module.exports = (self) => {
|
|
|
98
98
|
const source = info.body();
|
|
99
99
|
const input = createRenderInput(info);
|
|
100
100
|
|
|
101
|
-
const req = context.
|
|
101
|
+
const req = context.ctx.__req;
|
|
102
102
|
const env = self.getEnv(req, context.env.opts.module);
|
|
103
|
-
input.apos = self.templateApos;
|
|
104
103
|
|
|
105
104
|
// attach the render caller as a function
|
|
106
105
|
// it's just a string, but we keep
|
|
@@ -108,6 +107,11 @@ module.exports = (self) => {
|
|
|
108
107
|
input.rendercaller = rendercaller;
|
|
109
108
|
|
|
110
109
|
const result = await require('util').promisify((s, args, callback) => {
|
|
110
|
+
args = {
|
|
111
|
+
...self.getRenderArgs(req, {}, context.env.opts.module),
|
|
112
|
+
// Parameters to fragments are top level, they are not in `data`
|
|
113
|
+
...args
|
|
114
|
+
};
|
|
111
115
|
return env.renderString(s, args, callback);
|
|
112
116
|
})(source, input);
|
|
113
117
|
return result;
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
:type="buttonType"
|
|
16
16
|
:role="role"
|
|
17
17
|
:id="attrs.id ? attrs.id : id"
|
|
18
|
+
:style="{color: textColor}"
|
|
18
19
|
v-bind="attrs"
|
|
19
20
|
>
|
|
20
21
|
<transition name="fade">
|
|
@@ -71,6 +72,10 @@ export default {
|
|
|
71
72
|
type: String,
|
|
72
73
|
default: null
|
|
73
74
|
},
|
|
75
|
+
textColor: {
|
|
76
|
+
type: String,
|
|
77
|
+
default: null
|
|
78
|
+
},
|
|
74
79
|
href: {
|
|
75
80
|
type: [ String, Boolean ],
|
|
76
81
|
default: false
|
|
@@ -402,8 +407,6 @@ export default {
|
|
|
402
407
|
box-sizing: border-box;
|
|
403
408
|
display: block;
|
|
404
409
|
width: 100%;
|
|
405
|
-
height: 47px;
|
|
406
|
-
max-width: 400px;
|
|
407
410
|
}
|
|
408
411
|
|
|
409
412
|
.apos-button--icon-right {
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<label
|
|
4
|
+
class="apos-input-wrapper apos-file-dropzone"
|
|
5
|
+
:class="{
|
|
6
|
+
'apos-file-dropzone--dragover': dragging,
|
|
7
|
+
'apos-is-disabled': disabled || fileOrAttachment
|
|
8
|
+
}"
|
|
9
|
+
@drop.prevent="uploadFile"
|
|
10
|
+
@dragover="dragHandler"
|
|
11
|
+
@dragleave="dragging = false"
|
|
12
|
+
>
|
|
13
|
+
<p class="apos-file-instructions">
|
|
14
|
+
<template v-if="dragging">
|
|
15
|
+
<cloud-upload-icon :size="38" />
|
|
16
|
+
</template>
|
|
17
|
+
<AposSpinner v-else-if="uploading" />
|
|
18
|
+
<template v-else>
|
|
19
|
+
<paperclip-icon :size="14" class="apos-file-icon" />
|
|
20
|
+
{{ messages.primary }}
|
|
21
|
+
<span class="apos-file-highlight" v-if="messages.highlighted">
|
|
22
|
+
{{ messages.highlighted }}
|
|
23
|
+
</span>
|
|
24
|
+
</template>
|
|
25
|
+
</p>
|
|
26
|
+
<input
|
|
27
|
+
type="file"
|
|
28
|
+
class="apos-sr-only"
|
|
29
|
+
:disabled="disabled || fileOrAttachment"
|
|
30
|
+
@input="uploadFile"
|
|
31
|
+
:accept="allowedExtensions"
|
|
32
|
+
>
|
|
33
|
+
</label>
|
|
34
|
+
<div v-if="fileOrAttachment" class="apos-file-files">
|
|
35
|
+
<AposSlatList
|
|
36
|
+
:value="[fileOrAttachment]"
|
|
37
|
+
@input="update"
|
|
38
|
+
:disabled="readOnly"
|
|
39
|
+
/>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<script>
|
|
45
|
+
export default {
|
|
46
|
+
props: {
|
|
47
|
+
uploading: {
|
|
48
|
+
type: Boolean,
|
|
49
|
+
default: false
|
|
50
|
+
},
|
|
51
|
+
disabled: {
|
|
52
|
+
type: Boolean,
|
|
53
|
+
default: false
|
|
54
|
+
},
|
|
55
|
+
attachment: {
|
|
56
|
+
type: Object,
|
|
57
|
+
default: null
|
|
58
|
+
},
|
|
59
|
+
allowedExtensions: {
|
|
60
|
+
type: String,
|
|
61
|
+
default: '*'
|
|
62
|
+
},
|
|
63
|
+
readOnly: {
|
|
64
|
+
type: Boolean,
|
|
65
|
+
default: false
|
|
66
|
+
},
|
|
67
|
+
def: {
|
|
68
|
+
type: String,
|
|
69
|
+
default: null
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
emits: [ 'upload-file', 'update' ],
|
|
73
|
+
data () {
|
|
74
|
+
return {
|
|
75
|
+
selectedFile: null,
|
|
76
|
+
dragging: false
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
computed: {
|
|
80
|
+
fileOrAttachment () {
|
|
81
|
+
return this.selectedFile || this.attachment;
|
|
82
|
+
},
|
|
83
|
+
messages () {
|
|
84
|
+
const msgs = {
|
|
85
|
+
primary: 'Drop a file here or',
|
|
86
|
+
highlighted: 'click to open the file explorer'
|
|
87
|
+
};
|
|
88
|
+
if (this.disabled) {
|
|
89
|
+
msgs.primary = 'Field is disabled';
|
|
90
|
+
msgs.highlighted = '';
|
|
91
|
+
}
|
|
92
|
+
if (this.fileOrAttachment) {
|
|
93
|
+
msgs.primary = 'Attachment limit reached';
|
|
94
|
+
msgs.highlighted = '';
|
|
95
|
+
}
|
|
96
|
+
return msgs;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
methods: {
|
|
100
|
+
async uploadFile ({ target, dataTransfer }) {
|
|
101
|
+
this.dragging = false;
|
|
102
|
+
const [ file ] = target.files ? target.files : (dataTransfer.files || []);
|
|
103
|
+
|
|
104
|
+
const extension = file.name.split('.').pop();
|
|
105
|
+
const allowedFile = await this.checkFileGroup(`.${extension}`);
|
|
106
|
+
|
|
107
|
+
if (!allowedFile) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
this.selectedFile = {
|
|
112
|
+
_id: file.name,
|
|
113
|
+
title: file.name,
|
|
114
|
+
extension,
|
|
115
|
+
_url: URL.createObjectURL(file)
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
this.$emit('upload-file', file);
|
|
119
|
+
},
|
|
120
|
+
dragHandler (event) {
|
|
121
|
+
event.preventDefault();
|
|
122
|
+
|
|
123
|
+
if (!this.disabled && !this.dragging) {
|
|
124
|
+
this.dragging = true;
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
update(items) {
|
|
128
|
+
if (this.selectedFile && this.selectedFile._url) {
|
|
129
|
+
URL.revokeObjectURL(this.selectedFile._url);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this.selectedFile = null;
|
|
133
|
+
this.$emit('update', items);
|
|
134
|
+
},
|
|
135
|
+
async checkFileGroup(fileExt) {
|
|
136
|
+
const allowedExt = this.allowedExtensions.split(',');
|
|
137
|
+
const allowed = allowedExt.includes(fileExt);
|
|
138
|
+
|
|
139
|
+
if (!allowed) {
|
|
140
|
+
await apos.notify('apostrophe:fileTypeNotAccepted', {
|
|
141
|
+
type: 'warning',
|
|
142
|
+
icon: 'alert-circle-icon',
|
|
143
|
+
interpolate: {
|
|
144
|
+
extensions: this.allowedExtensions
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return allowed;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
</script>
|
|
154
|
+
<style scoped lang='scss'>
|
|
155
|
+
.apos-file-dropzone {
|
|
156
|
+
@include apos-button-reset();
|
|
157
|
+
@include type-base;
|
|
158
|
+
display: block;
|
|
159
|
+
margin: 10px 0;
|
|
160
|
+
padding: 20px;
|
|
161
|
+
border: 2px dashed var(--a-base-8);
|
|
162
|
+
border-radius: var(--a-border-radius);
|
|
163
|
+
transition: all 0.2s ease;
|
|
164
|
+
&:hover {
|
|
165
|
+
border-color: var(--a-primary);
|
|
166
|
+
background-color: var(--a-base-10);
|
|
167
|
+
}
|
|
168
|
+
&:active,
|
|
169
|
+
&:focus {
|
|
170
|
+
border: 2px solid var(--a-primary);
|
|
171
|
+
}
|
|
172
|
+
&.apos-is-disabled {
|
|
173
|
+
color: var(--a-base-4);
|
|
174
|
+
background-color: var(--a-base-7);
|
|
175
|
+
border-color: var(--a-base-4);
|
|
176
|
+
|
|
177
|
+
&:hover {
|
|
178
|
+
cursor: not-allowed;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.apos-file-dropzone--dragover {
|
|
184
|
+
border: 2px dashed var(--a-primary);
|
|
185
|
+
background-color: var(--a-base-10);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.apos-file-instructions {
|
|
189
|
+
display: flex;
|
|
190
|
+
flex-wrap: wrap;
|
|
191
|
+
align-items: center;
|
|
192
|
+
justify-content: center;
|
|
193
|
+
pointer-events: none;
|
|
194
|
+
// v-html goofiness
|
|
195
|
+
& ::v-deep .apos-file-highlight {
|
|
196
|
+
color: var(--a-primary);
|
|
197
|
+
font-weight: var(--a-weight-bold);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.apos-file-icon {
|
|
202
|
+
transform: rotate(45deg);
|
|
203
|
+
margin-right: 5px;
|
|
204
|
+
}
|
|
205
|
+
</style>
|
|
@@ -10,11 +10,20 @@ export default {
|
|
|
10
10
|
install(Vue, options) {
|
|
11
11
|
const i18n = options.i18n;
|
|
12
12
|
|
|
13
|
+
const fallbackLng = [ i18n.defaultLocale ];
|
|
14
|
+
// In case the default locale also has inadequate admin UI phrases
|
|
15
|
+
if (fallbackLng[0] !== 'en') {
|
|
16
|
+
fallbackLng.push('en');
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
i18next.init({
|
|
14
20
|
lng: i18n.locale,
|
|
15
|
-
fallbackLng
|
|
21
|
+
fallbackLng,
|
|
16
22
|
resources: {},
|
|
17
|
-
debug: i18n.debug
|
|
23
|
+
debug: i18n.debug,
|
|
24
|
+
interpolation: {
|
|
25
|
+
escapeValue: false
|
|
26
|
+
}
|
|
18
27
|
});
|
|
19
28
|
|
|
20
29
|
for (const [ ns, phrases ] of Object.entries(i18n.i18n[i18n.locale])) {
|
|
@@ -25,6 +34,11 @@ export default {
|
|
|
25
34
|
i18next.addResourceBundle(i18n.defaultLocale, ns, phrases, true, true);
|
|
26
35
|
}
|
|
27
36
|
}
|
|
37
|
+
if ((i18n.locale !== 'en') && (i18n.defaultLocale !== 'en')) {
|
|
38
|
+
for (const [ ns, phrases ] of Object.entries(i18n.i18n.en)) {
|
|
39
|
+
i18next.addResourceBundle('en', ns, phrases, true, true);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
28
42
|
|
|
29
43
|
// Like standard i18next $t, but also with support
|
|
30
44
|
// for just one object argument with at least a `key`
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
.apos-table__header {
|
|
7
7
|
margin-bottom: $spacing-base;
|
|
8
|
-
padding: 12.5px
|
|
8
|
+
padding: 12.5px 15px;
|
|
9
9
|
border-bottom: 1px solid var(--a-base-8);
|
|
10
10
|
color: var(--a-base-3);
|
|
11
11
|
text-align: left;
|
|
@@ -52,12 +52,13 @@ span.apos-table__header-label:hover {
|
|
|
52
52
|
@include apos-transition(all, 0.05s);
|
|
53
53
|
}
|
|
54
54
|
.apos-table__cell {
|
|
55
|
-
padding: 5px;
|
|
55
|
+
padding: 5px 15px;
|
|
56
56
|
border-bottom: 1px solid var(--a-base-10);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
.apos-table__cell--context-menu {
|
|
60
|
-
|
|
60
|
+
padding-right: 0;
|
|
61
|
+
padding-left: 0;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
.apos-table__cell-field--context-menu {
|
|
@@ -414,7 +414,7 @@ module.exports = {
|
|
|
414
414
|
return self.insensitiveSortCompare(a[property], b[property]);
|
|
415
415
|
});
|
|
416
416
|
},
|
|
417
|
-
//
|
|
417
|
+
// Compare two strings in a case-insensitive way, returning -1, 0 or 1, suitable for use with sort().
|
|
418
418
|
// If the two strings represent numbers, compare them as numbers for a natural sort order
|
|
419
419
|
// when comparing strings like '4' and '10'.
|
|
420
420
|
insensitiveSortCompare(a, b) {
|
|
@@ -858,7 +858,7 @@ module.exports = {
|
|
|
858
858
|
return _.startCase(o);
|
|
859
859
|
},
|
|
860
860
|
|
|
861
|
-
// check if something is a function (as
|
|
861
|
+
// check if something is a function (as opposed to property)
|
|
862
862
|
isFunction: function(o) {
|
|
863
863
|
return (typeof o === 'function');
|
|
864
864
|
},
|
|
@@ -150,10 +150,12 @@ export default () => {
|
|
|
150
150
|
if (options.busy) {
|
|
151
151
|
if (!busyActive[busyName]) {
|
|
152
152
|
busyActive[busyName] = 0;
|
|
153
|
-
apos.bus
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
if (apos.bus) {
|
|
154
|
+
apos.bus.$emit('busy', {
|
|
155
|
+
active: true,
|
|
156
|
+
name: busyName
|
|
157
|
+
});
|
|
158
|
+
}
|
|
157
159
|
}
|
|
158
160
|
// keep track of nested calls
|
|
159
161
|
busyActive[busyName]++;
|
|
@@ -240,10 +242,12 @@ export default () => {
|
|
|
240
242
|
busyActive[busyName]--;
|
|
241
243
|
if (!busyActive[busyName]) {
|
|
242
244
|
// if no nested calls, disable the "busy" state
|
|
243
|
-
apos.bus
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
if (apos.bus) {
|
|
246
|
+
apos.bus.$emit('busy', {
|
|
247
|
+
active: false,
|
|
248
|
+
name: busyName
|
|
249
|
+
});
|
|
250
|
+
}
|
|
247
251
|
}
|
|
248
252
|
}
|
|
249
253
|
});
|
|
@@ -257,6 +257,21 @@ export default () => {
|
|
|
257
257
|
return path + '.' + file.extension;
|
|
258
258
|
};
|
|
259
259
|
|
|
260
|
+
// Given an asset path such as `/modules/modulename/images/file.png`, this
|
|
261
|
+
// method will return a URL for it. This is used when frontend JavaScript
|
|
262
|
+
// code needs to access static assets shipped in the `public` subdirectory of
|
|
263
|
+
// individual modules. Currently `path` must begin with `/modules/` followed
|
|
264
|
+
// by a module name; other namespaces may exist later. The remainder of the
|
|
265
|
+
// path, such as `/images/file.png` in the above example, must currespond
|
|
266
|
+
// to a file that exists in the `public` subdirectory of the named module.
|
|
267
|
+
//
|
|
268
|
+
// Asset paths of this type are also automatically supported by CSS and
|
|
269
|
+
// SCSS files in the project when using `url()`.
|
|
270
|
+
|
|
271
|
+
apos.util.assetUrl = function(path) {
|
|
272
|
+
return apos.assetBaseUrl + path;
|
|
273
|
+
};
|
|
274
|
+
|
|
260
275
|
// Returns true if the uri references the same site (same host and port) as the
|
|
261
276
|
// current page. Cross-browser implementation, valid at least back to IE11.
|
|
262
277
|
// Regarding port numbers, this will match as long as the URIs are consistent
|
|
@@ -156,7 +156,7 @@ module.exports = {
|
|
|
156
156
|
self.schema = self.apos.schema.compose({
|
|
157
157
|
addFields: self.apos.schema.fieldsToArray(`Module ${self.__meta.name}`, self.fields),
|
|
158
158
|
arrangeFields: self.apos.schema.groupsToArray(self.fieldsGroups)
|
|
159
|
-
});
|
|
159
|
+
}, self);
|
|
160
160
|
const forbiddenFields = [
|
|
161
161
|
'_id',
|
|
162
162
|
'type'
|
|
@@ -23,7 +23,8 @@ export default {
|
|
|
23
23
|
data() {
|
|
24
24
|
return {
|
|
25
25
|
rendered: '...',
|
|
26
|
-
playerOpts: null
|
|
26
|
+
playerOpts: null,
|
|
27
|
+
playerEl: null
|
|
27
28
|
};
|
|
28
29
|
},
|
|
29
30
|
mounted() {
|
|
@@ -40,6 +41,7 @@ export default {
|
|
|
40
41
|
},
|
|
41
42
|
methods: {
|
|
42
43
|
async renderContent() {
|
|
44
|
+
const self = this;
|
|
43
45
|
const parameters = {
|
|
44
46
|
_docId: this.docId,
|
|
45
47
|
widget: this.value,
|
|
@@ -61,6 +63,7 @@ export default {
|
|
|
61
63
|
// AposAreas manager can spot any new area divs.
|
|
62
64
|
// This will also run the player
|
|
63
65
|
setTimeout(function() {
|
|
66
|
+
self.setPlayerEl();
|
|
64
67
|
apos.bus.$emit('widget-rendered');
|
|
65
68
|
}, 0);
|
|
66
69
|
} catch (e) {
|
|
@@ -68,13 +71,18 @@ export default {
|
|
|
68
71
|
console.error('Unable to render widget. Possibly the schema has been changed and the existing widget does not pass validation.', e);
|
|
69
72
|
}
|
|
70
73
|
},
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
+
setPlayerEl() {
|
|
75
|
+
if (this.playerOpts) {
|
|
76
|
+
const el = this.$el.querySelector(this.playerOpts.selector);
|
|
77
|
+
if (el && this.playerOpts.player) {
|
|
78
|
+
this.playerEl = el;
|
|
79
|
+
}
|
|
74
80
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
},
|
|
82
|
+
runPlayer() {
|
|
83
|
+
if (this.playerEl && !this.playerEl.aposWidgetPlayed) {
|
|
84
|
+
this.playerOpts.player(this.playerEl);
|
|
85
|
+
this.playerEl.aposWidgetPlayed = true;
|
|
78
86
|
}
|
|
79
87
|
},
|
|
80
88
|
clicked(e) {
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apostrophe",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.8.1",
|
|
4
4
|
"description": "The Apostrophe Content Management System.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"pretest": "npm run lint
|
|
7
|
+
"pretest": "npm run lint",
|
|
8
8
|
"test": "nyc --reporter=html mocha -t 10000",
|
|
9
9
|
"lint": "eslint . && node scripts/lint-i18n"
|
|
10
10
|
},
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"eslint-loader": "^4.0.2",
|
|
128
128
|
"eslint-plugin-node": "^11.1.0",
|
|
129
129
|
"eslint-plugin-vue": "^7.9.0",
|
|
130
|
-
"mocha": "^
|
|
130
|
+
"mocha": "^9.1.2",
|
|
131
131
|
"nyc": "^15.1.0",
|
|
132
132
|
"replace-in-file": "^6.1.0",
|
|
133
133
|
"vue-eslint-parser": "^7.1.1",
|