apostrophe 4.3.2 → 4.4.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 (158) hide show
  1. package/.stylelintrc +1 -93
  2. package/CHANGELOG.md +56 -0
  3. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBar.vue +7 -1
  4. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarLocale.vue +6 -3
  5. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarUser.vue +2 -1
  6. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue +1 -0
  7. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +3 -2
  8. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposSavingIndicator.vue +6 -2
  9. package/modules/@apostrophecms/any-page-type/index.js +2 -2
  10. package/modules/@apostrophecms/area/ui/apos/components/AposAreaContextualMenu.vue +13 -7
  11. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +4 -4
  12. package/modules/@apostrophecms/area/ui/apos/components/AposAreaExpandedMenu.vue +17 -11
  13. package/modules/@apostrophecms/area/ui/apos/components/AposAreaMenu.vue +13 -7
  14. package/modules/@apostrophecms/area/ui/apos/components/AposAreaMenuItem.vue +3 -0
  15. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +36 -20
  16. package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +18 -11
  17. package/modules/@apostrophecms/busy/ui/apos/components/TheAposBusy.vue +6 -8
  18. package/modules/@apostrophecms/command-menu/index.js +12 -7
  19. package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuKey.vue +9 -7
  20. package/modules/@apostrophecms/command-menu/ui/apos/components/AposCommandMenuShortcut.vue +28 -15
  21. package/modules/@apostrophecms/command-menu/ui/apos/components/TheAposCommandMenu.vue +44 -42
  22. package/modules/@apostrophecms/doc-type/index.js +2 -1
  23. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +4 -3
  24. package/modules/@apostrophecms/file-tag/index.js +1 -1
  25. package/modules/@apostrophecms/global/index.js +29 -0
  26. package/modules/@apostrophecms/i18n/i18n/de.json +20 -0
  27. package/modules/@apostrophecms/i18n/i18n/en.json +1 -0
  28. package/modules/@apostrophecms/i18n/i18n/es.json +1 -0
  29. package/modules/@apostrophecms/i18n/i18n/fr.json +1 -0
  30. package/modules/@apostrophecms/i18n/i18n/it.json +1 -0
  31. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +1 -0
  32. package/modules/@apostrophecms/i18n/i18n/sk.json +1 -0
  33. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +25 -17
  34. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalizeErrors.vue +5 -4
  35. package/modules/@apostrophecms/image/index.js +2 -1
  36. package/modules/@apostrophecms/image/ui/apos/components/AposImageCropper.vue +1 -1
  37. package/modules/@apostrophecms/image/ui/apos/components/AposImageRelationshipEditor.vue +9 -8
  38. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +13 -10
  39. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerDisplay.vue +11 -7
  40. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +12 -2
  41. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerSelections.vue +8 -5
  42. package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +17 -8
  43. package/modules/@apostrophecms/image-tag/index.js +1 -1
  44. package/modules/@apostrophecms/login/ui/apos/components/AposLoginForm.vue +1 -0
  45. package/modules/@apostrophecms/login/ui/apos/components/AposResetPasswordForm.vue +1 -0
  46. package/modules/@apostrophecms/login/ui/apos/components/TheAposLogin.vue +16 -14
  47. package/modules/@apostrophecms/login/ui/apos/components/TheAposLoginHeader.vue +9 -4
  48. package/modules/@apostrophecms/modal/ui/apos/apps/AposModals.js +11 -59
  49. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +1 -0
  50. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +59 -55
  51. package/modules/@apostrophecms/modal/ui/apos/components/AposModalBody.vue +1 -0
  52. package/modules/@apostrophecms/modal/ui/apos/components/AposModalBreadcrumbs.vue +5 -0
  53. package/modules/@apostrophecms/modal/ui/apos/components/AposModalConfirm.vue +6 -10
  54. package/modules/@apostrophecms/modal/ui/apos/components/AposModalShareDraft.vue +14 -16
  55. package/modules/@apostrophecms/modal/ui/apos/components/AposModalTabs.vue +68 -5
  56. package/modules/@apostrophecms/modal/ui/apos/components/AposWidgetModalTabs.vue +15 -9
  57. package/modules/@apostrophecms/modal/ui/apos/components/TheAposModals.vue +48 -122
  58. package/modules/@apostrophecms/modal/ui/apos/composables/AposFocus.js +9 -6
  59. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +38 -36
  60. package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +15 -9
  61. package/modules/@apostrophecms/notification/ui/apos/components/TheAposNotifications.vue +2 -2
  62. package/modules/@apostrophecms/page/index.js +9 -6
  63. package/modules/@apostrophecms/page/ui/apos/components/AposPagesManager.vue +3 -4
  64. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +5 -10
  65. package/modules/@apostrophecms/page/views/notFound.html +5 -5
  66. package/modules/@apostrophecms/permission/ui/apos/components/AposPermissionGrid.vue +6 -2
  67. package/modules/@apostrophecms/piece-page-type/index.js +1 -0
  68. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManager.vue +8 -12
  69. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerSelectBox.vue +2 -2
  70. package/modules/@apostrophecms/piece-type/ui/apos/components/AposRelationshipEditor.vue +1 -2
  71. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +59 -41
  72. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapDivider.vue +1 -0
  73. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapMarks.vue +3 -1
  74. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +3 -1
  75. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapTable.vue +2 -0
  76. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Classes.js +1 -7
  77. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Heading.js +10 -0
  78. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +8 -3
  79. package/modules/@apostrophecms/schema/ui/apos/components/AposArrayEditor.vue +4 -6
  80. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +1 -0
  81. package/modules/@apostrophecms/schema/ui/apos/components/AposInputBoolean.vue +5 -2
  82. package/modules/@apostrophecms/schema/ui/apos/components/AposInputColor.vue +1 -0
  83. package/modules/@apostrophecms/schema/ui/apos/components/AposInputDateAndTime.vue +1 -1
  84. package/modules/@apostrophecms/schema/ui/apos/components/AposInputObject.vue +2 -0
  85. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRadio.vue +1 -0
  86. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRange.vue +14 -8
  87. package/modules/@apostrophecms/schema/ui/apos/components/AposInputRelationship.vue +4 -2
  88. package/modules/@apostrophecms/schema/ui/apos/components/AposInputSlug.vue +7 -1
  89. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +3 -1
  90. package/modules/@apostrophecms/schema/ui/apos/components/AposInputWrapper.vue +35 -5
  91. package/modules/@apostrophecms/schema/ui/apos/components/AposSchema.vue +7 -4
  92. package/modules/@apostrophecms/schema/ui/apos/components/AposSearchList.vue +50 -15
  93. package/modules/@apostrophecms/schema/ui/apos/components/AposSubform.vue +5 -10
  94. package/modules/@apostrophecms/schema/ui/apos/logic/AposArrayEditor.js +19 -31
  95. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +3 -0
  96. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputString.js +9 -0
  97. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +7 -0
  98. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +11 -2
  99. package/modules/@apostrophecms/schema/ui/apos/logic/AposSearchList.js +3 -0
  100. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +5 -0
  101. package/modules/@apostrophecms/schema/ui/apos/scss/AposInputArray.scss +42 -9
  102. package/modules/@apostrophecms/search/index.js +1 -0
  103. package/modules/@apostrophecms/settings/index.js +33 -0
  104. package/modules/@apostrophecms/settings/ui/apos/components/AposSettingsManager.vue +6 -7
  105. package/modules/@apostrophecms/settings/ui/apos/logic/AposSettingsManager.js +0 -1
  106. package/modules/@apostrophecms/submitted-draft/index.js +26 -0
  107. package/modules/@apostrophecms/submitted-draft/ui/apos/components/AposSubmittedDraftIcon.vue +4 -4
  108. package/modules/@apostrophecms/template/views/templateError.html +4 -4
  109. package/modules/@apostrophecms/translation/ui/apos/components/AposTranslationIndicator.vue +2 -1
  110. package/modules/@apostrophecms/ui/ui/apos/components/AposAvatar.vue +3 -2
  111. package/modules/@apostrophecms/ui/ui/apos/components/AposButton.vue +62 -15
  112. package/modules/@apostrophecms/ui/ui/apos/components/AposButtonGroup.vue +8 -0
  113. package/modules/@apostrophecms/ui/ui/apos/components/AposButtonSplit.vue +9 -3
  114. package/modules/@apostrophecms/ui/ui/apos/components/AposCellContextMenu.vue +2 -0
  115. package/modules/@apostrophecms/ui/ui/apos/components/AposCellLabels.vue +1 -0
  116. package/modules/@apostrophecms/ui/ui/apos/components/AposCloudUploadIcon.vue +5 -5
  117. package/modules/@apostrophecms/ui/ui/apos/components/AposCombo.vue +7 -5
  118. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +8 -5
  119. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuDialog.vue +5 -3
  120. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuItem.vue +22 -2
  121. package/modules/@apostrophecms/ui/ui/apos/components/AposEmptyState.vue +3 -1
  122. package/modules/@apostrophecms/ui/ui/apos/components/AposFile.vue +5 -1
  123. package/modules/@apostrophecms/ui/ui/apos/components/AposFilterMenu.vue +2 -1
  124. package/modules/@apostrophecms/ui/ui/apos/components/AposLabel.vue +1 -0
  125. package/modules/@apostrophecms/ui/ui/apos/components/AposMinMaxCount.vue +1 -0
  126. package/modules/@apostrophecms/ui/ui/apos/components/AposPager.vue +1 -0
  127. package/modules/@apostrophecms/ui/ui/apos/components/AposPagerDots.vue +3 -1
  128. package/modules/@apostrophecms/ui/ui/apos/components/AposSlat.vue +15 -2
  129. package/modules/@apostrophecms/ui/ui/apos/components/AposSlatList.vue +3 -1
  130. package/modules/@apostrophecms/ui/ui/apos/components/AposSpinner.vue +33 -7
  131. package/modules/@apostrophecms/ui/ui/apos/components/AposSubformPreview.vue +3 -2
  132. package/modules/@apostrophecms/ui/ui/apos/components/AposTag.vue +5 -3
  133. package/modules/@apostrophecms/ui/ui/apos/components/AposTagApply.vue +5 -2
  134. package/modules/@apostrophecms/ui/ui/apos/components/AposTagList.vue +5 -1
  135. package/modules/@apostrophecms/ui/ui/apos/components/AposTagListItem.vue +9 -1
  136. package/modules/@apostrophecms/ui/ui/apos/components/AposToggle.vue +13 -13
  137. package/modules/@apostrophecms/ui/ui/apos/components/AposTree.vue +1 -0
  138. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeRows.vue +17 -7
  139. package/modules/@apostrophecms/ui/ui/apos/lib/vue.js +4 -0
  140. package/modules/@apostrophecms/ui/ui/apos/scss/global/_admin.scss +1 -1
  141. package/modules/@apostrophecms/ui/ui/apos/scss/global/_inputs.scss +28 -5
  142. package/modules/@apostrophecms/ui/ui/apos/scss/global/_scrollbars.scss +16 -0
  143. package/modules/@apostrophecms/ui/ui/apos/scss/global/_tables.scss +8 -3
  144. package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +9 -8
  145. package/modules/@apostrophecms/ui/ui/apos/scss/global/_tooltips.scss +13 -7
  146. package/modules/@apostrophecms/ui/ui/apos/scss/global/import-all.scss +1 -1
  147. package/modules/@apostrophecms/ui/ui/apos/scss/mixins/_input_mixins.scss +5 -3
  148. package/modules/@apostrophecms/ui/ui/apos/scss/mixins/_mixins.scss +1 -0
  149. package/modules/@apostrophecms/ui/ui/apos/scss/mixins/_responsive.scss +4 -3
  150. package/modules/@apostrophecms/ui/ui/apos/scss/mixins/_theme_mixins.scss +6 -0
  151. package/modules/@apostrophecms/ui/ui/apos/scss/shared/_table-rows.scss +6 -3
  152. package/modules/@apostrophecms/ui/ui/apos/stores/modal.js +180 -0
  153. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +2 -2
  154. package/package.json +4 -4
  155. package/test/modules/@apostrophecms/search/views/index.html +1 -0
  156. package/test/pages.js +227 -0
  157. package/test/schemas.js +184 -0
  158. package/test/search.js +49 -13
@@ -0,0 +1,180 @@
1
+ import { defineStore } from 'pinia';
2
+ import { ref, computed } from 'vue';
3
+ import cuid from 'cuid';
4
+
5
+ export const useModalStore = defineStore('modal', () => {
6
+ const stack = ref([]);
7
+ const activeId = ref(null);
8
+
9
+ const activeModal = computed(() => {
10
+ return stack.value.find(modal => activeId.value === modal.id);
11
+ });
12
+
13
+ function add(modal) {
14
+ stack.value.push(modal);
15
+ }
16
+
17
+ function remove(id) {
18
+ const modal = get(id);
19
+ if (!modal) {
20
+ return;
21
+ }
22
+
23
+ modal.resolve(modal.result);
24
+ stack.value = stack.value.filter(modal => id !== modal.id);
25
+ const current = getAt(-1);
26
+ activeId.value = current.id || null;
27
+ apos.bus.$emit('modal-resolved', modal);
28
+ }
29
+
30
+ function get(id) {
31
+ return id
32
+ ? stack.value.find(modal => id === modal.id)
33
+ : stack.value;
34
+ }
35
+
36
+ function getAt(index) {
37
+ const last = stack.value.length - 1;
38
+ const target = index < 0
39
+ ? last + 1 + index
40
+ : index > stack.value.length
41
+ ? last
42
+ : index;
43
+
44
+ return stack.value[target] || {};
45
+ }
46
+
47
+ function getProperties(id) {
48
+ const stackModal = stack.value.find(modal => id === modal.id);
49
+ if (!stackModal || !apos.modal.modals) {
50
+ return {};
51
+ }
52
+
53
+ const properties = {
54
+ ...apos.modal.modals
55
+ .find(modal => modal.componentName === stackModal.componentName &&
56
+ modal.props.moduleName === stackModal.props.moduleName)
57
+ };
58
+
59
+ return properties;
60
+ }
61
+
62
+ async function execute(componentName, props) {
63
+ return new Promise((resolve) => {
64
+ const item = {
65
+ id: `modal:${cuid()}`,
66
+ componentName,
67
+ resolve,
68
+ props: props || {},
69
+ elementsToFocus: [],
70
+ focusedElement: null
71
+ };
72
+
73
+ activeId.value = item.id;
74
+ stack.value.push(item);
75
+ apos.bus.$emit('modal-launched', item);
76
+ });
77
+ }
78
+
79
+ function updateModalData(id, data) {
80
+ stack.value = stack.value.map((modal) => {
81
+ return modal.id === id ? {
82
+ ...modal,
83
+ ...data
84
+ } : modal;
85
+ });
86
+ }
87
+
88
+ function setModalResult(modalId, result) {
89
+ stack.value = stack.value.map((modal) => {
90
+ return modal.id === modalId
91
+ ? {
92
+ ...modal,
93
+ result
94
+ }
95
+ : modal;
96
+ });
97
+ }
98
+
99
+ async function confirm(content, options = {}) {
100
+ return execute(apos.modal.components.confirm, {
101
+ content,
102
+ mode: 'confirm',
103
+ options
104
+ });
105
+ }
106
+
107
+ async function alert(alertContent, options = {}) {
108
+ return execute(apos.modal.components.confirm, {
109
+ content: alertContent,
110
+ mode: 'alert',
111
+ options
112
+ });
113
+ }
114
+
115
+ // Returns true if el1 is "on top of" el2 in the
116
+ // modal stack, as viewed by the user. If el1 is a
117
+ // modal that appears later in the stack than el2
118
+ // (visually stacked on top), this method returns true.
119
+ // If el2 is `document` and el1 is a modal, this
120
+ // method returns true. For convenenience, if el1
121
+ // or el2 is not a modal, it is treated as its DOM
122
+ // parent modal, or as `document`. If el1 has no
123
+ // parent modal this method always returns false.
124
+ //
125
+ // If el1 is no longer connected to the DOM then it
126
+ // is also considered to be "on top" e.g. not something
127
+ // that should concern `v-click-outside-element` and
128
+ // similar functionality. This is necessary because
129
+ // sometimes Vue removes elements from the DOM before
130
+ // we can examine their relationships.
131
+ function onTopOf(el1, el2) {
132
+ if (el2.matches('[data-apos-menu]')) {
133
+ return false;
134
+ }
135
+ if (!el1.isConnected) {
136
+ // If el1 is no longer in the DOM we can't make a proper determination,
137
+ // returning true prevents unwanted things like click-outside-element
138
+ // events from firing
139
+ return true;
140
+ }
141
+ if (!el1.matches('[data-apos-modal]')) {
142
+ el1 = el1.closest('[data-apos-modal]') || document;
143
+ }
144
+ if (!el2.matches('[data-apos-modal]')) {
145
+ el2 = el2.closest('[data-apos-modal]') || document;
146
+ }
147
+ if (el1 === document) {
148
+ return false;
149
+ }
150
+ if (el2 === document) {
151
+ return true;
152
+ }
153
+ const index1 = stack.value.findIndex(modal => modal.modalEl === el1);
154
+ const index2 = stack.value.findIndex(modal => modal.modalEl === el2);
155
+ if (index1 === -1) {
156
+ throw new Error('apos.modal.onTopOf: el1 is not in the modal stack');
157
+ }
158
+ if (index2 === -1) {
159
+ throw new Error('apos.modal.onTopOf: el2 is not in the modal stack');
160
+ }
161
+ return index1 > index2;
162
+ }
163
+
164
+ return {
165
+ stack,
166
+ activeId,
167
+ activeModal,
168
+ add,
169
+ remove,
170
+ get,
171
+ getAt,
172
+ getProperties,
173
+ execute,
174
+ updateModalData,
175
+ setModalResult,
176
+ confirm,
177
+ alert,
178
+ onTopOf
179
+ };
180
+ });
@@ -6,7 +6,6 @@
6
6
  @inactive="modal.active = false"
7
7
  @show-modal="modal.showModal = true"
8
8
  @esc="confirmAndCancel"
9
- @no-modal="$emit('safe-close')"
10
9
  >
11
10
  <template #breadcrumbs>
12
11
  <AposModalBreadcrumbs v-if="breadcrumbs && breadcrumbs.length" :items="breadcrumbs" />
@@ -29,6 +28,7 @@
29
28
  v-show="tab.name === currentTab"
30
29
  :key="tab.name"
31
30
  :ref="tab.name"
31
+ :data-apos-test="`schema:${tab.name}`"
32
32
  :trigger-validation="triggerValidation"
33
33
  :current-fields="groups[tab.name].fields"
34
34
  :schema="groups[tab.name].schema"
@@ -108,7 +108,7 @@ export default {
108
108
  default: null
109
109
  }
110
110
  },
111
- emits: [ 'safe-close', 'modal-result' ],
111
+ emits: [ 'modal-result' ],
112
112
  data() {
113
113
  const moduleOptions = window.apos.modules[apos.area.widgetManagers[this.type]];
114
114
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apostrophe",
3
- "version": "4.3.2",
3
+ "version": "4.4.0",
4
4
  "description": "The Apostrophe Content Management System.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -99,6 +99,7 @@
99
99
  "passport-local": "^1.0.0",
100
100
  "path-to-regexp": "^1.8.0",
101
101
  "performance-now": "^2.1.0",
102
+ "pinia": "^2.1.7",
102
103
  "postcss-html": "^1.3.0",
103
104
  "postcss-loader": "^5.0.0",
104
105
  "postcss-scss": "^4.0.3",
@@ -138,9 +139,8 @@
138
139
  "mocha": "^9.1.2",
139
140
  "nyc": "^15.1.0",
140
141
  "replace-in-file": "^6.1.0",
141
- "stylelint": "^14.6.1",
142
- "stylelint-declaration-strict-value": "^1.8.0",
143
- "stylelint-order": "^5.0.0",
142
+ "stylelint": "^16.5.0",
143
+ "stylelint-config-apostrophe": "github:apostrophecms/stylelint-config-apostrophe",
144
144
  "vue-eslint-parser": "^7.1.1",
145
145
  "vue-template-compiler": "^2.7.14"
146
146
  },
@@ -0,0 +1 @@
1
+ {{ data.docs | json }}
package/test/pages.js CHANGED
@@ -457,6 +457,233 @@ describe('Pages', function() {
457
457
  assert.strictEqual(page.path, `${homeId.replace(':en:published', '')}/another-parent/parent/sibling`);
458
458
  });
459
459
 
460
+ describe('move peer pages', function () {
461
+ this.afterEach(async function() {
462
+ await apos.doc.db.deleteMany({
463
+ type: 'test-page'
464
+ });
465
+ });
466
+
467
+ it('moving /bar under /foo should wind up with /foo/bar', async function() {
468
+ const fooPage = await apos.page.insert(
469
+ apos.task.getReq(),
470
+ '_home',
471
+ 'lastChild',
472
+ {
473
+ slug: '/foo',
474
+ visibility: 'public',
475
+ type: 'test-page',
476
+ title: 'Foo Page'
477
+ }
478
+ );
479
+ const barPage = await apos.page.insert(
480
+ apos.task.getReq(),
481
+ '_home',
482
+ 'lastChild',
483
+ {
484
+ slug: '/bar',
485
+ visibility: 'public',
486
+ type: 'test-page',
487
+ title: 'Bar Page'
488
+ }
489
+ );
490
+ const childPage = await apos.page.insert(
491
+ apos.task.getReq(),
492
+ barPage._id,
493
+ 'lastChild',
494
+ {
495
+ slug: '/bar/child',
496
+ visibility: 'public',
497
+ type: 'test-page',
498
+ title: 'Child Page'
499
+ }
500
+ );
501
+
502
+ await apos.page.move(
503
+ apos.task.getReq(),
504
+ barPage._id,
505
+ fooPage._id,
506
+ 'lastChild'
507
+ );
508
+
509
+ const movedPage = await apos.page.find(apos.task.getAnonReq(), { _id: barPage._id }).toObject();
510
+ const movedChildPage = await apos.page.find(apos.task.getAnonReq(), { _id: childPage._id }).toObject();
511
+
512
+ const actual = {
513
+ bar: {
514
+ path: movedPage.path,
515
+ rank: movedPage.rank,
516
+ slug: movedPage.slug
517
+ },
518
+ child: {
519
+ path: movedChildPage.path,
520
+ rank: movedChildPage.rank,
521
+ slug: movedChildPage.slug
522
+ }
523
+ };
524
+ const expected = {
525
+ bar: {
526
+ path: fooPage.path.concat('/', barPage.aposDocId),
527
+ rank: 0,
528
+ slug: '/foo/bar'
529
+ },
530
+ child: {
531
+ path: movedPage.path.concat('/', childPage.aposDocId),
532
+ rank: 0,
533
+ slug: '/foo/bar/child'
534
+ }
535
+ };
536
+
537
+ assert.deepEqual(actual, expected);
538
+ });
539
+
540
+ it('moving peer /foo/bar under /foo should wind up with /foo/bar', async function() {
541
+ const fooPage = await apos.page.insert(
542
+ apos.task.getReq(),
543
+ '_home',
544
+ 'lastChild',
545
+ {
546
+ slug: '/foo',
547
+ visibility: 'public',
548
+ type: 'test-page',
549
+ title: 'Foo Page'
550
+ }
551
+ );
552
+ const barPage = await apos.page.insert(
553
+ apos.task.getReq(),
554
+ '_home',
555
+ 'lastChild',
556
+ {
557
+ slug: '/foo/bar',
558
+ visibility: 'public',
559
+ type: 'test-page',
560
+ title: 'Bar Page'
561
+ }
562
+ );
563
+ const childPage = await apos.page.insert(
564
+ apos.task.getReq(),
565
+ barPage._id,
566
+ 'lastChild',
567
+ {
568
+ slug: '/foo/bar/child',
569
+ visibility: 'public',
570
+ type: 'test-page',
571
+ title: 'Child Page'
572
+ }
573
+ );
574
+
575
+ await apos.page.move(
576
+ apos.task.getReq(),
577
+ barPage._id,
578
+ fooPage._id,
579
+ 'lastChild'
580
+ );
581
+
582
+ const movedPage = await apos.page.find(apos.task.getAnonReq(), { _id: barPage._id }).toObject();
583
+ const movedChildPage = await apos.page.find(apos.task.getAnonReq(), { _id: childPage._id }).toObject();
584
+
585
+ const actual = {
586
+ bar: {
587
+ path: movedPage.path,
588
+ rank: movedPage.rank,
589
+ slug: movedPage.slug
590
+ },
591
+ child: {
592
+ path: movedChildPage.path,
593
+ rank: movedChildPage.rank,
594
+ slug: movedChildPage.slug
595
+ }
596
+ };
597
+ const expected = {
598
+ bar: {
599
+ path: fooPage.path.concat('/', barPage.aposDocId),
600
+ rank: 0,
601
+ slug: '/foo/bar'
602
+ },
603
+ child: {
604
+ path: movedPage.path.concat('/', childPage.aposDocId),
605
+ rank: 0,
606
+ slug: '/foo/bar/child'
607
+ }
608
+ };
609
+
610
+ assert.deepEqual(actual, expected);
611
+ });
612
+
613
+ it('moving /foobar under /foo should wind up with /foo/foobar', async function() {
614
+ const fooPage = await apos.page.insert(
615
+ apos.task.getReq(),
616
+ '_home',
617
+ 'lastChild',
618
+ {
619
+ slug: '/foo',
620
+ visibility: 'public',
621
+ type: 'test-page',
622
+ title: 'Foo Page'
623
+ }
624
+ );
625
+ const foobarPage = await apos.page.insert(
626
+ apos.task.getReq(),
627
+ '_home',
628
+ 'lastChild',
629
+ {
630
+ slug: '/foobar',
631
+ visibility: 'public',
632
+ type: 'test-page',
633
+ title: 'Foobar Page'
634
+ }
635
+ );
636
+ const childPage = await apos.page.insert(
637
+ apos.task.getReq(),
638
+ foobarPage._id,
639
+ 'lastChild',
640
+ {
641
+ slug: '/foobar/child',
642
+ visibility: 'public',
643
+ type: 'test-page',
644
+ title: 'Child Page'
645
+ }
646
+ );
647
+
648
+ await apos.page.move(
649
+ apos.task.getReq(),
650
+ foobarPage._id,
651
+ fooPage._id,
652
+ 'lastChild'
653
+ );
654
+
655
+ const movedPage = await apos.page.find(apos.task.getAnonReq(), { _id: foobarPage._id }).toObject();
656
+ const movedChildPage = await apos.page.find(apos.task.getAnonReq(), { _id: childPage._id }).toObject();
657
+
658
+ const actual = {
659
+ foobar: {
660
+ path: movedPage.path,
661
+ rank: movedPage.rank,
662
+ slug: movedPage.slug
663
+ },
664
+ child: {
665
+ path: movedChildPage.path,
666
+ rank: movedChildPage.rank,
667
+ slug: movedChildPage.slug
668
+ }
669
+ };
670
+ const expected = {
671
+ foobar: {
672
+ path: fooPage.path.concat('/', foobarPage.aposDocId),
673
+ rank: 0,
674
+ slug: '/foo/foobar'
675
+ },
676
+ child: {
677
+ path: movedPage.path.concat('/', childPage.aposDocId),
678
+ rank: 0,
679
+ slug: '/foo/foobar/child'
680
+ }
681
+ };
682
+
683
+ assert.deepEqual(actual, expected);
684
+ });
685
+ });
686
+
460
687
  it('inferred page relationships are correct', async function() {
461
688
  const req = apos.task.getReq();
462
689
  const pages = await apos.page.find(req, {}).toArray();
package/test/schemas.js CHANGED
@@ -2401,6 +2401,190 @@ describe('Schemas', function() {
2401
2401
  assert.deepEqual(actual, expected);
2402
2402
  });
2403
2403
 
2404
+ describe('field.readOnly with default value', function() {
2405
+ const givenSchema = [
2406
+ {
2407
+ name: 'title',
2408
+ type: 'string'
2409
+ },
2410
+ {
2411
+ name: 'array',
2412
+ type: 'array',
2413
+ schema: [
2414
+ {
2415
+ name: 'planet',
2416
+ type: 'string',
2417
+ def: 'Earth',
2418
+ readOnly: true
2419
+ },
2420
+ {
2421
+ name: 'moon',
2422
+ type: 'string'
2423
+ }
2424
+ ]
2425
+ },
2426
+ {
2427
+ name: 'object',
2428
+ type: 'object',
2429
+ schema: [
2430
+ {
2431
+ name: 'planet',
2432
+ type: 'string',
2433
+ def: 'Earth',
2434
+ readOnly: true
2435
+ },
2436
+ {
2437
+ name: 'moon',
2438
+ type: 'string'
2439
+ }
2440
+ ]
2441
+ },
2442
+ {
2443
+ name: '_relationship',
2444
+ type: 'relationship',
2445
+ limit: 1,
2446
+ withType: '@apostrophecms/any-page-type',
2447
+ label: 'Page Title',
2448
+ idsStorage: 'pageId',
2449
+ schema: [
2450
+ {
2451
+ name: 'planet',
2452
+ type: 'string',
2453
+ def: 'Earth',
2454
+ readOnly: true
2455
+ },
2456
+ {
2457
+ name: 'moon',
2458
+ type: 'string'
2459
+ }
2460
+ ]
2461
+ }
2462
+ ];
2463
+
2464
+ it('should keep read only values when editing a document', async function() {
2465
+ const req = apos.task.getReq();
2466
+ const schema = apos.schema.compose({
2467
+ addFields: givenSchema
2468
+ });
2469
+ const home = await apos.page.find(req, { slug: '/' }).toObject();
2470
+
2471
+ const data = {
2472
+ _relationship: [
2473
+ {
2474
+ ...home,
2475
+ _fields: {
2476
+ planet: 'Saturn',
2477
+ moon: 'Titan'
2478
+ }
2479
+ }
2480
+ ],
2481
+ array: [
2482
+ {
2483
+ _id: 'Jupiter-Io',
2484
+ moon: 'Io'
2485
+ },
2486
+ {
2487
+ _id: 'Mars-Phobos',
2488
+ moon: 'Phobos'
2489
+ }
2490
+ ],
2491
+ object: {
2492
+ _id: 'Neptune-Triton',
2493
+ moon: 'Triton'
2494
+ },
2495
+ pageId: [ home._id ],
2496
+ pageFields: {
2497
+ [home._id]: {
2498
+ planet: 'Saturn'
2499
+ }
2500
+ },
2501
+ title: 'Sol'
2502
+ };
2503
+ const destination = {
2504
+ _relationship: [
2505
+ {
2506
+ ...home,
2507
+ _fields: {
2508
+ planet: 'Saturn'
2509
+ }
2510
+ }
2511
+ ],
2512
+ array: [
2513
+ {
2514
+ _id: 'Jupiter-Io',
2515
+ planet: 'Jupiter'
2516
+ },
2517
+ {
2518
+ _id: 'Mars-Phobos',
2519
+ planet: 'Mars'
2520
+ }
2521
+ ],
2522
+ object: {
2523
+ _id: 'Neptune-Triton',
2524
+ planet: 'Neptune'
2525
+ },
2526
+ pageId: [ home._id ],
2527
+ pageFields: {
2528
+ [home._id]: {
2529
+ planet: 'Saturn'
2530
+ }
2531
+ },
2532
+ title: 'Default'
2533
+ };
2534
+ await apos.schema.convert(
2535
+ req,
2536
+ schema,
2537
+ data,
2538
+ destination
2539
+ );
2540
+
2541
+ const actual = destination;
2542
+ const expected = {
2543
+ _relationship: [
2544
+ {
2545
+ _fields: {
2546
+ planet: 'Saturn',
2547
+ moon: 'Titan'
2548
+ },
2549
+ ...home
2550
+ }
2551
+ ],
2552
+ array: [
2553
+ {
2554
+ _id: 'Jupiter-Io',
2555
+ metaType: 'arrayItem',
2556
+ moon: 'Io',
2557
+ planet: 'Jupiter',
2558
+ scopedArrayName: undefined
2559
+ },
2560
+ {
2561
+ _id: 'Mars-Phobos',
2562
+ metaType: 'arrayItem',
2563
+ moon: 'Phobos',
2564
+ planet: 'Mars',
2565
+ scopedArrayName: undefined
2566
+ }
2567
+ ],
2568
+ object: {
2569
+ _id: 'Neptune-Triton',
2570
+ metaType: 'objectItem',
2571
+ moon: 'Triton',
2572
+ planet: 'Neptune',
2573
+ scopedObjectName: undefined
2574
+ },
2575
+ pageId: [ home._id ],
2576
+ pageFields: {
2577
+ [home._id]: {
2578
+ planet: 'Saturn'
2579
+ }
2580
+ },
2581
+ title: 'Sol'
2582
+ };
2583
+
2584
+ assert.deepEqual(actual, expected);
2585
+ });
2586
+ });
2587
+
2404
2588
  describe('field editPermission|viewPermission', function() {
2405
2589
  const schema = [
2406
2590
  {