apostrophe 3.53.0 → 3.55.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.
Files changed (77) hide show
  1. package/CHANGELOG.md +58 -1
  2. package/defaults.js +1 -0
  3. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue +5 -2
  4. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +28 -19
  5. package/modules/@apostrophecms/any-doc-type/index.js +2 -2
  6. package/modules/@apostrophecms/any-page-type/index.js +2 -2
  7. package/modules/@apostrophecms/doc/index.js +55 -29
  8. package/modules/@apostrophecms/doc-type/index.js +11 -6
  9. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocContextMenu.vue +4 -440
  10. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +445 -0
  11. package/modules/@apostrophecms/i18n/i18n/de.json +113 -105
  12. package/modules/@apostrophecms/i18n/i18n/es.json +10 -0
  13. package/modules/@apostrophecms/i18n/i18n/fr.json +8 -0
  14. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +10 -0
  15. package/modules/@apostrophecms/i18n/i18n/sk.json +8 -0
  16. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +1 -0
  17. package/modules/@apostrophecms/log/index.js +429 -0
  18. package/modules/@apostrophecms/login/index.js +47 -4
  19. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +14 -1
  20. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +1 -1
  21. package/modules/@apostrophecms/module/index.js +32 -6
  22. package/modules/@apostrophecms/module/lib/log.js +68 -0
  23. package/modules/@apostrophecms/page/index.js +71 -19
  24. package/modules/@apostrophecms/page/lib/legacy-migrations.js +0 -57
  25. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +8 -285
  26. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +291 -0
  27. package/modules/@apostrophecms/page-type/index.js +39 -26
  28. package/modules/@apostrophecms/piece-type/index.js +19 -11
  29. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +1 -0
  30. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +2 -357
  31. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +2 -86
  32. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArray.vue +2 -254
  33. package/modules/@apostrophecms/schema/ui/apos/components/AposInputAttachment.vue +2 -77
  34. package/modules/@apostrophecms/schema/ui/apos/components/AposInputBoolean.vue +2 -44
  35. package/modules/@apostrophecms/schema/ui/apos/components/AposInputCheckboxes.vue +2 -64
  36. package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +2 -94
  37. package/modules/@apostrophecms/schema/ui/apos/components/AposInputDateAndTime.vue +3 -47
  38. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +2 -82
  39. package/modules/@apostrophecms/schema/ui/apos/components/AposInputPassword.vue +2 -37
  40. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +2 -26
  41. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +2 -57
  42. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +2 -259
  43. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSelect.vue +2 -38
  44. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +2 -275
  45. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +2 -167
  46. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +2 -115
  47. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +3 -279
  48. package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +2 -83
  49. package/modules/@apostrophecms/schema/ui/apos/lib/detectChange.js +10 -1
  50. package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +361 -0
  51. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArea.js +89 -0
  52. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +257 -0
  53. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputAttachment.js +81 -0
  54. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputBoolean.js +48 -0
  55. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputCheckboxes.js +68 -0
  56. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputColor.js +98 -0
  57. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputDateAndTime.js +49 -0
  58. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +86 -0
  59. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputPassword.js +41 -0
  60. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRadio.js +29 -0
  61. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRange.js +60 -0
  62. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +262 -0
  63. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSelect.js +41 -0
  64. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +278 -0
  65. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputString.js +170 -0
  66. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +118 -0
  67. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +281 -0
  68. package/modules/@apostrophecms/schema/ui/apos/logic/AposSearchList.js +85 -0
  69. package/modules/@apostrophecms/template/index.js +1 -1
  70. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeHeader.vue +2 -2
  71. package/modules/@apostrophecms/util/index.js +83 -13
  72. package/modules/@apostrophecms/util/lib/logger.js +19 -17
  73. package/package.json +1 -1
  74. package/test/docs.js +35 -2
  75. package/test/log.js +1765 -0
  76. package/test/pages.js +57 -0
  77. package/test-lib/util.js +1 -1
@@ -0,0 +1,291 @@
1
+ // Pages manager (tree) modal business logic.
2
+
3
+ import AposModifiedMixin from 'Modules/@apostrophecms/ui/mixins/AposModifiedMixin';
4
+ import AposArchiveMixin from 'Modules/@apostrophecms/ui/mixins/AposArchiveMixin';
5
+ import AposPublishMixin from 'Modules/@apostrophecms/ui/mixins/AposPublishMixin';
6
+ import AposDocsManagerMixin from 'Modules/@apostrophecms/modal/mixins/AposDocsManagerMixin';
7
+ import { klona } from 'klona';
8
+
9
+ export default {
10
+ name: 'AposPagesManager',
11
+ mixins: [ AposModifiedMixin, AposDocsManagerMixin, AposArchiveMixin, AposPublishMixin ],
12
+ emits: [ 'archive', 'search', 'safe-close', 'modal-result' ],
13
+ data() {
14
+
15
+ return {
16
+ moduleName: '@apostrophecms/page',
17
+ modal: {
18
+ active: false,
19
+ triggerFocusRefresh: 0,
20
+ type: 'slide',
21
+ showModal: false,
22
+ width: 'two-thirds'
23
+ },
24
+ pages: [],
25
+ pagesFlat: [],
26
+ options: {
27
+ columns: [
28
+ {
29
+ columnHeader: 'apostrophe:pageTitle',
30
+ property: 'title',
31
+ cellValue: 'title'
32
+ },
33
+ {
34
+ name: 'labels',
35
+ component: 'AposCellLabels'
36
+ },
37
+ {
38
+ columnHeader: 'apostrophe:lastEdited',
39
+ property: 'updatedAt',
40
+ component: 'AposCellLastEdited',
41
+ cellValue: 'updatedAt'
42
+ },
43
+ {
44
+ property: 'contextMenu',
45
+ component: 'AposCellContextMenu'
46
+ }
47
+ ]
48
+ },
49
+ treeOptions: {
50
+ bulkSelect: !!this.relationshipField,
51
+ draggable: true,
52
+ ghostUnpublished: true
53
+ },
54
+ moreMenu: [
55
+ {
56
+ label: 'New Page',
57
+ action: 'new'
58
+ }
59
+ ],
60
+ moreMenuButton: {
61
+ tooltip: {
62
+ content: 'More Options',
63
+ placement: 'bottom'
64
+ },
65
+ label: 'More Options',
66
+ icon: 'dots-vertical-icon',
67
+ iconOnly: true,
68
+ type: 'subtle',
69
+ modifiers: [ 'small', 'no-motion' ]
70
+ },
71
+ pageSetMenuSelection: 'live'
72
+ };
73
+ },
74
+ computed: {
75
+ moduleOptions() {
76
+ return apos.page;
77
+ },
78
+ items() {
79
+ if (!this.pages || !this.headers.length) {
80
+ return [];
81
+ }
82
+ return klona(this.pages);
83
+ },
84
+ selectAllChoice() {
85
+ const checkLen = this.checked.length;
86
+ const rowLen = this.pagesFlat.length;
87
+
88
+ return checkLen > 0 && checkLen !== rowLen ? {
89
+ value: 'checked',
90
+ indeterminate: true
91
+ } : {
92
+ value: 'checked'
93
+ };
94
+ },
95
+ saveRelationshipLabel() {
96
+ if (this.relationshipField && (this.relationshipField.max === 1)) {
97
+ return 'Select Page';
98
+ } else {
99
+ return 'Select Pages';
100
+ }
101
+ },
102
+ headers() {
103
+ let headers = this.options.columns || [];
104
+ if (!this.pageSetMenuSelectionIsLive) {
105
+ headers = headers.filter(h => h.component !== 'AposCellLabels');
106
+ }
107
+ return headers;
108
+ },
109
+ pageSetMenu() {
110
+ return [ {
111
+ label: 'apostrophe:live',
112
+ action: 'live',
113
+ modifiers: this.pageSetMenuSelectionIsLive ? [ 'selected', 'disabled' ] : []
114
+ }, {
115
+ label: 'apostrophe:archived',
116
+ action: 'archive',
117
+ modifiers: !this.pageSetMenuSelectionIsLive ? [ 'selected', 'disabled' ] : []
118
+ } ];
119
+ },
120
+ pageSetMenuButton() {
121
+ const button = {
122
+ label: this.pageSetMenuSelectionIsLive ? 'apostrophe:live' : 'apostrophe:archived',
123
+ icon: 'chevron-down-icon',
124
+ modifiers: [ 'no-motion', 'outline', 'icon-right' ],
125
+ class: 'apos-pages-manager__page-set-menu-button'
126
+ };
127
+ return button;
128
+ },
129
+ pageSetMenuSelectionIsLive() {
130
+ return this.pageSetMenuSelection === 'live';
131
+ }
132
+ },
133
+ watch: {
134
+ async pageSetMenuSelection() {
135
+ await this.getPages();
136
+ }
137
+ },
138
+ async mounted() {
139
+ // Get the data. This will be more complex in actuality.
140
+ this.modal.active = true;
141
+ await this.getPages();
142
+ this.modal.triggerFocusRefresh++;
143
+
144
+ apos.bus.$on('content-changed', this.getPages);
145
+ apos.bus.$on('command-menu-manager-create-new', this.create);
146
+ apos.bus.$on('command-menu-manager-close', this.confirmAndCancel);
147
+ },
148
+ destroyed() {
149
+ apos.bus.$off('content-changed', this.getPages);
150
+ apos.bus.$off('command-menu-manager-create-new', this.create);
151
+ apos.bus.$off('command-menu-manager-close', this.confirmAndCancel);
152
+ },
153
+ methods: {
154
+ moreMenuHandler(action) {
155
+ if (action === 'new') {
156
+ this.create();
157
+ }
158
+ },
159
+ async getPages () {
160
+ const self = this;
161
+ if (this.gettingPages) {
162
+ // Avoid race conditions by trying again later if already in progress
163
+ setTimeout(this.getPages, 100);
164
+ return;
165
+ }
166
+ // Not reactive, so not in data()
167
+ this.gettingPages = true;
168
+ try {
169
+ this.pages = [];
170
+ this.pagesFlat = [];
171
+
172
+ let pageTree = (await apos.http.get(
173
+ '/api/v1/@apostrophecms/page', {
174
+ busy: true,
175
+ qs: {
176
+ all: '1',
177
+ archived: this.relationshipField || this.pageSetMenuSelectionIsLive ? '0' : 'any',
178
+ // Also fetch published docs as _publishedDoc subproperties
179
+ withPublished: 1
180
+ },
181
+ draft: true
182
+ }
183
+ ));
184
+
185
+ // If editor is looking at the archive tree, trim the normal page tree response
186
+ if (this.pageSetMenuSelection === 'archive') {
187
+ pageTree = pageTree._children.find(page => page.slug === '/archive');
188
+ pageTree = pageTree._children;
189
+ }
190
+
191
+ formatPage(pageTree);
192
+
193
+ if (!pageTree.length && pageTree.length !== 0) {
194
+ pageTree = [ pageTree ];
195
+ }
196
+
197
+ this.pages = [ ...pageTree ];
198
+
199
+ } finally {
200
+ this.gettingPages = false;
201
+ }
202
+
203
+ function formatPage(page) {
204
+ if (page.length) {
205
+ page.forEach(formatPage);
206
+ return;
207
+ }
208
+
209
+ self.pagesFlat.push(klona(page));
210
+
211
+ if (Array.isArray(page._children)) {
212
+ page._children.forEach(formatPage);
213
+ }
214
+ }
215
+ },
216
+ async update(page) {
217
+ const body = {
218
+ _targetId: page.endContext,
219
+ _position: page.endIndex
220
+ };
221
+
222
+ const route = `${this.moduleOptions.action}/${page.changedId}`;
223
+ try {
224
+ await apos.http.patch(route, {
225
+ busy: true,
226
+ body,
227
+ draft: true
228
+ });
229
+ } catch (error) {
230
+ await apos.notify(error.body.message || this.$t('apostrophe:treeError'), {
231
+ type: 'danger',
232
+ icon: 'alert-circle-icon',
233
+ dismiss: true,
234
+ localize: false
235
+ });
236
+ }
237
+
238
+ await this.getPages();
239
+ if (this.pagesFlat.find(page => {
240
+ return (page.aposDocId === (window.apos.page.page && window.apos.page.page.aposDocId)) && page.archived;
241
+ })) {
242
+ // With the current page gone, we need to move to safe ground
243
+ location.assign(`${window.apos.prefix}/`);
244
+ }
245
+ },
246
+ toggleRowCheck(id) {
247
+ if (this.checked.includes(id)) {
248
+ this.checked = this.checked.filter(item => item !== id);
249
+ } else {
250
+ this.checked.push(id);
251
+ }
252
+ },
253
+ selectAll(event) {
254
+ if (!this.checked.length) {
255
+ this.pagesFlat.forEach((row) => {
256
+ this.toggleRowCheck(row._id);
257
+ });
258
+ return;
259
+ }
260
+
261
+ if (this.checked.length <= this.pagesFlat.length) {
262
+ this.checked.forEach((id) => {
263
+ this.toggleRowCheck(id);
264
+ });
265
+ }
266
+ },
267
+ async create() {
268
+ const doc = await apos.modal.execute(this.moduleOptions.components.editorModal, {
269
+ moduleName: this.moduleName
270
+ });
271
+ if (!doc) {
272
+ // Cancel clicked
273
+ return;
274
+ }
275
+ await this.getPages();
276
+ if (this.relationshipField) {
277
+ doc._fields = doc._fields || {};
278
+ // Must push to checked docs or it will try to do it for us
279
+ // and not include _fields
280
+ this.checkedDocs.push(doc);
281
+ this.checked.push(doc._id);
282
+ }
283
+ },
284
+ setCheckedDocs(checkedDocs) {
285
+ this.checked = checkedDocs.map(doc => doc._id);
286
+ },
287
+ updateCheckedDocs() {
288
+ this.checkedDocs = this.checked.map(_id => this.pagesFlat.find(page => page._id === _id));
289
+ }
290
+ }
291
+ };
@@ -121,10 +121,16 @@ module.exports = {
121
121
  }, {
122
122
  permission: false
123
123
  });
124
- if (published && doc.aposLastTargetId) {
125
- return self.apos.page.move(req.clone({
126
- mode: 'published'
127
- }), published._id, doc.aposLastTargetId.replace(':draft', ':published'), doc.aposLastPosition);
124
+ if (published && (doc.level > 0)) {
125
+ const { lastTargetId, lastPosition } = await self.apos.page.inferLastTargetIdAndPosition(doc);
126
+ return self.apos.page.move(
127
+ req.clone({
128
+ mode: 'published'
129
+ }),
130
+ published._id,
131
+ lastTargetId.replace(':draft', ':published'),
132
+ lastPosition
133
+ );
128
134
  }
129
135
  }
130
136
  },
@@ -200,7 +206,13 @@ module.exports = {
200
206
  // chance we need to "replay" such a move
201
207
  return;
202
208
  }
203
- await self.apos.page.move(publishedReq, result.published._id, result.published.aposLastTargetId, result.published.aposLastPosition);
209
+ const { lastTargetId, lastPosition } = await self.apos.page.inferLastTargetIdAndPosition(result.published);
210
+ await self.apos.page.move(
211
+ publishedReq,
212
+ result.published._id,
213
+ lastTargetId,
214
+ lastPosition
215
+ );
204
216
  const published = await self.apos.page.findOneForEditing(publishedReq, {
205
217
  _id: result.published._id
206
218
  });
@@ -322,27 +334,37 @@ module.exports = {
322
334
  ...options,
323
335
  setModified: false
324
336
  };
325
- if (doc.aposLastTargetId) {
337
+ if (doc.level > 0) {
338
+ const { lastTargetId, lastPosition } = await self.apos.page.inferLastTargetIdAndPosition(doc);
326
339
  // Replay the high level positioning used to place it in the published locale
327
- return self.apos.page.insert(_req, doc.aposLastTargetId.replace(':published', ':draft'), doc.aposLastPosition, draft, options);
328
- } else if (!doc.level) {
340
+ return self.apos.page.insert(
341
+ _req,
342
+ lastTargetId.replace(':published', ':draft'),
343
+ lastPosition,
344
+ draft,
345
+ options
346
+ );
347
+ } else {
329
348
  // Insert the home page
330
349
  return self.apos.doc.insert(_req, draft, options);
331
- } else {
332
- throw new Error('Page inserted without using the page APIs, has no aposLastTargetId and aposLastPosition, cannot insert equivalent draft');
333
350
  }
334
351
  },
335
- // Called for you when a page is inserted in
336
- // the published locale, to ensure there is an equivalent
337
- // draft page. You don't need to invoke this.
352
+ // Called for you when a page is published for the first time.
353
+ // You don't need to invoke this.
338
354
  async insertPublishedOf(req, doc, published, options = {}) {
339
355
  const _req = req.clone({
340
356
  mode: 'published'
341
357
  });
342
- if (doc.aposLastTargetId) {
343
- // Replay the high level positioning used to place it in the published locale
344
- return self.apos.page.insert(_req, doc.aposLastTargetId.replace(':draft', ':published'), doc.aposLastPosition, published, options);
345
- } else if (!doc.level) {
358
+ if (doc.level > 0) {
359
+ const { lastTargetId, lastPosition } = await self.apos.page.inferLastTargetIdAndPosition(doc);
360
+ // Replay the high level positioning used to place it in the draft locale
361
+ return self.apos.page.insert(
362
+ _req,
363
+ lastTargetId.replace(':draft', ':published'),
364
+ lastPosition,
365
+ published,
366
+ options);
367
+ } else {
346
368
  // Insert the home page
347
369
  Object.assign(published, {
348
370
  path: doc.path,
@@ -352,8 +374,6 @@ module.exports = {
352
374
  parkedId: doc.parkedId
353
375
  });
354
376
  return self.apos.doc.insert(_req, published, options);
355
- } else {
356
- throw new Error('insertPublishedOf called on a page that was never inserted via the standard page APIs, has no aposLastTargetId and aposLastPosition, cannot insert equivalent published page');
357
377
  }
358
378
  },
359
379
  // Update a page. The `options` argument may be omitted entirely.
@@ -399,13 +419,6 @@ module.exports = {
399
419
  },
400
420
  copyForPublication(_super, req, from, to) {
401
421
  _super(req, from, to);
402
- const newMode = to.aposLocale.endsWith(':published') ? ':published' : ':draft';
403
- const oldMode = (newMode === ':published') ? ':draft' : ':published';
404
- // Home pages will not have this
405
- if (from.aposLastTargetId) {
406
- to.aposLastTargetId = from.aposLastTargetId.replace(oldMode, newMode);
407
- to.aposLastPosition = from.aposLastPosition;
408
- }
409
422
  to.parkedId = from.parkedId;
410
423
  to.parked = from.parked;
411
424
  },
@@ -232,7 +232,7 @@ module.exports = {
232
232
  getAll: [
233
233
  ...enableCacheOnDemand ? [ expressCacheOnDemand ] : [],
234
234
  async (req) => {
235
- self.publicApiCheck(req);
235
+ await self.publicApiCheckAsync(req);
236
236
  const query = self.getRestQuery(req);
237
237
  if (!query.get('perPage')) {
238
238
  query.perPage(
@@ -272,7 +272,7 @@ module.exports = {
272
272
  ...enableCacheOnDemand ? [ expressCacheOnDemand ] : [],
273
273
  async (req, _id) => {
274
274
  _id = self.inferIdLocaleAndMode(req, _id);
275
- self.publicApiCheck(req);
275
+ await self.publicApiCheckAsync(req);
276
276
  const doc = self.removeForbiddenFields(
277
277
  req,
278
278
  await self.getRestQuery(req).and({ _id }).toObject()
@@ -299,7 +299,7 @@ module.exports = {
299
299
  }
300
300
  ],
301
301
  async post(req) {
302
- self.publicApiCheck(req);
302
+ await self.publicApiCheckAsync(req);
303
303
  if (req.body._newInstance) {
304
304
  const newInstance = self.newInstance();
305
305
  newInstance._previewable = self.addUrlsViaModule && (await self.addUrlsViaModule.readyToAddUrlsToPieces(req, self.name));
@@ -310,12 +310,12 @@ module.exports = {
310
310
  },
311
311
  async put(req, _id) {
312
312
  _id = self.inferIdLocaleAndMode(req, _id);
313
- self.publicApiCheck(req);
313
+ await self.publicApiCheckAsync(req);
314
314
  return self.convertUpdateAndRefresh(req, req.body, _id);
315
315
  },
316
316
  async delete(req, _id) {
317
317
  _id = self.inferIdLocaleAndMode(req, _id);
318
- self.publicApiCheck(req);
318
+ await self.publicApiCheckAsync(req);
319
319
  const piece = await self.findOneForEditing(req, {
320
320
  _id
321
321
  });
@@ -323,7 +323,7 @@ module.exports = {
323
323
  },
324
324
  async patch(req, _id) {
325
325
  _id = self.inferIdLocaleAndMode(req, _id);
326
- self.publicApiCheck(req);
326
+ await self.publicApiCheckAsync(req);
327
327
  return self.convertPatchAndRefresh(req, req.body, _id);
328
328
  }
329
329
  };
@@ -614,7 +614,7 @@ module.exports = {
614
614
 
615
615
  // Return the operation group with the new operation added.
616
616
  return {
617
- name: groupName,
617
+ action: groupName,
618
618
  ...groupProperties,
619
619
  operations: [
620
620
  ...(acc[groupName] && acc[groupName].operations) || [],
@@ -1028,10 +1028,13 @@ module.exports = {
1028
1028
  piece.title = 'Generated #' + (i + 1);
1029
1029
  return piece;
1030
1030
  },
1031
- getRestQuery(req) {
1031
+ // Can be extended on a project level with `_super(req, true)` to disable
1032
+ // permission check and public API projection. You shouldn't do this
1033
+ // if you're not sure what you're doing.
1034
+ getRestQuery(req, omitPermissionCheck = false) {
1032
1035
  const query = self.find(req).attachments(true);
1033
1036
  query.applyBuildersSafely(req.query);
1034
- if (!self.canAccessApi(req)) {
1037
+ if (!omitPermissionCheck && !self.canAccessApi(req)) {
1035
1038
  if (!self.options.publicApiProjection) {
1036
1039
  // Shouldn't be needed thanks to publicApiCheck, but be sure
1037
1040
  query.and({
@@ -1058,6 +1061,11 @@ module.exports = {
1058
1061
  }
1059
1062
  }
1060
1063
  },
1064
+ // An async version of the above. It can be overridden to implement
1065
+ // an asynchronous check of the public API permissions.
1066
+ async publicApiCheckAsync(req) {
1067
+ return self.publicApiCheck(req);
1068
+ },
1061
1069
  // If the piece does not yet have a slug, add one based on the
1062
1070
  // title; throw an error if there is no title
1063
1071
  ensureSlug(piece) {
@@ -1164,8 +1172,8 @@ module.exports = {
1164
1172
 
1165
1173
  return browserOptions;
1166
1174
  },
1167
- find(_super, req, criteria, projection) {
1168
- return _super(req, criteria, projection).defaultSort(self.options.sort || { updatedAt: -1 });
1175
+ find(_super, req, criteria, options) {
1176
+ return _super(req, criteria, options).defaultSort(self.options.sort || { updatedAt: -1 });
1169
1177
  },
1170
1178
  newInstance(_super) {
1171
1179
  if (!self.options.singletonAuto) {
@@ -72,6 +72,7 @@
72
72
  :is-relationship="!!relationshipField"
73
73
  :checked-count="checked.length"
74
74
  :batch-operations="moduleOptions.batchOperations"
75
+ :module-name="moduleName"
75
76
  @select-click="selectAll"
76
77
  @search="onSearch"
77
78
  @page-change="updatePage"