Svn external contains sub directory

In case of using Svn external with sub directory the ui showed an empty page

Committed-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
Florian Scholdei
2023-06-15 11:55:12 +02:00
parent d74ed28542
commit 217a2e155e
4 changed files with 101 additions and 58 deletions

View File

@@ -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 (
<FileLink ref={ref} baseUrl={baseUrl} file={file} tabIndex={0}>
<FileLink ref={ref} baseUrl={baseUrl} file={file} tabIndex={0} repositoryType={repositoryType}>
{file.name}
</FileLink>
);
};
class FileTreeLeaf extends React.Component<Props> {
createFileIcon = (file: File) => {
createFileIcon = (file: File, repositoryType: string) => {
return (
<FileLink baseUrl={this.props.baseUrl} file={file} tabIndex={-1}>
<FileLink baseUrl={this.props.baseUrl} file={file} tabIndex={-1} repositoryType={repositoryType}>
<FileIcon file={file} />
</FileLink>
);
@@ -112,9 +112,9 @@ class FileTreeLeaf extends React.Component<Props> {
return (
<>
<tr>
<td>{this.createFileIcon(file)}</td>
<td>{this.createFileIcon(file, repository.type)}</td>
<MinWidthTd className="is-word-break">
<FileName file={file} baseUrl={baseUrl} />
<FileName file={file} baseUrl={baseUrl} repositoryType={repository.type} />
</MinWidthTd>
<NoWrapTd className="is-hidden-mobile">
{file.directory ? "" : this.contentIfPresent(file, "length", renderFileSize)}

View File

@@ -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: [],
},
};
}

View File

@@ -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<HTMLAnchorElement, Props>(({ 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<HTMLAnchorElement, Props>(
({ 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 (
<a href={link} tabIndex={tabIndex}>
{children}
</a>
);
} 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 (
<Link to={link} tabIndex={tabIndex}>
{children}
</Link>
);
} else if (link.startsWith("http://") || link.startsWith("https://")) {
return (
<a href={link} tabIndex={tabIndex}>
{children}
</a>
);
} else {
// subRepository url cannot be linked
return (
<Tooltip location="top" message={t("sources.fileTree.subRepository") + ": \n" + link}>
{children}
</Tooltip>
);
}
return (
<Link to={link} tabIndex={tabIndex}>
{children}
</Link>
);
} else {
// subRepository url cannot be linked
return (
<Tooltip location="top" message={t("sources.fileTree.subRepository") + ": \n" + link}>
{children}
</Tooltip>
);
}
// normal file or folder
return (
<Link ref={ref} to={createFolderLink(baseUrl, file)} tabIndex={tabIndex}>
{children}
</Link>
);
}
// normal file or folder
return (
<Link ref={ref} to={createFolderLink(baseUrl, file)} tabIndex={tabIndex}>
{children}
</Link>
);
});
);
export default FileLink;