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
|
@@ -18,6 +18,7 @@ const _ = require('lodash');
|
|
|
18
18
|
const dayjs = require('dayjs');
|
|
19
19
|
const tinycolor = require('tinycolor2');
|
|
20
20
|
const { klona } = require('klona');
|
|
21
|
+
const { stripIndent } = require('common-tags');
|
|
21
22
|
|
|
22
23
|
module.exports = {
|
|
23
24
|
options: {
|
|
@@ -323,7 +324,13 @@ module.exports = {
|
|
|
323
324
|
self.addFieldType({
|
|
324
325
|
name: 'select',
|
|
325
326
|
async convert(req, field, data, destination) {
|
|
326
|
-
|
|
327
|
+
let choices;
|
|
328
|
+
if ((typeof field.choices) === 'string') {
|
|
329
|
+
choices = await self.apos.modules[field.moduleName][field.choices](req);
|
|
330
|
+
} else {
|
|
331
|
+
choices = field.choices;
|
|
332
|
+
}
|
|
333
|
+
destination[field.name] = self.apos.launder.select(data[field.name], choices, field.def);
|
|
327
334
|
},
|
|
328
335
|
index: function (value, field, texts) {
|
|
329
336
|
const silent = field.silent === undefined ? true : field.silent;
|
|
@@ -354,7 +361,9 @@ module.exports = {
|
|
|
354
361
|
return self.apos.launder.select(v, field.choices, null);
|
|
355
362
|
});
|
|
356
363
|
} else {
|
|
357
|
-
value =
|
|
364
|
+
value = (typeof field.choices) === 'string'
|
|
365
|
+
? self.apos.launder.string(value)
|
|
366
|
+
: self.apos.launder.select(value, field.choices, null);
|
|
358
367
|
if (value === null) {
|
|
359
368
|
return null;
|
|
360
369
|
}
|
|
@@ -362,9 +371,16 @@ module.exports = {
|
|
|
362
371
|
}
|
|
363
372
|
},
|
|
364
373
|
choices: async function () {
|
|
374
|
+
let allChoices;
|
|
365
375
|
const values = await query.toDistinct(field.name);
|
|
376
|
+
if ((typeof field.choices) === 'string') {
|
|
377
|
+
const req = self.apos.task.getReq();
|
|
378
|
+
allChoices = await self.apos.modules[field.moduleName][field.choices](req);
|
|
379
|
+
} else {
|
|
380
|
+
allChoices = field.choices;
|
|
381
|
+
}
|
|
366
382
|
const choices = _.map(values, function (value) {
|
|
367
|
-
const choice = _.find(
|
|
383
|
+
const choice = _.find(allChoices, { value: value });
|
|
368
384
|
return {
|
|
369
385
|
value: value,
|
|
370
386
|
label: choice && (choice.label || value)
|
|
@@ -980,6 +996,12 @@ module.exports = {
|
|
|
980
996
|
if (field.schema && !Array.isArray(field.schema)) {
|
|
981
997
|
fail('schema property should be an array if present at this stage');
|
|
982
998
|
}
|
|
999
|
+
if (field.filters) {
|
|
1000
|
+
fail('"filters" property should be changed to "builders" for 3.x');
|
|
1001
|
+
}
|
|
1002
|
+
if (field.builders && field.builders.projection) {
|
|
1003
|
+
fail('"projection" sub-property should be changed to "project" for 3.x');
|
|
1004
|
+
}
|
|
983
1005
|
function lintType(type) {
|
|
984
1006
|
type = self.apos.doc.normalizeType(type);
|
|
985
1007
|
if (!_.find(self.apos.doc.managers, { name: type })) {
|
|
@@ -988,11 +1010,15 @@ module.exports = {
|
|
|
988
1010
|
}
|
|
989
1011
|
},
|
|
990
1012
|
isEqual(req, field, one, two) {
|
|
991
|
-
|
|
1013
|
+
const ids1 = one[field.idsStorage] || [];
|
|
1014
|
+
const ids2 = two[field.idsStorage] || [];
|
|
1015
|
+
if (!_.isEqual(ids1, ids2)) {
|
|
992
1016
|
return false;
|
|
993
1017
|
}
|
|
994
1018
|
if (field.fieldsStorage) {
|
|
995
|
-
|
|
1019
|
+
const fields1 = one[field.fieldsStorage] || {};
|
|
1020
|
+
const fields2 = two[field.fieldsStorage] || {};
|
|
1021
|
+
if (!_.isEqual(fields1, fields2)) {
|
|
996
1022
|
return false;
|
|
997
1023
|
}
|
|
998
1024
|
}
|
|
@@ -1098,7 +1124,7 @@ module.exports = {
|
|
|
1098
1124
|
// alterFields option should be avoided if your needs can be met
|
|
1099
1125
|
// via another option.
|
|
1100
1126
|
|
|
1101
|
-
compose(options) {
|
|
1127
|
+
compose(options, module) {
|
|
1102
1128
|
let schema = [];
|
|
1103
1129
|
|
|
1104
1130
|
// Useful for finding good unit test cases
|
|
@@ -1288,9 +1314,29 @@ module.exports = {
|
|
|
1288
1314
|
// like workflow to patch schema fields of various modules
|
|
1289
1315
|
// without inadvertently impacting other apos instances
|
|
1290
1316
|
// when running with @apostrophecms/multisite
|
|
1291
|
-
|
|
1317
|
+
schema = _.map(schema, function (field) {
|
|
1292
1318
|
return _.clone(field);
|
|
1293
1319
|
});
|
|
1320
|
+
|
|
1321
|
+
_.each(schema, function(field) {
|
|
1322
|
+
// For use in resolving options like "choices" when they
|
|
1323
|
+
// contain a method name. For bc don't mess with possible
|
|
1324
|
+
// existing usages in custom schema field types predating
|
|
1325
|
+
// this feature
|
|
1326
|
+
self.setModuleName(field, module);
|
|
1327
|
+
});
|
|
1328
|
+
return schema;
|
|
1329
|
+
},
|
|
1330
|
+
|
|
1331
|
+
// Recursively set moduleName property of the field and any subfields,
|
|
1332
|
+
// as might be found in array or object fields. `module` is an actual module
|
|
1333
|
+
setModuleName(field, module) {
|
|
1334
|
+
field.moduleName = field.moduleName || (module && module.__meta.name);
|
|
1335
|
+
if ((field.type === 'array') || (field.type === 'object')) {
|
|
1336
|
+
_.each(field.schema || [], function(subfield) {
|
|
1337
|
+
self.setModuleName(subfield, module);
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1294
1340
|
},
|
|
1295
1341
|
|
|
1296
1342
|
// refine is like compose, but it starts with an existing schema array
|
|
@@ -1473,10 +1519,8 @@ module.exports = {
|
|
|
1473
1519
|
for (const field of schema) {
|
|
1474
1520
|
const fieldType = self.fieldTypes[field.type];
|
|
1475
1521
|
if (!fieldType.isEqual) {
|
|
1476
|
-
if ((one[field.name]
|
|
1477
|
-
|
|
1478
|
-
}
|
|
1479
|
-
if (!_.isEqual(one[field.name], two[field.name])) {
|
|
1522
|
+
if ((!_.isEqual(one[field.name], two[field.name])) &&
|
|
1523
|
+
!((one[field.name] == null) && (two[field.name] == null))) {
|
|
1480
1524
|
return false;
|
|
1481
1525
|
}
|
|
1482
1526
|
} else {
|
|
@@ -1928,6 +1972,7 @@ module.exports = {
|
|
|
1928
1972
|
} else if (field.type === 'array') {
|
|
1929
1973
|
if (doc[field.name]) {
|
|
1930
1974
|
doc[field.name].forEach(item => {
|
|
1975
|
+
item._id = item._id || self.apos.util.generateId();
|
|
1931
1976
|
item.metaType = 'arrayItem';
|
|
1932
1977
|
item.scopedArrayName = field.scopedArrayName;
|
|
1933
1978
|
forSchema(field.schema, item);
|
|
@@ -2179,13 +2224,14 @@ module.exports = {
|
|
|
2179
2224
|
// of a `relationship` field, or the `label` property of anything.
|
|
2180
2225
|
|
|
2181
2226
|
validate(schema, options) {
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2227
|
+
schema.forEach(field => {
|
|
2228
|
+
// Infinite recursion prevention
|
|
2229
|
+
const key = `${options.type}:${options.subtype}.${field.name}`;
|
|
2230
|
+
if (!self.validatedSchemas[key]) {
|
|
2231
|
+
self.validatedSchemas[key] = true;
|
|
2232
|
+
self.validateField(field, options);
|
|
2233
|
+
}
|
|
2234
|
+
});
|
|
2189
2235
|
},
|
|
2190
2236
|
|
|
2191
2237
|
// Validates a single schema field. See `validate`.
|
|
@@ -2213,7 +2259,12 @@ module.exports = {
|
|
|
2213
2259
|
self.apos.util.error(format(s));
|
|
2214
2260
|
}
|
|
2215
2261
|
function format(s) {
|
|
2216
|
-
return
|
|
2262
|
+
return stripIndent`
|
|
2263
|
+
${options.type} ${options.subtype}, ${field.type} field "${field.name}":
|
|
2264
|
+
|
|
2265
|
+
${s}
|
|
2266
|
+
|
|
2267
|
+
`;
|
|
2217
2268
|
}
|
|
2218
2269
|
},
|
|
2219
2270
|
|
|
@@ -2526,6 +2577,32 @@ module.exports = {
|
|
|
2526
2577
|
|
|
2527
2578
|
};
|
|
2528
2579
|
},
|
|
2580
|
+
apiRoutes(self) {
|
|
2581
|
+
return {
|
|
2582
|
+
get: {
|
|
2583
|
+
async choices(req) {
|
|
2584
|
+
const id = self.apos.launder.string(req.query.fieldId);
|
|
2585
|
+
const field = self.getFieldById(id);
|
|
2586
|
+
let choices = [];
|
|
2587
|
+
if (
|
|
2588
|
+
!field ||
|
|
2589
|
+
field.type !== 'select' ||
|
|
2590
|
+
!(field.choices && typeof field.choices === 'string')
|
|
2591
|
+
) {
|
|
2592
|
+
throw self.apos.error('invalid');
|
|
2593
|
+
}
|
|
2594
|
+
choices = await self.apos.modules[field.moduleName][field.choices](req);
|
|
2595
|
+
if (Array.isArray(choices)) {
|
|
2596
|
+
return {
|
|
2597
|
+
choices
|
|
2598
|
+
};
|
|
2599
|
+
} else {
|
|
2600
|
+
throw self.apos.error('invalid', `The method ${field.choices} from the module ${field.moduleName} did not return an array`);
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
};
|
|
2605
|
+
},
|
|
2529
2606
|
extendMethods(self) {
|
|
2530
2607
|
return {
|
|
2531
2608
|
getBrowserData(_super, req) {
|
|
@@ -2540,7 +2617,7 @@ module.exports = {
|
|
|
2540
2617
|
}
|
|
2541
2618
|
fields[name] = component;
|
|
2542
2619
|
}
|
|
2543
|
-
|
|
2620
|
+
browserOptions.action = self.action;
|
|
2544
2621
|
browserOptions.components = { fields: fields };
|
|
2545
2622
|
return browserOptions;
|
|
2546
2623
|
}
|
|
@@ -6,44 +6,15 @@
|
|
|
6
6
|
>
|
|
7
7
|
<template #body>
|
|
8
8
|
<div class="apos-attachment">
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@
|
|
16
|
-
@
|
|
17
|
-
|
|
18
|
-
>
|
|
19
|
-
<p class="apos-attachment-instructions">
|
|
20
|
-
<template v-if="dragging">
|
|
21
|
-
<cloud-upload-icon :size="38" />
|
|
22
|
-
</template>
|
|
23
|
-
<AposSpinner v-else-if="uploading" />
|
|
24
|
-
<template v-else>
|
|
25
|
-
<paperclip-icon :size="14" class="apos-attachment-icon" />
|
|
26
|
-
{{ messages.primary }}
|
|
27
|
-
<span class="apos-attachment-highlight" v-if="messages.highlighted">
|
|
28
|
-
{{ messages.highlighted }}
|
|
29
|
-
</span>
|
|
30
|
-
</template>
|
|
31
|
-
</p>
|
|
32
|
-
<input
|
|
33
|
-
type="file"
|
|
34
|
-
class="apos-sr-only"
|
|
35
|
-
:disabled="disabled || limitReached"
|
|
36
|
-
@input="uploadMedia"
|
|
37
|
-
:accept="field.accept"
|
|
38
|
-
>
|
|
39
|
-
</label>
|
|
40
|
-
<div v-if="next && next._id" class="apos-attachment-files">
|
|
41
|
-
<AposSlatList
|
|
42
|
-
:value="next ? [ next ] : []"
|
|
43
|
-
@input="updated"
|
|
44
|
-
:disabled="field.readOnly"
|
|
45
|
-
/>
|
|
46
|
-
</div>
|
|
9
|
+
<AposFile
|
|
10
|
+
:allowed-extensions="field.accept"
|
|
11
|
+
:uploading="uploading"
|
|
12
|
+
:disabled="disabled"
|
|
13
|
+
:attachment="next"
|
|
14
|
+
:def="field.def"
|
|
15
|
+
@upload-file="uploadMedia"
|
|
16
|
+
@update="updated"
|
|
17
|
+
/>
|
|
47
18
|
</div>
|
|
48
19
|
</template>
|
|
49
20
|
</AposInputWrapper>
|
|
@@ -51,13 +22,9 @@
|
|
|
51
22
|
|
|
52
23
|
<script>
|
|
53
24
|
import AposInputMixin from 'Modules/@apostrophecms/schema/mixins/AposInputMixin.js';
|
|
54
|
-
import AposSlatList from 'Modules/@apostrophecms/ui/components/AposSlatList';
|
|
55
25
|
|
|
56
26
|
export default {
|
|
57
27
|
name: 'AposInputAttachment',
|
|
58
|
-
components: {
|
|
59
|
-
AposSlatList
|
|
60
|
-
},
|
|
61
28
|
mixins: [ AposInputMixin ],
|
|
62
29
|
emits: [ 'upload-started', 'upload-complete' ],
|
|
63
30
|
data () {
|
|
@@ -67,46 +34,13 @@ export default {
|
|
|
67
34
|
next: (this.value && (typeof this.value.data === 'object'))
|
|
68
35
|
? this.value.data : (this.field.def || null),
|
|
69
36
|
disabled: false,
|
|
70
|
-
|
|
71
|
-
uploading: false,
|
|
72
|
-
allowedExtensions: [ '*' ]
|
|
37
|
+
uploading: false
|
|
73
38
|
};
|
|
74
39
|
},
|
|
75
|
-
computed: {
|
|
76
|
-
messages () {
|
|
77
|
-
const msgs = {
|
|
78
|
-
primary: 'Drop a file here or',
|
|
79
|
-
highlighted: 'click to open the file explorer'
|
|
80
|
-
};
|
|
81
|
-
if (this.disabled) {
|
|
82
|
-
msgs.primary = 'Field is disabled';
|
|
83
|
-
msgs.highlighted = '';
|
|
84
|
-
}
|
|
85
|
-
if (this.limitReached) {
|
|
86
|
-
msgs.primary = 'Attachment limit reached';
|
|
87
|
-
msgs.highlighted = '';
|
|
88
|
-
}
|
|
89
|
-
return msgs;
|
|
90
|
-
},
|
|
91
|
-
limitReached () {
|
|
92
|
-
return !!(this.value.data && this.value.data._id);
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
40
|
async mounted () {
|
|
96
41
|
this.disabled = this.field.readOnly;
|
|
97
|
-
|
|
98
|
-
const groups = apos.modules['@apostrophecms/attachment'].fileGroups;
|
|
99
|
-
const groupInfo = groups.find(group => {
|
|
100
|
-
return group.name === this.field.fileGroup;
|
|
101
|
-
});
|
|
102
|
-
if (groupInfo && groupInfo.extensions) {
|
|
103
|
-
this.allowedExtensions = groupInfo.extensions;
|
|
104
|
-
}
|
|
105
42
|
},
|
|
106
43
|
methods: {
|
|
107
|
-
watchNext () {
|
|
108
|
-
this.validateAndEmit();
|
|
109
|
-
},
|
|
110
44
|
updated (items) {
|
|
111
45
|
// NOTE: This is limited to a single item.
|
|
112
46
|
this.next = items.length > 0 ? items[0] : null;
|
|
@@ -118,31 +52,12 @@ export default {
|
|
|
118
52
|
|
|
119
53
|
return false;
|
|
120
54
|
},
|
|
121
|
-
async uploadMedia (
|
|
55
|
+
async uploadMedia (file) {
|
|
122
56
|
if (!this.disabled || !this.limitReached) {
|
|
123
57
|
try {
|
|
124
|
-
this.dragging = false;
|
|
125
58
|
this.disabled = true;
|
|
126
59
|
this.uploading = true;
|
|
127
60
|
|
|
128
|
-
const file = event.target.files ? event.target.files[0] : event.dataTransfer.files[0];
|
|
129
|
-
|
|
130
|
-
if (!this.checkFileGroup(file.name)) {
|
|
131
|
-
const joined = this.allowedExtensions.join(this.$t('apostrophe:listJoiner'));
|
|
132
|
-
await apos.notify('apostrophe:fileTypeNotAccepted', {
|
|
133
|
-
type: 'warning',
|
|
134
|
-
icon: 'alert-circle-icon',
|
|
135
|
-
interpolate: {
|
|
136
|
-
extensions: joined
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
this.disabled = false;
|
|
141
|
-
this.uploading = false;
|
|
142
|
-
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
61
|
await apos.notify('apostrophe:uploading', {
|
|
147
62
|
dismiss: true,
|
|
148
63
|
icon: 'cloud-upload-icon',
|
|
@@ -183,71 +98,7 @@ export default {
|
|
|
183
98
|
this.uploading = false;
|
|
184
99
|
}
|
|
185
100
|
}
|
|
186
|
-
},
|
|
187
|
-
checkFileGroup(filename) {
|
|
188
|
-
const fileExt = filename.split('.').pop();
|
|
189
|
-
return this.allowedExtensions[0] === '*' ||
|
|
190
|
-
this.allowedExtensions.includes(fileExt);
|
|
191
|
-
},
|
|
192
|
-
dragHandler (event) {
|
|
193
|
-
event.preventDefault();
|
|
194
|
-
if (!this.disabled && !this.dragging) {
|
|
195
|
-
this.dragging = true;
|
|
196
|
-
}
|
|
197
101
|
}
|
|
198
102
|
}
|
|
199
103
|
};
|
|
200
104
|
</script>
|
|
201
|
-
|
|
202
|
-
<style lang="scss" scoped>
|
|
203
|
-
.apos-attachment-dropzone {
|
|
204
|
-
@include apos-button-reset();
|
|
205
|
-
@include type-base;
|
|
206
|
-
display: block;
|
|
207
|
-
margin: 10px 0;
|
|
208
|
-
padding: 20px;
|
|
209
|
-
border: 2px dashed var(--a-base-8);
|
|
210
|
-
border-radius: var(--a-border-radius);
|
|
211
|
-
transition: all 0.2s ease;
|
|
212
|
-
&:hover {
|
|
213
|
-
border-color: var(--a-primary);
|
|
214
|
-
background-color: var(--a-base-10);
|
|
215
|
-
}
|
|
216
|
-
&:active,
|
|
217
|
-
&:focus {
|
|
218
|
-
border: 2px solid var(--a-primary);
|
|
219
|
-
}
|
|
220
|
-
&.apos-is-disabled {
|
|
221
|
-
color: var(--a-base-4);
|
|
222
|
-
background-color: var(--a-base-7);
|
|
223
|
-
border-color: var(--a-base-4);
|
|
224
|
-
|
|
225
|
-
&:hover {
|
|
226
|
-
cursor: not-allowed;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
.apos-attachment-dropzone--dragover {
|
|
232
|
-
border: 2px dashed var(--a-primary);
|
|
233
|
-
background-color: var(--a-base-10);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
.apos-attachment-instructions {
|
|
237
|
-
display: flex;
|
|
238
|
-
flex-wrap: wrap;
|
|
239
|
-
align-items: center;
|
|
240
|
-
justify-content: center;
|
|
241
|
-
pointer-events: none;
|
|
242
|
-
// v-html goofiness
|
|
243
|
-
& ::v-deep .apos-attachment-highlight {
|
|
244
|
-
color: var(--a-primary);
|
|
245
|
-
font-weight: var(--a-weight-bold);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
.apos-attachment-icon {
|
|
250
|
-
transform: rotate(45deg);
|
|
251
|
-
margin-right: 5px;
|
|
252
|
-
}
|
|
253
|
-
</style>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
:field="field" :error="effectiveError"
|
|
4
4
|
:uid="uid" :items="next"
|
|
5
5
|
:display-options="displayOptions"
|
|
6
|
+
:modifiers="modifiers"
|
|
6
7
|
>
|
|
7
8
|
<template #additional>
|
|
8
9
|
<AposMinMaxCount
|
|
@@ -14,6 +15,7 @@
|
|
|
14
15
|
<div class="apos-input-wrapper apos-input-relationship">
|
|
15
16
|
<div class="apos-input-relationship__input-wrapper">
|
|
16
17
|
<input
|
|
18
|
+
v-if="!modifiers.includes('no-search')"
|
|
17
19
|
class="apos-input apos-input--text apos-input--relationship"
|
|
18
20
|
v-model="searchTerm" type="text"
|
|
19
21
|
:placeholder="$t(placeholder)"
|
|
@@ -28,7 +30,7 @@
|
|
|
28
30
|
class="apos-input-relationship__button"
|
|
29
31
|
:disabled="field.readOnly || limitReached"
|
|
30
32
|
:label="browseLabel"
|
|
31
|
-
:modifiers="
|
|
33
|
+
:modifiers="buttonModifiers"
|
|
32
34
|
type="input"
|
|
33
35
|
@click="choose"
|
|
34
36
|
/>
|
|
@@ -106,6 +108,13 @@ export default {
|
|
|
106
108
|
},
|
|
107
109
|
disableUnpublished() {
|
|
108
110
|
return apos.modules[this.field.withType].localized;
|
|
111
|
+
},
|
|
112
|
+
buttonModifiers() {
|
|
113
|
+
const modifiers = [ 'small' ];
|
|
114
|
+
if (this.modifiers.includes('no-search')) {
|
|
115
|
+
modifiers.push('block');
|
|
116
|
+
}
|
|
117
|
+
return modifiers;
|
|
109
118
|
}
|
|
110
119
|
},
|
|
111
120
|
watch: {
|
|
@@ -225,6 +234,19 @@ export default {
|
|
|
225
234
|
|
|
226
235
|
.apos-input-relationship__items {
|
|
227
236
|
padding: relative;
|
|
228
|
-
margin-top:
|
|
237
|
+
margin-top: $spacing-base;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.apos-field--small {
|
|
241
|
+
.apos-input-relationship__button {
|
|
242
|
+
padding: $spacing-half;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
.apos-field--no-search {
|
|
246
|
+
.apos-input-relationship__button {
|
|
247
|
+
position: relative;
|
|
248
|
+
width: 100%;
|
|
249
|
+
padding: 0;
|
|
250
|
+
}
|
|
229
251
|
}
|
|
230
252
|
</style>
|
|
@@ -49,9 +49,27 @@ export default {
|
|
|
49
49
|
choices: []
|
|
50
50
|
};
|
|
51
51
|
},
|
|
52
|
-
mounted() {
|
|
52
|
+
async mounted() {
|
|
53
|
+
let choices;
|
|
54
|
+
if (typeof this.field.choices === 'string') {
|
|
55
|
+
const action = this.options.action;
|
|
56
|
+
const response = await apos.http.get(
|
|
57
|
+
`${action}/choices`,
|
|
58
|
+
{
|
|
59
|
+
qs: {
|
|
60
|
+
fieldId: this.field._id
|
|
61
|
+
},
|
|
62
|
+
busy: true
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
if (response.choices) {
|
|
66
|
+
choices = response.choices;
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
choices = this.field.choices;
|
|
70
|
+
}
|
|
53
71
|
// Add an null option if there isn't one already
|
|
54
|
-
if (!this.field.required && !
|
|
72
|
+
if (!this.field.required && !choices.find(choice => {
|
|
55
73
|
return choice.value === null;
|
|
56
74
|
})) {
|
|
57
75
|
this.choices.push({
|
|
@@ -59,12 +77,12 @@ export default {
|
|
|
59
77
|
value: null
|
|
60
78
|
});
|
|
61
79
|
}
|
|
62
|
-
this.choices = this.choices.concat(
|
|
80
|
+
this.choices = this.choices.concat(choices);
|
|
63
81
|
this.$nextTick(() => {
|
|
64
82
|
// this has to happen on nextTick to avoid emitting before schemaReady is
|
|
65
83
|
// set in AposSchema
|
|
66
|
-
if (this.field.required && (this.next == null) && (this.
|
|
67
|
-
this.next = this.
|
|
84
|
+
if (this.field.required && (this.next == null) && (this.choices[0] != null)) {
|
|
85
|
+
this.next = this.choices[0].value;
|
|
68
86
|
}
|
|
69
87
|
});
|
|
70
88
|
},
|
|
@@ -74,7 +92,7 @@ export default {
|
|
|
74
92
|
return 'required';
|
|
75
93
|
}
|
|
76
94
|
|
|
77
|
-
if (value && !this.
|
|
95
|
+
if (value && !this.choices.find(choice => choice.value === value)) {
|
|
78
96
|
return 'invalid';
|
|
79
97
|
}
|
|
80
98
|
|
|
@@ -73,10 +73,6 @@ export default {
|
|
|
73
73
|
icon () {
|
|
74
74
|
if (this.error) {
|
|
75
75
|
return 'circle-medium-icon';
|
|
76
|
-
} else if (this.field.type === 'date') {
|
|
77
|
-
return 'calendar-icon';
|
|
78
|
-
} else if (this.field.type === 'time') {
|
|
79
|
-
return 'clock-icon';
|
|
80
76
|
} else if (this.field.icon) {
|
|
81
77
|
return this.field.icon;
|
|
82
78
|
} else {
|
|
@@ -71,10 +71,6 @@ export default {
|
|
|
71
71
|
icon () {
|
|
72
72
|
if (this.error) {
|
|
73
73
|
return 'circle-medium-icon';
|
|
74
|
-
} else if (this.field.type === 'date') {
|
|
75
|
-
return 'calendar-icon';
|
|
76
|
-
} else if (this.field.type === 'time') {
|
|
77
|
-
return 'clock-icon';
|
|
78
74
|
} else if (this.field.icon) {
|
|
79
75
|
return this.field.icon;
|
|
80
76
|
} else {
|
|
@@ -207,9 +203,6 @@ export default {
|
|
|
207
203
|
// height of date/time input is slightly larger than others due to the browser spinner ui
|
|
208
204
|
height: 46px;
|
|
209
205
|
padding-right: 40px;
|
|
210
|
-
&::-webkit-calendar-picker-indicator {
|
|
211
|
-
background: none;
|
|
212
|
-
}
|
|
213
206
|
}
|
|
214
207
|
.apos-input--date {
|
|
215
208
|
&::-webkit-clear-button {
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
<component
|
|
11
11
|
v-show="displayComponent(field.name)"
|
|
12
12
|
v-model="fieldState[field.name]"
|
|
13
|
-
:following-values="followingValues[field.name]"
|
|
14
13
|
:is="fieldComponentMap[field.type]"
|
|
14
|
+
:following-values="followingValues[field.name]"
|
|
15
|
+
:condition-met="conditionalFields[field.name]"
|
|
15
16
|
:field="fields[field.name].field"
|
|
16
17
|
:modifiers="fields[field.name].modifiers"
|
|
17
18
|
:display-options="getDisplayOptions(field.name)"
|
|
@@ -89,7 +90,11 @@ export default {
|
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
},
|
|
92
|
-
emits: [
|
|
93
|
+
emits: [
|
|
94
|
+
'input',
|
|
95
|
+
'reset',
|
|
96
|
+
'validate'
|
|
97
|
+
],
|
|
93
98
|
data() {
|
|
94
99
|
return {
|
|
95
100
|
schemaReady: false,
|
|
@@ -112,7 +117,10 @@ export default {
|
|
|
112
117
|
data: this.value[item.name]
|
|
113
118
|
};
|
|
114
119
|
fields[item.name].serverError = this.serverErrors && this.serverErrors[item.name];
|
|
115
|
-
fields[item.name].modifiers =
|
|
120
|
+
fields[item.name].modifiers = [
|
|
121
|
+
...(this.modifiers || []),
|
|
122
|
+
...(item.modifiers || [])
|
|
123
|
+
];
|
|
116
124
|
});
|
|
117
125
|
return fields;
|
|
118
126
|
}
|
|
@@ -142,6 +150,20 @@ export default {
|
|
|
142
150
|
this.populateDocData();
|
|
143
151
|
}
|
|
144
152
|
}
|
|
153
|
+
},
|
|
154
|
+
conditionalFields(newVal, oldVal) {
|
|
155
|
+
for (const field in oldVal) {
|
|
156
|
+
if (!this.fieldState[field] || (newVal[field] === oldVal[field]) || !this.fieldState[field].ranValidation) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (
|
|
161
|
+
(newVal[field] === false) ||
|
|
162
|
+
(newVal[field] && this.fieldState[field].ranValidation)
|
|
163
|
+
) {
|
|
164
|
+
this.$emit('validate');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
145
167
|
}
|
|
146
168
|
},
|
|
147
169
|
created() {
|
|
@@ -22,6 +22,10 @@ export default {
|
|
|
22
22
|
type: Object,
|
|
23
23
|
required: false
|
|
24
24
|
},
|
|
25
|
+
conditionMet: {
|
|
26
|
+
type: Boolean,
|
|
27
|
+
required: false
|
|
28
|
+
},
|
|
25
29
|
triggerValidation: {
|
|
26
30
|
type: Boolean,
|
|
27
31
|
default: false
|
|
@@ -117,10 +121,14 @@ export default {
|
|
|
117
121
|
// You must supply the validate method. It receives the
|
|
118
122
|
// internal representation used for editing (a string, for instance)
|
|
119
123
|
validateAndEmit () {
|
|
120
|
-
|
|
124
|
+
// If the field is conditional and isn't shown, disregard any errors.
|
|
125
|
+
const error = this.conditionMet === false ? false
|
|
126
|
+
: this.validate(this.next);
|
|
121
127
|
this.$emit('input', {
|
|
122
128
|
data: error ? this.next : this.convert(this.next),
|
|
123
|
-
error
|
|
129
|
+
error,
|
|
130
|
+
ranValidation: this.conditionMet === false ? this.value.ranValidation
|
|
131
|
+
: true
|
|
124
132
|
});
|
|
125
133
|
},
|
|
126
134
|
watchValue () {
|