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
|
@@ -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 () {
|
|
@@ -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;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
options: {
|
|
3
|
-
alias: 'ui'
|
|
3
|
+
alias: 'ui',
|
|
4
|
+
widgetMargin: '20px 0'
|
|
4
5
|
},
|
|
5
6
|
icons: {
|
|
6
7
|
'earth-icon': 'Earth',
|
|
@@ -21,7 +22,10 @@ module.exports = {
|
|
|
21
22
|
if (req.data.user && req.data.user.aposThemePrimary) {
|
|
22
23
|
theme.primary = req.data.user.aposThemePrimary;
|
|
23
24
|
}
|
|
24
|
-
return {
|
|
25
|
+
return {
|
|
26
|
+
theme,
|
|
27
|
+
widgetMargin: self.options.widgetMargin
|
|
28
|
+
};
|
|
25
29
|
}
|
|
26
30
|
};
|
|
27
31
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<span
|
|
2
|
+
<span
|
|
3
|
+
v-apos-tooltip="tooltip"
|
|
4
|
+
class="apos-button__wrapper"
|
|
5
|
+
:class="{ 'apos-button__wrapper--block': modifiers.includes('block') }"
|
|
6
|
+
>
|
|
3
7
|
<component
|
|
4
8
|
:is="href ? 'a' : 'button'"
|
|
5
9
|
v-on="href ? {} : {click: click}"
|
|
@@ -11,6 +15,7 @@
|
|
|
11
15
|
:type="buttonType"
|
|
12
16
|
:role="role"
|
|
13
17
|
:id="attrs.id ? attrs.id : id"
|
|
18
|
+
:style="{color: textColor}"
|
|
14
19
|
v-bind="attrs"
|
|
15
20
|
>
|
|
16
21
|
<transition name="fade">
|
|
@@ -67,6 +72,10 @@ export default {
|
|
|
67
72
|
type: String,
|
|
68
73
|
default: null
|
|
69
74
|
},
|
|
75
|
+
textColor: {
|
|
76
|
+
type: String,
|
|
77
|
+
default: null
|
|
78
|
+
},
|
|
70
79
|
href: {
|
|
71
80
|
type: [ String, Boolean ],
|
|
72
81
|
default: false
|
|
@@ -332,6 +341,9 @@ export default {
|
|
|
332
341
|
color: var(--a-base-5);
|
|
333
342
|
}
|
|
334
343
|
}
|
|
344
|
+
.apos-button__label {
|
|
345
|
+
line-height: var(--a-line-tall);
|
|
346
|
+
}
|
|
335
347
|
}
|
|
336
348
|
|
|
337
349
|
.apos-button--subtle {
|
|
@@ -395,8 +407,6 @@ export default {
|
|
|
395
407
|
box-sizing: border-box;
|
|
396
408
|
display: block;
|
|
397
409
|
width: 100%;
|
|
398
|
-
height: 47px;
|
|
399
|
-
max-width: 400px;
|
|
400
410
|
}
|
|
401
411
|
|
|
402
412
|
.apos-button--icon-right {
|
|
@@ -574,6 +584,10 @@ export default {
|
|
|
574
584
|
padding: 3px;
|
|
575
585
|
}
|
|
576
586
|
|
|
587
|
+
.apos-button--uppercase .apos-button__label {
|
|
588
|
+
text-transform: uppercase;
|
|
589
|
+
}
|
|
590
|
+
|
|
577
591
|
.apos-button--inline {
|
|
578
592
|
padding: 0;
|
|
579
593
|
&, &[disabled], &:hover, &:active, &:focus {
|
|
@@ -605,6 +619,10 @@ export default {
|
|
|
605
619
|
display: inline-block;
|
|
606
620
|
}
|
|
607
621
|
|
|
622
|
+
.apos-button__wrapper--block {
|
|
623
|
+
display: block;
|
|
624
|
+
}
|
|
625
|
+
|
|
608
626
|
@keyframes animateGradient {
|
|
609
627
|
0% {
|
|
610
628
|
background-position: 0% 50%;
|
|
@@ -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>
|