@conduction/nextcloud-vue 2.56.0-beta.4 → 2.56.0-beta.6
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
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
class="cn-tasks-widget__row"
|
|
32
32
|
:data-state="task.state"
|
|
33
33
|
@click="onRowClick(task, $event)"
|
|
34
|
+
@mousedown="preventMiddleClickAutoscroll"
|
|
34
35
|
@auxclick="onRowAuxClick(task, $event)">
|
|
35
36
|
<span class="cn-tasks-widget__body">
|
|
36
37
|
<span class="cn-tasks-widget__name">{{ titleOf(task) }}</span>
|
|
@@ -90,7 +91,7 @@ import { inject, ref } from 'vue'
|
|
|
90
91
|
import { taskDeepLink, taskDueLabel } from '../../composables/indexSources.js'
|
|
91
92
|
import { useEndpointSource } from '../../composables/useEndpointSource.js'
|
|
92
93
|
import { openRowTarget } from '../../utils/linkNavigation.js'
|
|
93
|
-
import { isRowMiddleClick } from '../../utils/rowAuxClick.js'
|
|
94
|
+
import { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'
|
|
94
95
|
|
|
95
96
|
/**
|
|
96
97
|
* The OpenRegister inbox read (openregister flow-task-entity). One endpoint
|
|
@@ -367,6 +368,8 @@ export default {
|
|
|
367
368
|
},
|
|
368
369
|
|
|
369
370
|
methods: {
|
|
371
|
+
preventMiddleClickAutoscroll,
|
|
372
|
+
|
|
370
373
|
/**
|
|
371
374
|
* Start (or restart) the refetch interval, unless polling is off.
|
|
372
375
|
*
|
package/src/css/table.css
CHANGED
|
@@ -334,6 +334,32 @@
|
|
|
334
334
|
background-color: var(--color-primary-light);
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
+
/* A row with a route is a real link (see CnDataTable's row link): the anchor
|
|
338
|
+
in its first cell is stretched over the whole row. The row's own controls
|
|
339
|
+
sit above it so they stay usable; keep the list in step with NESTED_CONTROL
|
|
340
|
+
in utils/rowAuxClick.js. */
|
|
341
|
+
.cn-table-row--linked {
|
|
342
|
+
position: relative;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.cn-table-row__link {
|
|
346
|
+
position: absolute;
|
|
347
|
+
inset: 0;
|
|
348
|
+
cursor: pointer;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.cn-table-row__link:focus-visible {
|
|
352
|
+
outline: 2px solid var(--color-main-text);
|
|
353
|
+
outline-offset: -2px;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.cn-table-row--linked .cn-table-col--checkbox,
|
|
357
|
+
.cn-table-row--linked .cn-table-col--actions,
|
|
358
|
+
.cn-table-row--linked td :is(a, button, input, select, textarea, label, summary, [role='button'], [role='checkbox'], [role='link'], [contenteditable='true']):not(.cn-table-row__link) {
|
|
359
|
+
position: relative;
|
|
360
|
+
z-index: 1;
|
|
361
|
+
}
|
|
362
|
+
|
|
337
363
|
/* Column Types */
|
|
338
364
|
.cn-table-col--checkbox {
|
|
339
365
|
width: 50px;
|
|
@@ -122,7 +122,7 @@ import ChartPie from 'vue-material-design-icons/ChartPie.vue'
|
|
|
122
122
|
import TableIcon from 'vue-material-design-icons/Table.vue'
|
|
123
123
|
import ViewDashboard from 'vue-material-design-icons/ViewDashboard.vue'
|
|
124
124
|
import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
|
|
125
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
125
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
126
126
|
|
|
127
127
|
const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
|
|
128
128
|
const COMPACT_LIMIT = 5
|
|
@@ -287,8 +287,9 @@ export default {
|
|
|
287
287
|
return `cn-analytics-card__badge--type-${type ?? 'unknown'}`
|
|
288
288
|
},
|
|
289
289
|
|
|
290
|
+
// A provider URL is used only when it is a safe scheme; else the app link.
|
|
290
291
|
reportUrl(report) {
|
|
291
|
-
if (report && report.url) {
|
|
292
|
+
if (report && report.url && safeHref(report.url) !== '#') {
|
|
292
293
|
return report.url
|
|
293
294
|
}
|
|
294
295
|
const id = this.reportKey(report)
|
|
@@ -82,8 +82,7 @@
|
|
|
82
82
|
<NcButton
|
|
83
83
|
variant="primary"
|
|
84
84
|
:href="analyticsAppUrl"
|
|
85
|
-
target="_blank"
|
|
86
|
-
rel="noopener noreferrer">
|
|
85
|
+
target="_blank">
|
|
87
86
|
<template #icon>
|
|
88
87
|
<ChartBar :size="20" />
|
|
89
88
|
</template>
|
|
@@ -131,8 +130,7 @@
|
|
|
131
130
|
<NcActionLink
|
|
132
131
|
:closeAfterClick="true"
|
|
133
132
|
:href="reportUrl(report)"
|
|
134
|
-
target="_blank"
|
|
135
|
-
rel="noopener noreferrer">
|
|
133
|
+
target="_blank">
|
|
136
134
|
<template #icon>
|
|
137
135
|
<OpenInNew :size="20" />
|
|
138
136
|
</template>
|
|
@@ -178,7 +176,7 @@ import ViewDashboard from 'vue-material-design-icons/ViewDashboard.vue'
|
|
|
178
176
|
import CnAnalyticsReportCreate from '../../../components/CnAnalyticsReportCreate/CnAnalyticsReportCreate.vue'
|
|
179
177
|
import CnAnalyticsReportPicker from '../../../components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue'
|
|
180
178
|
import CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'
|
|
181
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
179
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
182
180
|
|
|
183
181
|
/**
|
|
184
182
|
* CnAnalyticsTab — bespoke dataset list for the `analytics` integration.
|
|
@@ -408,8 +406,9 @@ export default {
|
|
|
408
406
|
return t('nextcloud-vue', 'Latest value')
|
|
409
407
|
},
|
|
410
408
|
|
|
409
|
+
// A provider URL is used only when it is a safe scheme; else the app link.
|
|
411
410
|
reportUrl(report) {
|
|
412
|
-
if (report && report.url) {
|
|
411
|
+
if (report && report.url && safeHref(report.url) !== '#') {
|
|
413
412
|
return report.url
|
|
414
413
|
}
|
|
415
414
|
const id = this.reportKey(report)
|
|
@@ -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 } })
|