apostrophe 4.4.2 → 4.5.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 (85) hide show
  1. package/CHANGELOG.md +62 -3
  2. package/index.js +2 -3
  3. package/lib/glob.js +12 -0
  4. package/lib/moog-require.js +3 -3
  5. package/modules/@apostrophecms/admin-bar/index.js +9 -2
  6. package/modules/@apostrophecms/admin-bar/ui/apos/apps/AposAdminBar.js +1 -1
  7. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +2 -2
  8. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +0 -1
  9. package/modules/@apostrophecms/area/ui/apos/components/AposAreaContextualMenu.vue +2 -2
  10. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +6 -6
  11. package/modules/@apostrophecms/area/ui/apos/components/AposAreaExpandedMenu.vue +7 -1
  12. package/modules/@apostrophecms/asset/index.js +5 -6
  13. package/modules/@apostrophecms/command-menu/index.js +7 -2
  14. package/modules/@apostrophecms/doc/index.js +2 -2
  15. package/modules/@apostrophecms/doc-type/index.js +1 -1
  16. package/modules/@apostrophecms/i18n/i18n/en.json +37 -1
  17. package/modules/@apostrophecms/i18n/index.js +2 -2
  18. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +5 -5
  19. package/modules/@apostrophecms/image/index.js +1 -1
  20. package/modules/@apostrophecms/image/ui/apos/components/AposImageRelationshipEditor.vue +4 -3
  21. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +221 -76
  22. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerDisplay.vue +91 -24
  23. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +48 -51
  24. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerSelections.vue +1 -0
  25. package/modules/@apostrophecms/login/index.js +3 -3
  26. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +3 -11
  27. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +4 -12
  28. package/modules/@apostrophecms/modal/ui/apos/components/AposModalBody.vue +20 -6
  29. package/modules/@apostrophecms/modal/ui/apos/components/AposModalBreadcrumbs.vue +1 -1
  30. package/modules/@apostrophecms/modal/ui/apos/components/AposModalShareDraft.vue +1 -1
  31. package/modules/@apostrophecms/modal/ui/apos/components/AposModalTabs.vue +3 -5
  32. package/modules/@apostrophecms/modal/ui/apos/components/AposWidgetModalTabs.vue +3 -5
  33. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocErrorsMixin.js +2 -2
  34. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +44 -39
  35. package/modules/@apostrophecms/modal/ui/apos/mixins/AposModalTabsMixin.js +2 -2
  36. package/modules/@apostrophecms/oembed/index.js +2 -2
  37. package/modules/@apostrophecms/oembed-field/ui/apos/components/AposInputOembed.vue +2 -2
  38. package/modules/@apostrophecms/page/index.js +5 -2
  39. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +1 -2
  40. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +14 -7
  41. package/modules/@apostrophecms/piece-type/index.js +5 -2
  42. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +23 -11
  43. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +4 -1
  44. package/modules/@apostrophecms/rich-text-widget/index.js +31 -3
  45. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapColor.vue +285 -0
  46. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Color.js +5 -0
  47. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +38 -25
  48. package/modules/@apostrophecms/schema/ui/apos/components/AposInputBoolean.vue +2 -2
  49. package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +0 -1
  50. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +1 -0
  51. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +13 -2
  52. package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +11 -9
  53. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArea.js +4 -4
  54. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +7 -7
  55. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputObject.js +11 -0
  56. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +45 -51
  57. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +3 -1
  58. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +1 -1
  59. package/modules/@apostrophecms/submitted-draft/index.js +2 -2
  60. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +7 -3
  61. package/modules/@apostrophecms/ui/ui/apos/components/AposButtonSplit.vue +0 -1
  62. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +12 -4
  63. package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +4 -4
  64. package/modules/@apostrophecms/ui/ui/apos/components/AposFilterMenu.vue +2 -1
  65. package/modules/@apostrophecms/ui/ui/apos/components/AposLoading.vue +1 -1
  66. package/modules/@apostrophecms/ui/ui/apos/components/AposLoadingBlock.vue +50 -0
  67. package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +0 -1
  68. package/modules/@apostrophecms/ui/ui/apos/components/AposSlatList.vue +6 -4
  69. package/modules/@apostrophecms/ui/ui/apos/components/AposTagApply.vue +17 -11
  70. package/modules/@apostrophecms/ui/ui/apos/components/AposTree.vue +2 -2
  71. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeRows.vue +2 -2
  72. package/modules/@apostrophecms/ui/ui/apos/lib/tooltip.js +2 -2
  73. package/modules/@apostrophecms/ui/ui/apos/stores/modal.js +3 -3
  74. package/modules/@apostrophecms/user/index.js +1 -0
  75. package/modules/@apostrophecms/util/index.js +12 -4
  76. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +2 -2
  77. package/package.json +8 -7
  78. package/scripts/lint-i18n.js +8 -3
  79. package/test/command-menu.js +76 -12
  80. package/test/pages-rest.js +102 -0
  81. package/test/pieces.js +133 -10
  82. package/test/recursionGuard.js +5 -5
  83. package/test/schemas.js +1 -1
  84. package/test/utils/commands.js +26 -0
  85. package/test-lib/util.js +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,58 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.5.0 (2024-07-10)
4
+
5
+ ### Adds
6
+
7
+ * Allow to disable shortcut by setting the option `shortcut: false`
8
+ * Adds a new color picker tool for the rich-text-widget toolbar that matches the existing `color` schema field. This also adds the same `pickerOptions` and `format` options to the rich-text-widget configuration that exist in the `color` schema field.
9
+ * Add missing UI translation keys.
10
+ * Infite scroll in media manager instead of pagination and related search fixes.
11
+ * Improves loaders by using new `AposLoadingBlock` that uses `AposLoading` instead of the purple screen in media manager.
12
+ * Select the configured aspect ratio and add `data-apos-field` attributes to the fields inside `AposImageRelationshipEditor.vue`.
13
+ * Add `getShowAdminBar` method. This method can be overriden in projects to drive the admin bar visibility for logged-in users.
14
+
15
+ ### Fixes
16
+
17
+ * Remove double GET request when saving image update.
18
+ * Fix filter menu forgetting selecting filters and not instantiating them.
19
+ * Remove blur emit for filter buttons and search bar to avoid re requesting when clicking outside…
20
+ * `this.modified` was not working properly (set to false when saving). We can now avoid to reload images when saving no changes.
21
+ * In media manager images checkboxes are disabled when max is reached.
22
+ * In media manager when updating an image or archiving, update the list instead of fetching and update checked documents to see changes in the right panel selected list.
23
+ * The `password` field type now has a proper fallback default, the empty string, just like the string field type
24
+ and its derivatives. This resolves bugs in which the unexpected `null` caused problems during validation. This bug
25
+ was old, but was masked in some situations until the release of version `4.4.3`.
26
+ * Identify and mark server validation errors in the admin UI. This helps editors identify already existing data fields, having validation errors when schema changes (e.g. optional field becomes required).
27
+ * Removes `menu-offset` props that were causing `AposContextMenu` to not display properly.
28
+ * Allows to pass a number or an array to `AposContextMenu` to set the offset of the context menu (main and cross axis see `floating-ui` documentation).
29
+ * Fixes the relationship fields not having the data when coming from the relationship modal.
30
+ * Fixes watch on `checkedDocs` passed to `AposSlatList` not being reactive and not seeing updated relationship fields.
31
+ * Adds styles for 1 column expanded area ([#4608](https://github.com/apostrophecms/apostrophe/issues/4608))
32
+ * Fixes weird slug computations based on followed values like title. Simplifies based on the new tech design.
33
+ * Prevent broken admin UI when there is a missing widget.
34
+ * Fixes media manager not loading images when last infinite scroll page have been reached (when uploading image for example).
35
+ * Upgrade oembetter versions to allow all vimeo urls.
36
+
37
+ ### Changes
38
+
39
+ * Update `Choose Images` selection behavior. When choosing images as part of a relationship, you click on the image or checkbox to add the image to the selection.
40
+ If a max is set to allow only one image, clicking on the selected image will remove it from the selection. Clicking on another image will update the selection with the newly clicked image.
41
+ If a max is set to allow multiple images, you can remove images from the selection by using the checkbox. Clicking on the image will bring the image schema in the right panel.
42
+ You can upload images even if the max has been reached. We will append the uploaded images to the existing selection up to the max if any.
43
+ * Update `@apostrophecms/emulate-mongo-3-driver` dependency to keep supporting `mongodb@3.x` queries while using `mongodb@6.x`.
44
+
45
+ ## 4.4.3 (2024-06-17)
46
+
47
+ ### Fixes
48
+
49
+ * Do not use schema `field.def` when calling `convert`. Applying defaults to new documents is the job of `newInstance()` and similar code.
50
+ If you wish a field to be mandatory use `required: true`.
51
+ * As a convenience, using `POST` for pieces and pages with `_newInstance: true` keeps any additional `req.body` properties in the API response.
52
+ This feature unofficially existed before, it is now supported.
53
+ * Rollbacks watcher on `checked` array. Fixes, checked docs not being properly updated.
54
+
55
+
3
56
  ## 4.4.2 (2024-06-14)
4
57
 
5
58
  ### Fixes
@@ -58,13 +111,19 @@ We will now end up with page B slug as `/peer/page` and not `/peer/peer/page` as
58
111
  * Detect shortcut conflicts when using multiple shortcuts.
59
112
  * Updating schema fields as read-only no longer reset the value when updating the document.
60
113
  * Fixes stylelint config file, uses config from our shared configuration, fixes all lint errors.
61
- * Removes `$nextTick` use to re render schema in `AposArrayEditor` because it was triggering weird vue error in production.
62
- Instead, makes the AposSchema for loop keys more unique using `modelValue.data._id`,
63
- if document changes it re-renders schema fields.
64
114
  * Fixes `TheAposCommandMenu` modals not computing shortcuts from the current opened modal.
65
115
  * Fixes select boxes of relationships, we can now check manually published relationships, and `AposSlatList` renders properly checked relationships.
66
116
  * Fixes issues in `AposInputArray` on production build to be able to add, remove and edit array items after `required` error.
67
117
  * Relationships browse button isn't disabled when max is reached.
118
+ * In media manager images checkboxes are disabled when max is reached.
119
+
120
+ ## 4.3.3 (2024-06-04)
121
+
122
+ ### Fixes
123
+
124
+ * Removes `$nextTick` use to re render schema in `AposArrayEditor` because it was triggering weird vue error in production.
125
+ Instead, makes the AposSchema for loop keys more unique using `modelValue.data._id`,
126
+ if document changes it re-renders schema fields.
68
127
  * In media manager image checkboxes are disabled when max is reached.
69
128
  * Fixes tiptap bubble menu jumping on Firefox when clicking on buttons. Also fixes the fact that
70
129
  double clicking on bubble menu out of buttons would prevent it from closing when unfocusing the rich text area.
package/index.js CHANGED
@@ -9,8 +9,7 @@ const cluster = require('cluster');
9
9
  const { cpus } = require('os');
10
10
  const process = require('process');
11
11
  const npmResolve = require('resolve');
12
- const glob = require('glob');
13
-
12
+ const glob = require('./lib/glob.js');
14
13
  let defaults = require('./defaults.js');
15
14
 
16
15
  // ## Top-level options
@@ -389,7 +388,7 @@ async function apostrophe(options, telemetry, rootSpan) {
389
388
  if (!options.nestedModuleSubdirs) {
390
389
  return;
391
390
  }
392
- const configs = glob.sync(self.localModules + '/**/modules.js', { follow: true });
391
+ const configs = glob(self.localModules + '/**/modules.js', { follow: true });
393
392
  _.each(configs, function(config) {
394
393
  try {
395
394
  _.merge(self.options.modules, require(config));
package/lib/glob.js ADDED
@@ -0,0 +1,12 @@
1
+ const { globSync } = require('glob');
2
+
3
+ // synchronous glob 10 but with the sorting semantics of glob 8,
4
+ // to ease backwards compatibility in Apostrophe startup logic
5
+
6
+ module.exports = (pattern, options) => {
7
+ const result = globSync(pattern, options);
8
+ if (!options.nosort) {
9
+ result.sort((a, b) => a.localeCompare(b, 'en'));
10
+ }
11
+ return result;
12
+ };
@@ -2,7 +2,7 @@ const _ = require('lodash');
2
2
  const fs = require('fs');
3
3
  const npmResolve = require('resolve');
4
4
  const path = require('path');
5
- const glob = require('glob');
5
+ const glob = require('./glob.js');
6
6
  const importFresh = require('import-fresh');
7
7
  const resolveFrom = require('resolve-from');
8
8
  const regExpQuote = require('regexp-quote');
@@ -60,7 +60,7 @@ module.exports = function(options) {
60
60
  // Fetching a list of index.js files on the first call and then searching it each time for
61
61
  // one that refers to the right type name shaves as much as 60 seconds off the startup
62
62
  // time in a large project, compared to using the glob cache feature
63
- self._indexes = glob.sync(self.options.localModules + '/**/index.js', { follow: true });
63
+ self._indexes = glob(self.options.localModules + '/**/index.js', { follow: true });
64
64
  }
65
65
  const matches = self._indexes.filter(function(index) {
66
66
  // Double-check that we're not confusing "@apostrophecms/asset" with "asset" by
@@ -255,7 +255,7 @@ module.exports = function(options) {
255
255
  // Ternary is required because glob expects at least 2 entries when using curly braces
256
256
  const pattern = workspaces.length === 1 ? workspaces[0] : `{${workspaces.join(',')}}`;
257
257
  const packagePath = path.resolve(folder, pattern, 'package.json');
258
- const workspacePackages = glob.sync(packagePath, { follow: true });
258
+ const workspacePackages = glob(packagePath, { follow: true });
259
259
 
260
260
  return workspacePackages.flatMap(packagePath => {
261
261
  const info = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
@@ -5,7 +5,7 @@
5
5
  // takes advantage of this module.
6
6
 
7
7
  const _ = require('lodash');
8
- const cuid = require('cuid');
8
+ const { createId } = require('@paralleldrive/cuid2');
9
9
 
10
10
  module.exports = {
11
11
  options: {
@@ -310,6 +310,12 @@ module.exports = {
310
310
  return self.apos.permission.can(req, item.permission.action, item.permission.type, 'draft');
311
311
  },
312
312
 
313
+ // Show admin bar for logged-in user only
314
+
315
+ getShowAdminBar(req) {
316
+ return !!req.user;
317
+ },
318
+
313
319
  getBrowserData(req) {
314
320
  if (!req.user) {
315
321
  return false;
@@ -351,9 +357,10 @@ module.exports = {
351
357
  },
352
358
  // Base API URL appropriate to the context document
353
359
  contextBar: context && self.apos.doc.getManager(context.type).options.contextBar,
360
+ showAdminBar: self.getShowAdminBar(req),
354
361
  // Simplifies frontend logic
355
362
  contextId: context && context._id,
356
- tabId: cuid(),
363
+ tabId: createId(),
357
364
  contextEditorName,
358
365
  pageTree: self.options.pageTree && self.apos.permission.can(req, 'edit', '@apostrophecms/any-page-type', 'draft'),
359
366
  bars: self.bars,
@@ -4,7 +4,7 @@ export default function() {
4
4
  const component = apos.vueComponents.TheAposAdminBar;
5
5
  // Careful, login page is in user scene but has no admin bar
6
6
  const el = document.querySelector('#apos-admin-bar');
7
- if (!apos.adminBar || !el) {
7
+ if (!apos.adminBar || !el || apos?.adminBar.showAdminBar === false) {
8
8
  return;
9
9
  }
10
10
  const app = createApp(component, { items: apos.adminBar.items || [] });
@@ -35,7 +35,7 @@
35
35
 
36
36
  <script>
37
37
  import { klona } from 'klona';
38
- import cuid from 'cuid';
38
+ import { createId } from '@paralleldrive/cuid2';
39
39
  import AposPublishMixin from 'Modules/@apostrophecms/ui/mixins/AposPublishMixin';
40
40
  import AposAdvisoryLockMixin from 'Modules/@apostrophecms/ui/mixins/AposAdvisoryLockMixin';
41
41
 
@@ -159,7 +159,7 @@ export default {
159
159
  // sessionStorage because it is deliberately browser-tab specific
160
160
  let tabId = sessionStorage.getItem('aposTabId');
161
161
  if (!tabId) {
162
- tabId = cuid();
162
+ tabId = createId();
163
163
  sessionStorage.setItem('aposTabId', tabId);
164
164
  }
165
165
  window.apos.adminBar.tabId = tabId;
@@ -31,7 +31,6 @@
31
31
  :button="draftButton"
32
32
  :menu="draftMenu"
33
33
  :disabled="hasCustomUi || isUnpublished"
34
- menu-offset="13, 10"
35
34
  menu-placement="bottom-end"
36
35
  @item-clicked="switchDraftMode"
37
36
  />
@@ -82,7 +82,7 @@
82
82
  </template>
83
83
 
84
84
  <script>
85
- import cuid from 'cuid';
85
+ import { createId } from '@paralleldrive/cuid2';
86
86
 
87
87
  export default {
88
88
  name: 'AposAreaContextualMenu',
@@ -184,7 +184,7 @@ export default {
184
184
  }
185
185
  },
186
186
  menuId() {
187
- return `areaMenu-${cuid()}`;
187
+ return `areaMenu-${createId()}`;
188
188
  }
189
189
  },
190
190
  mounted() {
@@ -70,7 +70,7 @@
70
70
  </template>
71
71
 
72
72
  <script>
73
- import cuid from 'cuid';
73
+ import { createId } from '@paralleldrive/cuid2';
74
74
  import { klona } from 'klona';
75
75
  import AposThemeMixin from 'Modules/@apostrophecms/ui/mixins/AposThemeMixin';
76
76
 
@@ -148,7 +148,7 @@ export default {
148
148
  addWidgetEditor: null,
149
149
  addWidgetOptions: null,
150
150
  addWidgetType: null,
151
- areaId: cuid(),
151
+ areaId: createId(),
152
152
  next: this.getValidItems(),
153
153
  hoveredWidget: null,
154
154
  hoveredNonForeignWidget: null,
@@ -406,7 +406,7 @@ export default {
406
406
  // Regenerate all array item, area, object and widget ids so they are considered
407
407
  // new. Useful when copying a widget with nested content.
408
408
  regenerateIds(schema, object) {
409
- object._id = cuid();
409
+ object._id = createId();
410
410
  for (const field of schema) {
411
411
  if (field.type === 'array') {
412
412
  for (const item of (object[field.name] || [])) {
@@ -416,7 +416,7 @@ export default {
416
416
  this.regenerateIds(field.schema, object[field.name] || {});
417
417
  } else if (field.type === 'area') {
418
418
  if (object[field.name]) {
419
- object[field.name]._id = cuid();
419
+ object[field.name]._id = createId();
420
420
  for (const item of (object[field.name].items || [])) {
421
421
  const schema = apos.modules[apos.area.widgetManagers[item.type]].schema;
422
422
  this.regenerateIds(schema, item);
@@ -513,7 +513,7 @@ export default {
513
513
  },
514
514
  async insert({ index, widget }) {
515
515
  if (!widget._id) {
516
- widget._id = cuid();
516
+ widget._id = createId();
517
517
  }
518
518
  if (!widget.metaType) {
519
519
  widget.metaType = 'widget';
@@ -600,7 +600,7 @@ export default {
600
600
  schema.forEach(field => {
601
601
  if (field.type === 'area') {
602
602
  widget[field.name] = {
603
- _id: cuid(),
603
+ _id: createId(),
604
604
  metaType: 'area',
605
605
  items: []
606
606
  };
@@ -140,7 +140,7 @@ export default {
140
140
  }
141
141
 
142
142
  const group = {
143
- label: 'Clipboard',
143
+ label: this.$t('apostrophe:areaExpandedMenuClipboard'),
144
144
  widgets: [
145
145
  {
146
146
  type: 'clipboard',
@@ -220,6 +220,12 @@ export default {
220
220
  background-color: var(--a-base-10);
221
221
  }
222
222
 
223
+ &--1-column {
224
+ display: grid;
225
+ grid-template-columns: 1fr;
226
+ gap: 15px;
227
+ }
228
+
223
229
  &--2-columns {
224
230
  display: grid;
225
231
  grid-template-columns: repeat(2, 1fr);
@@ -1,4 +1,3 @@
1
- const glob = require('glob');
2
1
  const fs = require('fs-extra');
3
2
  const Promise = require('bluebird');
4
3
  const webpackModule = require('webpack');
@@ -8,7 +7,7 @@ const util = require('util');
8
7
  const express = require('express');
9
8
  const { stripIndent } = require('common-tags');
10
9
  const { mergeWithCustomize: webpackMerge } = require('webpack-merge');
11
- const cuid = require('cuid');
10
+ const { createId } = require('@paralleldrive/cuid2');
12
11
  const chokidar = require('chokidar');
13
12
  const _ = require('lodash');
14
13
  const {
@@ -110,7 +109,7 @@ module.exports = {
110
109
  components(self) {
111
110
  return {
112
111
  scripts(req, data) {
113
- const placeholder = `[scripts-placeholder:${cuid()}]`;
112
+ const placeholder = `[scripts-placeholder:${createId()}]`;
114
113
 
115
114
  req.scriptsPlaceholder = placeholder;
116
115
 
@@ -119,7 +118,7 @@ module.exports = {
119
118
  };
120
119
  },
121
120
  stylesheets(req, data) {
122
- const placeholder = `[stylesheets-placeholder:${cuid()}]`;
121
+ const placeholder = `[stylesheets-placeholder:${createId()}]`;
123
122
 
124
123
  req.stylesheetsPlaceholder = placeholder;
125
124
 
@@ -247,7 +246,7 @@ module.exports = {
247
246
  const scenes = [ ...new Set(Object.values(self.builds).map(options => options.scenes).flat()) ];
248
247
 
249
248
  // enumerate public assets and include them in deployment if appropriate
250
- const publicAssets = glob.sync('modules/**/*', {
249
+ const publicAssets = self.apos.util.glob('modules/**/*', {
251
250
  cwd: bundleDir,
252
251
  mark: true
253
252
  }).filter(match => !match.endsWith('/'));
@@ -702,7 +701,7 @@ module.exports = {
702
701
  if (seen[entry.dirname]) {
703
702
  continue;
704
703
  }
705
- components = components.concat(glob.sync(`${entry.dirname}/ui/${folder}/${pattern}`));
704
+ components = components.concat(self.apos.util.glob(`${entry.dirname}/ui/${folder}/${pattern}`));
706
705
  seen[entry.dirname] = true;
707
706
  }
708
707
  }
@@ -205,7 +205,8 @@ module.exports = {
205
205
  );
206
206
  command.modal &&
207
207
  assert.equal(typeof command.modal, 'string', `Invalid command modal for ${name}`);
208
- assert.equal(typeof command.shortcut, 'string', `Invalid command shortcut, must be a string, for ${name}`);
208
+ command.shortcut !== false &&
209
+ assert.equal(typeof command.shortcut, 'string', `Invalid command shortcut, must be a string, for ${name}`);
209
210
 
210
211
  return [ true, null ];
211
212
  } catch (error) {
@@ -246,9 +247,13 @@ module.exports = {
246
247
  }
247
248
  },
248
249
  buildCommands(initialState) {
250
+ const additionalRemoves = Object.entries(initialState.composed.commands)
251
+ .filter(([ , field ]) => field.shortcut === false)
252
+ .map(([ name ]) => name);
253
+
249
254
  const concatenate = self.apos.util.omit(
250
255
  initialState.composed.commands,
251
- initialState.composed.removes
256
+ initialState.composed.removes.concat(additionalRemoves)
252
257
  );
253
258
 
254
259
  return {
@@ -1,5 +1,5 @@
1
1
  const _ = require('lodash');
2
- const cuid = require('cuid');
2
+ const { createId } = require('@paralleldrive/cuid2');
3
3
  const { SemanticAttributes } = require('@opentelemetry/semantic-conventions');
4
4
  const { klona } = require('klona');
5
5
 
@@ -1696,7 +1696,7 @@ module.exports = {
1696
1696
  }
1697
1697
  for (const widget of area.items || []) {
1698
1698
  if ((!widget._id) || seen.has(widget._id)) {
1699
- widget._id = cuid();
1699
+ widget._id = createId();
1700
1700
  } else {
1701
1701
  seen.add(widget._id);
1702
1702
  }
@@ -1554,7 +1554,7 @@ module.exports = {
1554
1554
  } else {
1555
1555
  // Dynamic choices from the REST API, but
1556
1556
  // we need a label for "no opinion"
1557
- filter.nullLabel = 'Choose One';
1557
+ filter.nullLabel = 'apostrophe:filterMenuChooseOne';
1558
1558
  }
1559
1559
  });
1560
1560
  },
@@ -33,6 +33,7 @@
33
33
  "archivingWillDeleteDraftChildren": "Child pages that have never been published will be permanently deleted.",
34
34
  "archivingWillLoseDraftChanges": "Unpublished draft changes to this {{ type }} will be permanently deleted.",
35
35
  "archivingWillUnpublish": "This will also un-publish the {{ type }}.",
36
+ "areaExpandedMenuClipboard": "Clipboard",
36
37
  "areYouSure": "Are You Sure?",
37
38
  "arrayCancelDescription": "Do you want to discard changes to this list?",
38
39
  "aspectRatio": "Aspect Ratio",
@@ -195,8 +196,14 @@
195
196
  "fileTypeCannotBeCropped": "{{ extension }} files cannot be cropped, do not present the cropping UI for this type",
196
197
  "fileTypeNotAccepted": "File type was not accepted. Allowed extensions: {{ extensions }}",
197
198
  "files": "Files",
199
+ "fileUploaderAttachmentLimitReached": "Attachment limit reached",
200
+ "fileUploaderDropFile": "Drop a file here or",
201
+ "fileUploaderFieldIsDisabled": "Field is disabled",
202
+ "fileUploaderOpenExplorer": "click to open the file explorer",
198
203
  "filter": "Filter",
199
204
  "filterByTag": "Filter by Tag",
205
+ "filterMenuChooseOne": "Choose One",
206
+ "filterMenuLoadingChoices": "Loading Choices...",
200
207
  "filterResultsByType": "Filter Results By Type",
201
208
  "findOrAddTag": "Find an existing tag or add a new one",
202
209
  "focalPoint": "Select a focal point for this image",
@@ -205,6 +212,9 @@
205
212
  "guest": "Guest",
206
213
  "hideInNavigation": "Hide in Navigation",
207
214
  "home": "Home",
215
+ "i18nCurrentLocale": "Current Locale",
216
+ "i18nCurrentlySelectedLocale": "Currently selected locale",
217
+ "i18nDefaultLocale": "Default locale",
208
218
  "image": "Image",
209
219
  "imageOnReplaceAutocropMessage": "The replaced image has been autocropped for \"{{ title }}\".",
210
220
  "imageOnReplaceAutocropError": "An error occurred while autocropping the replaced image for {{ title }}",
@@ -214,10 +224,12 @@
214
224
  "imageTag": "Image Tag",
215
225
  "imageTags": "Image Tags",
216
226
  "images": "Images",
227
+ "inputFieldIsDisabled": "This field is disabled",
217
228
  "insertAndNew": "{{ saveLabel }} {{ typeLabel }} and create a new one.",
218
229
  "insertAndRedirect": "{{ saveLabel }} {{ typeLabel }} and be redirected to the {{ typeLabel }}.",
219
230
  "insertAndReturn": "{{ saveLabel }} and return to the {{ typeLabel }} listing.",
220
231
  "insertTable": "Insert Table",
232
+ "invalid": "invalid",
221
233
  "lastEdited": "Last Edited",
222
234
  "lastUpdatedBy": "Last saved on {{ updatedAt }} <br /> by {{ updatedBy }}",
223
235
  "leavePageDescription": "The content you're trying to edit belongs to another document and must be edited there.\nChanges made to {{ oldTitle }} are saved automatically.",
@@ -231,7 +243,9 @@
231
243
  "localize": "Localize...",
232
244
  "localizeAllRelated": "Localize all related documents and overwrite existing documents",
233
245
  "localizeContent": "Localize Content",
246
+ "localizeLocalized": "Localized",
234
247
  "localizeNewRelated": "Localize new related documents",
248
+ "localizeNotYetLocalized": "Not Yet Localized",
235
249
  "localized": "Localized the {{ type }} {{ title }} to {{ locale }}",
236
250
  "localizingBusy": "Localizing Content",
237
251
  "logOut": "Log Out",
@@ -266,13 +280,19 @@
266
280
  "mediaDimensions": "Dimensions: {{ width }} 𝗑 {{ height }}",
267
281
  "mediaFileSize": "File Size: {{ fileSize }}",
268
282
  "mediaKB": "{{ size }}KB",
283
+ "mediaLibraryEndReached": "You've reached the end of the media library.",
269
284
  "mediaMB": "{{ size }}MB",
285
+ "mediaManagerErrorRestoring": "Error Restoring",
286
+ "mediaManagerErrorSaving": "Error Saving",
270
287
  "mediaUploadViaDrop": "Drop ’em when you’re ready",
271
288
  "mediaUploadViaExplorer": "Or click to open the file explorer",
272
289
  "mergeCells": "Merge Cells",
273
290
  "minLabel": "Min:",
274
291
  "minSize": "Min size of {{ width }}x{{ height }}",
275
292
  "minUi": "Min: {{ number }}",
293
+ "modalBreadcrumbsDefaultLabel": "Set a label",
294
+ "modalTabsError": "Error",
295
+ "modalTabsErrors": "Errors",
276
296
  "modernBuild": "Public-facing modern JavaScript and Sass",
277
297
  "modify": "Modify",
278
298
  "modifyOrDelete": "Modify / Delete",
@@ -306,6 +326,9 @@
306
326
  "nudgeDown": "Nudge Down",
307
327
  "nudgeUp": "Nudge Up",
308
328
  "numberAdded": "{{ count }} Added",
329
+ "oembedInvalidEmbedUrl": "Invalid embed URL",
330
+ "oembedTypeNotSupported": "Embed type not supported",
331
+ "oembedVideoUrlInvalid": "Video URL invalid",
309
332
  "office": "Office",
310
333
  "openGlobal": "Open Global Site Settings",
311
334
  "openLinkInNewTab": "Open link in new tab",
@@ -341,10 +364,11 @@
341
364
  "public": "Public",
342
365
  "publish": "Publish",
343
366
  "publishBeforeUsingTooltip": "Publish this content before using it in a relationship",
344
- "publishType": "Publish {{ type }}",
367
+ "publishType": "Publish {{ type }}",
345
368
  "published": "Published",
346
369
  "publishingBatchConfirmation": "Are you sure you want to publish {{ count }} {{ type }}?",
347
370
  "publishingBatchConfirmationButton": "Yes, publish content.",
371
+ "required": "required",
348
372
  "rawCssAndJs": "Raw CSS and JS",
349
373
  "rawHtml": "Raw HTML",
350
374
  "rawHtmlCode": "Raw HTML (Code)",
@@ -393,6 +417,7 @@
393
417
  "richTextBold": "Bold",
394
418
  "richTextBulletedList": "Bulleted List",
395
419
  "richTextCodeBlock": "Code Block",
420
+ "richTextColor": "Color",
396
421
  "richTextH2": "Heading 2 (H2)",
397
422
  "richTextH3": "Heading 3 (H3)",
398
423
  "richTextH4": "Heading 4 (H4)",
@@ -415,6 +440,7 @@
415
440
  "richTextStyleConfigWarning": "Misconfigured rich text style: label: {{ label }}, {{ tag }}",
416
441
  "richTextUnderline": "Underline",
417
442
  "richTextUndo": "Undo",
443
+ "role": "Role",
418
444
  "safeModeActive": "Running in safe mode, not showing raw HTML.",
419
445
  "save": "Save",
420
446
  "saveDraft": "Save Draft",
@@ -444,6 +470,7 @@
444
470
  "selectManyLabel": "Select {{ typeLabel }}",
445
471
  "selectOneLabel": "Select {{ typeLabel }}",
446
472
  "selectPage": "Select Page",
473
+ "selectPages": "Select Pages",
447
474
  "selectedMenuItem": "✓ {{ label }}",
448
475
  "sentenceJoiner": " ",
449
476
  "settings": "Personal Settings",
@@ -463,6 +490,7 @@
463
490
  "submitUpdate": "Submit Update",
464
491
  "submitted": "Submitted",
465
492
  "submittedDraft": "Submitted Draft",
493
+ "submittedDrafts": "Submitted Drafts",
466
494
  "submittedForReview": "Submitted to Admins and Editors for review",
467
495
  "subscript": "Subscript",
468
496
  "suggestionsHeader": "Try one of these suggestions:",
@@ -470,6 +498,14 @@
470
498
  "switchLocalesAndLocalizePage": "Switch locales and localize page to {{ label }}?",
471
499
  "table": "Table",
472
500
  "tableDescription": "Add tabular content to your page",
501
+ "tagAddTag": "Add Tag",
502
+ "tagSearchApplyTags": "Apply Tags",
503
+ "tagSearchPlaceholder": "Tags...",
504
+ "tagCreateTag": "Create tag \"{{ tag }}\"",
505
+ "tagCreateNewTag": "Create new tag",
506
+ "tagNewTag": "New Tag",
507
+ "tagNoTagsFoundCreateOne": "create {{ tag }}?",
508
+ "tagNoTagsFoundPerhaps": "We couldn't find any matching tags. Perhaps",
473
509
  "tagYourImages": "Tag your images to make searching and filtering the media manager easier",
474
510
  "tags": "Tags",
475
511
  "takeActionAndCreateNew": "{{ saveLabel }} and Create New",
@@ -595,7 +595,7 @@ module.exports = {
595
595
  // if the appropriate query parameters were set, rewrite
596
596
  // `_id` accordingly. Returns `_id`, after rewriting if appropriate.
597
597
  inferIdLocaleAndMode(req, _id) {
598
- let [ cuid, locale, mode ] = _id.split(':');
598
+ let [ id, locale, mode ] = _id.split(':');
599
599
  if (locale && mode) {
600
600
  if (!req.query.aposLocale) {
601
601
  req.locale = locale;
@@ -621,7 +621,7 @@ module.exports = {
621
621
  // will be interpreted later
622
622
  return _id;
623
623
  } else {
624
- return `${cuid}:${locale}:${mode}`;
624
+ return `${id}:${locale}:${mode}`;
625
625
  }
626
626
  },
627
627
  getBrowserData(req) {
@@ -120,15 +120,15 @@
120
120
  icon="map-marker-icon"
121
121
  class="apos-current-locale-icon"
122
122
  :icon-size="14"
123
- title="Default locale"
124
- tooltip="Current Locale"
123
+ :title="$t('apostrophe:i18nDefaultLocale')"
124
+ tooltip="apostrophe:i18nCurrentLocale"
125
125
  />
126
126
  <AposIndicator
127
127
  v-if="isSelected(loc)"
128
128
  icon="check-bold-icon"
129
129
  class="apos-check-icon"
130
130
  :icon-size="10"
131
- title="Currently selected locale"
131
+ :title="$t('apostrophe:i18nCurrentlySelectedLocale')"
132
132
  />
133
133
  {{ loc.label }}
134
134
  <span class="apos-locale-name">
@@ -136,8 +136,8 @@
136
136
  </span>
137
137
  <span
138
138
  v-apos-tooltip="isLocalized(loc)
139
- ? 'Localized'
140
- : 'Not Yet Localized'"
139
+ ? 'apostrophe:localizeLocalized'
140
+ : 'apostrophe:localizeNotYetLocalized'"
141
141
  class="apos-locale-localized"
142
142
  :class="{
143
143
  'apos-state-is-localized': isLocalized(loc),
@@ -17,7 +17,7 @@ module.exports = {
17
17
  label: 'apostrophe:image',
18
18
  pluralLabel: 'apostrophe:images',
19
19
  alias: 'image',
20
- perPage: 31,
20
+ perPage: 50,
21
21
  sort: { createdAt: -1 },
22
22
  quickCreate: false,
23
23
  insertViaUpload: true,
@@ -25,7 +25,7 @@
25
25
  <template #leftRail>
26
26
  <AposModalRail>
27
27
  <div class="apos-schema">
28
- <div class="apos-field">
28
+ <div class="apos-field" data-apos-field="aspectRatio">
29
29
  <label class="apos-field__label">
30
30
  {{ $t('apostrophe:aspectRatio') }}
31
31
  <AposIndicator
@@ -37,6 +37,7 @@
37
37
  />
38
38
  </label>
39
39
  <AposSelect
40
+ :selected="aspectRatio"
40
41
  :choices="aspectRatios"
41
42
  :disabled="disableAspectRatio"
42
43
  @change="updateAspectRatio"
@@ -58,7 +59,7 @@
58
59
  }}
59
60
  </div>
60
61
  <div class="apos-schema__aligned-fields">
61
- <div class="apos-field">
62
+ <div class="apos-field" data-apos-field="width">
62
63
  <label class="apos-field__label apos-field__label--aligned">
63
64
  W
64
65
  </label>
@@ -72,7 +73,7 @@
72
73
  @blur="blurInput"
73
74
  >
74
75
  </div>
75
- <div class="apos-field">
76
+ <div class="apos-field" data-apos-field="height">
76
77
  <label class="apos-field__label apos-field__label--aligned">
77
78
  H
78
79
  </label>