apostrophe 3.7.0 → 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 +34 -3
- package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +5 -1
- 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-type/index.js +23 -3
- package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +13 -1
- package/modules/@apostrophecms/i18n/i18n/en.json +15 -4
- package/modules/@apostrophecms/i18n/i18n/es.json +0 -1
- package/modules/@apostrophecms/i18n/i18n/pt-BR.json +0 -1
- package/modules/@apostrophecms/i18n/i18n/sk.json +3 -4
- package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +6 -3
- 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/modal/ui/apos/components/AposDocsManagerToolbar.vue +151 -61
- 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/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/piece-type/index.js +178 -61
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +179 -50
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +0 -2
- package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +138 -0
- package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +11 -160
- package/modules/@apostrophecms/task/index.js +2 -2
- package/modules/@apostrophecms/template/index.js +2 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +5 -0
- package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +205 -0
- package/modules/@apostrophecms/util/ui/src/util.js +15 -0
- package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +15 -7
- package/package.json +2 -2
- package/test/job.js +224 -0
- package/test/pieces.js +17 -0
- package/test-lib/util.js +32 -0
|
@@ -15,27 +15,34 @@
|
|
|
15
15
|
<button
|
|
16
16
|
v-for="(button, i) in notification.buttons"
|
|
17
17
|
:key="i"
|
|
18
|
-
:data-apos-bus-event="JSON.stringify({
|
|
18
|
+
:data-apos-bus-event="JSON.stringify({
|
|
19
|
+
name: button.name,
|
|
20
|
+
data: button.data
|
|
21
|
+
})"
|
|
19
22
|
>
|
|
20
23
|
{{ localize(button.label) }}
|
|
21
24
|
</button>
|
|
22
25
|
</span>
|
|
23
26
|
<div
|
|
24
27
|
class="apos-notification__progress"
|
|
25
|
-
v-if="
|
|
28
|
+
v-if="job && job.total"
|
|
26
29
|
>
|
|
27
30
|
<div class="apos-notification__progress-bar">
|
|
28
31
|
<div
|
|
29
32
|
class="apos-notification__progress-now" role="progressbar"
|
|
30
|
-
:aria-valuenow="
|
|
31
|
-
|
|
33
|
+
:aria-valuenow="job.processed || 0"
|
|
34
|
+
:style="`width: ${job.percentage + '%'}`"
|
|
35
|
+
aria-valuemin="0" :aria-valuemax="job.total"
|
|
32
36
|
/>
|
|
33
37
|
</div>
|
|
34
38
|
<span class="apos-notification__progress-value">
|
|
35
|
-
{{
|
|
39
|
+
{{ Math.floor(job.percentage) + '%' }}
|
|
36
40
|
</span>
|
|
37
41
|
</div>
|
|
38
|
-
<button
|
|
42
|
+
<button
|
|
43
|
+
v-if="!job"
|
|
44
|
+
@click="close" class="apos-notification__button"
|
|
45
|
+
>
|
|
39
46
|
<Close
|
|
40
47
|
class="apos-notification__close-icon"
|
|
41
48
|
title="Close Notification"
|
|
@@ -58,6 +65,16 @@ export default {
|
|
|
58
65
|
}
|
|
59
66
|
},
|
|
60
67
|
emits: [ 'close' ],
|
|
68
|
+
data () {
|
|
69
|
+
return {
|
|
70
|
+
job: this.notification.job && this.notification.job._id ? {
|
|
71
|
+
route: `${apos.modules['@apostrophecms/job'].action}/${this.notification.job._id}`,
|
|
72
|
+
processed: 0,
|
|
73
|
+
total: 1,
|
|
74
|
+
action: this.notification.job.action
|
|
75
|
+
} : null
|
|
76
|
+
};
|
|
77
|
+
},
|
|
61
78
|
computed: {
|
|
62
79
|
classList() {
|
|
63
80
|
const classes = [ 'apos-notification' ];
|
|
@@ -65,7 +82,7 @@ export default {
|
|
|
65
82
|
classes.push(`apos-notification--${this.notification.type}`);
|
|
66
83
|
}
|
|
67
84
|
|
|
68
|
-
if (this.
|
|
85
|
+
if (this.job) {
|
|
69
86
|
classes.push('apos-notification--progress');
|
|
70
87
|
}
|
|
71
88
|
|
|
@@ -86,9 +103,6 @@ export default {
|
|
|
86
103
|
} else {
|
|
87
104
|
return 'circle-icon';
|
|
88
105
|
}
|
|
89
|
-
},
|
|
90
|
-
progressPercent () {
|
|
91
|
-
return `${Math.floor((this.notification.progress.current / 100) * 100)}%`;
|
|
92
106
|
}
|
|
93
107
|
},
|
|
94
108
|
async mounted() {
|
|
@@ -102,6 +116,40 @@ export default {
|
|
|
102
116
|
this.close();
|
|
103
117
|
}
|
|
104
118
|
});
|
|
119
|
+
|
|
120
|
+
if (this.job) {
|
|
121
|
+
try {
|
|
122
|
+
const {
|
|
123
|
+
total,
|
|
124
|
+
processed,
|
|
125
|
+
percentage
|
|
126
|
+
} = await apos.http.get(this.job.route, {});
|
|
127
|
+
|
|
128
|
+
this.job.total = total;
|
|
129
|
+
this.job.processed = processed || 0;
|
|
130
|
+
this.job.percentage = percentage;
|
|
131
|
+
this.job.ids = this.notification.job.ids || [];
|
|
132
|
+
|
|
133
|
+
await this.pollJob();
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error('Unable to find notification job:', this.notification.job._id);
|
|
136
|
+
this.job = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// Notifications may include events to emit.
|
|
140
|
+
if (this.notification.event?.name) {
|
|
141
|
+
try {
|
|
142
|
+
// Clear the event to make sure it's only emitted once across browsers.
|
|
143
|
+
const safe = await this.clearEvent(this.notification._id);
|
|
144
|
+
|
|
145
|
+
if (safe) {
|
|
146
|
+
// The notification doc will only still have the event in one instance.
|
|
147
|
+
apos.bus.$emit(this.notification.event.name, this.notification.event.data);
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error(this.$t('apostrophe:notificationClearEventError'));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
105
153
|
},
|
|
106
154
|
methods: {
|
|
107
155
|
close() {
|
|
@@ -116,6 +164,36 @@ export default {
|
|
|
116
164
|
result = s;
|
|
117
165
|
}
|
|
118
166
|
return result;
|
|
167
|
+
},
|
|
168
|
+
async pollJob () {
|
|
169
|
+
if (!this.job?.total) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const job = await apos.http.get(this.job.route, {});
|
|
173
|
+
this.job.processed = job.processed;
|
|
174
|
+
this.job.percentage = job.percentage;
|
|
175
|
+
|
|
176
|
+
if (this.job.processed < this.job.total) {
|
|
177
|
+
await new Promise(resolve => {
|
|
178
|
+
setTimeout(resolve, 500);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
await this.pollJob();
|
|
182
|
+
} else {
|
|
183
|
+
if (this.job.ids) {
|
|
184
|
+
apos.bus.$emit('content-changed', {
|
|
185
|
+
docIds: this.job.ids,
|
|
186
|
+
action: this.job.action || 'batch-update'
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
// `clearEvent` returns true if the event was found and cleared. Otherwise
|
|
192
|
+
// returns `false`
|
|
193
|
+
async clearEvent(id) {
|
|
194
|
+
return await apos.http.post(`${apos.notification.action}/${id}/clear-event`, {
|
|
195
|
+
body: {}
|
|
196
|
+
});
|
|
119
197
|
}
|
|
120
198
|
}
|
|
121
199
|
};
|
|
@@ -228,7 +306,7 @@ export default {
|
|
|
228
306
|
height: 100%;
|
|
229
307
|
background-color: var(--a-brand-green);
|
|
230
308
|
background-image: linear-gradient(46deg, var(--a-brand-gold) 0%, var(--a-brand-red) 26%, var(--a-brand-magenta) 47%, var(--a-brand-blue) 76%, var(--a-brand-green) 100%);
|
|
231
|
-
transition: width 0.
|
|
309
|
+
transition: width 0.5s ease-out;
|
|
232
310
|
}
|
|
233
311
|
|
|
234
312
|
.apos-notification__progress-value {
|
|
@@ -1996,35 +1996,38 @@ database.`);
|
|
|
1996
1996
|
//
|
|
1997
1997
|
// To avoid RAM issues with very large selections, the current
|
|
1998
1998
|
// implementation processes the pages in series.
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
}
|
|
1999
|
+
// TODO: restore this method when fully implemented.
|
|
2000
|
+
// async batchSimpleRoute(req, name, change) {
|
|
2001
|
+
// const batchOperation = _.find(self.options.batchOperations, { name: name });
|
|
2002
|
+
// const schema = batchOperation.schema || [];
|
|
2003
|
+
// const data = self.apos.schema.newInstance(schema);
|
|
2004
|
+
// await self.apos.schema.convert(req, schema, 'form', req.body, data);
|
|
2005
|
+
// let ids = self.apos.launder.ids(req.body.ids);
|
|
2006
|
+
// if (!ids) {
|
|
2007
|
+
// if (req.body._id) {
|
|
2008
|
+
// ids = self.apos.launder.id(req.body._id);
|
|
2009
|
+
// }
|
|
2010
|
+
// }
|
|
2011
|
+
// if (req.body.job) {
|
|
2012
|
+
// return runJob();
|
|
2013
|
+
// } else {
|
|
2014
|
+
// for (const id of ids) {
|
|
2015
|
+
// await one(req, id);
|
|
2016
|
+
// }
|
|
2017
|
+
// }
|
|
2018
|
+
// async function runJob() {
|
|
2019
|
+
// return self.apos.modules['@apostrophecms/job'].runBatch(req, ids, one, {
|
|
2020
|
+
// // TODO: Update with new progress notification config
|
|
2021
|
+
// });
|
|
2022
|
+
// }
|
|
2023
|
+
// async function one(req, id) {
|
|
2024
|
+
// const page = await self.findForBatch(req, { _id: id }).toObject();
|
|
2025
|
+
// if (!page) {
|
|
2026
|
+
// throw new Error('notfound');
|
|
2027
|
+
// }
|
|
2028
|
+
// await change(req, page, data);
|
|
2029
|
+
// }
|
|
2030
|
+
// },
|
|
2028
2031
|
// Backward compatible method following moving this to page-type module.
|
|
2029
2032
|
// This page module method may be deprecated in the next major version.
|
|
2030
2033
|
allowedSchema(req, page, parentPage) {
|
|
@@ -2032,7 +2035,11 @@ database.`);
|
|
|
2032
2035
|
.allowedSchema(req, page, parentPage);
|
|
2033
2036
|
},
|
|
2034
2037
|
getRestQuery(req) {
|
|
2035
|
-
const query = self.find(req)
|
|
2038
|
+
const query = self.find(req)
|
|
2039
|
+
.ancestors(true)
|
|
2040
|
+
.children(true)
|
|
2041
|
+
.attachments(true)
|
|
2042
|
+
.applyBuildersSafely(req.query);
|
|
2036
2043
|
// Minimum standard for a REST query without a public projection
|
|
2037
2044
|
// is being allowed to view drafts on the site
|
|
2038
2045
|
if (!self.apos.permission.can(req, 'view-draft')) {
|
|
@@ -2,7 +2,12 @@ const _ = require('lodash');
|
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
extend: '@apostrophecms/doc-type',
|
|
5
|
-
cascades: [
|
|
5
|
+
cascades: [
|
|
6
|
+
'filters',
|
|
7
|
+
'columns',
|
|
8
|
+
'batchOperations',
|
|
9
|
+
'utilityOperations'
|
|
10
|
+
],
|
|
6
11
|
options: {
|
|
7
12
|
perPage: 10,
|
|
8
13
|
quickCreate: true,
|
|
@@ -74,7 +79,7 @@ module.exports = {
|
|
|
74
79
|
],
|
|
75
80
|
// TODO: Delete `allowedInChooser` if not used.
|
|
76
81
|
allowedInChooser: false,
|
|
77
|
-
def:
|
|
82
|
+
def: null
|
|
78
83
|
},
|
|
79
84
|
archived: {
|
|
80
85
|
label: 'apostrophe:archive',
|
|
@@ -96,44 +101,47 @@ module.exports = {
|
|
|
96
101
|
}
|
|
97
102
|
}
|
|
98
103
|
},
|
|
104
|
+
utilityOperations: {},
|
|
99
105
|
batchOperations: {
|
|
100
106
|
add: {
|
|
101
107
|
archive: {
|
|
102
108
|
label: 'apostrophe:archive',
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
messages: {
|
|
110
|
+
progress: 'Archiving {{ type }}...',
|
|
111
|
+
completed: 'Archived {{ count }} {{ type }}.'
|
|
112
|
+
},
|
|
113
|
+
icon: 'archive-arrow-down-icon',
|
|
114
|
+
if: {
|
|
115
|
+
archived: false
|
|
116
|
+
},
|
|
117
|
+
modalOptions: {
|
|
118
|
+
title: 'apostrophe:archiveType',
|
|
119
|
+
description: 'apostrophe:archivingBatchConfirmation',
|
|
120
|
+
confirmationButton: 'apostrophe:archivingBatchConfirmationButton'
|
|
106
121
|
}
|
|
107
122
|
},
|
|
108
123
|
restore: {
|
|
109
124
|
label: 'apostrophe:restore',
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def: 'public',
|
|
123
|
-
choices: [
|
|
124
|
-
{
|
|
125
|
-
value: 'public',
|
|
126
|
-
label: 'apostrophe:public'
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
value: 'loginRequired',
|
|
130
|
-
label: 'apostrophe:loginRequired'
|
|
131
|
-
}
|
|
132
|
-
]
|
|
133
|
-
}
|
|
134
|
-
}
|
|
125
|
+
messages: {
|
|
126
|
+
progress: 'Restoring {{ type }}...',
|
|
127
|
+
completed: 'Restoring {{ count }} {{ type }}.'
|
|
128
|
+
},
|
|
129
|
+
icon: 'archive-arrow-up-icon',
|
|
130
|
+
if: {
|
|
131
|
+
archived: true
|
|
132
|
+
},
|
|
133
|
+
modalOptions: {
|
|
134
|
+
title: 'apostrophe:restoreType',
|
|
135
|
+
description: 'apostrophe:restoreBatchConfirmation',
|
|
136
|
+
confirmationButton: 'apostrophe:restoreBatchConfirmationButton'
|
|
135
137
|
}
|
|
136
138
|
}
|
|
139
|
+
},
|
|
140
|
+
group: {
|
|
141
|
+
more: {
|
|
142
|
+
icon: 'dots-vertical-icon',
|
|
143
|
+
operations: []
|
|
144
|
+
}
|
|
137
145
|
}
|
|
138
146
|
},
|
|
139
147
|
init(self) {
|
|
@@ -179,7 +187,6 @@ module.exports = {
|
|
|
179
187
|
if (self.apos.launder.boolean(req.query['render-areas']) === true) {
|
|
180
188
|
await self.apos.area.renderDocsAreas(req, result.results);
|
|
181
189
|
}
|
|
182
|
-
self.apos.attachment.all(result.results, { annotate: true });
|
|
183
190
|
if (query.get('choicesResults')) {
|
|
184
191
|
result.choices = query.get('choicesResults');
|
|
185
192
|
}
|
|
@@ -259,6 +266,58 @@ module.exports = {
|
|
|
259
266
|
}
|
|
260
267
|
return self.publish(req, draft);
|
|
261
268
|
},
|
|
269
|
+
async archive (req) {
|
|
270
|
+
if (!Array.isArray(req.body._ids)) {
|
|
271
|
+
throw self.apos.error('invalid');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
req.body._ids = req.body._ids.map(_id => {
|
|
275
|
+
return self.inferIdLocaleAndMode(req, _id);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
return self.apos.modules['@apostrophecms/job'].runBatch(
|
|
279
|
+
req,
|
|
280
|
+
req.body._ids,
|
|
281
|
+
async function(req, id) {
|
|
282
|
+
const piece = await self.findOneForEditing(req, { _id: id });
|
|
283
|
+
|
|
284
|
+
if (!piece) {
|
|
285
|
+
throw self.apos.error('notfound');
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
piece.archived = true;
|
|
289
|
+
await self.update(req, piece);
|
|
290
|
+
}, {
|
|
291
|
+
action: 'archive'
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
},
|
|
295
|
+
async restore (req) {
|
|
296
|
+
if (!Array.isArray(req.body._ids)) {
|
|
297
|
+
throw self.apos.error('invalid');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
req.body._ids = req.body._ids.map(_id => {
|
|
301
|
+
return self.inferIdLocaleAndMode(req, _id);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
return self.apos.modules['@apostrophecms/job'].runBatch(
|
|
305
|
+
req,
|
|
306
|
+
req.body._ids,
|
|
307
|
+
async function(req, id) {
|
|
308
|
+
const piece = await self.findOneForEditing(req, { _id: id });
|
|
309
|
+
|
|
310
|
+
if (!piece) {
|
|
311
|
+
throw self.apos.error('notfound');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
piece.archived = false;
|
|
315
|
+
await self.update(req, piece);
|
|
316
|
+
}, {
|
|
317
|
+
action: 'restore'
|
|
318
|
+
}
|
|
319
|
+
);
|
|
320
|
+
},
|
|
262
321
|
':_id/localize': async (req) => {
|
|
263
322
|
const _id = self.inferIdLocaleAndMode(req, req.params._id);
|
|
264
323
|
const draft = await self.findOneForEditing(req.clone({
|
|
@@ -388,20 +447,72 @@ module.exports = {
|
|
|
388
447
|
},
|
|
389
448
|
'apostrophe:modulesRegistered': {
|
|
390
449
|
composeBatchOperations() {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
if (!batchOperation.onlyIf(self.name)) {
|
|
400
|
-
return false;
|
|
450
|
+
const groupedOperations = Object.entries(self.batchOperations)
|
|
451
|
+
.reduce((acc, [ opName, properties ]) => {
|
|
452
|
+
// Check if there is a required schema field for this batch operation.
|
|
453
|
+
const requiredFieldNotFound = properties.requiredField && !self.schema
|
|
454
|
+
.some((field) => field.name === properties.requiredField);
|
|
455
|
+
|
|
456
|
+
if (requiredFieldNotFound) {
|
|
457
|
+
return acc;
|
|
401
458
|
}
|
|
459
|
+
// Find a group for the operation, if there is one.
|
|
460
|
+
const associatedGroup = getAssociatedGroup(opName);
|
|
461
|
+
const currentOperation = {
|
|
462
|
+
action: opName,
|
|
463
|
+
...properties
|
|
464
|
+
};
|
|
465
|
+
const { action, ...props } = getOperationOrGroup(
|
|
466
|
+
currentOperation,
|
|
467
|
+
associatedGroup,
|
|
468
|
+
acc
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
return {
|
|
472
|
+
...acc,
|
|
473
|
+
[action]: {
|
|
474
|
+
...props
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
}, {});
|
|
478
|
+
|
|
479
|
+
self.batchOperations = Object.entries(groupedOperations)
|
|
480
|
+
.map(([ action, properties ]) => ({
|
|
481
|
+
action,
|
|
482
|
+
...properties
|
|
483
|
+
}));
|
|
484
|
+
|
|
485
|
+
function getOperationOrGroup (currentOp, [ groupName, groupProperties ], acc) {
|
|
486
|
+
if (!groupName) {
|
|
487
|
+
// Operation is not grouped. Return it as it is.
|
|
488
|
+
return currentOp;
|
|
402
489
|
}
|
|
403
|
-
|
|
404
|
-
|
|
490
|
+
|
|
491
|
+
// Return the operation group with the new operation added.
|
|
492
|
+
return {
|
|
493
|
+
name: groupName,
|
|
494
|
+
...groupProperties,
|
|
495
|
+
operations: [
|
|
496
|
+
...(acc[groupName] && acc[groupName].operations) || [],
|
|
497
|
+
currentOp
|
|
498
|
+
]
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Returns the object entry, e.g., `[groupName, { ...groupProperties }]`
|
|
503
|
+
function getAssociatedGroup (operation) {
|
|
504
|
+
return Object.entries(self.batchOperationsGroups)
|
|
505
|
+
.find(([ _key, { operations } ]) => {
|
|
506
|
+
return operations.includes(operation);
|
|
507
|
+
}) || [];
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
composeUtilityOperations() {
|
|
511
|
+
self.utilityOperations = Object.entries(self.utilityOperations || {})
|
|
512
|
+
.map(([ action, properties ]) => ({
|
|
513
|
+
action,
|
|
514
|
+
...properties
|
|
515
|
+
}));
|
|
405
516
|
}
|
|
406
517
|
}
|
|
407
518
|
};
|
|
@@ -538,33 +649,37 @@ module.exports = {
|
|
|
538
649
|
// Pass `req`, the `name` of a configured batch operation, and
|
|
539
650
|
// and a function that accepts (req, piece, data),
|
|
540
651
|
// and returns a promise to perform the modification on that
|
|
541
|
-
// one piece (including calling`update` if appropriate).
|
|
652
|
+
// one piece (including calling `update` if appropriate).
|
|
542
653
|
//
|
|
543
654
|
// `data` is an object containing any schema fields specified
|
|
544
655
|
// for the batch operation. If there is no schema it will be
|
|
545
656
|
// an empty object.
|
|
546
657
|
//
|
|
547
|
-
// Replies immediately to the request with `{ jobId: '
|
|
658
|
+
// Replies immediately to the request with `{ jobId: 'xxxxx' }`.
|
|
548
659
|
// This can then be passed to appropriate browser-side APIs
|
|
549
660
|
// to monitor progress.
|
|
550
661
|
//
|
|
551
662
|
// To avoid RAM issues with very large selections while ensuring
|
|
552
663
|
// that all lifecycle events are fired correctly, the current
|
|
553
664
|
// implementation processes the pieces in series.
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
665
|
+
// TODO: restore this method when fully implemented.
|
|
666
|
+
// async batchSimpleRoute(req, name, change) {
|
|
667
|
+
// const batchOperation = _.find(self.batchOperations, { name: name });
|
|
668
|
+
// const schema = batchOperation.schema || [];
|
|
669
|
+
// const data = self.apos.schema.newInstance(schema);
|
|
670
|
+
|
|
671
|
+
// await self.apos.schema.convert(req, schema, req.body, data);
|
|
672
|
+
// await self.apos.modules['@apostrophecms/job'].runBatch(req, one, {
|
|
673
|
+
// // TODO: Update with new progress notification config
|
|
674
|
+
// });
|
|
675
|
+
// async function one(req, id) {
|
|
676
|
+
// const piece = self.findForEditing(req, { _id: id }).toObject();
|
|
677
|
+
// if (!piece) {
|
|
678
|
+
// throw self.apos.error('notfound');
|
|
679
|
+
// }
|
|
680
|
+
// await change(req, piece, data);
|
|
681
|
+
// }
|
|
682
|
+
// },
|
|
568
683
|
|
|
569
684
|
// Accept a piece as untrusted input potentially
|
|
570
685
|
// found in `input` (hint: you can pass `req.body`
|
|
@@ -765,7 +880,7 @@ module.exports = {
|
|
|
765
880
|
return piece;
|
|
766
881
|
},
|
|
767
882
|
getRestQuery(req) {
|
|
768
|
-
const query = self.find(req);
|
|
883
|
+
const query = self.find(req).attachments(true);
|
|
769
884
|
query.applyBuildersSafely(req.query);
|
|
770
885
|
if (!self.apos.permission.can(req, 'view-draft')) {
|
|
771
886
|
if (!self.options.publicApiProjection) {
|
|
@@ -773,7 +888,7 @@ module.exports = {
|
|
|
773
888
|
query.and({
|
|
774
889
|
_id: null
|
|
775
890
|
});
|
|
776
|
-
} else {
|
|
891
|
+
} else if (!query.state.project) {
|
|
777
892
|
query.project(self.options.publicApiProjection);
|
|
778
893
|
}
|
|
779
894
|
}
|
|
@@ -812,6 +927,7 @@ module.exports = {
|
|
|
812
927
|
browserOptions.filters = self.filters;
|
|
813
928
|
browserOptions.columns = self.columns;
|
|
814
929
|
browserOptions.batchOperations = self.batchOperations;
|
|
930
|
+
browserOptions.utilityOperations = self.utilityOperations;
|
|
815
931
|
browserOptions.insertViaUpload = self.options.insertViaUpload;
|
|
816
932
|
browserOptions.quickCreate = !self.options.singleton && self.options.quickCreate && self.apos.permission.can(req, 'edit', self.name, 'draft');
|
|
817
933
|
browserOptions.singleton = self.options.singleton;
|
|
@@ -828,6 +944,7 @@ module.exports = {
|
|
|
828
944
|
editorModal: 'AposDocEditor',
|
|
829
945
|
managerModal: 'AposDocsManager'
|
|
830
946
|
});
|
|
947
|
+
|
|
831
948
|
return browserOptions;
|
|
832
949
|
},
|
|
833
950
|
find(_super, req, criteria, projection) {
|