apostrophe 4.28.0 → 4.29.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 (88) hide show
  1. package/CHANGELOG.md +33 -3
  2. package/README.md +142 -0
  3. package/defaults.js +1 -0
  4. package/lib/safe-json-script.js +27 -0
  5. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +1 -1
  6. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +1 -0
  7. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +3 -5
  8. package/modules/@apostrophecms/area/ui/apos/components/AposBreadcrumbOperations.vue +13 -1
  9. package/modules/@apostrophecms/asset/lib/globalIcons.js +3 -0
  10. package/modules/@apostrophecms/attachment/index.js +43 -1
  11. package/modules/@apostrophecms/color-field/index.js +7 -1
  12. package/modules/@apostrophecms/doc/index.js +11 -1
  13. package/modules/@apostrophecms/doc-type/index.js +165 -32
  14. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +1 -1
  15. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +104 -59
  16. package/modules/@apostrophecms/file/index.js +109 -9
  17. package/modules/@apostrophecms/i18n/i18n/de.json +0 -2
  18. package/modules/@apostrophecms/i18n/i18n/en.json +40 -1
  19. package/modules/@apostrophecms/i18n/i18n/es.json +0 -1
  20. package/modules/@apostrophecms/i18n/i18n/fr.json +0 -1
  21. package/modules/@apostrophecms/i18n/i18n/it.json +0 -1
  22. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +0 -1
  23. package/modules/@apostrophecms/i18n/i18n/sk.json +0 -1
  24. package/modules/@apostrophecms/i18n/ui/apos/apps/AposI18nBatchReporting.js +18 -1
  25. package/modules/@apostrophecms/i18n/ui/apos/apps/AposI18nLocalizeActions.js +50 -0
  26. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +56 -13
  27. package/modules/@apostrophecms/image/ui/apos/components/AposImageRelationshipEditor.vue +8 -2
  28. package/modules/@apostrophecms/layout-column-widget/index.js +156 -163
  29. package/modules/@apostrophecms/layout-widget/index.js +7 -2
  30. package/modules/@apostrophecms/layout-widget/ui/apos/components/AposAreaLayoutEditor.vue +6 -11
  31. package/modules/@apostrophecms/layout-widget/ui/apos/components/AposGridColumn.vue +3 -5
  32. package/modules/@apostrophecms/layout-widget/ui/apos/components/AposGridLayout.vue +4 -4
  33. package/modules/@apostrophecms/layout-widget/ui/apos/components/AposGridManager.vue +0 -16
  34. package/modules/@apostrophecms/layout-widget/ui/apos/lib/grid-state.mjs +7 -27
  35. package/modules/@apostrophecms/layout-widget/views/column.html +7 -9
  36. package/modules/@apostrophecms/login/index.js +39 -40
  37. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +17 -2
  38. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +3 -2
  39. package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +1 -0
  40. package/modules/@apostrophecms/page/index.js +2 -0
  41. package/modules/@apostrophecms/piece-type/index.js +3 -1
  42. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +1 -0
  43. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +5 -0
  44. package/modules/@apostrophecms/recently-edited/index.js +831 -0
  45. package/modules/@apostrophecms/recently-edited/ui/apos/components/AposCellTitle.vue +54 -0
  46. package/modules/@apostrophecms/recently-edited/ui/apos/components/AposRecentlyEditedCombo.vue +454 -0
  47. package/modules/@apostrophecms/recently-edited/ui/apos/components/AposRecentlyEditedFilterTag.vue +75 -0
  48. package/modules/@apostrophecms/recently-edited/ui/apos/components/AposRecentlyEditedFilters.vue +287 -0
  49. package/modules/@apostrophecms/recently-edited/ui/apos/components/AposRecentlyEditedIcon.vue +16 -0
  50. package/modules/@apostrophecms/recently-edited/ui/apos/components/AposRecentlyEditedManager.vue +346 -0
  51. package/modules/@apostrophecms/recently-edited/ui/apos/composables/useRecentlyEditedBatch.js +193 -0
  52. package/modules/@apostrophecms/recently-edited/ui/apos/composables/useRecentlyEditedData.js +276 -0
  53. package/modules/@apostrophecms/recently-edited/ui/apos/composables/useRecentlyEditedFetch.js +199 -0
  54. package/modules/@apostrophecms/recently-edited/ui/apos/composables/useRecentlyEditedFilters.js +100 -0
  55. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +8 -4
  56. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +1 -1
  57. package/modules/@apostrophecms/styles/index.js +10 -0
  58. package/modules/@apostrophecms/styles/lib/apiRoutes.js +6 -0
  59. package/modules/@apostrophecms/styles/lib/handlers.js +5 -0
  60. package/modules/@apostrophecms/styles/lib/methods.js +9 -3
  61. package/modules/@apostrophecms/styles/lib/presets.js +119 -0
  62. package/modules/@apostrophecms/styles/ui/apos/components/TheAposStyles.vue +3 -8
  63. package/modules/@apostrophecms/styles/ui/apos/composables/AposStyles.js +1 -3
  64. package/modules/@apostrophecms/styles/ui/apos/render-factory.js +29 -0
  65. package/modules/@apostrophecms/styles/ui/apos/universal/backgroundHelpers.mjs +140 -0
  66. package/modules/@apostrophecms/styles/ui/apos/universal/customRules.mjs +105 -0
  67. package/modules/@apostrophecms/styles/ui/apos/universal/render.mjs +195 -15
  68. package/modules/@apostrophecms/template/index.js +22 -6
  69. package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +2 -0
  70. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +18 -4
  71. package/modules/@apostrophecms/ui/ui/apos/composables/useInfiniteScroll.js +91 -0
  72. package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +1 -0
  73. package/modules/@apostrophecms/ui/ui/apos/stores/modal.js +5 -2
  74. package/modules/@apostrophecms/ui/ui/apos/utils/index.js +9 -0
  75. package/modules/@apostrophecms/url/index.js +38 -4
  76. package/modules/@apostrophecms/widget-type/index.js +22 -6
  77. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +8 -4
  78. package/package.json +19 -19
  79. package/test/files.js +129 -0
  80. package/test/layout-widget-migration.js +719 -0
  81. package/test/login-requirements.js +1 -1
  82. package/test/pieces-public-api.js +80 -0
  83. package/test/pieces.js +25 -0
  84. package/test/recently-edited.js +2311 -0
  85. package/test/schemas.js +39 -3
  86. package/test/static-build.js +642 -0
  87. package/test/styles.js +2569 -0
  88. package/modules/@apostrophecms/layout-widget/ui/apos/components/AposLayoutColControlDialog.vue +0 -171
@@ -293,68 +293,67 @@ module.exports = {
293
293
  },
294
294
  ...self.isPasswordResetEnabled() && {
295
295
  async resetRequest(req) {
296
- const wait = (t = 2000) => Promise.delay(t);
296
+ const MIN_RESPONSE_TIME = 2000;
297
+ const startTime = Date.now();
297
298
  const site = (req.headers.host || '').replace(/:\d+$/, '');
298
299
  const email = self.apos.launder.string(req.body.email);
299
300
  if (!email.length) {
300
301
  throw self.apos.error('invalid', req.t('apostrophe:loginResetEmailRequired'));
301
302
  }
302
303
  let user;
303
- // error not reported to browser for security reasons
304
304
  try {
305
305
  user = await self.getPasswordResetUser(req.body.email);
306
306
  } catch (e) {
307
307
  self.apos.util.error(e);
308
308
  }
309
309
  if (!user) {
310
- await wait();
311
310
  self.apos.util.error(
312
311
  `Reset password request error - the user ${email} doesn\`t exist.`
313
312
  );
314
- return;
315
- }
316
- if (!user.email) {
317
- await wait();
313
+ } else if (!user.email) {
318
314
  self.apos.util.error(
319
315
  `Reset password request error - the user ${user.username} doesn\`t have an email.`
320
316
  );
321
- return;
322
- }
323
- const reset = self.apos.util.generateId();
324
- user.passwordReset = reset;
325
- user.passwordResetAt = new Date();
326
- await self.apos.user.update(req, user, { permissions: false });
327
- // Fix - missing host in the absoluteUrl results in a panic.
328
- let port = (req.headers.host || '').split(':')[1];
329
- if (!port || [ '80', '443' ].includes(port)) {
330
- port = '';
331
317
  } else {
332
- port = `:${port}`;
318
+ const reset = self.apos.util.generateId();
319
+ user.passwordReset = reset;
320
+ user.passwordResetAt = new Date();
321
+ await self.apos.user.update(req, user, { permissions: false });
322
+ let port = (req.headers.host || '').split(':')[1];
323
+ if (!port || [ '80', '443' ].includes(port)) {
324
+ port = '';
325
+ } else {
326
+ port = `:${port}`;
327
+ }
328
+ const parsed = new URL(
329
+ req.absoluteUrl,
330
+ self.apos.baseUrl
331
+ ? undefined
332
+ : `${req.protocol}://${req.hostname}${port}`
333
+ );
334
+ parsed.pathname = self.login();
335
+ parsed.search = '?';
336
+ parsed.searchParams.append('reset', reset);
337
+ parsed.searchParams.append('email', user.email);
338
+ try {
339
+ await self.email(req, 'passwordResetEmail', {
340
+ user,
341
+ url: parsed.toString(),
342
+ site
343
+ }, {
344
+ to: user.email,
345
+ subject: req.t('apostrophe:passwordResetRequest', { site })
346
+ });
347
+ } catch (err) {
348
+ self.apos.util.error(`Error while sending email to ${user.email}`, err);
349
+ }
333
350
  }
334
- const parsed = new URL(
335
- req.absoluteUrl,
336
- self.apos.baseUrl
337
- ? undefined
338
- : `${req.protocol}://${req.hostname}${port}`
339
- );
340
- parsed.pathname = self.login();
341
- parsed.search = '?';
342
- parsed.searchParams.append('reset', reset);
343
- parsed.searchParams.append('email', user.email);
344
- try {
345
- await self.email(req, 'passwordResetEmail', {
346
- user,
347
- url: parsed.toString(),
348
- site
349
- }, {
350
- to: user.email,
351
- subject: req.t('apostrophe:passwordResetRequest', { site })
352
- });
353
- } catch (err) {
354
- self.apos.util.error(`Error while sending email to ${user.email}`, err);
351
+ // Pad all paths to a constant minimum duration
352
+ const elapsed = Date.now() - startTime;
353
+ if (elapsed < MIN_RESPONSE_TIME) {
354
+ await Promise.delay(MIN_RESPONSE_TIME - elapsed);
355
355
  }
356
356
  },
357
-
358
357
  async reset(req) {
359
358
  const password = self.apos.launder.string(req.body.password);
360
359
  if (!password.length) {
@@ -158,6 +158,10 @@ export default {
158
158
  moduleName: {
159
159
  type: String,
160
160
  required: true
161
+ },
162
+ searchPlaceholder: {
163
+ type: [ String, Object ],
164
+ default: null
161
165
  }
162
166
  },
163
167
  emits: [
@@ -173,7 +177,7 @@ export default {
173
177
  searchField: {
174
178
  field: {
175
179
  name: 'search',
176
- placeholder: {
180
+ placeholder: this.searchPlaceholder || {
177
181
  key: 'apostrophe:searchDocType',
178
182
  type: this.$t(this.labels.plural)
179
183
  },
@@ -197,7 +201,7 @@ export default {
197
201
  }
198
202
  },
199
203
  canSelectAll() {
200
- return this.displayedItems;
204
+ return this.displayedItems && (this.batchOperations.length || this.isRelationship);
201
205
  },
202
206
  canArchive() {
203
207
  return this.checkedCount;
@@ -206,6 +210,17 @@ export default {
206
210
  return !this.options.noSearch;
207
211
  }
208
212
  },
213
+ watch: {
214
+ batchOperations() {
215
+ this.computeActiveOperations();
216
+ },
217
+ filterValues: {
218
+ deep: true,
219
+ handler() {
220
+ this.computeActiveOperations();
221
+ }
222
+ }
223
+ },
209
224
  mounted () {
210
225
  this.computeActiveOperations();
211
226
  apos.bus.$on('command-menu-manager-select-all', this.selectAll);
@@ -100,7 +100,7 @@
100
100
  <slot name="localeDisplay" />
101
101
  </div>
102
102
  <AposLocale
103
- v-else-if="hasBeenLocalized"
103
+ v-else-if="hasBeenLocalized && !isCrossLocale"
104
104
  class="apos-modal__locale"
105
105
  :locale="currentLocale"
106
106
  />
@@ -205,7 +205,8 @@ const modalInnerEl = useTemplateRef('modalInnerEl');
205
205
  const findPriorityFocusElementRetryMax = ref(3);
206
206
  const currentPriorityFocusElementRetry = ref(0);
207
207
  const renderingElements = ref(true);
208
- const currentLocale = ref(store.activeModal?.locale || apos.i18n.locale);
208
+ const currentLocale = computed(() => store.activeModal?.locale || apos.i18n.locale);
209
+ const isCrossLocale = computed(() => store.activeModal?.crossLocale || false);
209
210
  const nonDraggableElements = [
210
211
  '.apos-input-wrapper',
211
212
  '.apos-field--inline-array-field',
@@ -49,6 +49,7 @@
49
49
  <button
50
50
  v-if="!process"
51
51
  class="apos-notification__button"
52
+ data-apos-test="notification-close"
52
53
  @click="close"
53
54
  >
54
55
  <Close
@@ -2951,10 +2951,12 @@ database.`);
2951
2951
  _id: null
2952
2952
  });
2953
2953
  } else {
2954
+ // Note that we MUST NOT honor the "project" query builder here
2954
2955
  query.project({
2955
2956
  ...self.options.publicApiProjection,
2956
2957
  cacheInvalidatedAt: 1
2957
2958
  });
2959
+ query.set('publicApiProjection', self.options.publicApiProjection);
2958
2960
  }
2959
2961
  }
2960
2962
  return query;
@@ -1126,11 +1126,13 @@ module.exports = {
1126
1126
  query.and({
1127
1127
  _id: null
1128
1128
  });
1129
- } else if (!query.state.project) {
1129
+ } else {
1130
+ // Note that we MUST NOT honor the "project" query builder here
1130
1131
  query.project({
1131
1132
  ...self.options.publicApiProjection,
1132
1133
  cacheInvalidatedAt: 1
1133
1134
  });
1135
+ query.set('publicApiProjection', self.options.publicApiProjection);
1134
1136
  }
1135
1137
  }
1136
1138
  return query;
@@ -125,6 +125,7 @@
125
125
  :headers="headers"
126
126
  :options="{
127
127
  ...moduleOptions,
128
+ isRelationship: !!relationshipField,
128
129
  disableUnchecked: maxReached(),
129
130
  disableUnpublished: disableUnpublished,
130
131
  manuallyPublished: manuallyPublished
@@ -3,6 +3,7 @@
3
3
  <tbody>
4
4
  <tr>
5
5
  <th
6
+ v-if="hasBatchOperations"
6
7
  class="apos-table__header"
7
8
  />
8
9
  <th
@@ -46,6 +47,7 @@
46
47
  @mouseout="out(item._id)"
47
48
  >
48
49
  <td
50
+ v-if="hasBatchOperations"
49
51
  class="apos-table__cell"
50
52
  >
51
53
  <AposCheckbox
@@ -166,6 +168,9 @@ export default {
166
168
  },
167
169
  contextMenuOptions() {
168
170
  return this.options;
171
+ },
172
+ hasBatchOperations() {
173
+ return this.options.batchOperations?.length > 0 || this.options.isRelationship;
169
174
  }
170
175
  },
171
176
  watch: {