From 217a2e155e9b02f60520fd1ef1e035cf0192290c Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Thu, 15 Jun 2023 11:55:12 +0200 Subject: [PATCH] Svn external contains sub directory In case of using Svn external with sub directory the ui showed an empty page Committed-by: Eduard Heimbuch --- .../link_subrepository_to_directory.yaml | 2 + .../repos/sources/components/FileTreeLeaf.tsx | 12 +- .../components/content/FileLink.test.ts | 32 +++-- .../sources/components/content/FileLink.tsx | 113 +++++++++++------- 4 files changed, 101 insertions(+), 58 deletions(-) create mode 100644 gradle/changelog/link_subrepository_to_directory.yaml diff --git a/gradle/changelog/link_subrepository_to_directory.yaml b/gradle/changelog/link_subrepository_to_directory.yaml new file mode 100644 index 0000000000..cca386df19 --- /dev/null +++ b/gradle/changelog/link_subrepository_to_directory.yaml @@ -0,0 +1,2 @@ +- type: fixed + description: Svn external contains sub directory diff --git a/scm-ui/ui-webapp/src/repos/sources/components/FileTreeLeaf.tsx b/scm-ui/ui-webapp/src/repos/sources/components/FileTreeLeaf.tsx index edb8e59ae2..d70fd6c5b7 100644 --- a/scm-ui/ui-webapp/src/repos/sources/components/FileTreeLeaf.tsx +++ b/scm-ui/ui-webapp/src/repos/sources/components/FileTreeLeaf.tsx @@ -59,19 +59,19 @@ const ExtensionTd = styled.td` } `; -const FileName: FC<{ file: File; baseUrl: string }> = ({ file, baseUrl }) => { +const FileName: FC<{ file: File; baseUrl: string; repositoryType: string }> = ({ file, baseUrl, repositoryType }) => { const ref = useKeyboardIteratorTarget(); return ( - + {file.name} ); }; class FileTreeLeaf extends React.Component { - createFileIcon = (file: File) => { + createFileIcon = (file: File, repositoryType: string) => { return ( - + ); @@ -112,9 +112,9 @@ class FileTreeLeaf extends React.Component { return ( <> - {this.createFileIcon(file)} + {this.createFileIcon(file, repository.type)} - + {file.directory ? "" : this.contentIfPresent(file, "length", renderFileSize)} diff --git a/scm-ui/ui-webapp/src/repos/sources/components/content/FileLink.test.ts b/scm-ui/ui-webapp/src/repos/sources/components/content/FileLink.test.ts index 04707603db..51834681c4 100644 --- a/scm-ui/ui-webapp/src/repos/sources/components/content/FileLink.test.ts +++ b/scm-ui/ui-webapp/src/repos/sources/components/content/FileLink.test.ts @@ -28,13 +28,31 @@ import { File } from "@scm-manager/ui-types"; describe("create relative link tests", () => { it("should create relative link", () => { - expect(createRelativeLink("http://localhost:8081/scm/repo/scmadmin/scm-manager")).toBe( - "/scm/repo/scmadmin/scm-manager" + expect(createRelativeLink("http://localhost:8081/scm/repo/scmadmin/scm-manager", "/scm")).toBe( + "/repo/scmadmin/scm-manager/code/sources/" ); - expect(createRelativeLink("ssh://_anonymous@repo.scm-manager.org:1234/repo/public/anonymous-access")).toBe( - "/repo/public/anonymous-access" + expect(createRelativeLink("ssh://_anonymous@repo.scm-manager.org:1234/repo/public/anonymous-access", "")).toBe( + "/repo/public/anonymous-access/code/sources/" + ); + expect(createRelativeLink("ssh://server.local/project.git", "")).toBe("/project.git/code/sources/"); + expect(createRelativeLink("https://localhost:8081/scm/repo/scmadmin/scm-manager", "/scm", "2", "svn")).toBe( + "/repo/scmadmin/scm-manager/code/sources/2/" + ); + expect(createRelativeLink("https://localhost:8081/longContext/repo/scmadmin/scm-manager", "/longContext")).toBe( + "/repo/scmadmin/scm-manager/code/sources/" + ); + }); + + it("with sub directory", () => { + expect(createRelativeLink("http://localhost:8081/scm/repo/scmadmin/test02/subfolder", "/scm")).toBe( + "/repo/scmadmin/test02/code/sources/" + ); + expect(createRelativeLink("https://localhost:8081/scm/repo/scmadmin/test02/subfolder", "/scm", "2")).toBe( + "/repo/scmadmin/test02/code/sources/2/subfolder/" + ); + expect(createRelativeLink("http://localhost:8081/scm/repo/scmadmin/test02/dir1/dir2/dir3", "/scm", null, "svn")).toBe( + "/repo/scmadmin/test02/code/sources/-1/dir1/dir2/dir3/" ); - expect(createRelativeLink("ssh://server.local/project.git")).toBe("/project.git"); }); }); @@ -48,8 +66,8 @@ describe("create folder link tests", () => { revision: "1a", _links: {}, _embedded: { - children: [] - } + children: [], + }, }; } diff --git a/scm-ui/ui-webapp/src/repos/sources/components/content/FileLink.tsx b/scm-ui/ui-webapp/src/repos/sources/components/content/FileLink.tsx index d24d2513c7..e8fee16950 100644 --- a/scm-ui/ui-webapp/src/repos/sources/components/content/FileLink.tsx +++ b/scm-ui/ui-webapp/src/repos/sources/components/content/FileLink.tsx @@ -25,16 +25,17 @@ import React, { ReactNode } from "react"; import { Link } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { File } from "@scm-manager/ui-types"; -import { Tooltip } from "@scm-manager/ui-components"; +import { Tooltip, urls } from "@scm-manager/ui-components"; type Props = { baseUrl: string; file: File; children: ReactNode; tabIndex?: number; + repositoryType: string; }; -const isLocalRepository = (repositoryUrl: string) => { +const getHostname = (repositoryUrl: string) => { let host = repositoryUrl.split("/")[2]; if (host.includes("@")) { // remove prefix @@ -44,7 +45,11 @@ const isLocalRepository = (repositoryUrl: string) => { host = host.split(":")[0]; // remove query host = host.split("?")[0]; - return host === window.location.hostname; + return host; +}; + +const isLocalRepository = (repositoryUrl: string) => { + return getHostname(repositoryUrl) === window.location.hostname; }; export const encodePart = (part: string) => { @@ -54,9 +59,30 @@ export const encodePart = (part: string) => { return encodeURIComponent(part); }; -export const createRelativeLink = (repositoryUrl: string) => { +export const createRelativeLink = ( + repositoryUrl: string, + contextPath: string, + revision?: string, + repositoryType?: string +) => { const paths = repositoryUrl.split("/"); - return "/" + paths.slice(3).join("/"); + const CONTEXT_PART_IN_URL = 3; + const FOLDER_PART_IN_URL = 7; + + const folder = paths.splice(FOLDER_PART_IN_URL).join("/"); + let url = "/" + paths.slice(CONTEXT_PART_IN_URL, FOLDER_PART_IN_URL).join("/"); + url = url.replace(contextPath, ""); + url += "/code/sources/"; + if (revision) { + url += revision + "/"; + if (folder !== "") { + url += folder + "/"; + } + } else if (repositoryType === "svn" && folder !== "") { + // type of outgoing repo is svn + url += `-1/${folder}/`; + } + return url; }; export const createFolderLink = (base: string, file: File) => { @@ -74,49 +100,46 @@ export const createFolderLink = (base: string, file: File) => { return link; }; -const FileLink = React.forwardRef(({ baseUrl, file, children, tabIndex }, ref) => { - const [t] = useTranslation("repos"); - if (file?.subRepository?.repositoryUrl) { - // file link represents a subRepository - let link = file.subRepository.repositoryUrl; - if (file.subRepository.browserUrl) { - // replace upstream url with public browser url - link = file.subRepository.browserUrl; - } - if (link.startsWith("http://") || link.startsWith("https://")) { - if (file.subRepository.revision && isLocalRepository(link)) { - link += "/code/sources/" + file.subRepository.revision; +const FileLink = React.forwardRef( + ({ baseUrl, file, children, tabIndex, repositoryType }, ref) => { + const [t] = useTranslation("repos"); + if (file?.subRepository?.repositoryUrl) { + // file link represents a subRepository + let link = file.subRepository.repositoryUrl; + if (file.subRepository.browserUrl) { + // replace upstream url with public browser url + link = file.subRepository.browserUrl; } - return ( - - {children} - - ); - } else if (link.startsWith("ssh://") && isLocalRepository(link)) { - link = createRelativeLink(link); - if (file.subRepository.revision) { - link += "/code/sources/" + file.subRepository.revision; + + if (isLocalRepository(link)) { + link = createRelativeLink(link, urls.withContextPath(""), file.subRepository.revision, repositoryType); + return ( + + {children} + + ); + } else if (link.startsWith("http://") || link.startsWith("https://")) { + return ( + + {children} + + ); + } else { + // subRepository url cannot be linked + return ( + + {children} + + ); } - return ( - - {children} - - ); - } else { - // subRepository url cannot be linked - return ( - - {children} - - ); } + // normal file or folder + return ( + + {children} + + ); } - // normal file or folder - return ( - - {children} - - ); -}); +); export default FileLink;