@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
|
@@ -63,8 +63,7 @@
|
|
|
63
63
|
<NcButton
|
|
64
64
|
variant="primary"
|
|
65
65
|
:href="pollsAppUrl"
|
|
66
|
-
target="_blank"
|
|
67
|
-
rel="noopener noreferrer">
|
|
66
|
+
target="_blank">
|
|
68
67
|
<template #icon>
|
|
69
68
|
<Poll :size="20" />
|
|
70
69
|
</template>
|
|
@@ -179,7 +178,7 @@ import Poll from 'vue-material-design-icons/Poll.vue'
|
|
|
179
178
|
import CnPollCreate from '../../../components/CnPollCreate/CnPollCreate.vue'
|
|
180
179
|
import CnPollPicker from '../../../components/CnPollPicker/CnPollPicker.vue'
|
|
181
180
|
import CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'
|
|
182
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
181
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
183
182
|
import { stripMarker } from '../../utils/marker.js'
|
|
184
183
|
|
|
185
184
|
/**
|
|
@@ -365,7 +364,7 @@ export default {
|
|
|
365
364
|
},
|
|
366
365
|
|
|
367
366
|
pollUrl(poll) {
|
|
368
|
-
if (poll.url) {
|
|
367
|
+
if (poll.url && safeHref(poll.url) !== '#') {
|
|
369
368
|
return poll.url
|
|
370
369
|
}
|
|
371
370
|
const id = this.pollKey(poll)
|
|
@@ -50,8 +50,7 @@
|
|
|
50
50
|
<NcButton
|
|
51
51
|
variant="primary"
|
|
52
52
|
:href="filesAppUrl"
|
|
53
|
-
target="_blank"
|
|
54
|
-
rel="noopener noreferrer">
|
|
53
|
+
target="_blank">
|
|
55
54
|
<template #icon>
|
|
56
55
|
<FolderOutline :size="20" />
|
|
57
56
|
</template>
|
|
@@ -125,8 +124,7 @@
|
|
|
125
124
|
<NcActionLink
|
|
126
125
|
:closeAfterClick="true"
|
|
127
126
|
:href="filesAppUrl"
|
|
128
|
-
target="_blank"
|
|
129
|
-
rel="noopener noreferrer">
|
|
127
|
+
target="_blank">
|
|
130
128
|
<template #icon>
|
|
131
129
|
<FolderOutline :size="20" />
|
|
132
130
|
</template>
|
|
@@ -140,8 +138,7 @@
|
|
|
140
138
|
<NcButton
|
|
141
139
|
variant="tertiary"
|
|
142
140
|
:href="filesAppUrl"
|
|
143
|
-
target="_blank"
|
|
144
|
-
rel="noopener noreferrer">
|
|
141
|
+
target="_blank">
|
|
145
142
|
<template #icon>
|
|
146
143
|
<FolderOutline :size="18" />
|
|
147
144
|
</template>
|
|
@@ -111,7 +111,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
|
|
|
111
111
|
import { NcAvatar, NcCounterBubble, NcLoadingIcon } from '@nextcloud/vue'
|
|
112
112
|
import ChatOutline from 'vue-material-design-icons/ChatOutline.vue'
|
|
113
113
|
import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
|
|
114
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
114
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
115
115
|
import { stripMarker } from '../../utils/marker.js'
|
|
116
116
|
|
|
117
117
|
const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
|
|
@@ -252,8 +252,9 @@ export default {
|
|
|
252
252
|
return stripMarker(raw) || String(this.roomKey(room))
|
|
253
253
|
},
|
|
254
254
|
|
|
255
|
+
// A provider URL is used only when it is a safe scheme; else the app link.
|
|
255
256
|
roomUrl(room) {
|
|
256
|
-
if (room.url) {
|
|
257
|
+
if (room.url && safeHref(room.url) !== '#') {
|
|
257
258
|
return room.url
|
|
258
259
|
}
|
|
259
260
|
const token = room.token ?? room.id ?? ''
|
|
@@ -60,8 +60,7 @@
|
|
|
60
60
|
<NcButton
|
|
61
61
|
variant="primary"
|
|
62
62
|
:href="talkAppUrl"
|
|
63
|
-
target="_blank"
|
|
64
|
-
rel="noopener noreferrer">
|
|
63
|
+
target="_blank">
|
|
65
64
|
<template #icon>
|
|
66
65
|
<ChatOutline :size="20" />
|
|
67
66
|
</template>
|
|
@@ -107,8 +106,7 @@
|
|
|
107
106
|
<NcActionLink
|
|
108
107
|
:closeAfterClick="true"
|
|
109
108
|
:href="roomUrl(room)"
|
|
110
|
-
target="_blank"
|
|
111
|
-
rel="noopener noreferrer">
|
|
109
|
+
target="_blank">
|
|
112
110
|
<template #icon>
|
|
113
111
|
<OpenInNew :size="20" />
|
|
114
112
|
</template>
|
|
@@ -149,7 +147,7 @@ import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
|
|
|
149
147
|
import Plus from 'vue-material-design-icons/Plus.vue'
|
|
150
148
|
import CnTalkRoomCreate from '../../../components/CnTalkRoomCreate/CnTalkRoomCreate.vue'
|
|
151
149
|
import CnTalkRoomPicker from '../../../components/CnTalkRoomPicker/CnTalkRoomPicker.vue'
|
|
152
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
150
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
153
151
|
import { stripMarker } from '../../utils/marker.js'
|
|
154
152
|
|
|
155
153
|
/**
|
|
@@ -247,8 +245,9 @@ export default {
|
|
|
247
245
|
return stripMarker(raw) || String(this.roomKey(room))
|
|
248
246
|
},
|
|
249
247
|
|
|
248
|
+
// A provider URL is used only when it is a safe scheme; else the app link.
|
|
250
249
|
roomUrl(room) {
|
|
251
|
-
if (room.url) {
|
|
250
|
+
if (room.url && safeHref(room.url) !== '#') {
|
|
252
251
|
return room.url
|
|
253
252
|
}
|
|
254
253
|
const token = room.roomToken ?? room.token ?? room.id ?? ''
|
|
@@ -119,7 +119,7 @@ import { NcLoadingIcon } from '@nextcloud/vue'
|
|
|
119
119
|
import Clock from 'vue-material-design-icons/Clock.vue'
|
|
120
120
|
import Timer from 'vue-material-design-icons/Timer.vue'
|
|
121
121
|
import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
|
|
122
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
122
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
123
123
|
|
|
124
124
|
const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
|
|
125
125
|
|
|
@@ -307,7 +307,7 @@ export default {
|
|
|
307
307
|
},
|
|
308
308
|
|
|
309
309
|
rowUrl(row) {
|
|
310
|
-
if (row.url) {
|
|
310
|
+
if (row.url && safeHref(row.url) !== '#') {
|
|
311
311
|
return row.url
|
|
312
312
|
}
|
|
313
313
|
const kind = this.rowKind(row)
|
|
@@ -81,8 +81,7 @@
|
|
|
81
81
|
<NcButton
|
|
82
82
|
variant="primary"
|
|
83
83
|
:href="timeTrackerAppUrl"
|
|
84
|
-
target="_blank"
|
|
85
|
-
rel="noopener noreferrer">
|
|
84
|
+
target="_blank">
|
|
86
85
|
<template #icon>
|
|
87
86
|
<Clock :size="20" />
|
|
88
87
|
</template>
|
|
@@ -185,7 +184,7 @@ import Plus from 'vue-material-design-icons/Plus.vue'
|
|
|
185
184
|
import Timer from 'vue-material-design-icons/Timer.vue'
|
|
186
185
|
import CnTimeTrackerCreate from '../../../components/CnTimeTrackerCreate/CnTimeTrackerCreate.vue'
|
|
187
186
|
import CnTimeTrackerPicker from '../../../components/CnTimeTrackerPicker/CnTimeTrackerPicker.vue'
|
|
188
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
187
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
189
188
|
|
|
190
189
|
/**
|
|
191
190
|
* CnTimeTrackerTab — bespoke sidebar tab for the `time-tracker`
|
|
@@ -361,7 +360,7 @@ export default {
|
|
|
361
360
|
},
|
|
362
361
|
|
|
363
362
|
rowUrl(row) {
|
|
364
|
-
if (row.url) {
|
|
363
|
+
if (row.url && safeHref(row.url) !== '#') {
|
|
365
364
|
return row.url
|
|
366
365
|
}
|
|
367
366
|
const kind = this.rowKind(row)
|
|
@@ -138,7 +138,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
|
|
|
138
138
|
import { NcLoadingIcon } from '@nextcloud/vue'
|
|
139
139
|
import FileDocumentMultiple from 'vue-material-design-icons/FileDocumentMultiple.vue'
|
|
140
140
|
import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
|
|
141
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
141
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
142
142
|
|
|
143
143
|
const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
|
|
144
144
|
const PREVIEW_MAX_CHARS = 500
|
|
@@ -303,8 +303,10 @@ export default {
|
|
|
303
303
|
return String(page.title ?? page.page ?? page.reference ?? this.pageKey(page))
|
|
304
304
|
},
|
|
305
305
|
|
|
306
|
+
// An unsafe scheme is treated like a missing URL.
|
|
306
307
|
pageUrl(page) {
|
|
307
|
-
|
|
308
|
+
const url = page.url ?? ''
|
|
309
|
+
return url && safeHref(url) !== '#' ? url : ''
|
|
308
310
|
},
|
|
309
311
|
|
|
310
312
|
breadcrumbLabel(page) {
|
|
@@ -64,7 +64,6 @@
|
|
|
64
64
|
class="cn-xwiki-tab__banner-cta"
|
|
65
65
|
:href="banner.ctaHref || undefined"
|
|
66
66
|
:target="banner.ctaHref ? '_blank' : undefined"
|
|
67
|
-
:rel="banner.ctaHref ? 'noopener noreferrer' : undefined"
|
|
68
67
|
@click="banner.ctaHandler">
|
|
69
68
|
{{ banner.ctaLabel }}
|
|
70
69
|
</NcButton>
|
|
@@ -132,8 +131,7 @@
|
|
|
132
131
|
class="cn-xwiki-tab__open"
|
|
133
132
|
:closeAfterClick="true"
|
|
134
133
|
:href="pageHref(page)"
|
|
135
|
-
target="_blank"
|
|
136
|
-
rel="noopener noreferrer">
|
|
134
|
+
target="_blank">
|
|
137
135
|
<template #icon>
|
|
138
136
|
<OpenInNew :size="20" />
|
|
139
137
|
</template>
|
|
@@ -181,7 +179,7 @@ import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
|
|
|
181
179
|
import Plus from 'vue-material-design-icons/Plus.vue'
|
|
182
180
|
import CnXwikiPageCreate from '../../../components/CnXwikiPageCreate/CnXwikiPageCreate.vue'
|
|
183
181
|
import CnXwikiPagePicker from '../../../components/CnXwikiPagePicker/CnXwikiPagePicker.vue'
|
|
184
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
182
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
185
183
|
|
|
186
184
|
const EXCERPT_MAX_CHARS = 140
|
|
187
185
|
|
|
@@ -386,15 +384,17 @@ export default {
|
|
|
386
384
|
|
|
387
385
|
/**
|
|
388
386
|
* External XWiki deep-link for the page. Empty string when the
|
|
389
|
-
* provider didn't surface a `url`,
|
|
390
|
-
* (non-anchored) NcListItem rather than a
|
|
387
|
+
* provider didn't surface a `url`, or surfaced an unsafe scheme, so
|
|
388
|
+
* the row renders as a plain (non-anchored) NcListItem rather than a
|
|
389
|
+
* dead link.
|
|
391
390
|
*
|
|
392
391
|
* @param {object} page Provider row.
|
|
393
392
|
*
|
|
394
393
|
* @return {string} Absolute XWiki URL or ''.
|
|
395
394
|
*/
|
|
396
395
|
pageHref(page) {
|
|
397
|
-
|
|
396
|
+
const url = String(page.url ?? '')
|
|
397
|
+
return url && safeHref(url) !== '#' ? url : ''
|
|
398
398
|
},
|
|
399
399
|
|
|
400
400
|
breadcrumbLabel(page) {
|
|
@@ -55,6 +55,15 @@ describe('CnXwikiTab', () => {
|
|
|
55
55
|
delete global.fetch
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
+
it('gives a page with an unsafe URL no link', async () => {
|
|
59
|
+
global.fetch = jest.fn().mockResolvedValue({ ok: true, status: 200, json: () => Promise.resolve({ results: [] }) })
|
|
60
|
+
const wrapper = mount(CnXwikiTab, { propsData: { ...DEFAULT_PROPS } })
|
|
61
|
+
await flushPromises()
|
|
62
|
+
expect(wrapper.vm.pageHref({ url: 'javascript:alert(1)' })).toBe('')
|
|
63
|
+
expect(wrapper.vm.pageHref({ url: 'https://wiki.example/p' })).toBe('https://wiki.example/p')
|
|
64
|
+
wrapper.unmount()
|
|
65
|
+
})
|
|
66
|
+
|
|
58
67
|
it('renders the empty state when no pages are linked', async () => {
|
|
59
68
|
global.fetch = jest.fn().mockResolvedValueOnce({ ok: true, status: 200, json: () => Promise.resolve({ results: [] }) })
|
|
60
69
|
const wrapper = mount(CnXwikiTab, { propsData: { ...DEFAULT_PROPS } })
|
package/src/utils/actionLink.js
CHANGED
|
@@ -62,6 +62,11 @@ export function resolveItemActionLink(action, item, router) {
|
|
|
62
62
|
* on an in-app link that opens in place goes through the router; a URL, a
|
|
63
63
|
* new-tab target or a modified click is left to the browser.
|
|
64
64
|
*
|
|
65
|
+
* Hand-rolled rather than `NcActionRouter` / `NcButton :to`: the menus and
|
|
66
|
+
* bars using it must still emit their own events on click, and without an
|
|
67
|
+
* installed router `NcButton :to` falls back to a button that goes nowhere,
|
|
68
|
+
* while an `<a href>` still navigates.
|
|
69
|
+
*
|
|
65
70
|
* @param {MouseEvent} event The click event.
|
|
66
71
|
* @param {{to: (string|object|null), target: (string|undefined)}} link The resolved link.
|
|
67
72
|
* @param {object} [router] The app's router.
|
|
@@ -341,6 +341,21 @@ export function interpolateActionTarget(target, ctx) {
|
|
|
341
341
|
return interpolateActionString(target || '', ctx || {})
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
+
// The tokens interpolateActionString resolves; a bare `@` or `{` is not one.
|
|
345
|
+
const ACTION_TARGET_TOKEN = /@(?:objectId|object\.|page\.|workspace\.|config\.|range\.)|\{objectId\}/
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Whether an action target carries a token that interpolation would resolve,
|
|
349
|
+
* so it can only be known once the page context is. A `mailto:` address or a
|
|
350
|
+
* URL with an `@handle` in it has none.
|
|
351
|
+
*
|
|
352
|
+
* @param {string} target The action's raw `target`.
|
|
353
|
+
* @return {boolean}
|
|
354
|
+
*/
|
|
355
|
+
export function hasActionTargetTokens(target) {
|
|
356
|
+
return typeof target === 'string' && ACTION_TARGET_TOKEN.test(target)
|
|
357
|
+
}
|
|
358
|
+
|
|
344
359
|
/**
|
|
345
360
|
* The link an action renders as when its only job is to navigate to a target
|
|
346
361
|
* known at render time: `navigate` (token-interpolated like the dispatcher)
|
|
@@ -69,8 +69,11 @@ export function resolveHref(target, router) {
|
|
|
69
69
|
* click is routed through the router; a modified click, or one a handler
|
|
70
70
|
* already prevented, is left to the browser.
|
|
71
71
|
*
|
|
72
|
+
* Unlike `resolveHref`, a string target is a router path, so pair the two
|
|
73
|
+
* with a location object (or `{ path }`) for the href and click to agree.
|
|
74
|
+
*
|
|
72
75
|
* @param {MouseEvent} event The click event.
|
|
73
|
-
* @param {string|object} target The router location the link points at.
|
|
76
|
+
* @param {string|object} target The router location the link points at; a string is a router path.
|
|
74
77
|
* @param {object} [router] The app's router.
|
|
75
78
|
* @return {boolean} True when the app navigated.
|
|
76
79
|
*/
|
|
@@ -96,8 +99,8 @@ export function followLinkClick(event, target, router) {
|
|
|
96
99
|
*
|
|
97
100
|
* A click that came from a control inside the surface (a button, a link, a
|
|
98
101
|
* checkbox) is left to that control. Opening a new tab marks the event with
|
|
99
|
-
* `
|
|
100
|
-
*
|
|
102
|
+
* `markNewTabHandled`, and an event already marked is skipped, so two
|
|
103
|
+
* listeners on the same click never open two tabs.
|
|
101
104
|
*
|
|
102
105
|
* @param {MouseEvent|KeyboardEvent|null|undefined} event The click event.
|
|
103
106
|
* @param {string|object} target A URL, or a vue-router location.
|
package/src/utils/rowAuxClick.js
CHANGED
|
@@ -9,13 +9,17 @@
|
|
|
9
9
|
* also fire `auxclick` for the right button, so a row that listens to it must
|
|
10
10
|
* filter both out itself.
|
|
11
11
|
*
|
|
12
|
-
* Whoever opens a row in a new tab
|
|
13
|
-
* second listener for the same click (a host behind a library default) can
|
|
12
|
+
* Whoever opens a row in a new tab marks the event with `markNewTabHandled`, so
|
|
13
|
+
* a second listener for the same click (a host behind a library default) can
|
|
14
14
|
* check `isNewTabHandled` and not open a second tab.
|
|
15
15
|
*
|
|
16
16
|
* @module utils/rowAuxClick
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
// Events that opened a new tab. Not `defaultPrevented`, which any unrelated
|
|
20
|
+
// handler can set on a modified or middle click.
|
|
21
|
+
const newTabHandled = new WeakSet()
|
|
22
|
+
|
|
19
23
|
const NESTED_CONTROL = 'a, button, input, select, textarea, label, summary, [role="button"], [role="checkbox"], [role="link"], [contenteditable="true"]'
|
|
20
24
|
|
|
21
25
|
/**
|
|
@@ -49,6 +53,21 @@ export function isRowMiddleClick(event) {
|
|
|
49
53
|
return Boolean(event && event.button === 1 && !isFromNestedControl(event))
|
|
50
54
|
}
|
|
51
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Cancel a middle-button press on a clickable row or card body, so the
|
|
58
|
+
* browser does not start autoscroll (Chrome/Edge on Windows) instead of the
|
|
59
|
+
* middle click opening the row. A press on a nested control, a link
|
|
60
|
+
* included, keeps its default.
|
|
61
|
+
*
|
|
62
|
+
* @param {MouseEvent} event The mousedown event.
|
|
63
|
+
* @return {void}
|
|
64
|
+
*/
|
|
65
|
+
export function preventMiddleClickAutoscroll(event) {
|
|
66
|
+
if (event && event.button === 1 && !isFromNestedControl(event) && typeof event.preventDefault === 'function') {
|
|
67
|
+
event.preventDefault()
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
52
71
|
/**
|
|
53
72
|
* Whether `openRowTarget` would open this click in a new tab.
|
|
54
73
|
*
|
|
@@ -66,18 +85,24 @@ export function isNewTabClick(event) {
|
|
|
66
85
|
* @return {boolean}
|
|
67
86
|
*/
|
|
68
87
|
export function isNewTabHandled(event) {
|
|
69
|
-
return Boolean(isNewTabClick(event) && event
|
|
88
|
+
return Boolean(isNewTabClick(event) && newTabHandled.has(event))
|
|
70
89
|
}
|
|
71
90
|
|
|
72
91
|
/**
|
|
73
92
|
* Mark a new-tab click as handled after opening it, see `isNewTabHandled`.
|
|
93
|
+
* Also prevents the event's default, so the browser does not act on the
|
|
94
|
+
* click as well.
|
|
74
95
|
*
|
|
75
96
|
* @param {MouseEvent|KeyboardEvent|null|undefined} event The click event.
|
|
76
97
|
* @param {boolean} opened Whether the target was opened.
|
|
77
98
|
* @return {void}
|
|
78
99
|
*/
|
|
79
100
|
export function markNewTabHandled(event, opened) {
|
|
80
|
-
if (opened
|
|
101
|
+
if (!opened || !isNewTabClick(event)) {
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
newTabHandled.add(event)
|
|
105
|
+
if (typeof event.preventDefault === 'function') {
|
|
81
106
|
event.preventDefault()
|
|
82
107
|
}
|
|
83
108
|
}
|
package/src/utils/safeHref.js
CHANGED
|
@@ -18,6 +18,20 @@
|
|
|
18
18
|
* @module utils/safeHref
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Whether a URL is protocol-relative the way a browser reads it: leading
|
|
23
|
+
* control characters and spaces are dropped, tabs and newlines are ignored
|
|
24
|
+
* anywhere, and a backslash counts as `/`.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} url The URL.
|
|
27
|
+
* @return {boolean}
|
|
28
|
+
*/
|
|
29
|
+
function isProtocolRelative(url) {
|
|
30
|
+
// eslint-disable-next-line no-control-regex
|
|
31
|
+
const normalized = url.replace(/[\t\n\r]/g, '').replace(/^[\u0000-\u0020]+/, '')
|
|
32
|
+
return /^[/\\]{2}/.test(normalized)
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
/**
|
|
22
36
|
* Validate a URL and return it only if the scheme is safe for `:href`.
|
|
23
37
|
*
|
|
@@ -30,7 +44,8 @@
|
|
|
30
44
|
* - `javascript:...`
|
|
31
45
|
* - `data:...`
|
|
32
46
|
* - `vbscript:...`
|
|
33
|
-
* - Protocol-relative `//attacker.com/...` (ambiguous origin)
|
|
47
|
+
* - Protocol-relative `//attacker.com/...` (ambiguous origin), also as
|
|
48
|
+
* `/\attacker.com` or with leading whitespace
|
|
34
49
|
* - Any other unrecognised scheme
|
|
35
50
|
* - `null`, `undefined`, empty string
|
|
36
51
|
*
|
|
@@ -52,7 +67,7 @@ export function safeHref(url) {
|
|
|
52
67
|
}
|
|
53
68
|
// Reject protocol-relative URLs — `//attacker.com/x` looks like a relative
|
|
54
69
|
// path but resolves to an arbitrary origin, bypassing same-origin intent.
|
|
55
|
-
if (url
|
|
70
|
+
if (isProtocolRelative(url)) {
|
|
56
71
|
return '#'
|
|
57
72
|
}
|
|
58
73
|
// Allow root-relative paths (same-origin navigation)
|
|
@@ -97,7 +112,7 @@ export function safeImageSrc(url) {
|
|
|
97
112
|
return url
|
|
98
113
|
}
|
|
99
114
|
// Reject protocol-relative
|
|
100
|
-
if (url
|
|
115
|
+
if (isProtocolRelative(url)) {
|
|
101
116
|
return ''
|
|
102
117
|
}
|
|
103
118
|
try {
|