@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.
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +12 -2
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +1 -2
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +0 -3
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -2
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +12 -5
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +2 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +9 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +6 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +116 -15
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +27 -14
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js +13 -6
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js.map +1 -1
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js +2 -0
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +1 -2
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +3 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +17 -17
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js +4 -21
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +0 -13
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js +8 -8
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js +3 -2
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +67 -13
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +15 -8
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/manifestActionDispatch.js +31 -11
- package/dist/esm/components/CnIndexPage/manifestActionDispatch.js.map +1 -1
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue2.js.map +1 -1
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js +1 -2
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +0 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js +2 -17
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js +35 -11
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js +3 -2
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +23 -5
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectList/CnObjectList.vue2.js +19 -3
- package/dist/esm/components/CnObjectList/CnObjectList.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectList/CnObjectList.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +45 -15
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +24 -6
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +0 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +4 -2
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +7 -2
- package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
- package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js +5 -3
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +3 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +1 -0
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js +6 -0
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js +68 -53
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js +17 -0
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js +21 -19
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js +3 -6
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js +4 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js +6 -3
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js.map +1 -1
- package/dist/esm/l10n/en.json.js +1 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +1 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +71 -7
- package/dist/esm/utils/actionLink.js +5 -0
- package/dist/esm/utils/actionLink.js.map +1 -1
- package/dist/esm/utils/actionsDispatcher.js +16 -1
- package/dist/esm/utils/actionsDispatcher.js.map +1 -1
- package/dist/esm/utils/linkNavigation.js +6 -3
- package/dist/esm/utils/linkNavigation.js.map +1 -1
- package/dist/esm/utils/rowAuxClick.js +30 -5
- package/dist/esm/utils/rowAuxClick.js.map +1 -1
- package/dist/esm/utils/safeHref.js +18 -3
- package/dist/esm/utils/safeHref.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +809 -383
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +71 -7
- package/l10n/en.json +1 -0
- package/l10n/nl.json +1 -0
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
- package/src/components/CnActionsMenu/CnActionsMenu.vue +0 -3
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
- package/src/components/CnBoardView/CnBoardView.vue +13 -5
- package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
- package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
- package/src/components/CnDataTable/CnDataTable.md +1 -1
- package/src/components/CnDataTable/CnDataTable.vue +131 -17
- package/src/components/CnDateAxisView/CnDateAxisView.vue +15 -6
- package/src/components/CnDetailPage/CnDetailPage.md +1 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +29 -1
- package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -2
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +5 -3
- package/src/components/CnFilesWidget/CnFilesWidget.vue +4 -21
- package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +8 -21
- package/src/components/CnFlowsPage/CnFlowsPage.vue +2 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +75 -14
- package/src/components/CnIndexPage/manifestActionDispatch.js +31 -11
- package/src/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue +1 -2
- package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +0 -1
- package/src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue +2 -17
- package/src/components/CnLogsPage/CnLogsPage.vue +37 -12
- package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
- package/src/components/CnObjectList/CnObjectList.vue +19 -3
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +47 -15
- package/src/components/CnObjectRow/CnObjectRow.vue +25 -7
- package/src/components/CnObjectSidebar/CnFilesTab.vue +0 -1
- package/src/components/CnPageRenderer/CnPageRenderer.vue +2 -0
- package/src/components/CnRowActions/CnRowActions.vue +7 -2
- package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
- package/src/components/CnTabs/CnTabs.vue +7 -3
- package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
- package/src/css/table.css +26 -0
- package/src/integrations/builtin/analytics/CnAnalyticsCard.vue +3 -2
- package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +5 -6
- package/src/integrations/builtin/bookmarks/CnBookmarksCard.vue +12 -1
- package/src/integrations/builtin/bookmarks/CnBookmarksTab.vue +23 -6
- package/src/integrations/builtin/bookmarks/__tests__/CnBookmarksTab.spec.js +7 -2
- package/src/integrations/builtin/collectives/CnCollectivesCard.vue +3 -2
- package/src/integrations/builtin/collectives/CnCollectivesTab.vue +5 -6
- package/src/integrations/builtin/cospend/CnCospendCard.vue +2 -2
- package/src/integrations/builtin/cospend/CnCospendTab.vue +3 -4
- package/src/integrations/builtin/deck/CnDeckCard.vue +2 -2
- package/src/integrations/builtin/deck/CnDeckTab.vue +3 -4
- package/src/integrations/builtin/email/CnEmailCard.vue +6 -2
- package/src/integrations/builtin/email/CnEmailTab.vue +7 -4
- package/src/integrations/builtin/flow/CnFlowCard.vue +2 -2
- package/src/integrations/builtin/flow/CnFlowTab.vue +3 -4
- package/src/integrations/builtin/forms/CnFormsCard.vue +3 -2
- package/src/integrations/builtin/forms/CnFormsTab.vue +5 -6
- package/src/integrations/builtin/maps/CnMapsCard.vue +3 -2
- package/src/integrations/builtin/maps/CnMapsTab.vue +5 -6
- package/src/integrations/builtin/maps/__tests__/CnMapsTab.spec.js +9 -0
- package/src/integrations/builtin/openproject/CnOpenprojectCard.vue +3 -2
- package/src/integrations/builtin/openproject/CnOpenprojectTab.vue +5 -6
- package/src/integrations/builtin/photos/CnPhotosCard.vue +2 -2
- package/src/integrations/builtin/photos/CnPhotosTab.vue +3 -4
- package/src/integrations/builtin/polls/CnPollsCard.vue +2 -2
- package/src/integrations/builtin/polls/CnPollsTab.vue +3 -4
- package/src/integrations/builtin/shares/CnSharesTab.vue +3 -6
- package/src/integrations/builtin/talk/CnTalkCard.vue +3 -2
- package/src/integrations/builtin/talk/CnTalkTab.vue +5 -6
- package/src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue +2 -2
- package/src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue +3 -4
- package/src/integrations/builtin/xwiki/CnXwikiCard.vue +4 -2
- package/src/integrations/builtin/xwiki/CnXwikiTab.vue +7 -7
- package/src/integrations/builtin/xwiki/__tests__/CnXwikiTab.spec.js +9 -0
- package/src/utils/actionLink.js +5 -0
- package/src/utils/actionsDispatcher.js +15 -0
- package/src/utils/linkNavigation.js +6 -3
- package/src/utils/rowAuxClick.js +29 -4
- 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('
|
|
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.
|
|
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
|
-
|
|
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
|
-
.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 ?? ''
|