@conduction/nextcloud-vue 2.58.0 → 2.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js +100 -3
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.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 +909 -386
- 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/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue +100 -3
- 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
|
@@ -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 ?? ''
|
|
@@ -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 {
|