apostrophe 3.13.0 → 3.14.2-alpha.20220401

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.
@@ -6,8 +6,9 @@
6
6
  :class="themeClass"
7
7
  >
8
8
  <div class="apos-login__wrapper">
9
- <transition name="fade-body">
9
+ <transition name="fade-body" mode="out-in">
10
10
  <div
11
+ key="1"
11
12
  class="apos-login__upper"
12
13
  v-if="loaded && phase === 'beforeSubmit'"
13
14
  >
@@ -18,25 +19,19 @@
18
19
  />
19
20
 
20
21
  <div class="apos-login__body">
21
- <form
22
- @submit.prevent="submit"
23
- >
22
+ <form @submit.prevent="submit">
24
23
  <AposSchema
25
24
  :schema="schema"
26
25
  v-model="doc"
27
26
  />
28
- <!-- Do not ask these components to render without their props,
29
- v-show is not enough -->
30
- <template v-if="loaded">
31
- <Component
32
- v-for="requirement in beforeSubmitRequirements"
33
- :key="requirement.name"
34
- :is="requirement.component"
35
- v-bind="getRequirementProps(requirement.name)"
36
- @done="requirementDone(requirement, $event)"
37
- @block="requirementBlock(requirement)"
38
- />
39
- </template>
27
+ <Component
28
+ v-for="requirement in beforeSubmitRequirements"
29
+ :key="requirement.name"
30
+ :is="requirement.component"
31
+ v-bind="getRequirementProps(requirement.name)"
32
+ @done="requirementDone(requirement, $event)"
33
+ @block="requirementBlock(requirement)"
34
+ />
40
35
  <!-- TODO -->
41
36
  <!-- <a href="#" class="apos-login__link">Forgot Password</a> -->
42
37
  <AposButton
@@ -53,8 +48,9 @@
53
48
  </div>
54
49
  </div>
55
50
  <div
51
+ key="2"
56
52
  class="apos-login__upper"
57
- v-else-if="activeSoloRequirement && !fetchingRequirementProps"
53
+ v-else-if="activeSoloRequirement"
58
54
  >
59
55
  <TheAposLoginHeader
60
56
  :env="context.env"
@@ -64,6 +60,7 @@
64
60
  />
65
61
  <div class="apos-login__body">
66
62
  <Component
63
+ v-if="!fetchingRequirementProps"
67
64
  v-bind="getRequirementProps(activeSoloRequirement.name)"
68
65
  :is="activeSoloRequirement.component"
69
66
  :success="activeSoloRequirement.success"
@@ -131,7 +128,8 @@ export default {
131
128
  return this.mounted && this.beforeCreateFinished;
132
129
  },
133
130
  disabled() {
134
- return this.doc.hasErrors || !!this.beforeSubmitRequirements.find(requirement => !requirement.done);
131
+ return this.doc.hasErrors ||
132
+ !!this.beforeSubmitRequirements.find(requirement => !requirement.done);
135
133
  },
136
134
  beforeSubmitRequirements() {
137
135
  return this.requirements.filter(requirement => requirement.phase === 'beforeSubmit');
@@ -277,12 +275,14 @@ export default {
277
275
  location.assign(`${apos.prefix}/`);
278
276
  },
279
277
  async requirementBlock(requirementBlock) {
280
- const requirement = this.requirements.find(requirement => requirement.name === requirementBlock.name);
278
+ const requirement = this.requirements
279
+ .find(requirement => requirement.name === requirementBlock.name);
281
280
  requirement.done = false;
282
281
  requirement.value = undefined;
283
282
  },
284
283
  async requirementDone(requirementDone, value) {
285
- const requirement = this.requirements.find(requirement => requirement.name === requirementDone.name);
284
+ const requirement = this.requirements
285
+ .find(requirement => requirement.name === requirementDone.name);
286
286
 
287
287
  if (requirement.phase === 'beforeSubmit') {
288
288
  requirement.done = true;
@@ -392,12 +392,11 @@ function getRequirements() {
392
392
  transition-delay: 0.6s;
393
393
  }
394
394
 
395
- .fade-leave-active {
395
+ .fade-body-leave-active {
396
396
  transition: all 0.25s linear;
397
- transition-delay: 0;
398
397
  }
399
398
 
400
- .fade-body-enter-to,.fade-body-leave {
399
+ .fade-body-enter-to, .fade-body-leave {
401
400
  transform: translateY(0);
402
401
  }
403
402
 
@@ -459,7 +458,7 @@ function getRequirements() {
459
458
  max-width: $login-container;
460
459
  margin: auto;
461
460
  align-items: center;
462
- justify-content: start;
461
+ justify-content: flex-start;
463
462
  }
464
463
 
465
464
  &__project-version {
@@ -50,7 +50,7 @@ export default {
50
50
  display: flex;
51
51
  flex-direction: column;
52
52
  justify-content: center;
53
- align-items: start;
53
+ align-items: flex-start;
54
54
  width: max-content;
55
55
  }
56
56
 
@@ -231,41 +231,36 @@ module.exports = {
231
231
  // Perform the actual migrations. Implementation of
232
232
  // the @apostrophecms/migration:migrate task
233
233
  async migrate(options) {
234
- await self.apos.lock.lock(self.__meta.name);
235
234
  await self.emit('before');
236
- try {
237
- if (self.apos.isNew) {
238
- // Since the site is brand new (zero documents), we may assume
239
- // it requires no migrations. Mark them all as "done" but note
240
- // that they were skipped, just in case we decide that's an issue later
241
- const at = new Date();
242
- // Just in case the db has no documents but did
243
- // start to run migrations on a previous attempt,
244
- // which causes an occasional unique key error if not
245
- // corrected for here
246
- await self.db.removeMany({});
247
- await self.db.insertMany(self.migrations.map(migration => ({
248
- _id: migration.name,
249
- at,
250
- skipped: true
251
- })));
252
- } else {
253
- for (const migration of self.migrations) {
254
- await self.runOne(migration);
255
- }
235
+ if (self.apos.isNew) {
236
+ // Since the site is brand new (zero documents), we may assume
237
+ // it requires no migrations. Mark them all as "done" but note
238
+ // that they were skipped, just in case we decide that's an issue later
239
+ const at = new Date();
240
+ // Just in case the db has no documents but did
241
+ // start to run migrations on a previous attempt,
242
+ // which causes an occasional unique key error if not
243
+ // corrected for here
244
+ await self.db.removeMany({});
245
+ await self.db.insertMany(self.migrations.map(migration => ({
246
+ _id: migration.name,
247
+ at,
248
+ skipped: true
249
+ })));
250
+ } else {
251
+ for (const migration of self.migrations) {
252
+ await self.runOne(migration);
256
253
  }
257
- // In production, this event is emitted only at the end of the migrate command line task.
258
- // In dev it is emitted at every startup after the automatic migration.
259
- //
260
- // Intentionally emitted regardless of whether the site is new or not.
261
- //
262
- // This is the right time to park pages, for instance, because the
263
- // database is guaranteed to be in a stable state, whether because the
264
- // site is new or because migrations ran successfully.
265
- await self.emit('after');
266
- } finally {
267
- await self.apos.lock.unlock(self.__meta.name);
268
254
  }
255
+ // In production, this event is emitted only at the end of the migrate command line task.
256
+ // In dev it is emitted at every startup after the automatic migration.
257
+ //
258
+ // Intentionally emitted regardless of whether the site is new or not.
259
+ //
260
+ // This is the right time to park pages, for instance, because the
261
+ // database is guaranteed to be in a stable state, whether because the
262
+ // site is new or because migrations ran successfully.
263
+ await self.emit('after');
269
264
  },
270
265
  async runOne(migration) {
271
266
  const info = await self.db.findOne({ _id: migration.name });
@@ -1,6 +1,7 @@
1
1
  const _ = require('lodash');
2
2
  const path = require('path');
3
3
  const { klona } = require('klona');
4
+ const expressCacheOnDemand = require('express-cache-on-demand')();
4
5
 
5
6
  module.exports = {
6
7
  cascades: [ 'batchOperations' ],
@@ -47,6 +48,9 @@ module.exports = {
47
48
  }
48
49
  },
49
50
  async init(self) {
51
+ const { enableCacheOnDemand = true } = self.apos
52
+ .modules['@apostrophecms/express'].options;
53
+ self.enableCacheOnDemand = enableCacheOnDemand;
50
54
  self.typeChoices = self.options.types || [];
51
55
  // If "park" redeclares something with a parkedId present in "minimumPark",
52
56
  // the later one should win
@@ -55,9 +59,11 @@ module.exports = {
55
59
  self.addEditorModal();
56
60
  self.enableBrowserData();
57
61
  self.addLegacyMigrations();
62
+ self.addMisreplicatedParkedPagesMigration();
58
63
  await self.createIndexes();
59
64
  },
60
65
  restApiRoutes(self) {
66
+
61
67
  return {
62
68
  // Trees are arranged in a tree, not a list. So this API returns the home page,
63
69
  // with _children populated if ?_children=1 is in the query string. An editor can
@@ -77,95 +83,102 @@ module.exports = {
77
83
  // If querying for draft pages, you may add ?published=1 to attach a
78
84
  // `_publishedDoc` property to each draft that also exists in a published form.
79
85
 
80
- async getAll(req) {
81
- self.publicApiCheck(req);
82
- const all = self.apos.launder.boolean(req.query.all);
83
- const archived = self.apos.launder.booleanOrNull(req.query.archived);
84
- const flat = self.apos.launder.boolean(req.query.flat);
85
- const autocomplete = self.apos.launder.string(req.query.autocomplete);
86
+ getAll: [
87
+ ...self.enableCacheOnDemand ? [ expressCacheOnDemand ] : [],
88
+ async (req) => {
89
+ self.publicApiCheck(req);
90
+ const all = self.apos.launder.boolean(req.query.all);
91
+ const archived = self.apos.launder.booleanOrNull(req.query.archived);
92
+ const flat = self.apos.launder.boolean(req.query.flat);
93
+ const autocomplete = self.apos.launder.string(req.query.autocomplete);
86
94
 
87
- if (autocomplete.length) {
88
- if (!self.apos.permission.can(req, 'edit', '@apostrophecms/any-page-type')) {
89
- throw self.apos.error('forbidden');
95
+ if (autocomplete.length) {
96
+ if (!self.apos.permission.can(req, 'edit', '@apostrophecms/any-page-type')) {
97
+ throw self.apos.error('forbidden');
98
+ }
99
+ return {
100
+ // For consistency with the pieces REST API we
101
+ // use a results property when returning a flat list
102
+ results: await self.getRestQuery(req).limit(10).relationships(false)
103
+ .areas(false).toArray()
104
+ };
90
105
  }
91
- return {
92
- // For consistency with the pieces REST API we
93
- // use a results property when returning a flat list
94
- results: await self.getRestQuery(req).limit(10).relationships(false)
95
- .areas(false).toArray()
96
- };
97
- }
98
106
 
99
- if (all) {
100
- if (!self.apos.permission.can(req, 'edit', '@apostrophecms/any-page-type')) {
101
- throw self.apos.error('forbidden');
102
- }
103
- const page = await self.getRestQuery(req).and({ level: 0 }).children({
104
- depth: 1000,
105
- archived,
106
- orphan: null,
107
- relationships: false,
108
- areas: false,
109
- permission: false,
110
- withPublished: self.apos.launder.boolean(req.query.withPublished),
111
- project: self.getAllProjection()
112
- }).toObject();
107
+ if (all) {
108
+ if (!self.apos.permission.can(req, 'edit', '@apostrophecms/any-page-type')) {
109
+ throw self.apos.error('forbidden');
110
+ }
111
+ const page = await self.getRestQuery(req).and({ level: 0 }).children({
112
+ depth: 1000,
113
+ archived,
114
+ orphan: null,
115
+ relationships: false,
116
+ areas: false,
117
+ permission: false,
118
+ withPublished: self.apos.launder.boolean(req.query.withPublished),
119
+ project: self.getAllProjection()
120
+ }).toObject();
113
121
 
114
- if (!page) {
115
- throw self.apos.error('notfound');
116
- }
122
+ if (!page) {
123
+ throw self.apos.error('notfound');
124
+ }
117
125
 
118
- if (flat) {
119
- const result = [];
120
- flatten(result, page);
126
+ if (flat) {
127
+ const result = [];
128
+ flatten(result, page);
121
129
 
122
- return {
123
- // For consistency with the pieces REST API we
124
- // use a results property when returning a flat list
125
- results: result
126
- };
130
+ return {
131
+ // For consistency with the pieces REST API we
132
+ // use a results property when returning a flat list
133
+ results: result
134
+ };
135
+ } else {
136
+ return page;
137
+ }
127
138
  } else {
128
- return page;
129
- }
130
- } else {
131
- const result = await self.getRestQuery(req).and({ level: 0 }).toObject();
132
- if (!result) {
133
- throw self.apos.error('notfound');
134
- }
139
+ const result = await self.getRestQuery(req).and({ level: 0 }).toObject();
140
+ if (!result) {
141
+ throw self.apos.error('notfound');
142
+ }
135
143
 
136
- // Attach `_url` and `_urls` properties to the home page
137
- self.apos.attachment.all(result, { annotate: true });
138
- return result;
139
- }
144
+ // Attach `_url` and `_urls` properties to the home page
145
+ self.apos.attachment.all(result, { annotate: true });
146
+ return result;
147
+ }
140
148
 
141
- function flatten(result, node) {
142
- const children = node._children;
143
- node._children = _.map(node._children, '_id');
144
- result.push(node);
145
- _.each(children || [], function(child) {
146
- flatten(result, child);
147
- });
149
+ function flatten(result, node) {
150
+ const children = node._children;
151
+ node._children = _.map(node._children, '_id');
152
+ result.push(node);
153
+ _.each(children || [], function(child) {
154
+ flatten(result, child);
155
+ });
148
156
 
157
+ }
149
158
  }
150
- },
159
+ ],
151
160
  // _id may be a page _id, or the convenient shorthands
152
161
  // `_home` or `_archive`
153
- async getOne(req, _id) {
154
- _id = self.inferIdLocaleAndMode(req, _id);
155
- // Edit access to draft is sufficient to fetch either
156
- self.publicApiCheck(req);
157
- const criteria = self.getIdCriteria(_id);
158
- const result = await self.getRestQuery(req).and(criteria).toObject();
159
- if (!result) {
160
- throw self.apos.error('notfound');
161
- }
162
- if (self.apos.launder.boolean(req.query['render-areas']) === true) {
163
- await self.apos.area.renderDocsAreas(req, [ result ]);
162
+
163
+ getOne: [
164
+ ...self.enableCacheOnDemand ? [ expressCacheOnDemand ] : [],
165
+ async (req, _id) => {
166
+ _id = self.inferIdLocaleAndMode(req, _id);
167
+ // Edit access to draft is sufficient to fetch either
168
+ self.publicApiCheck(req);
169
+ const criteria = self.getIdCriteria(_id);
170
+ const result = await self.getRestQuery(req).and(criteria).toObject();
171
+ if (!result) {
172
+ throw self.apos.error('notfound');
173
+ }
174
+ if (self.apos.launder.boolean(req.query['render-areas']) === true) {
175
+ await self.apos.area.renderDocsAreas(req, [ result ]);
176
+ }
177
+ // Attach `_url` and `_urls` properties
178
+ self.apos.attachment.all(result, { annotate: true });
179
+ return result;
164
180
  }
165
- // Attach `_url` and `_urls` properties
166
- self.apos.attachment.all(result, { annotate: true });
167
- return result;
168
- },
181
+ ],
169
182
  // POST a new page to the site. The schema fields should be part of the JSON request body.
170
183
  //
171
184
  // You may pass `_targetId` and `_position` to specify the location in the page tree.
@@ -576,7 +589,14 @@ database.`);
576
589
  },
577
590
  'apostrophe:ready': {
578
591
  addServeRoute() {
579
- self.apos.app.get('*', self.serve);
592
+ self.apos.app.get('*',
593
+ (req, res, next) => {
594
+ return self.enableCacheOnDemand
595
+ ? expressCacheOnDemand(req, res, next)
596
+ : next();
597
+ },
598
+ self.serve
599
+ );
580
600
  }
581
601
  }
582
602
  };
@@ -1298,9 +1318,6 @@ database.`);
1298
1318
  if (!options) {
1299
1319
  options = {};
1300
1320
  }
1301
- const manager = self.apos.doc.getManager(page.type);
1302
- await manager.emit('beforeUpdate', req, page, options);
1303
- await manager.emit('beforeSave', req, page, options);
1304
1321
  await self.apos.doc.update(req, page, options);
1305
1322
  return page;
1306
1323
  },
@@ -2171,7 +2188,44 @@ database.`);
2171
2188
  }
2172
2189
  }
2173
2190
  },
2174
- ...require('./lib/legacy-migrations')(self)
2191
+ ...require('./lib/legacy-migrations')(self),
2192
+ addMisreplicatedParkedPagesMigration() {
2193
+ self.apos.migration.add('misreplicated-parked-pages', async () => {
2194
+ const parkedPages = await self.apos.doc.db.find({
2195
+ parkedId: {
2196
+ $ne: null
2197
+ }
2198
+ }).toArray();
2199
+ const locales = [ self.apos.i18n.defaultLocale, ...Object.keys(self.apos.i18n.locales) ];
2200
+ const parkedIds = [ ...new Set(parkedPages.map(page => page.parkedId)) ];
2201
+ for (const parkedId of parkedIds) {
2202
+ let aposDocId;
2203
+ for (const locale of locales) {
2204
+ for (const mode of [ 'draft', 'published' ]) {
2205
+ const page = parkedPages.find(page => (page.parkedId === parkedId) && (page.aposLocale === `${locale}:${mode}`));
2206
+ if (!page) {
2207
+ continue;
2208
+ }
2209
+ if (!aposDocId) {
2210
+ aposDocId = page.aposDocId;
2211
+ } else {
2212
+ if (page.aposDocId !== aposDocId) {
2213
+ await self.apos.doc.db.removeOne({
2214
+ _id: page._id
2215
+ });
2216
+ await self.apos.doc.db.insertOne({
2217
+ ...page,
2218
+ _id: `${aposDocId}:${locale}:${mode}`,
2219
+ aposDocId,
2220
+ path: page.path.replace(page.aposDocId, aposDocId)
2221
+ });
2222
+ }
2223
+ }
2224
+ }
2225
+ }
2226
+ }
2227
+ });
2228
+ }
2175
2229
  };
2176
2230
  },
2177
2231
  helpers(self) {
@@ -220,7 +220,7 @@ module.exports = {
220
220
  if (!page) {
221
221
  return false;
222
222
  }
223
- return page._url + '/' + piece.slug;
223
+ return self.apos.util.addSlashIfNeeded(page._url) + piece.slug;
224
224
  },
225
225
 
226
226
  // Adds the `._url` property to all of the provided pieces,
@@ -1,4 +1,5 @@
1
1
  const _ = require('lodash');
2
+ const expressCacheOnDemand = require('express-cache-on-demand')();
2
3
 
3
4
  module.exports = {
4
5
  extend: '@apostrophecms/doc-type',
@@ -164,79 +165,91 @@ module.exports = {
164
165
  self.addManagerModal();
165
166
  self.addEditorModal();
166
167
  },
167
- restApiRoutes: (self) => ({
168
- async getAll(req) {
169
- self.publicApiCheck(req);
170
- const query = self.getRestQuery(req);
171
- if (!query.get('perPage')) {
172
- query.perPage(
173
- self.options.perPage
174
- );
175
- }
176
- const result = {};
177
- // Also populates totalPages when perPage is present
178
- const count = await query.toCount();
179
- if (self.apos.launder.boolean(req.query.count)) {
180
- return {
181
- count
182
- };
183
- }
184
- result.pages = query.get('totalPages');
185
- result.currentPage = query.get('page') || 1;
186
- result.results = await query.toArray();
187
- if (self.apos.launder.boolean(req.query['render-areas']) === true) {
188
- await self.apos.area.renderDocsAreas(req, result.results);
189
- }
190
- if (query.get('choicesResults')) {
191
- result.choices = query.get('choicesResults');
192
- }
193
- if (query.get('countsResults')) {
194
- result.counts = query.get('countsResults');
195
- }
196
- return result;
197
- },
198
- async getOne(req, _id) {
199
- _id = self.inferIdLocaleAndMode(req, _id);
200
- self.publicApiCheck(req);
201
- const doc = await self.getRestQuery(req).and({ _id }).toObject();
202
- if (!doc) {
203
- throw self.apos.error('notfound');
204
- }
205
- if (self.apos.launder.boolean(req.query['render-areas']) === true) {
206
- await self.apos.area.renderDocsAreas(req, [ doc ]);
207
- }
208
- self.apos.attachment.all(doc, { annotate: true });
209
- return doc;
210
- },
211
- async post(req) {
212
- self.publicApiCheck(req);
213
- if (req.body._newInstance) {
214
- const newInstance = self.newInstance();
215
- newInstance._previewable = self.addUrlsViaModule && (await self.addUrlsViaModule.readyToAddUrlsToPieces(req, self.name));
216
- delete newInstance._url;
217
- return newInstance;
168
+ restApiRoutes(self) {
169
+ const { enableCacheOnDemand = true } = self.apos
170
+ .modules['@apostrophecms/express'].options;
171
+
172
+ return {
173
+ getAll: [
174
+ ...enableCacheOnDemand ? [ expressCacheOnDemand ] : [],
175
+ async (req) => {
176
+ self.publicApiCheck(req);
177
+ const query = self.getRestQuery(req);
178
+ if (!query.get('perPage')) {
179
+ query.perPage(
180
+ self.options.perPage
181
+ );
182
+ }
183
+ const result = {};
184
+ // Also populates totalPages when perPage is present
185
+ const count = await query.toCount();
186
+ if (self.apos.launder.boolean(req.query.count)) {
187
+ return {
188
+ count
189
+ };
190
+ }
191
+ result.pages = query.get('totalPages');
192
+ result.currentPage = query.get('page') || 1;
193
+ result.results = await query.toArray();
194
+ if (self.apos.launder.boolean(req.query['render-areas']) === true) {
195
+ await self.apos.area.renderDocsAreas(req, result.results);
196
+ }
197
+ if (query.get('choicesResults')) {
198
+ result.choices = query.get('choicesResults');
199
+ }
200
+ if (query.get('countsResults')) {
201
+ result.counts = query.get('countsResults');
202
+ }
203
+ return result;
204
+ }
205
+ ],
206
+ getOne: [
207
+ ...enableCacheOnDemand ? [ expressCacheOnDemand ] : [],
208
+ async (req, _id) => {
209
+ _id = self.inferIdLocaleAndMode(req, _id);
210
+ self.publicApiCheck(req);
211
+ const doc = await self.getRestQuery(req).and({ _id }).toObject();
212
+ if (!doc) {
213
+ throw self.apos.error('notfound');
214
+ }
215
+ if (self.apos.launder.boolean(req.query['render-areas']) === true) {
216
+ await self.apos.area.renderDocsAreas(req, [ doc ]);
217
+ }
218
+ self.apos.attachment.all(doc, { annotate: true });
219
+ return doc;
220
+ }
221
+ ],
222
+ async post(req) {
223
+ self.publicApiCheck(req);
224
+ if (req.body._newInstance) {
225
+ const newInstance = self.newInstance();
226
+ newInstance._previewable = self.addUrlsViaModule && (await self.addUrlsViaModule.readyToAddUrlsToPieces(req, self.name));
227
+ delete newInstance._url;
228
+ return newInstance;
229
+ }
230
+ return await self.convertInsertAndRefresh(req, req.body);
231
+ },
232
+ async put(req, _id) {
233
+ _id = self.inferIdLocaleAndMode(req, _id);
234
+ self.publicApiCheck(req);
235
+ return self.convertUpdateAndRefresh(req, req.body, _id);
236
+ },
237
+ async delete(req, _id) {
238
+ _id = self.inferIdLocaleAndMode(req, _id);
239
+ self.publicApiCheck(req);
240
+ const piece = await self.findOneForEditing(req, {
241
+ _id
242
+ });
243
+ return self.delete(req, piece);
244
+ },
245
+ async patch(req, _id) {
246
+ _id = self.inferIdLocaleAndMode(req, _id);
247
+ self.publicApiCheck(req);
248
+ return self.convertPatchAndRefresh(req, req.body, _id);
218
249
  }
219
- return await self.convertInsertAndRefresh(req, req.body);
220
- },
221
- async put(req, _id) {
222
- _id = self.inferIdLocaleAndMode(req, _id);
223
- self.publicApiCheck(req);
224
- return self.convertUpdateAndRefresh(req, req.body, _id);
225
- },
226
- async delete(req, _id) {
227
- _id = self.inferIdLocaleAndMode(req, _id);
228
- self.publicApiCheck(req);
229
- const piece = await self.findOneForEditing(req, {
230
- _id
231
- });
232
- return self.delete(req, piece);
233
- },
234
- async patch(req, _id) {
235
- _id = self.inferIdLocaleAndMode(req, _id);
236
- self.publicApiCheck(req);
237
- return self.convertPatchAndRefresh(req, req.body, _id);
238
- }
239
- }),
250
+ };
251
+
252
+ },
240
253
  apiRoutes(self) {
241
254
  return {
242
255
  get: {