From 9496779f1c35cdd60dc017e6421d5e6b611a26c0 Mon Sep 17 00:00:00 2001 From: Kai Vandivier Date: Mon, 7 Apr 2025 13:45:34 +0200 Subject: [PATCH 1/2] fix: remove redirect=false from iframe src --- src/components/PluginLoader.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/PluginLoader.jsx b/src/components/PluginLoader.jsx index e84e57e..0554045 100644 --- a/src/components/PluginLoader.jsx +++ b/src/components/PluginLoader.jsx @@ -136,7 +136,6 @@ export const PluginLoader = ({ appsInfoQuery }) => { const pluginUrl = new URL(newPluginEntrypoint, window.location) pluginUrl.hash = location.hash pluginUrl.search = location.search - pluginUrl.searchParams.append('redirect', 'false') return pluginUrl.href }, [location.hash, location.search, appsInfoQuery.data, params.appName]) From 967d2010bd4e9e1e37075c9e591a0f4197f4fed4 Mon Sep 17 00:00:00 2001 From: Kai Vandivier Date: Tue, 8 Apr 2025 15:51:30 +0200 Subject: [PATCH 2/2] fix: handle potential redirect loops --- i18n/en.pot | 7 +++++-- src/components/PluginLoader.jsx | 31 +++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 8737659..9d02a2d 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2025-03-17T20:48:36.929Z\n" -"PO-Revision-Date: 2025-03-17T20:48:36.929Z\n" +"POT-Creation-Date: 2025-04-08T13:42:10.776Z\n" +"PO-Revision-Date: 2025-04-08T13:42:10.777Z\n" msgid "Save your data" msgstr "Save your data" @@ -33,6 +33,9 @@ msgstr "Cancel" msgid "Reload" msgstr "Reload" +msgid "A redirect error was encountered" +msgstr "A redirect error was encountered" + msgid "Unable to find an app for this URL." msgstr "Unable to find an app for this URL." diff --git a/src/components/PluginLoader.jsx b/src/components/PluginLoader.jsx index 0554045..24c8726 100644 --- a/src/components/PluginLoader.jsx +++ b/src/components/PluginLoader.jsx @@ -1,3 +1,4 @@ +import { useConfig } from '@dhis2/app-runtime' // eslint-disable-next-line import/no-unresolved import { Plugin } from '@dhis2/app-runtime/experimental' import { CircularLoader, CenteredContent, NoticeBox } from '@dhis2/ui' @@ -104,11 +105,15 @@ const failedLoadErrorMessage = 'different domain. ' + 'In the second case, the link should be opened in a new tab instead.' +const redirectErrorMessage = i18n.t('A redirect error was encountered') + export const PluginLoader = ({ appsInfoQuery }) => { + const { baseUrl } = useConfig() const params = useParams() const location = useLocation() const initClientOfflineInterface = useClientOfflineInterface() const [error, setError] = useState(null) + const [redirectError, setRedirectError] = useState(false) const iframeRef = useRef() const originalSrcRef = useRef() @@ -137,8 +142,14 @@ export const PluginLoader = ({ appsInfoQuery }) => { pluginUrl.hash = location.hash pluginUrl.search = location.search + // If the app in the iframe got redirected back to the iframe, try again + // with ?redirect=false + if (redirectError) { + pluginUrl.searchParams.append('redirect', 'false') + } + return pluginUrl.href - }, [location.hash, location.search, appsInfoQuery.data, params.appName]) + }, [location.hash, location.search, appsInfoQuery.data, params.appName, redirectError]) const handleLoad = useCallback( (event) => { @@ -154,6 +165,22 @@ export const PluginLoader = ({ appsInfoQuery }) => { return } + const targetLocation = event.target.contentWindow.location + const isTryingToLoadGlobalShell = targetLocation.href.startsWith(baseUrl + '/apps/') + if (isTryingToLoadGlobalShell && redirectError) { + // If this is the second time in a row we get redirected to the + // global shell, we're probably in a loop :( + // Set an error for the UI + setError(redirectErrorMessage) + return + } + if (isTryingToLoadGlobalShell) { + // Got redirected to the global shell... Set this error, + // so pluginHref adds ?redirect=false to the location + setRedirectError(true) + return + } + if (handleExternalNavigation(event, pluginHref)) { return } @@ -164,7 +191,7 @@ export const PluginLoader = ({ appsInfoQuery }) => { }) listenForCommandPaletteToggle(event) }, - [pluginHref, initClientOfflineInterface] + [pluginHref, initClientOfflineInterface, baseUrl, redirectError] ) useEffect(() => {