apostrophe 4.15.2 → 4.16.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 (223) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/defaults.js +3 -3
  3. package/index.js +39 -22
  4. package/lib/check-if-conditions.js +27 -9
  5. package/lib/moog-require.js +23 -16
  6. package/lib/moog.js +23 -18
  7. package/lib/opentelemetry.js +4 -3
  8. package/modules/@apostrophecms/admin-bar/index.js +23 -16
  9. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarMenu.vue +21 -20
  10. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposAdminBarUser.vue +2 -2
  11. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBar.vue +22 -19
  12. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextBreakpointPreviewMode.vue +4 -3
  13. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextModeAndSettings.vue +20 -10
  14. package/modules/@apostrophecms/admin-bar/ui/apos/components/TheAposContextTitle.vue +8 -6
  15. package/modules/@apostrophecms/any-page-type/index.js +53 -40
  16. package/modules/@apostrophecms/archive-page/index.js +2 -1
  17. package/modules/@apostrophecms/area/index.js +142 -71
  18. package/modules/@apostrophecms/area/ui/apos/apps/AposAreas.js +6 -22
  19. package/modules/@apostrophecms/area/ui/apos/components/AposAreaContextualMenu.vue +28 -6
  20. package/modules/@apostrophecms/area/ui/apos/components/AposAreaEditor.vue +19 -14
  21. package/modules/@apostrophecms/area/ui/apos/components/AposAreaMenu.vue +4 -0
  22. package/modules/@apostrophecms/area/ui/apos/components/AposAreaWidget.vue +32 -14
  23. package/modules/@apostrophecms/area/ui/apos/components/AposWidgetControls.vue +179 -109
  24. package/modules/@apostrophecms/asset/index.js +173 -140
  25. package/modules/@apostrophecms/asset/lib/build/external-module-api.js +110 -120
  26. package/modules/@apostrophecms/asset/lib/build/internals.js +57 -56
  27. package/modules/@apostrophecms/asset/lib/build/manager-apos.js +2 -1
  28. package/modules/@apostrophecms/asset/lib/build/manager-index.js +5 -4
  29. package/modules/@apostrophecms/asset/lib/build/task.js +16 -13
  30. package/modules/@apostrophecms/asset/lib/globalIcons.js +3 -0
  31. package/modules/@apostrophecms/asset/lib/webpack/apos/webpack.config.js +8 -6
  32. package/modules/@apostrophecms/asset/lib/webpack/src/webpack.config.js +8 -6
  33. package/modules/@apostrophecms/asset/lib/webpack/src/webpack.scss.js +2 -1
  34. package/modules/@apostrophecms/asset/lib/webpack/utils.js +16 -14
  35. package/modules/@apostrophecms/attachment/index.js +57 -36
  36. package/modules/@apostrophecms/attachment/lib/legacy-migrations.js +2 -2
  37. package/modules/@apostrophecms/cache/index.js +8 -7
  38. package/modules/@apostrophecms/color-field/index.js +3 -1
  39. package/modules/@apostrophecms/color-field/ui/apos/lib/AposColorHue.vue +20 -2
  40. package/modules/@apostrophecms/color-field/ui/apos/lib/AposColorSaturation.vue +3 -1
  41. package/modules/@apostrophecms/command-menu/index.js +35 -10
  42. package/modules/@apostrophecms/db/index.js +6 -6
  43. package/modules/@apostrophecms/doc/index.js +132 -109
  44. package/modules/@apostrophecms/doc/lib/legacy-migrations.js +2 -2
  45. package/modules/@apostrophecms/doc/lib/migrations.js +11 -7
  46. package/modules/@apostrophecms/doc/ui/apos/apps/AposDoc.js +4 -4
  47. package/modules/@apostrophecms/doc-type/index.js +242 -172
  48. package/modules/@apostrophecms/doc-type/lib/autocomplete.js +2 -1
  49. package/modules/@apostrophecms/doc-type/ui/apos/components/AposDocEditor.vue +14 -6
  50. package/modules/@apostrophecms/doc-type/ui/apos/logic/AposDocContextMenu.js +4 -4
  51. package/modules/@apostrophecms/email/index.js +6 -2
  52. package/modules/@apostrophecms/error/index.js +7 -6
  53. package/modules/@apostrophecms/express/index.js +17 -17
  54. package/modules/@apostrophecms/file/index.js +6 -5
  55. package/modules/@apostrophecms/global/index.js +25 -23
  56. package/modules/@apostrophecms/html-widget/index.js +4 -3
  57. package/modules/@apostrophecms/http/index.js +80 -66
  58. package/modules/@apostrophecms/i18n/i18n/de.json +0 -1
  59. package/modules/@apostrophecms/i18n/i18n/en.json +0 -1
  60. package/modules/@apostrophecms/i18n/i18n/es.json +0 -1
  61. package/modules/@apostrophecms/i18n/i18n/fr.json +0 -1
  62. package/modules/@apostrophecms/i18n/i18n/it.json +0 -1
  63. package/modules/@apostrophecms/i18n/i18n/pt-BR.json +0 -1
  64. package/modules/@apostrophecms/i18n/i18n/sk.json +0 -1
  65. package/modules/@apostrophecms/i18n/index.js +55 -59
  66. package/modules/@apostrophecms/i18n/ui/apos/components/AposI18nLocalize.vue +10 -9
  67. package/modules/@apostrophecms/image/index.js +48 -37
  68. package/modules/@apostrophecms/image/ui/apos/components/AposImageCropper.vue +33 -14
  69. package/modules/@apostrophecms/image/ui/apos/components/AposImageRelationshipEditor.vue +58 -28
  70. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManager.vue +7 -6
  71. package/modules/@apostrophecms/image/ui/apos/components/AposMediaManagerEditor.vue +37 -17
  72. package/modules/@apostrophecms/image/ui/apos/components/AposMediaUploader.vue +3 -2
  73. package/modules/@apostrophecms/image-widget/index.js +37 -10
  74. package/modules/@apostrophecms/image-widget/views/widget.html +1 -0
  75. package/modules/@apostrophecms/job/index.js +17 -16
  76. package/modules/@apostrophecms/launder/index.js +2 -2
  77. package/modules/@apostrophecms/lock/index.js +17 -16
  78. package/modules/@apostrophecms/log/index.js +66 -79
  79. package/modules/@apostrophecms/login/index.js +29 -25
  80. package/modules/@apostrophecms/login/ui/apos/logic/AposForgotPasswordForm.js +2 -2
  81. package/modules/@apostrophecms/login/ui/apos/logic/AposLoginForm.js +24 -18
  82. package/modules/@apostrophecms/login/ui/apos/logic/AposResetPasswordForm.js +2 -2
  83. package/modules/@apostrophecms/login/ui/apos/logic/TheAposLogin.js +5 -4
  84. package/modules/@apostrophecms/migration/index.js +45 -39
  85. package/modules/@apostrophecms/migration/lib/addMissingSchemaFields.js +13 -5
  86. package/modules/@apostrophecms/modal/ui/apos/components/AposDocsManagerToolbar.vue +3 -3
  87. package/modules/@apostrophecms/modal/ui/apos/components/AposModal.vue +10 -4
  88. package/modules/@apostrophecms/modal/ui/apos/components/AposModalLip.vue +89 -16
  89. package/modules/@apostrophecms/modal/ui/apos/components/AposModalReport.vue +13 -7
  90. package/modules/@apostrophecms/modal/ui/apos/components/AposModalTabs.vue +5 -2
  91. package/modules/@apostrophecms/modal/ui/apos/components/AposWidgetModalTabs.vue +6 -3
  92. package/modules/@apostrophecms/modal/ui/apos/components/TheAposModals.vue +5 -4
  93. package/modules/@apostrophecms/modal/ui/apos/composables/AposFocus.js +17 -10
  94. package/modules/@apostrophecms/modal/ui/apos/mixins/AposDocsManagerMixin.js +6 -5
  95. package/modules/@apostrophecms/modal/ui/apos/mixins/AposEditorMixin.js +9 -5
  96. package/modules/@apostrophecms/modal/ui/apos/mixins/AposModalTabsMixin.js +4 -2
  97. package/modules/@apostrophecms/module/index.js +73 -50
  98. package/modules/@apostrophecms/module/lib/events.js +18 -7
  99. package/modules/@apostrophecms/notification/index.js +20 -15
  100. package/modules/@apostrophecms/notification/ui/apos/components/AposNotification.vue +3 -2
  101. package/modules/@apostrophecms/oembed/index.js +19 -13
  102. package/modules/@apostrophecms/oembed-field/index.js +7 -6
  103. package/modules/@apostrophecms/page/index.js +287 -180
  104. package/modules/@apostrophecms/page/lib/legacy-migrations.js +2 -2
  105. package/modules/@apostrophecms/page/ui/apos/logic/AposPagesManager.js +9 -8
  106. package/modules/@apostrophecms/page-type/index.js +34 -25
  107. package/modules/@apostrophecms/permission/index.js +30 -10
  108. package/modules/@apostrophecms/permission/lib/legacy-migrations.js +2 -2
  109. package/modules/@apostrophecms/piece-page-type/index.js +46 -35
  110. package/modules/@apostrophecms/piece-type/index.js +103 -71
  111. package/modules/@apostrophecms/piece-type/ui/apos/components/AposDocsManagerDisplay.vue +3 -1
  112. package/modules/@apostrophecms/piece-type/ui/apos/components/AposUtilityOperations.vue +5 -5
  113. package/modules/@apostrophecms/rich-text-widget/index.js +25 -12
  114. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposRichTextWidgetEditor.vue +3 -3
  115. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapColor.vue +3 -3
  116. package/modules/@apostrophecms/rich-text-widget/ui/apos/components/AposTiptapStyles.vue +2 -1
  117. package/modules/@apostrophecms/rich-text-widget/ui/apos/tiptap-extensions/Document.js +2 -1
  118. package/modules/@apostrophecms/schema/index.js +164 -141
  119. package/modules/@apostrophecms/schema/lib/addFieldTypes.js +78 -48
  120. package/modules/@apostrophecms/schema/lib/joinr.js +39 -23
  121. package/modules/@apostrophecms/schema/ui/apos/components/AposInputArea.vue +0 -2
  122. package/modules/@apostrophecms/schema/ui/apos/components/AposInputString.vue +2 -1
  123. package/modules/@apostrophecms/schema/ui/apos/components/AposLogo.vue +1 -0
  124. package/modules/@apostrophecms/schema/ui/apos/components/AposLogoIcon.vue +1 -0
  125. package/modules/@apostrophecms/schema/ui/apos/components/AposLogoPadless.vue +1 -0
  126. package/modules/@apostrophecms/schema/ui/apos/lib/conditionalFields.js +2 -1
  127. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputArray.js +19 -9
  128. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputCheckboxes.js +3 -1
  129. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputRelationship.js +3 -10
  130. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputSlug.js +2 -2
  131. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputString.js +12 -3
  132. package/modules/@apostrophecms/schema/ui/apos/logic/AposInputWrapper.js +4 -5
  133. package/modules/@apostrophecms/schema/ui/apos/logic/AposSchema.js +7 -2
  134. package/modules/@apostrophecms/schema/ui/apos/logic/AposSubform.js +3 -4
  135. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputFollowingMixin.js +3 -1
  136. package/modules/@apostrophecms/schema/ui/apos/mixins/AposInputMixin.js +2 -1
  137. package/modules/@apostrophecms/search/index.js +29 -19
  138. package/modules/@apostrophecms/settings/index.js +37 -29
  139. package/modules/@apostrophecms/soft-redirect/index.js +10 -6
  140. package/modules/@apostrophecms/submitted-draft/index.js +6 -4
  141. package/modules/@apostrophecms/task/index.js +16 -5
  142. package/modules/@apostrophecms/template/index.js +70 -68
  143. package/modules/@apostrophecms/template/lib/bundlesLoader.js +4 -4
  144. package/modules/@apostrophecms/template/lib/custom-tags/fragment.js +4 -3
  145. package/modules/@apostrophecms/template/lib/custom-tags/render.js +2 -1
  146. package/modules/@apostrophecms/template/lib/nunjucksLoader.js +6 -4
  147. package/modules/@apostrophecms/translation/index.js +21 -19
  148. package/modules/@apostrophecms/translation/ui/apos/components/AposTranslationIndicator.vue +1 -0
  149. package/modules/@apostrophecms/ui/ui/apos/apps/AposBusEvent.js +2 -1
  150. package/modules/@apostrophecms/ui/ui/apos/components/AposButtonSplit.vue +3 -1
  151. package/modules/@apostrophecms/ui/ui/apos/components/AposCloudUploadIcon.vue +1 -0
  152. package/modules/@apostrophecms/ui/ui/apos/components/AposCombo.vue +2 -2
  153. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenu.vue +18 -9
  154. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuDialog.vue +11 -11
  155. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuItem.vue +53 -15
  156. package/modules/@apostrophecms/ui/ui/apos/components/AposContextMenuTip.vue +1 -0
  157. package/modules/@apostrophecms/ui/ui/apos/components/AposFilterMenu.vue +9 -4
  158. package/modules/@apostrophecms/ui/ui/apos/components/AposLoading.vue +1 -1
  159. package/modules/@apostrophecms/ui/ui/apos/components/AposSlatList.vue +4 -1
  160. package/modules/@apostrophecms/ui/ui/apos/components/AposSpinner.vue +100 -18
  161. package/modules/@apostrophecms/ui/ui/apos/components/AposTable.vue +9 -7
  162. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeHeader.vue +2 -1
  163. package/modules/@apostrophecms/ui/ui/apos/components/AposTreeRows.vue +3 -2
  164. package/modules/@apostrophecms/ui/ui/apos/composables/AposFocusTrap.js +9 -5
  165. package/modules/@apostrophecms/ui/ui/apos/lib/click-outside-element.js +5 -1
  166. package/modules/@apostrophecms/ui/ui/apos/lib/i18next.js +7 -1
  167. package/modules/@apostrophecms/ui/ui/apos/lib/tooltip.js +2 -1
  168. package/modules/@apostrophecms/ui/ui/apos/mixins/AposAdvisoryLockMixin.js +31 -29
  169. package/modules/@apostrophecms/ui/ui/apos/mixins/AposArchiveMixin.js +10 -7
  170. package/modules/@apostrophecms/ui/ui/apos/mixins/AposCellMixin.js +2 -2
  171. package/modules/@apostrophecms/ui/ui/apos/mixins/AposModifiedMixin.js +2 -2
  172. package/modules/@apostrophecms/ui/ui/apos/mixins/AposPublishMixin.js +18 -14
  173. package/modules/@apostrophecms/ui/ui/apos/mixins/AposThemeMixin.js +2 -1
  174. package/modules/@apostrophecms/ui/ui/apos/scss/global/_theme.scss +2 -0
  175. package/modules/@apostrophecms/ui/ui/apos/scss/mixins/_theme_mixins.scss +1 -0
  176. package/modules/@apostrophecms/ui/ui/apos/stores/modal.js +2 -1
  177. package/modules/@apostrophecms/ui/ui/apos/utils/index.js +27 -22
  178. package/modules/@apostrophecms/uploadfs/index.js +6 -4
  179. package/modules/@apostrophecms/url/index.js +2 -2
  180. package/modules/@apostrophecms/user/index.js +34 -28
  181. package/modules/@apostrophecms/user/lib/legacy-migrations.js +2 -2
  182. package/modules/@apostrophecms/user/lib/password-hash.js +5 -1
  183. package/modules/@apostrophecms/util/index.js +87 -52
  184. package/modules/@apostrophecms/util/ui/src/http.js +29 -26
  185. package/modules/@apostrophecms/util/ui/src/util.js +24 -17
  186. package/modules/@apostrophecms/video-widget/index.js +3 -2
  187. package/modules/@apostrophecms/widget-type/index.js +117 -56
  188. package/modules/@apostrophecms/widget-type/ui/apos/components/AposWidgetEditor.vue +68 -23
  189. package/modules/@apostrophecms/widget-type/ui/apos/mixins/AposWidgetMixin.js +4 -4
  190. package/package.json +2 -2
  191. package/test/add-missing-schema-fields.js +4 -3
  192. package/test/areas.js +14 -17
  193. package/test/asset-external.js +2 -2
  194. package/test/assets.js +3 -1
  195. package/test/attachments.js +2 -1
  196. package/test/autocomplete.js +155 -0
  197. package/test/big-upload.js +2 -2
  198. package/test/change-doc-ids.js +6 -2
  199. package/test/concurrent-array-relationships.js +2 -2
  200. package/test/db.js +2 -1
  201. package/test/docs.js +4 -2
  202. package/test/draft-published.js +2 -2
  203. package/test/external-front.js +2 -1
  204. package/test/i18n-batch.js +4 -6
  205. package/test/i18n.js +6 -3
  206. package/test/images.js +8 -4
  207. package/test/oembed.js +3 -7
  208. package/test/pages-public-api.js +3 -1
  209. package/test/pages-rest.js +2 -552
  210. package/test/pages.js +4 -2
  211. package/test/parked-pages.js +2 -1
  212. package/test/pieces-public-api.js +3 -1
  213. package/test/pieces.js +32 -11
  214. package/test/relationships.js +2 -1
  215. package/test/restApiRoutes.js +2 -2
  216. package/test/reverse-relationship.js +5 -5
  217. package/test/schemas.js +52 -8
  218. package/test/settings.js +2 -2
  219. package/test/templates.js +2 -2
  220. package/test/users.js +16 -4
  221. package/test/widgets.js +105 -2
  222. package/test-lib/test.js +2 -2
  223. package/test-lib/util.js +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.16.0 (2025-05-14)
4
+
5
+ ### Adds
6
+
7
+ * Uses new `widgetOperations` to add the `adjustImage` operation to the image widget.
8
+ * Adds a server validation before adding a widget to an area. Introduces a new POST route `@apostrophecms/area/validate-widget`.
9
+ * The new `widgetOperations` cascade config property can be used to display custom operations for widgets. An `if` condition can be used to test properties of the widget before displaying an operation.
10
+
11
+ ### Changes
12
+
13
+ * Enable widget live preview by default.
14
+
15
+ ### Fixes
16
+
17
+ * Fixes `range` field type default value not being set properly.
18
+ * Fixes autocomplete and search sorting and as a consequence, fixes potential duplicates during pagination.
19
+ * Fixes all eslint warnings.
20
+ * When pasting a widget from the clipboard, the correct widget type is always offered on the "Add Content" menu.
21
+ * Widget live preview is now attempting to auto-position the Widget Editor modal only if no explicit widget configuration (`options.origin`) is provided.
22
+ * `required` is now implemented on the server side as well for `relationship` fields. It behaves like `min: 1`. It was always implemented on the front end. However, note that a relationship can still become empty if the related document is archived or deleted.
23
+ * Image widgets, and others with a placeholder when empty, now restore their placeholder view when canceling the widget editor in live preview mode.
24
+ * Fixes `z-index` of widget controls, going above the controls add button.
25
+
26
+ ### Changes
27
+
28
+ * Updates the default fields for the `getMangageApiProjection()` to include a more sensible base configuration and adds a `true` option to return the minimal default values.
29
+
3
30
  ## 4.15.2 (2025-04-28)
4
31
 
5
32
  ### Security
@@ -33,6 +60,7 @@
33
60
 
34
61
  ### Fixes
35
62
 
63
+ * Adds missing notifications and error handling in media manager and save notification for auto-published pieces.
36
64
  * Update `uploadfs` to `1.24.3`.
37
65
  * Fixes an edge case where reordering a page in the Page Manager might affect another locale.
38
66
  * Fixes chrome bug when pages manager checkboxes need a double click when coming from the rich text editor (because some text is selected).
package/defaults.js CHANGED
@@ -31,8 +31,8 @@ module.exports = {
31
31
  '@apostrophecms/oembed': {},
32
32
  '@apostrophecms/pager': {},
33
33
  '@apostrophecms/any-doc-type': {},
34
- // global comes first so it can register a doc type manager and clean things up before
35
- // pages claims any orphan page types
34
+ // global comes first so it can register a doc type manager and clean
35
+ // things up before pages claims any orphan page types
36
36
  '@apostrophecms/global': {},
37
37
  '@apostrophecms/polymorphic-type': {},
38
38
  '@apostrophecms/page': {},
@@ -43,7 +43,6 @@ module.exports = {
43
43
  '@apostrophecms/area': {},
44
44
  '@apostrophecms/rich-text-widget': {},
45
45
  '@apostrophecms/html-widget': {},
46
- '@apostrophecms/image-widget': {},
47
46
  '@apostrophecms/color-field': {},
48
47
  '@apostrophecms/oembed-field': {},
49
48
  '@apostrophecms/video-widget': {},
@@ -52,6 +51,7 @@ module.exports = {
52
51
  '@apostrophecms/settings': {},
53
52
  '@apostrophecms/image': {},
54
53
  '@apostrophecms/image-tag': {},
54
+ '@apostrophecms/image-widget': {},
55
55
  '@apostrophecms/file': {},
56
56
  '@apostrophecms/file-tag': {},
57
57
  '@apostrophecms/soft-redirect': {},
package/index.js CHANGED
@@ -44,24 +44,25 @@ let defaults = require('./defaults.js');
44
44
  //
45
45
  // `openTelemetryProvider`
46
46
  //
47
- // If set, Apostrophe will register it as a global OpenTelemetry tracer provider.
48
- // The expected value is an object, an instance of TracerProvider.
49
- // If the Node SDK is used in the application instead of manual configuration,
50
- // the provider instance is only available as a
51
- // private property: `sdkInstance._tracerProvider`. An issue can be opened
52
- // to discuss the exposure of a public getter with the OpenTelemetry developers.
47
+ // If set, Apostrophe will register it as a global OpenTelemetry tracer
48
+ // provider. The expected value is an object, an instance of TracerProvider. If
49
+ // the Node SDK is used in the application instead of manual configuration, the
50
+ // provider instance is only available as a private property:
51
+ // `sdkInstance._tracerProvider`. An issue can be opened to discuss the exposure
52
+ // of a public getter with the OpenTelemetry developers.
53
53
  //
54
54
  // `beforeExit`
55
55
  //
56
56
  // If set, Apostrophe will invoke it (await) before invoking process.exit.
57
- // `beforeExit` may be an async function, will be awaited, and takes no arguments.
57
+ // `beforeExit` may be an async function, will be awaited, and takes no
58
+ // arguments.
58
59
  //
59
60
  // `pnpm`
60
61
  // A boolean to force on or off the pnpm related build routines. If not set,
61
62
  // an automated check will be performed to determine if pnpm is in use. We offer
62
63
  // an option, because automated check is not 100% reliable. Monorepo tools are
63
- // often hiding package management specifics (lock files, node_module structure, etc.)
64
- // in a centralized store.
64
+ // often hiding package management specifics (lock files, node_module
65
+ // structure, etc.) in a centralized store.
65
66
  //
66
67
  // ## Awaiting the Apostrophe function
67
68
  //
@@ -147,7 +148,8 @@ module.exports = async function(options) {
147
148
  return null;
148
149
  });
149
150
  } else {
150
- // continue as a worker operation, the pid should be recorded by the auto instrumentation
151
+ // continue as a worker operation, the pid should be recorded
152
+ // by the auto instrumentation
151
153
  spanName += ':worker';
152
154
  console.log(`Cluster worker ${process.pid} started`);
153
155
  }
@@ -248,8 +250,9 @@ async function apostrophe(options, telemetry, rootSpan) {
248
250
  }
249
251
  };
250
252
 
251
- // Signals to various (build related) places that we are running a pnpm installation.
252
- // The relevant option, if set, has a higher precedence over the automated check.
253
+ // Signals to various (build related) places that we are running a pnpm
254
+ // installation. The relevant option, if set, has a higher precedence over
255
+ // the automated check.
253
256
  self.isPnpm = options.pnpm ??
254
257
  fs.existsSync(path.join(self.npmRootDir, 'pnpm-lock.yaml'));
255
258
 
@@ -320,8 +323,8 @@ async function apostrophe(options, telemetry, rootSpan) {
320
323
  self.apos.schema.registerAllSchemas();
321
324
  await self.apos.lock.withLock('@apostrophecms/migration:migrate', async () => {
322
325
  await self.apos.migration.migrate(self.argv);
323
- // Inserts the global doc in the default locale if it does not exist; same for other
324
- // singleton piece types registered by other modules
326
+ // Inserts the global doc in the default locale if it does not exist;
327
+ // same for other singleton piece types registered by other modules
325
328
  for (const apostropheModule of Object.values(self.modules)) {
326
329
  if (self.instanceOf(apostropheModule, '@apostrophecms/piece-type') && apostropheModule.options.singletonAuto) {
327
330
  await apostropheModule.insertIfMissing();
@@ -329,8 +332,8 @@ async function apostrophe(options, telemetry, rootSpan) {
329
332
  }
330
333
  await self.apos.page.implementParkAllInDefaultLocale();
331
334
  await self.apos.doc.replicate(); // emits beforeReplicate and afterReplicate events
332
- // Replicate will have created the parked pages across locales if needed, but we may
333
- // still need to reset parked properties
335
+ // Replicate will have created the parked pages across locales if needed,
336
+ // but we may still need to reset parked properties
334
337
  await self.apos.page.implementParkAllInOtherLocales();
335
338
  });
336
339
  await self.emit('ready'); // formerly afterInit
@@ -625,8 +628,9 @@ async function apostrophe(options, telemetry, rootSpan) {
625
628
  if (!target) {
626
629
  continue;
627
630
  }
628
- // Add all the modules that want to be before this one to the target's beforeSelf.
629
- // Do this recursively for every module from the beforeSelf array that has own `beforeSelf` members.
631
+ // Add all the modules that want to be before this one to the target's
632
+ // beforeSelf. Do this recursively for every module from the beforeSelf
633
+ // array that has own `beforeSelf` members.
630
634
  addBeforeSelfRecursive(name, m.beforeSelf, target.beforeSelf);
631
635
  }
632
636
 
@@ -670,7 +674,8 @@ async function apostrophe(options, telemetry, rootSpan) {
670
674
  function modulesToBeInstantiated() {
671
675
  return Object.keys(self.options.modules).filter(name => {
672
676
  const improvement = self.synth.isImprovement(name);
673
- return !(self.options.modules[name] && (improvement || self.options.modules[name].instantiate === false));
677
+ return !(self.options.modules[name] &&
678
+ (improvement || self.options.modules[name].instantiate === false));
674
679
  });
675
680
  }
676
681
 
@@ -712,7 +717,11 @@ async function apostrophe(options, telemetry, rootSpan) {
712
717
  return;
713
718
  }
714
719
  const submodule = await self.root.import(path.resolve(self.localModules, name, 'index.js'));
715
- if (submodule && submodule.options && submodule.options.ignoreUnusedFolderWarning) {
720
+ if (
721
+ submodule &&
722
+ submodule.options &&
723
+ submodule.options.ignoreUnusedFolderWarning
724
+ ) {
716
725
  return;
717
726
  }
718
727
  } catch (e) {
@@ -777,14 +786,22 @@ async function apostrophe(options, telemetry, rootSpan) {
777
786
  if (apostropheModule.options.extends && ((typeof apostropheModule.options.extends) === 'string')) {
778
787
  lint(`The module ${name} contains an "extends" option. This is probably a\nmistake. In Apostrophe "extend" is used to extend other modules.`);
779
788
  }
780
- if (apostropheModule.options.singletonWarningIfNot && (name !== apostropheModule.options.singletonWarningIfNot)) {
789
+ if (
790
+ apostropheModule.options.singletonWarningIfNot &&
791
+ (name !== apostropheModule.options.singletonWarningIfNot)
792
+ ) {
781
793
  lint(`The module ${name} extends ${apostropheModule.options.singletonWarningIfNot}, which is normally\na singleton (Apostrophe creates only one instance of it). Two competing\ninstances will lead to problems. If you are adding project-level code to it,\njust use modules/${apostropheModule.options.singletonWarningIfNot}/index.js and do not use "extend".\nIf you are improving it via an npm module, use "improve" rather than "extend".\nIf neither situation applies you should probably just make a new module that does\nnot extend anything.\n\nIf you are sure you know what you are doing, you can set the\nsingletonWarningIfNot: false option for this module.`);
782
794
  }
783
795
  if (name.match(/-widget$/) && (!extending(apostropheModule)) && (!apostropheModule.options.ignoreNoExtendWarning)) {
784
796
  lint(`The module ${name} does not extend anything.\n\nA -widget module usually extends @apostrophecms/widget-type or another widget type.\nOr possibly you forgot to npm install something.\n\nIf you are sure you are doing the right thing, set the\nignoreNoExtendWarning option to true for this module.`);
785
797
  } else if (name.match(/-page$/) && (name !== '@apostrophecms/page') && (!extending(apostropheModule)) && (!apostropheModule.options.ignoreNoExtendWarning)) {
786
798
  lint(`The module ${name} does not extend anything.\n\nA -page module usually extends @apostrophecms/page-type or\n@apostrophecms/piece-page-type or another page type.\nOr possibly you forgot to npm install something.\n\nIf you are sure you are doing the right thing, set the\nignoreNoExtendWarning option to true for this module.`);
787
- } else if ((!extending(apostropheModule)) && (!hasCode(name)) && (!isBundle(name)) && (!apostropheModule.options.ignoreNoCodeWarning)) {
799
+ } else if (
800
+ !extending(apostropheModule) &&
801
+ !hasCode(name) &&
802
+ !isBundle(name) &&
803
+ !apostropheModule.options.ignoreNoCodeWarning
804
+ ) {
788
805
  lint(`The module ${name} does not extend anything and does not have any code.\n\nThis usually means that you:\n\n1. Forgot to "extend" another module\n2. Configured a module that comes from npm without npm installing it\n3. Simply haven't written your "index.js" yet\n\nIf you really want a module with no code, set the ignoreNoCodeWarning option\nto true for this module.`);
789
806
  }
790
807
  }
@@ -9,15 +9,25 @@ export default function checkIfConditions(doc, conditions) {
9
9
  return checkAndConditions(doc, value);
10
10
  }
11
11
 
12
- const isNotEqualCondition = typeof value === 'object' &&
13
- !Array.isArray(value) &&
14
- value !== null &&
15
- Object.hasOwn(value, '$ne');
16
-
17
- if (isNotEqualCondition) {
18
- return getNestedPropValue(doc, key) !== value.$ne;
12
+ if (
13
+ typeof value === 'object' &&
14
+ !Array.isArray(value) &&
15
+ value !== null
16
+ ) {
17
+ if (Object.hasOwn(value, '$ne')) {
18
+ return getNestedPropValue(doc, key) !== value.$ne;
19
+ } else if (Object.hasOwn(value, '$exists')) {
20
+ // Per MongoDB documentation, $exists should treat null and undefined the same.
21
+ // == null and != null are documented to match or reject null, undefined
22
+ // and nothing else.
23
+ const actual = getNestedPropValue(doc, key);
24
+ if (value.$exists) {
25
+ return actual != null;
26
+ } else {
27
+ return actual == null;
28
+ }
29
+ }
19
30
  }
20
-
21
31
  return getNestedPropValue(doc, key) === value;
22
32
  });
23
33
  }
@@ -37,7 +47,15 @@ function checkAndConditions(doc, conditions) {
37
47
  function getNestedPropValue(doc, key) {
38
48
  if (key.includes('.')) {
39
49
  const keys = key.split('.');
40
- return keys.reduce((acc, cur) => acc[cur], doc);
50
+ // Do not crash if a key is not found — mongodb doesn't, which is useful because
51
+ // you can test for things like _image.0 where both might not exist yet
52
+ return keys.reduce((acc, cur) => {
53
+ if ((typeof acc !== 'object') || (acc == null)) {
54
+ return undefined;
55
+ } else {
56
+ return acc[cur];
57
+ }
58
+ }, doc);
41
59
  }
42
60
 
43
61
  return doc[key];
@@ -59,16 +59,18 @@ module.exports = async function(options) {
59
59
 
60
60
  if (options.nestedModuleSubdirs) {
61
61
  if (!self._indexes) {
62
- // Fetching a list of index.js files on the first call and then searching it each time for
63
- // one that refers to the right type name shaves as much as 60 seconds off the startup
64
- // time in a large project, compared to using the glob cache feature
62
+ // Fetching a list of index.js files on the first call and then
63
+ // searching it each time for one that refers to the right type name
64
+ // shaves as much as 60 seconds off the startup time in a large project,
65
+ // compared to using the glob cache feature
65
66
  self._indexes = glob(self.options.localModules + '/**/index.js', { follow: true });
66
67
  }
67
68
  const matches = self._indexes.filter(function(index) {
68
- // Double-check that we're not confusing "@apostrophecms/asset" with "asset" by
69
- // making sure "type" is not preceded by an npm namespace folder. If type is itself namespaced
70
- // this never comes up (because npm namespaces don't nest). The risk is that a legitimate
71
- // project level module that happens to end with the same name as a namespaced module
69
+ // Double-check that we're not confusing "@apostrophecms/asset" with
70
+ // "asset" by making sure "type" is not preceded by an npm namespace
71
+ // folder. If type is itself namespaced this never comes up (because npm
72
+ // namespaces don't nest). The risk is that a legitimate project level
73
+ // module that happens to end with the same name as a namespaced module
72
74
  // will be rejected as a duplicate when nestedModuleSubdirs is present
73
75
  return index.endsWith('/' + type + '/index.js') && !index.match(new RegExp(`/@[^/]+/${regExpQuote(type)}/index\\.js$`));
74
76
  });
@@ -78,7 +80,11 @@ module.exports = async function(options) {
78
80
  projectLevelPath = matches[0] ? path.normalize(matches[0]) : projectLevelPath;
79
81
  }
80
82
  if (fs.existsSync(projectLevelPath)) {
81
- const { default: defaultProjectLevelDefinition } = await importFresh(resolveFrom(path.dirname(self.root.filename), projectLevelPath));
83
+ const {
84
+ default: defaultProjectLevelDefinition
85
+ } = await importFresh(
86
+ resolveFrom(path.dirname(self.root.filename), projectLevelPath)
87
+ );
82
88
  projectLevelDefinition = defaultProjectLevelDefinition;
83
89
  if (Object.keys(projectLevelDefinition).length === 0) {
84
90
  /* eslint-disable-next-line no-console */
@@ -159,15 +165,15 @@ module.exports = async function(options) {
159
165
  if (npmDefinition) {
160
166
  result = await superDefine(type, npmDefinition);
161
167
  if (npmDefinition.improve) {
162
- // Restore the name of the improving module as otherwise our asset chains have
163
- // multiple references to my-foo which is ambiguous
168
+ // Restore the name of the improving module as otherwise our asset
169
+ // chains have multiple references to my-foo which is ambiguous
164
170
  result.__meta.name = originalType;
165
171
  }
166
172
  }
167
173
  result = await superDefine(type, definition);
168
174
  if (npmDefinition && npmDefinition.improve) {
169
- // Restore the name of the improving module as otherwise our asset chains have
170
- // multiple references to my-foo which is ambiguous
175
+ // Restore the name of the improving module as otherwise our asset chains
176
+ // have multiple references to my-foo which is ambiguous
171
177
  result.__meta.name = self.originalToMy(originalType);
172
178
  }
173
179
  // Mark "my" modules as such
@@ -256,7 +262,8 @@ module.exports = async function(options) {
256
262
  return [];
257
263
  }
258
264
 
259
- // Ternary is required because glob expects at least 2 entries when using curly braces
265
+ // Ternary is required because glob expects at least 2 entries
266
+ // when using curly braces
260
267
  const pattern = workspaces.length === 1 ? workspaces[0] : `{${workspaces.join(',')}}`;
261
268
  const packagePath = path.resolve(folder, pattern, 'package.json');
262
269
  const workspacePackages = glob(packagePath, { follow: true });
@@ -275,9 +282,9 @@ module.exports = async function(options) {
275
282
 
276
283
  self.applyImprovementsBeforeProjectLevel = () => {
277
284
  for (const [ name, definition ] of Object.entries(self.definitions)) {
278
- // At this stage the complete definition of a type is a linked list of `extend`
279
- // properties starting from what should be project level, unless there
280
- // are improvements. Shuffle project level to be the first in the
285
+ // At this stage the complete definition of a type is a linked list of
286
+ // `extend` properties starting from what should be project level, unless
287
+ // there are improvements. Shuffle project level to be the first in the
281
288
  // linked list
282
289
  if (definition.__meta.name !== self.originalToMy(name)) {
283
290
  let candidate = definition;
package/lib/moog.js CHANGED
@@ -131,7 +131,8 @@ module.exports = function(options) {
131
131
  // Now we want to start from the base class and go down
132
132
  steps.reverse();
133
133
 
134
- // Actually just the apos object merging in, app.js merging was handled elsewhere
134
+ // Actually just the apos object merging in, app.js merging was handled
135
+ // elsewhere
135
136
  Object.assign(options, _options || {});
136
137
 
137
138
  self.options.sections = self.options.sections || [];
@@ -186,7 +187,8 @@ module.exports = function(options) {
186
187
  if (!that[`${cascade}Groups`]) {
187
188
  that[`${cascade}Groups`] = {};
188
189
  }
189
- // You can have access to options within a function, if you choose to provide one
190
+ // You can have access to options within a function, if you choose to
191
+ // provide one
190
192
  const properties = ((typeof step[cascade]) === 'function') ? step[cascade](that, options) : step[cascade];
191
193
  if (properties) {
192
194
  const valid = [ 'add', 'remove', 'order', 'group' ];
@@ -235,7 +237,8 @@ module.exports = function(options) {
235
237
  if (properties.group) {
236
238
  const groups = klona(that[`${cascade}Groups`]);
237
239
  for (const value of Object.values(properties.group)) {
238
- // Handle `operations` alias of `fields`. Only one of them should be used.
240
+ // Handle `operations` alias of `fields`. Only one of them should
241
+ // be used.
239
242
  if (Array.isArray(value.operations)) {
240
243
  value.fields = value.operations;
241
244
  }
@@ -279,9 +282,9 @@ module.exports = function(options) {
279
282
  }
280
283
  }
281
284
 
282
- // This needs to be after the options and cascades are compiled so it can manipulate
283
- // the result, yet we've already reordered the steps with the superclass first,
284
- // so walk them backwards to implement beforeSuperClass
285
+ // This needs to be after the options and cascades are compiled so it can
286
+ // manipulate the result, yet we've already reordered the steps with the
287
+ // superclass first, so walk them backwards to implement beforeSuperClass
285
288
  for (let i = (steps.length - 1); (i >= 0); i--) {
286
289
  const step = steps[i];
287
290
  if (step.beforeSuperClass) {
@@ -293,14 +296,15 @@ module.exports = function(options) {
293
296
  // in other sections
294
297
  build(null, 'methods');
295
298
 
296
- // Unparsed sections, like `queries` and `extendQueries`. These are just captured
297
- // in an object with the props from each level so the module can do something with
298
- // them at runtime. Do it before init so that init can carry out queries
299
+ // Unparsed sections, like `queries` and `extendQueries`. These are just
300
+ // captured in an object with the props from each level so the module can do
301
+ // something with them at runtime. Do it before init so that init can carry
302
+ // out queries
299
303
  (self.options.unparsedSections || []).forEach(section => capture(section));
300
304
 
301
- // init is called BEFORE routes etc. are called so that the section functions
302
- // for those things can benefit from methods, properties, etc. set by init
303
- // when deciding what to return
305
+ // init is called BEFORE routes etc. are called so that the section
306
+ // functions for those things can benefit from methods, properties, etc. set
307
+ // by init when deciding what to return
304
308
  for (const step of steps) {
305
309
  if (step.init) {
306
310
  await step.init(that, options);
@@ -312,9 +316,9 @@ module.exports = function(options) {
312
316
 
313
317
  // afterAllSections is called last and has access to self.routes, etc.
314
318
  // For project-level developers this is usually not important, but
315
- // the core does call certain methods in the @apostrophecms/modules base class
316
- // implementation of afterAllSections to actually add the routes and handlers
317
- // to the system
319
+ // the core does call certain methods in the @apostrophecms/modules base
320
+ // class implementation of afterAllSections to actually add the routes and
321
+ // handlers to the system
318
322
  for (const step of steps) {
319
323
  if (step.afterAllSections) {
320
324
  await step.afterAllSections(that, options);
@@ -419,9 +423,10 @@ module.exports = function(options) {
419
423
 
420
424
  self.options.unparsedSections = self.options.unparsedSections || [];
421
425
 
422
- // Unparsed sections, like `queries` and `extendQueries`. These are just captured
423
- // in an object with the props from each level so the module can do something with
424
- // them at runtime. Do it before init so that init can carry out queries
426
+ // Unparsed sections, like `queries` and `extendQueries`. These are just
427
+ // captured in an object with the props from each level so the module can do
428
+ // something with them at runtime. Do it before init so that init can carry
429
+ // out queries
425
430
  (self.options.unparsedSections || []).forEach(section => capture(section));
426
431
 
427
432
  return that;
@@ -32,8 +32,8 @@ module.exports = function (options = {}) {
32
32
  }
33
33
 
34
34
  /**
35
- * Start and return a new span. Optionally provide a parent span or allow the parent
36
- * span to be auto-detected.
35
+ * Start and return a new span. Optionally provide a parent span or allow the
36
+ * parent span to be auto-detected.
37
37
  * Use this when the current code block does the tracing and it doesn't expect
38
38
  * more tracing to happen down the line.
39
39
  *
@@ -82,7 +82,8 @@ module.exports = function (options = {}) {
82
82
  * @returns {api.Span|any} the return value of the handler or the newly created span
83
83
  * @example
84
84
  * // Activate span, return some value
85
- * const value = await self.apos.telemetry.startActiveSpan(spanName, async (span) => {
85
+ * const value = await self.apos.telemetry.startActiveSpan(spanName, async
86
+ * (span) => {
86
87
  * // Use the span, do work, end span, return any value
87
88
  * span.end();
88
89
  * return value;
@@ -1,8 +1,9 @@
1
- // The admin bar module implements Apostrophe's admin bar at the top of the screen. Any module
2
- // can register a button (or more than one) for this bar by calling the `add` method of this
3
- // module. Buttons can also be grouped into dropdown menus and restricted to those with
4
- // particular permissions. [@apostrophecms/piece-type](../@apostrophecms/piece-type/index.html) automatically
5
- // takes advantage of this module.
1
+ // The admin bar module implements Apostrophe's admin bar at the top of the
2
+ // screen. Any module can register a button (or more than one) for this bar by
3
+ // calling the `add` method of this module. Buttons can also be grouped into
4
+ // dropdown menus and restricted to those with particular permissions.
5
+ // [@apostrophecms/piece-type](../@apostrophecms/piece-type/index.html)
6
+ // automatically takes advantage of this module.
6
7
 
7
8
  const _ = require('lodash');
8
9
  const { createId } = require('@paralleldrive/cuid2');
@@ -190,14 +191,15 @@ module.exports = {
190
191
  // wish to implement a custom admin bar item not powered by
191
192
  // the `AposModals` app.
192
193
  //
193
- // If `options.contextUtility` is true, the item will be displayed in a tray of
194
- // icons just to the right of the login and/or locales menu. If `options.toggle` is also true,
195
- // then the button will have the `active` state until toggled
196
- // off again. `options.tooltip.deactivate` and `options.tooltip.activate` may be
197
- // provided to offer a different tooltip during the active versus inactive states,
198
- // respectively. Otherwise, `options.tooltip` is used. The regular label is also present
199
- // for screenreaders only. The contextUtility functionality is typically used for
200
- // experiences that temporarily change the current editing context.
194
+ // If `options.contextUtility` is true, the item will be displayed in a
195
+ // tray of icons just to the right of the login and/or locales menu. If
196
+ // `options.toggle` is also true, then the button will have the `active`
197
+ // state until toggled off again. `options.tooltip.deactivate` and
198
+ // `options.tooltip.activate` may be provided to offer a different tooltip
199
+ // during the active versus inactive states, respectively. Otherwise,
200
+ // `options.tooltip` is used. The regular label is also present for
201
+ // screenreaders only. The contextUtility functionality is typically used
202
+ // for experiences that temporarily change the current editing context.
201
203
  //
202
204
  // If `options.user` is true, the menu bar item will appear
203
205
  // on the user's personal dropdown, where "Log Out" appears. Such items
@@ -256,7 +258,10 @@ module.exports = {
256
258
  }
257
259
  // Only build a menu if there are at least two items after filtering
258
260
  // for permissions
259
- if (item.menuLeader === item.name && (items[i + 1] && items[i + 1].menuLeader === item.name)) {
261
+ if (
262
+ item.menuLeader === item.name &&
263
+ items[i + 1]?.menuLeader === item.name
264
+ ) {
260
265
  menu = {
261
266
  menu: true,
262
267
  items: [ item ],
@@ -313,7 +318,8 @@ module.exports = {
313
318
  groupItems() {
314
319
  // Implement the groups and addGroups options. Mark the grouped items
315
320
  // with a `menuLeader` property.
316
- const groups = self.options.groups || self.groups.concat(self.options.addGroups || []);
321
+ const groups = self.options.groups ||
322
+ self.groups.concat(self.options.addGroups || []);
317
323
 
318
324
  groups.forEach(function (group) {
319
325
  if (!group.label) {
@@ -418,7 +424,8 @@ module.exports = {
418
424
  screens: {}
419
425
  },
420
426
  // Base API URL appropriate to the context document
421
- contextBar: context && self.apos.doc.getManager(context.type).options.contextBar,
427
+ contextBar: context && self.apos.doc
428
+ .getManager(context.type).options.contextBar,
422
429
  showAdminBar: self.getShowAdminBar(req),
423
430
  // Simplifies frontend logic
424
431
  contextId: context && context._id,
@@ -13,7 +13,7 @@
13
13
  class="apos-admin-bar__btn"
14
14
  :modifiers="['no-motion']"
15
15
  role="menuitem"
16
- @click="emitEvent('@apostrophecms/page:manager')"
16
+ @click="emitEvent({ action: '@apostrophecms/page:manager' })"
17
17
  />
18
18
  </li>
19
19
  <li
@@ -42,7 +42,7 @@
42
42
  :modifiers="['no-motion']"
43
43
  class="apos-admin-bar__btn"
44
44
  role="menuitem"
45
- @click="emitEvent(item.action)"
45
+ @click="emitEvent(item)"
46
46
  />
47
47
  </li>
48
48
  <li
@@ -85,7 +85,7 @@
85
85
  :label="item.label"
86
86
  :action="item.action"
87
87
  :state="trayItemState[item.name] ? [ 'active' ] : []"
88
- @click="emitEvent(item.action)"
88
+ @click="emitEvent(item)"
89
89
  />
90
90
  </template>
91
91
  </li>
@@ -122,10 +122,9 @@ export default {
122
122
  }
123
123
  },
124
124
  async mounted() {
125
- apos.bus.$on('admin-menu-click', this.onAdminMenuClick);
126
-
127
125
  const itemsSet = klona(this.items);
128
- this.menuItems = itemsSet.filter(item => !(item.options && item.options.contextUtility))
126
+ this.menuItems = itemsSet
127
+ .filter(item => !(item.options && item.options.contextUtility))
129
128
  .map(item => {
130
129
  if (item.items) {
131
130
  item.items.forEach(subitem => {
@@ -148,12 +147,25 @@ export default {
148
147
  });
149
148
  },
150
149
  methods: {
151
- emitEvent(name) {
152
- apos.bus.$emit('admin-menu-click', name);
150
+ emitEvent(item) {
151
+ apos.bus.$emit('admin-menu-click', item.action);
152
+
153
+ // Maintain a knowledge of which tray item toggles are active
154
+ const trayItem = this.trayItems.find(trayItem => trayItem.name === item.action);
155
+
156
+ if (trayItem && trayItem.options.toggle) {
157
+ this.trayItemState = {
158
+ ...this.trayItemState,
159
+ [item.action]: !this.trayItemState[item.action]
160
+ };
161
+ }
153
162
  },
154
163
  trayItemTooltip(item) {
155
164
  if (item.options.toggle) {
156
- if (this.trayItemState[item.name] && item.options.tooltip && item.options.tooltip.deactivate) {
165
+ if (
166
+ this.trayItemState[item.name] &&
167
+ item.options.tooltip?.deactivate
168
+ ) {
157
169
  return {
158
170
  content: item.options.tooltip.deactivate,
159
171
  placement: 'bottom'
@@ -169,17 +181,6 @@ export default {
169
181
  } else {
170
182
  return item.options.tooltip;
171
183
  }
172
- },
173
- // Maintain a knowledge of which tray item toggles are active
174
- onAdminMenuClick(e) {
175
- const name = e.itemName || e;
176
- const trayItem = this.trayItems.find(item => item.name === name);
177
- if (trayItem && trayItem.options.toggle) {
178
- this.trayItemState = {
179
- ...this.trayItemState,
180
- [name]: !this.trayItemState[name]
181
- };
182
- }
183
184
  }
184
185
  }
185
186
  };
@@ -43,8 +43,8 @@ export default {
43
43
  }
44
44
  },
45
45
  methods: {
46
- emitEvent(name) {
47
- apos.bus.$emit('admin-menu-click', name);
46
+ emitEvent(item) {
47
+ apos.bus.$emit('admin-menu-click', item.action);
48
48
  }
49
49
  }
50
50
  };