@conduction/nextcloud-vue 2.56.0-beta.4 → 2.56.0-beta.5

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 (300) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +12 -2
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +1 -2
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  5. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
  6. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +0 -3
  7. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
  8. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -2
  10. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  11. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +12 -5
  12. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  13. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +2 -1
  14. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  15. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +9 -1
  16. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  17. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  18. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +6 -1
  19. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
  20. package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
  21. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +116 -15
  22. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  23. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +27 -14
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  25. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js +13 -6
  26. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js.map +1 -1
  27. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js +2 -0
  28. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js.map +1 -1
  29. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -1
  30. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  31. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  32. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  33. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +1 -2
  34. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  35. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +3 -1
  36. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  37. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +17 -17
  38. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  39. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js +4 -21
  40. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js.map +1 -1
  41. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue3.js.map +1 -1
  42. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +0 -13
  43. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
  44. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js +8 -8
  45. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
  46. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue2.js.map +1 -1
  47. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js +3 -2
  48. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js.map +1 -1
  49. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +67 -13
  50. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  51. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +15 -8
  52. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  53. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js +31 -11
  54. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js.map +1 -1
  55. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue2.js.map +1 -1
  56. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js +1 -2
  57. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js.map +1 -1
  58. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
  59. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +0 -1
  60. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
  61. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js +2 -17
  62. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue3.js.map +1 -1
  64. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js +35 -11
  65. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js.map +1 -1
  66. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js +3 -2
  67. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js.map +1 -1
  68. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +23 -5
  69. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  70. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +1 -1
  71. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  72. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js +19 -3
  73. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js.map +1 -1
  74. package/dist/esm/components/CnObjectList/CnObjectList.vue3.js.map +1 -1
  75. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +45 -15
  76. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  77. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
  78. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  79. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +24 -6
  80. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
  81. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +1 -1
  82. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
  83. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  84. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +0 -1
  85. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  86. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
  87. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +4 -2
  88. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
  89. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +7 -2
  90. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
  91. package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
  92. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js +5 -3
  93. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js.map +1 -1
  94. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue3.js.map +1 -1
  95. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  96. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  97. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +3 -1
  98. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  99. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +1 -0
  100. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  101. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js +3 -1
  102. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js.map +1 -1
  103. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue3.js.map +1 -1
  104. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -1
  105. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
  106. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js +2 -4
  107. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
  108. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js +6 -0
  109. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js.map +1 -1
  110. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js +68 -53
  111. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js.map +1 -1
  112. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js +17 -0
  113. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js.map +1 -1
  114. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js +21 -19
  115. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js.map +1 -1
  116. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js +3 -1
  117. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js.map +1 -1
  118. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue3.js.map +1 -1
  119. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js +3 -1
  120. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js.map +1 -1
  121. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js +2 -4
  122. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js.map +1 -1
  123. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js +2 -1
  124. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js.map +1 -1
  125. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue3.js.map +1 -1
  126. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
  127. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
  128. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js +1 -2
  129. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
  130. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +2 -1
  131. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  132. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  133. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
  134. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  135. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js +1 -2
  136. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  137. package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
  138. package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
  139. package/dist/esm/integrations/builtin/email/CnEmailTab.vue2.js.map +1 -1
  140. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js +1 -2
  141. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js.map +1 -1
  142. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js +2 -1
  143. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js.map +1 -1
  144. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue3.js.map +1 -1
  145. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js +2 -1
  146. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js.map +1 -1
  147. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js +1 -2
  148. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js.map +1 -1
  149. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js +3 -1
  150. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js.map +1 -1
  151. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue3.js.map +1 -1
  152. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js +3 -1
  153. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js.map +1 -1
  154. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js +2 -4
  155. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js.map +1 -1
  156. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js +3 -1
  157. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js.map +1 -1
  158. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue3.js.map +1 -1
  159. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js +3 -1
  160. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js.map +1 -1
  161. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js +2 -4
  162. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js.map +1 -1
  163. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js +3 -1
  164. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js.map +1 -1
  165. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue3.js.map +1 -1
  166. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js +3 -1
  167. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js.map +1 -1
  168. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js +2 -4
  169. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js.map +1 -1
  170. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js +2 -1
  171. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js.map +1 -1
  172. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue3.js.map +1 -1
  173. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
  174. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
  175. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js +1 -2
  176. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
  177. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js +2 -1
  178. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js.map +1 -1
  179. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue3.js.map +1 -1
  180. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js +2 -1
  181. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js.map +1 -1
  182. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js +1 -2
  183. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js.map +1 -1
  184. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue2.js.map +1 -1
  185. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js +3 -6
  186. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js.map +1 -1
  187. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js +3 -1
  188. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js.map +1 -1
  189. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue3.js.map +1 -1
  190. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js +3 -1
  191. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js.map +1 -1
  192. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js +2 -4
  193. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js.map +1 -1
  194. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js +2 -1
  195. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js.map +1 -1
  196. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue3.js.map +1 -1
  197. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js +2 -1
  198. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js.map +1 -1
  199. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js +1 -2
  200. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js.map +1 -1
  201. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js +4 -1
  202. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js.map +1 -1
  203. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue3.js.map +1 -1
  204. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js +6 -3
  205. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js.map +1 -1
  206. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js +2 -4
  207. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js.map +1 -1
  208. package/dist/esm/l10n/en.json.js +1 -0
  209. package/dist/esm/l10n/en.json.js.map +1 -1
  210. package/dist/esm/l10n/nl.json.js +1 -0
  211. package/dist/esm/l10n/nl.json.js.map +1 -1
  212. package/dist/esm/nextcloud-vue.css +71 -7
  213. package/dist/esm/utils/actionLink.js +5 -0
  214. package/dist/esm/utils/actionLink.js.map +1 -1
  215. package/dist/esm/utils/actionsDispatcher.js +16 -1
  216. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  217. package/dist/esm/utils/linkNavigation.js +6 -3
  218. package/dist/esm/utils/linkNavigation.js.map +1 -1
  219. package/dist/esm/utils/rowAuxClick.js +30 -5
  220. package/dist/esm/utils/rowAuxClick.js.map +1 -1
  221. package/dist/esm/utils/safeHref.js +18 -3
  222. package/dist/esm/utils/safeHref.js.map +1 -1
  223. package/dist/nextcloud-vue.cjs.js +809 -383
  224. package/dist/nextcloud-vue.cjs.js.map +2 -2
  225. package/dist/nextcloud-vue.css +71 -7
  226. package/l10n/en.json +1 -0
  227. package/l10n/nl.json +1 -0
  228. package/package.json +1 -1
  229. package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
  230. package/src/components/CnActionsMenu/CnActionsMenu.vue +0 -3
  231. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
  232. package/src/components/CnBoardView/CnBoardView.vue +13 -5
  233. package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
  234. package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
  235. package/src/components/CnDataTable/CnDataTable.md +1 -1
  236. package/src/components/CnDataTable/CnDataTable.vue +131 -17
  237. package/src/components/CnDateAxisView/CnDateAxisView.vue +15 -6
  238. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  239. package/src/components/CnDetailPage/CnDetailPage.vue +29 -1
  240. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -2
  241. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +5 -3
  242. package/src/components/CnFilesWidget/CnFilesWidget.vue +4 -21
  243. package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +8 -21
  244. package/src/components/CnFlowsPage/CnFlowsPage.vue +2 -1
  245. package/src/components/CnIndexPage/CnIndexPage.vue +75 -14
  246. package/src/components/CnIndexPage/manifestActionDispatch.js +31 -11
  247. package/src/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue +1 -2
  248. package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +0 -1
  249. package/src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue +2 -17
  250. package/src/components/CnLogsPage/CnLogsPage.vue +37 -12
  251. package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
  252. package/src/components/CnObjectList/CnObjectList.vue +19 -3
  253. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +47 -15
  254. package/src/components/CnObjectRow/CnObjectRow.vue +25 -7
  255. package/src/components/CnObjectSidebar/CnFilesTab.vue +0 -1
  256. package/src/components/CnPageRenderer/CnPageRenderer.vue +2 -0
  257. package/src/components/CnRowActions/CnRowActions.vue +7 -2
  258. package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
  259. package/src/components/CnTabs/CnTabs.vue +7 -3
  260. package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
  261. package/src/css/table.css +26 -0
  262. package/src/integrations/builtin/analytics/CnAnalyticsCard.vue +3 -2
  263. package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +5 -6
  264. package/src/integrations/builtin/bookmarks/CnBookmarksCard.vue +12 -1
  265. package/src/integrations/builtin/bookmarks/CnBookmarksTab.vue +23 -6
  266. package/src/integrations/builtin/bookmarks/__tests__/CnBookmarksTab.spec.js +7 -2
  267. package/src/integrations/builtin/collectives/CnCollectivesCard.vue +3 -2
  268. package/src/integrations/builtin/collectives/CnCollectivesTab.vue +5 -6
  269. package/src/integrations/builtin/cospend/CnCospendCard.vue +2 -2
  270. package/src/integrations/builtin/cospend/CnCospendTab.vue +3 -4
  271. package/src/integrations/builtin/deck/CnDeckCard.vue +2 -2
  272. package/src/integrations/builtin/deck/CnDeckTab.vue +3 -4
  273. package/src/integrations/builtin/email/CnEmailCard.vue +6 -2
  274. package/src/integrations/builtin/email/CnEmailTab.vue +7 -4
  275. package/src/integrations/builtin/flow/CnFlowCard.vue +2 -2
  276. package/src/integrations/builtin/flow/CnFlowTab.vue +3 -4
  277. package/src/integrations/builtin/forms/CnFormsCard.vue +3 -2
  278. package/src/integrations/builtin/forms/CnFormsTab.vue +5 -6
  279. package/src/integrations/builtin/maps/CnMapsCard.vue +3 -2
  280. package/src/integrations/builtin/maps/CnMapsTab.vue +5 -6
  281. package/src/integrations/builtin/maps/__tests__/CnMapsTab.spec.js +9 -0
  282. package/src/integrations/builtin/openproject/CnOpenprojectCard.vue +3 -2
  283. package/src/integrations/builtin/openproject/CnOpenprojectTab.vue +5 -6
  284. package/src/integrations/builtin/photos/CnPhotosCard.vue +2 -2
  285. package/src/integrations/builtin/photos/CnPhotosTab.vue +3 -4
  286. package/src/integrations/builtin/polls/CnPollsCard.vue +2 -2
  287. package/src/integrations/builtin/polls/CnPollsTab.vue +3 -4
  288. package/src/integrations/builtin/shares/CnSharesTab.vue +3 -6
  289. package/src/integrations/builtin/talk/CnTalkCard.vue +3 -2
  290. package/src/integrations/builtin/talk/CnTalkTab.vue +5 -6
  291. package/src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue +2 -2
  292. package/src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue +3 -4
  293. package/src/integrations/builtin/xwiki/CnXwikiCard.vue +4 -2
  294. package/src/integrations/builtin/xwiki/CnXwikiTab.vue +7 -7
  295. package/src/integrations/builtin/xwiki/__tests__/CnXwikiTab.spec.js +9 -0
  296. package/src/utils/actionLink.js +5 -0
  297. package/src/utils/actionsDispatcher.js +15 -0
  298. package/src/utils/linkNavigation.js +6 -3
  299. package/src/utils/rowAuxClick.js +29 -4
  300. package/src/utils/safeHref.js +18 -3
@@ -34,9 +34,11 @@
34
34
  @error="onFaviconError(entity)">
35
35
  <Bookmark v-else :size="14" />
36
36
  <a
37
+ v-if="hasSafeUrl(entity)"
37
38
  :href="entity.url"
38
39
  target="_blank"
39
40
  rel="noopener noreferrer">{{ bookmarkTitle(entity) }}</a>
41
+ <span v-else>{{ bookmarkTitle(entity) }}</span>
40
42
  </span>
41
43
  <span v-else class="cn-bookmarks-card__empty">{{ emptyLabel }}</span>
42
44
  </template>
@@ -62,9 +64,11 @@
62
64
  @error="onFaviconError(mostRecent)">
63
65
  <Bookmark v-else :size="14" />
64
66
  <a
67
+ v-if="hasSafeUrl(mostRecent)"
65
68
  :href="mostRecent.url"
66
69
  target="_blank"
67
70
  rel="noopener noreferrer">{{ bookmarkTitle(mostRecent) }}</a>
71
+ <span v-else>{{ bookmarkTitle(mostRecent) }}</span>
68
72
  </div>
69
73
  <div v-if="bookmarks.length > 1" class="cn-bookmarks-card__view-all">
70
74
  <a
@@ -99,10 +103,12 @@
99
103
  </div>
100
104
  <div class="cn-bookmarks-card__row-main">
101
105
  <a
106
+ v-if="hasSafeUrl(bookmark)"
102
107
  :href="bookmark.url"
103
108
  target="_blank"
104
109
  rel="noopener noreferrer"
105
110
  class="cn-bookmarks-card__title">{{ bookmarkTitle(bookmark) }}</a>
111
+ <span v-else class="cn-bookmarks-card__title">{{ bookmarkTitle(bookmark) }}</span>
106
112
  </div>
107
113
  </li>
108
114
  </ul>
@@ -121,7 +127,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
121
127
  import { NcLoadingIcon } from '@nextcloud/vue'
122
128
  import Bookmark from 'vue-material-design-icons/Bookmark.vue'
123
129
  import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
124
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
130
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
125
131
 
126
132
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
127
133
  const COMPACT_LIMIT = 5
@@ -245,6 +251,11 @@ export default {
245
251
  return bookmark.title || bookmark.url || ''
246
252
  },
247
253
 
254
+ // Bookmark URLs are user-entered (bookmarklets are `javascript:`), so only a safe one is a link.
255
+ hasSafeUrl(bookmark) {
256
+ return safeHref(bookmark.url) !== '#'
257
+ },
258
+
248
259
  chipSubtitle(bookmark) {
249
260
  return bookmark.description || bookmark.url || this.bookmarkTitle(bookmark)
250
261
  },
@@ -57,8 +57,7 @@
57
57
  <NcButton
58
58
  variant="primary"
59
59
  :href="bookmarksAppUrl"
60
- target="_blank"
61
- rel="noopener noreferrer">
60
+ target="_blank">
62
61
  <template #icon>
63
62
  <Bookmark :size="20" />
64
63
  </template>
@@ -92,8 +91,9 @@
92
91
  :name="bookmarkTitle(bookmark)"
93
92
  :bold="true"
94
93
  :href="bookmarkHref(bookmark)"
95
- target="_blank"
96
- :forceDisplayActions="true">
94
+ :target="hasSafeUrl(bookmark) ? '_blank' : undefined"
95
+ :forceDisplayActions="true"
96
+ @click="onBookmarkClick(bookmark, $event)">
97
97
  <template #icon>
98
98
  <span class="cn-bookmarks-tab__row-icon">
99
99
  <Bookmark :size="20" />
@@ -121,10 +121,10 @@
121
121
  </template>
122
122
  <template #actions>
123
123
  <NcActionLink
124
+ v-if="hasSafeUrl(bookmark)"
124
125
  :closeAfterClick="true"
125
126
  :href="bookmarkHref(bookmark)"
126
- target="_blank"
127
- rel="noopener noreferrer">
127
+ target="_blank">
128
128
  <template #icon>
129
129
  <OpenInNew :size="20" />
130
130
  </template>
@@ -393,6 +393,23 @@ export default {
393
393
  return safeHref(bookmark.url)
394
394
  },
395
395
 
396
+ /**
397
+ * Whether the bookmark's URL passed `safeHref`, so its row is a link.
398
+ *
399
+ * @param {object} bookmark The bookmark row.
400
+ * @return {boolean}
401
+ */
402
+ hasSafeUrl(bookmark) {
403
+ return this.bookmarkHref(bookmark) !== '#'
404
+ },
405
+
406
+ // NcListItem always renders an anchor; one without a safe URL must not follow `#`.
407
+ onBookmarkClick(bookmark, event) {
408
+ if (!this.hasSafeUrl(bookmark)) {
409
+ event?.preventDefault?.()
410
+ }
411
+ },
412
+
396
413
  bookmarkTags(bookmark) {
397
414
  const tags = bookmark.tags
398
415
  if (!Array.isArray(tags)) {
@@ -78,7 +78,7 @@ describe('CnBookmarksTab', () => {
78
78
  expect(openLink.attributes('target')).toBe('_blank')
79
79
  })
80
80
 
81
- it('neutralises unsafe bookmark URL schemes in the row and action hrefs', async () => {
81
+ it('renders a bookmark with an unsafe URL as an inert row with no Open action', async () => {
82
82
  global.fetch = jest.fn().mockResolvedValueOnce({
83
83
  ok: true,
84
84
  status: 200,
@@ -90,7 +90,12 @@ describe('CnBookmarksTab', () => {
90
90
  await flushPromises()
91
91
  const row = wrapper.find('.cn-bookmarks-tab__row')
92
92
  expect(row.attributes('href')).toBe('#')
93
- expect(row.find('.stub.NcActionLink').attributes('href')).toBe('#')
93
+ expect(row.attributes('target')).toBeUndefined()
94
+ expect(row.find('.stub.NcActionLink').exists()).toBe(false)
95
+
96
+ const event = { preventDefault: jest.fn() }
97
+ wrapper.vm.onBookmarkClick(wrapper.vm.filteredBookmarks[0], event)
98
+ expect(event.preventDefault).toHaveBeenCalled()
94
99
  wrapper.unmount()
95
100
  })
96
101
 
@@ -126,7 +126,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
126
126
  import { NcLoadingIcon } from '@nextcloud/vue'
127
127
  import BookOpenPageVariant from 'vue-material-design-icons/BookOpenPageVariant.vue'
128
128
  import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
129
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
129
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
130
130
  import { stripMarker } from '../../utils/marker.js'
131
131
 
132
132
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
@@ -263,8 +263,9 @@ export default {
263
263
  return typeof emoji === 'string' ? emoji.trim() : ''
264
264
  },
265
265
 
266
+ // A provider URL is used only when it is a safe scheme; else the app link.
266
267
  pageUrl(page) {
267
- if (typeof page.url === 'string' && page.url !== '') {
268
+ if (typeof page.url === 'string' && page.url !== '' && safeHref(page.url) !== '#') {
268
269
  return page.url
269
270
  }
270
271
  const d = this.dataOf(page)
@@ -72,8 +72,7 @@
72
72
  <NcButton
73
73
  variant="primary"
74
74
  :href="collectivesAppUrl"
75
- target="_blank"
76
- rel="noopener noreferrer">
75
+ target="_blank">
77
76
  <template #icon>
78
77
  <BookOpenPageVariant :size="20" />
79
78
  </template>
@@ -122,8 +121,7 @@
122
121
  <NcActionLink
123
122
  :closeAfterClick="true"
124
123
  :href="pageUrl(page)"
125
- target="_blank"
126
- rel="noopener noreferrer">
124
+ target="_blank">
127
125
  <template #icon>
128
126
  <OpenInNew :size="20" />
129
127
  </template>
@@ -168,7 +166,7 @@ import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
168
166
  import Plus from 'vue-material-design-icons/Plus.vue'
169
167
  import CnCollectivePageCreate from '../../../components/CnCollectivePageCreate/CnCollectivePageCreate.vue'
170
168
  import CnCollectivePagePicker from '../../../components/CnCollectivePagePicker/CnCollectivePagePicker.vue'
171
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
169
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
172
170
  import { stripMarker } from '../../utils/marker.js'
173
171
 
174
172
  const MS_PER_DAY = 24 * 60 * 60 * 1000
@@ -333,8 +331,9 @@ export default {
333
331
  return text
334
332
  },
335
333
 
334
+ // A provider URL is used only when it is a safe scheme; else the app link.
336
335
  pageUrl(page) {
337
- if (typeof page.url === 'string' && page.url !== '') {
336
+ if (typeof page.url === 'string' && page.url !== '' && safeHref(page.url) !== '#') {
338
337
  return page.url
339
338
  }
340
339
  const d = this.dataOf(page)
@@ -108,7 +108,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
108
108
  import { NcLoadingIcon } from '@nextcloud/vue'
109
109
  import CurrencyEur from 'vue-material-design-icons/CurrencyEur.vue'
110
110
  import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
111
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
111
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
112
112
 
113
113
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
114
114
 
@@ -250,7 +250,7 @@ export default {
250
250
  },
251
251
 
252
252
  rowUrl(row) {
253
- if (row.url) {
253
+ if (row.url && safeHref(row.url) !== '#') {
254
254
  return row.url
255
255
  }
256
256
  const projectId = row.projectId ?? row.project_id ?? row.data?.projectid ?? row.data?.project_id ?? null
@@ -75,8 +75,7 @@
75
75
  <NcButton
76
76
  variant="primary"
77
77
  :href="cospendAppUrl"
78
- target="_blank"
79
- rel="noopener noreferrer">
78
+ target="_blank">
80
79
  <template #icon>
81
80
  <CurrencyEur :size="20" />
82
81
  </template>
@@ -175,7 +174,7 @@ import Plus from 'vue-material-design-icons/Plus.vue'
175
174
  import CnCospendCreate from '../../../components/CnCospendCreate/CnCospendCreate.vue'
176
175
  import CnCospendPicker from '../../../components/CnCospendPicker/CnCospendPicker.vue'
177
176
  import CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'
178
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
177
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
179
178
 
180
179
  /**
181
180
  * CnCospendTab — bespoke sidebar tab for the `cospend` integration.
@@ -389,7 +388,7 @@ export default {
389
388
  },
390
389
 
391
390
  rowUrl(row) {
392
- if (row.url) {
391
+ if (row.url && safeHref(row.url) !== '#') {
393
392
  return row.url
394
393
  }
395
394
  const projectId = row.projectId ?? row.project_id ?? row.data?.projectid ?? row.data?.project_id ?? null
@@ -110,7 +110,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
110
110
  import { NcLoadingIcon } from '@nextcloud/vue'
111
111
  import ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue'
112
112
  import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
113
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
113
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
114
114
 
115
115
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
116
116
 
@@ -276,7 +276,7 @@ export default {
276
276
  },
277
277
 
278
278
  cardUrl(card) {
279
- if (card.url) {
279
+ if (card.url && safeHref(card.url) !== '#') {
280
280
  return card.url
281
281
  }
282
282
  const boardId = card.boardId ?? card.board?.id ?? ''
@@ -58,8 +58,7 @@
58
58
  <NcButton
59
59
  variant="primary"
60
60
  :href="deckAppUrl"
61
- target="_blank"
62
- rel="noopener noreferrer">
61
+ target="_blank">
63
62
  <template #icon>
64
63
  <ViewColumnOutline :size="20" />
65
64
  </template>
@@ -168,7 +167,7 @@ import ViewColumnOutline from 'vue-material-design-icons/ViewColumnOutline.vue'
168
167
  import CnDeckCardCreate from '../../../components/CnDeckCardCreate/CnDeckCardCreate.vue'
169
168
  import CnDeckCardPicker from '../../../components/CnDeckCardPicker/CnDeckCardPicker.vue'
170
169
  import CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'
171
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
170
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
172
171
 
173
172
  /** Maximum assignee avatars shown before collapsing the rest into a +N overflow badge. */
174
173
  const MAX_VISIBLE_ASSIGNEES = 3
@@ -353,7 +352,7 @@ export default {
353
352
  },
354
353
 
355
354
  cardUrl(card) {
356
- if (card.url) {
355
+ if (card.url && safeHref(card.url) !== '#') {
357
356
  return card.url
358
357
  }
359
358
  const boardId = card.boardId ?? card.board?.id ?? ''
@@ -321,7 +321,6 @@ export default {
321
321
  background: none;
322
322
  border: none;
323
323
  padding: 6px 0;
324
- cursor: pointer;
325
324
  text-align: left;
326
325
  color: inherit;
327
326
  font: inherit;
@@ -329,7 +328,12 @@ export default {
329
328
  box-sizing: border-box;
330
329
  }
331
330
 
332
- .cn-email-card__open:hover .cn-email-card__subject {
331
+ /* Only a row with Mail ids is a link; the rest must not look clickable. */
332
+ a.cn-email-card__open {
333
+ cursor: pointer;
334
+ }
335
+
336
+ a.cn-email-card__open:hover .cn-email-card__subject {
333
337
  color: var(--color-primary-element);
334
338
  }
335
339
 
@@ -27,8 +27,7 @@
27
27
  <NcButton
28
28
  variant="primary"
29
29
  :href="composeUrl"
30
- target="_blank"
31
- rel="noopener noreferrer">
30
+ target="_blank">
32
31
  <template #icon>
33
32
  <EmailEditOutline :size="18" />
34
33
  </template>
@@ -497,11 +496,15 @@ export default {
497
496
  border-radius: var(--border-radius-large, 8px);
498
497
  color: inherit;
499
498
  text-decoration: none;
500
- cursor: pointer;
501
499
  position: relative;
502
500
  }
503
501
 
504
- .cn-email-tab__row:hover {
502
+ /* Only a row with Mail ids is a link; the rest must not look clickable. */
503
+ a.cn-email-tab__row {
504
+ cursor: pointer;
505
+ }
506
+
507
+ a.cn-email-tab__row:hover {
505
508
  background-color: var(--color-background-hover);
506
509
  }
507
510
 
@@ -104,7 +104,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
104
104
  import { NcLoadingIcon } from '@nextcloud/vue'
105
105
  import SitemapOutline from 'vue-material-design-icons/SitemapOutline.vue'
106
106
  import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
107
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
107
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
108
108
 
109
109
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
110
110
  const COMPACT_LIMIT = 5
@@ -239,7 +239,7 @@ export default {
239
239
  },
240
240
 
241
241
  opUrl(op) {
242
- return op.url || this.flowSettingsUrl
242
+ return op.url && safeHref(op.url) !== '#' ? op.url : this.flowSettingsUrl
243
243
  },
244
244
 
245
245
  shortClass(className) {
@@ -58,8 +58,7 @@
58
58
  <NcButton
59
59
  variant="primary"
60
60
  :href="flowSettingsUrl"
61
- target="_blank"
62
- rel="noopener noreferrer">
61
+ target="_blank">
63
62
  <template #icon>
64
63
  <SitemapOutline :size="20" />
65
64
  </template>
@@ -139,7 +138,7 @@ import Plus from 'vue-material-design-icons/Plus.vue'
139
138
  import SitemapOutline from 'vue-material-design-icons/SitemapOutline.vue'
140
139
  import CnFlowOperationPicker from '../../../components/CnFlowOperationPicker/CnFlowOperationPicker.vue'
141
140
  import CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'
142
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
141
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
143
142
 
144
143
  /**
145
144
  * CnFlowTab — bespoke automation-rule list for the `flow` integration.
@@ -218,7 +217,7 @@ export default {
218
217
  },
219
218
 
220
219
  opUrl(op) {
221
- return op.url || this.flowSettingsUrl
220
+ return op.url && safeHref(op.url) !== '#' ? op.url : this.flowSettingsUrl
222
221
  },
223
222
 
224
223
  shortClass(className) {
@@ -100,7 +100,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
100
100
  import { NcLoadingIcon } from '@nextcloud/vue'
101
101
  import ClipboardText from 'vue-material-design-icons/ClipboardText.vue'
102
102
  import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
103
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
103
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
104
104
 
105
105
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
106
106
  const COMPACT_LIMIT = 5
@@ -289,8 +289,9 @@ export default {
289
289
  return form.title || form.id || t('nextcloud-vue', 'Untitled form')
290
290
  },
291
291
 
292
+ // A provider URL is used only when it is a safe scheme; else the app link.
292
293
  formUrl(form) {
293
- if (form.url) {
294
+ if (form.url && safeHref(form.url) !== '#') {
294
295
  return form.url
295
296
  }
296
297
  if (form.hash) {
@@ -67,8 +67,7 @@
67
67
  <NcButton
68
68
  variant="primary"
69
69
  :href="formsAppUrl"
70
- target="_blank"
71
- rel="noopener noreferrer">
70
+ target="_blank">
72
71
  <template #icon>
73
72
  <ClipboardText :size="20" />
74
73
  </template>
@@ -118,8 +117,7 @@
118
117
  <NcActionLink
119
118
  :closeAfterClick="true"
120
119
  :href="formUrl(form)"
121
- target="_blank"
122
- rel="noopener noreferrer">
120
+ target="_blank">
123
121
  <template #icon>
124
122
  <OpenInNew :size="20" />
125
123
  </template>
@@ -168,7 +166,7 @@ import Plus from 'vue-material-design-icons/Plus.vue'
168
166
  import CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'
169
167
  import CnFormCreate from './CnFormCreate.vue'
170
168
  import CnFormPicker from './CnFormPicker.vue'
171
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
169
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
172
170
 
173
171
  /**
174
172
  * CnFormsTab — bespoke linked-forms list for the Tier-2 `forms` leaf.
@@ -332,8 +330,9 @@ export default {
332
330
  return form.description || ''
333
331
  },
334
332
 
333
+ // A provider URL is used only when it is a safe scheme; else the app link.
335
334
  formUrl(form) {
336
- if (form.url) {
335
+ if (form.url && safeHref(form.url) !== '#') {
337
336
  return form.url
338
337
  }
339
338
  if (form.hash) {
@@ -125,7 +125,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
125
125
  import { NcLoadingIcon } from '@nextcloud/vue'
126
126
  import MapMarker from 'vue-material-design-icons/MapMarker.vue'
127
127
  import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
128
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
128
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
129
129
  import { stripMarker } from '../../utils/marker.js'
130
130
 
131
131
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
@@ -291,8 +291,9 @@ export default {
291
291
  return `${lat.toFixed(5)}, ${lng.toFixed(5)}`
292
292
  },
293
293
 
294
+ // A provider URL is used only when it is a safe scheme; else the app link.
294
295
  pointUrl(point) {
295
- if (typeof point.url === 'string' && point.url !== '') {
296
+ if (typeof point.url === 'string' && point.url !== '' && safeHref(point.url) !== '#') {
296
297
  return point.url
297
298
  }
298
299
  const lat = this.pointLat(point)
@@ -86,8 +86,7 @@
86
86
  <NcButton
87
87
  variant="primary"
88
88
  :href="mapsAppUrl"
89
- target="_blank"
90
- rel="noopener noreferrer">
89
+ target="_blank">
91
90
  <template #icon>
92
91
  <MapMarker :size="20" />
93
92
  </template>
@@ -146,8 +145,7 @@
146
145
  <NcActionLink
147
146
  :closeAfterClick="true"
148
147
  :href="pointUrl(point)"
149
- target="_blank"
150
- rel="noopener noreferrer">
148
+ target="_blank">
151
149
  <template #icon>
152
150
  <OpenInNew :size="20" />
153
151
  </template>
@@ -188,7 +186,7 @@ import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
188
186
  import Plus from 'vue-material-design-icons/Plus.vue'
189
187
  import CnMapPoiCreate from '../../../components/CnMapPoiCreate/CnMapPoiCreate.vue'
190
188
  import CnMapPoiPicker from '../../../components/CnMapPoiPicker/CnMapPoiPicker.vue'
191
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
189
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
192
190
  import { stripMarker } from '../../utils/marker.js'
193
191
 
194
192
  const COMMENT_MAX_CHARS = 160
@@ -389,8 +387,9 @@ export default {
389
387
  return `${lat.toFixed(5)}, ${lng.toFixed(5)}`
390
388
  },
391
389
 
390
+ // A provider URL is used only when it is a safe scheme; else the app link.
392
391
  pointUrl(point) {
393
- if (typeof point.url === 'string' && point.url !== '') {
392
+ if (typeof point.url === 'string' && point.url !== '' && safeHref(point.url) !== '#') {
394
393
  return point.url
395
394
  }
396
395
  if (this.pointHasCoords(point)) {
@@ -45,6 +45,15 @@ describe('CnMapsTab', () => {
45
45
  delete global.fetch
46
46
  })
47
47
 
48
+ it('links to the Maps app instead of a provider URL with an unsafe scheme', async () => {
49
+ global.fetch = jest.fn().mockResolvedValueOnce({ ok: true, status: 200, json: () => Promise.resolve({ results: [] }) })
50
+ const wrapper = mount(CnMapsTab, { propsData: { ...DEFAULT_PROPS } })
51
+ await flushPromises()
52
+ expect(wrapper.vm.pointUrl({ url: 'javascript:alert(1)' })).toBe('/index.php/apps/maps')
53
+ expect(wrapper.vm.pointUrl({ url: 'https://maps.example/p/1' })).toBe('https://maps.example/p/1')
54
+ wrapper.unmount()
55
+ })
56
+
48
57
  it('renders the empty state with an "Open Locations" CTA when no points', async () => {
49
58
  global.fetch = jest.fn().mockResolvedValueOnce({ ok: true, status: 200, json: () => Promise.resolve({ results: [] }) })
50
59
  const wrapper = mount(CnMapsTab, { propsData: { ...DEFAULT_PROPS } })
@@ -156,7 +156,7 @@ import Briefcase from 'vue-material-design-icons/Briefcase.vue'
156
156
  import CheckCircleOutline from 'vue-material-design-icons/CheckCircleOutline.vue'
157
157
  import LockOutline from 'vue-material-design-icons/LockOutline.vue'
158
158
  import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
159
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
159
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
160
160
 
161
161
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
162
162
 
@@ -338,7 +338,8 @@ export default {
338
338
  },
339
339
 
340
340
  wpUrl(wp) {
341
- return wp.url ?? wp._links?.self?.href ?? this.openprojectAppUrl
341
+ const url = wp.url ?? wp._links?.self?.href
342
+ return url && safeHref(url) !== '#' ? url : this.openprojectAppUrl
342
343
  },
343
344
 
344
345
  wpStatus(wp) {
@@ -85,8 +85,7 @@
85
85
  <NcButton
86
86
  variant="primary"
87
87
  :href="openconnectorUrl"
88
- target="_blank"
89
- rel="noopener noreferrer">
88
+ target="_blank">
90
89
  <template #icon>
91
90
  <CogOutline :size="20" />
92
91
  </template>
@@ -104,8 +103,7 @@
104
103
  <NcButton
105
104
  variant="secondary"
106
105
  :href="openprojectAppUrl"
107
- target="_blank"
108
- rel="noopener noreferrer">
106
+ target="_blank">
109
107
  <template #icon>
110
108
  <OpenInNew :size="20" />
111
109
  </template>
@@ -233,7 +231,7 @@ import StarFourPointsOutline from 'vue-material-design-icons/StarFourPointsOutli
233
231
  import CnOpenProjectCreate from '../../../components/CnOpenProjectCreate/CnOpenProjectCreate.vue'
234
232
  import CnOpenProjectPicker from '../../../components/CnOpenProjectPicker/CnOpenProjectPicker.vue'
235
233
  import CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'
236
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
234
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
237
235
 
238
236
  /**
239
237
  * CnOpenprojectTab — bespoke sidebar tab for the `openproject` integration.
@@ -549,7 +547,8 @@ export default {
549
547
  },
550
548
 
551
549
  wpUrl(wp) {
552
- return wp.url ?? wp._links?.self?.href ?? this.openprojectAppUrl
550
+ const url = wp.url ?? wp._links?.self?.href
551
+ return url && safeHref(url) !== '#' ? url : this.openprojectAppUrl
553
552
  },
554
553
 
555
554
  wpStatus(wp) {
@@ -128,7 +128,7 @@ import { NcLoadingIcon } from '@nextcloud/vue'
128
128
  import ImageIcon from 'vue-material-design-icons/Image.vue'
129
129
  import ImageMultiple from 'vue-material-design-icons/ImageMultiple.vue'
130
130
  import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
131
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
131
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
132
132
 
133
133
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
134
134
  const COMPACT_LIMIT = 4
@@ -265,7 +265,7 @@ export default {
265
265
  },
266
266
 
267
267
  albumUrl(album) {
268
- if (album && album.url) {
268
+ if (album && album.url && safeHref(album.url) !== '#') {
269
269
  return album.url
270
270
  }
271
271
  const name = this.albumName(album)
@@ -72,8 +72,7 @@
72
72
  <NcButton
73
73
  variant="primary"
74
74
  :href="photosAppUrl"
75
- target="_blank"
76
- rel="noopener noreferrer">
75
+ target="_blank">
77
76
  <template #icon>
78
77
  <ImageIcon :size="20" />
79
78
  </template>
@@ -154,7 +153,7 @@ import LinkVariant from 'vue-material-design-icons/LinkVariant.vue'
154
153
  import Plus from 'vue-material-design-icons/Plus.vue'
155
154
  import CnPhotoAlbumCreate from '../../../components/CnPhotoAlbumCreate/CnPhotoAlbumCreate.vue'
156
155
  import CnPhotoAlbumPicker from '../../../components/CnPhotoAlbumPicker/CnPhotoAlbumPicker.vue'
157
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
156
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
158
157
 
159
158
  /**
160
159
  * CnPhotosTab — bespoke album grid for the `photos` integration.
@@ -262,7 +261,7 @@ export default {
262
261
  },
263
262
 
264
263
  albumUrl(album) {
265
- if (album && album.url) {
264
+ if (album && album.url && safeHref(album.url) !== '#') {
266
265
  return album.url
267
266
  }
268
267
  const id = this.albumKey(album)
@@ -129,7 +129,7 @@ import FormatListChecks from 'vue-material-design-icons/FormatListChecks.vue'
129
129
  import Poll from 'vue-material-design-icons/Poll.vue'
130
130
  import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
131
131
  import CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'
132
- import { buildHeaders, prefixUrl } from '../../../utils/index.js'
132
+ import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
133
133
  import { stripMarker } from '../../utils/marker.js'
134
134
 
135
135
  const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
@@ -266,7 +266,7 @@ export default {
266
266
  },
267
267
 
268
268
  pollUrl(poll) {
269
- if (poll.url) {
269
+ if (poll.url && safeHref(poll.url) !== '#') {
270
270
  return poll.url
271
271
  }
272
272
  const id = poll.id ?? ''