diff --git a/docs/en/development/plugins/extension-points.md b/docs/en/development/plugins/extension-points.md index 0359f5837e..2430327e9d 100644 --- a/docs/en/development/plugins/extension-points.md +++ b/docs/en/development/plugins/extension-points.md @@ -62,6 +62,17 @@ The following extension points are provided for the frontend: - Dynamic extension point for custom language-specific renderers - Overrides the default Syntax Highlighter - Used by the Markdown Plantuml Plugin +### markdown-renderer.link.protocol +- Define custom protocols and their renderers for links in markdown + +Example: +```markdown +[description](myprotocol:somelink) +``` + +```typescript +binder.bind("markdown-renderer.link.protocol", { protocol: "myprotocol", renderer: MyProtocolRenderer }) +``` # Deprecated diff --git a/gradle/changelog/link-renderer.yaml b/gradle/changelog/link-renderer.yaml new file mode 100644 index 0000000000..1af474b840 --- /dev/null +++ b/gradle/changelog/link-renderer.yaml @@ -0,0 +1,4 @@ +- type: added + description: Add extension point for custom link protocol renderers in markdown ([#1639](https://github.com/scm-manager/scm-manager/pull/1639)) +- type: fixed + description: External links and anchor links are now correctly rendered in markdown even if no base path is present ([#1639](https://github.com/scm-manager/scm-manager/pull/1639)) diff --git a/scm-ui/ui-components/src/__resources__/markdown-links.md.ts b/scm-ui/ui-components/src/__resources__/markdown-links.md.ts index 46be993a3a..78ad883348 100644 --- a/scm-ui/ui-components/src/__resources__/markdown-links.md.ts +++ b/scm-ui/ui-components/src/__resources__/markdown-links.md.ts @@ -40,6 +40,10 @@ Anchor Links should be rendered a simple a tag with an href: [anchor link](#samp Links with a protocol other than http should be rendered a simple a tag with an href e.g.: [mail link](mailto:marvin@hitchhiker.com) +## Custom Protocol + +Renderers for custom protocols can be added via the "markdown-renderer.link.protocol" extension point: [description of scw link](scw:marvin@hitchhiker.com) + ## Internal Internal links should be rendered by react-router: [internal link](/buttons) diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index 801964a316..2f196db6b1 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -47993,6 +47993,8 @@ exports[`Storyshots MarkdownView Commit Links 1`] = `
hitchhiker/heart-of-gold@42 @@ -48093,7 +48095,7 @@ exports[`Storyshots MarkdownView Default 1`] = `
[Top] @@ -48289,7 +48291,7 @@ exports[`Storyshots MarkdownView Default 1`] = `
[Top] @@ -48362,7 +48364,7 @@ exports[`Storyshots MarkdownView Default 1`] = `
[Top] @@ -48508,7 +48510,7 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip
[Top] @@ -48541,7 +48543,7 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip
[Top] @@ -48908,7 +48910,7 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip
[Top] @@ -50106,7 +50108,7 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip
[Top] @@ -50135,7 +50137,7 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip reprehenderit duis irure @@ -50165,7 +50167,7 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip anim aute reprehenderit id eu ea. Aute excepteur proident @@ -50204,6 +50206,8 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip
Reprehenderit non eu quis in ad elit esse qui aute id
incididunt
@@ -58105,6 +58109,166 @@ the story is mostly for checking if the links are rendered correct.
+ Renderers for custom protocols can be added via the "markdown-renderer.link.protocol" extension point: +
+ Internal links should be rendered by react-router: + + internal link + +
+ + + +`; + +exports[`Storyshots MarkdownView Links without Base Path 1`] = ` ++ Show case for different style of markdown links. +Please note that some of the links may not work in storybook, +the story is mostly for checking if the links are rendered correct. +
+ + ++ External Links should be opened in a new tab: + + external link + +
+ + ++ Anchor Links should be rendered a simple a tag with an href: + + anchor link + +
+ + ++ Links with a protocol other than http should be rendered a simple a tag with an href e.g.: + + mail link + +
+ + ++ Renderers for custom protocols can be added via the "markdown-renderer.link.protocol" extension point: +
[Top] @@ -58334,7 +58497,7 @@ exports[`Storyshots MarkdownView Skip Html 1`] = `
[Top] @@ -58403,7 +58566,7 @@ exports[`Storyshots MarkdownView Skip Html 1`] = `
[Top] @@ -58547,7 +58710,7 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip
[Top] @@ -58576,7 +58739,7 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip
[Top] @@ -58986,7 +59149,7 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip
[Top] @@ -60180,7 +60343,7 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip
[Top] @@ -60205,7 +60368,7 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip reprehenderit duis irure @@ -60235,7 +60398,7 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip anim aute reprehenderit id eu ea. Aute excepteur proident @@ -60274,6 +60437,8 @@ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip
Reprehenderit non eu quis in ad elit esse qui aute id
incididunt
diff --git a/scm-ui/ui-components/src/index.ts b/scm-ui/ui-components/src/index.ts
index 8878e85636..43df49cdeb 100644
--- a/scm-ui/ui-components/src/index.ts
+++ b/scm-ui/ui-components/src/index.ts
@@ -95,6 +95,7 @@ export * from "./repos";
export * from "./table";
export * from "./toast";
export * from "./popover";
+export * from "./markdown/markdownExtensions";
export {
File,
diff --git a/scm-ui/ui-components/src/markdown/MarkdownLinkRenderer.test.tsx b/scm-ui/ui-components/src/markdown/MarkdownLinkRenderer.test.tsx
index 037bf0f73f..0789b05000 100644
--- a/scm-ui/ui-components/src/markdown/MarkdownLinkRenderer.test.tsx
+++ b/scm-ui/ui-components/src/markdown/MarkdownLinkRenderer.test.tsx
@@ -57,19 +57,19 @@ describe("test isExternalLink", () => {
describe("test isLinkWithProtocol", () => {
it("should return true", () => {
- expect(isLinkWithProtocol("ldap://[2001:db8::7]/c=GB?objectClass?one")).toBe(true);
- expect(isLinkWithProtocol("mailto:trillian@hitchhiker.com")).toBe(true);
- expect(isLinkWithProtocol("tel:+1-816-555-1212")).toBe(true);
- expect(isLinkWithProtocol("urn:oasis:names:specification:docbook:dtd:xml:4.1.2")).toBe(true);
- expect(isLinkWithProtocol("about:config")).toBe(true);
- expect(isLinkWithProtocol("http://cloudogu.com")).toBe(true);
- expect(isLinkWithProtocol("file:///srv/git/project.git")).toBe(true);
- expect(isLinkWithProtocol("ssh://trillian@server/project.git")).toBe(true);
+ expect(isLinkWithProtocol("ldap://[2001:db8::7]/c=GB?objectClass?one")).toBeTruthy();
+ expect(isLinkWithProtocol("mailto:trillian@hitchhiker.com")).toBeTruthy();
+ expect(isLinkWithProtocol("tel:+1-816-555-1212")).toBeTruthy();
+ expect(isLinkWithProtocol("urn:oasis:names:specification:docbook:dtd:xml:4.1.2")).toBeTruthy();
+ expect(isLinkWithProtocol("about:config")).toBeTruthy();
+ expect(isLinkWithProtocol("http://cloudogu.com")).toBeTruthy();
+ expect(isLinkWithProtocol("file:///srv/git/project.git")).toBeTruthy();
+ expect(isLinkWithProtocol("ssh://trillian@server/project.git")).toBeTruthy();
});
it("should return false", () => {
- expect(isLinkWithProtocol("some/path/link")).toBe(false);
- expect(isLinkWithProtocol("/some/path/link")).toBe(false);
- expect(isLinkWithProtocol("#some-anchor")).toBe(false);
+ expect(isLinkWithProtocol("some/path/link")).toBeFalsy();
+ expect(isLinkWithProtocol("/some/path/link")).toBeFalsy();
+ expect(isLinkWithProtocol("#some-anchor")).toBeFalsy();
});
});
diff --git a/scm-ui/ui-components/src/markdown/MarkdownLinkRenderer.tsx b/scm-ui/ui-components/src/markdown/MarkdownLinkRenderer.tsx
index b285d91d8c..4144a41dfe 100644
--- a/scm-ui/ui-components/src/markdown/MarkdownLinkRenderer.tsx
+++ b/scm-ui/ui-components/src/markdown/MarkdownLinkRenderer.tsx
@@ -25,6 +25,7 @@ import React, { FC } from "react";
import { Link, useLocation } from "react-router-dom";
import ExternalLink from "../navigation/ExternalLink";
import { urls } from "@scm-manager/ui-api";
+import { ProtocolLinkRendererExtensionMap } from "./markdownExtensions";
const externalLinkRegex = new RegExp("^http(s)?://");
export const isExternalLink = (link: string) => {
@@ -39,9 +40,10 @@ export const isInternalScmRepoLink = (link: string) => {
return link.startsWith("/repo/");
};
-const linkWithProtcolRegex = new RegExp("^[a-z]+:");
+const linkWithProtocolRegex = new RegExp("^([a-z]+):(.+)");
export const isLinkWithProtocol = (link: string) => {
- return linkWithProtcolRegex.test(link);
+ const match = link.match(linkWithProtocolRegex);
+ return match && { protocol: match[1], link: match[2] };
};
const join = (left: string, right: string) => {
@@ -106,10 +108,10 @@ type LinkProps = {
};
type Props = LinkProps & {
- base: string;
+ base?: string;
};
-const MarkdownLinkRenderer: FC
+ Link: {href} [Protocol: {protocol}]
+
+