mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-27 04:43:59 +01:00
Rework markup link rendering (#26745)
Fixes #26548 This PR refactors the rendering of markup links. The old code uses `strings.Replace` to change some urls while the new code uses more context to decide which link should be generated. The added tests should ensure the same output for the old and new behaviour (besides the bug). We may need to refactor the rendering a bit more to make it clear how the different helper methods render the input string. There are lots of options (resolve links / images / mentions / git hashes / emojis / ...) but you don't really know what helper uses which options. For example, we currently support images in the user description which should not be allowed I think: <details> <summary>Profile</summary> https://try.gitea.io/KN4CK3R ![grafik](https://github.com/go-gitea/gitea/assets/1666336/109ae422-496d-4200-b52e-b3a528f553e5) </details> --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
parent
c7e4629c02
commit
637451a45e
42 changed files with 967 additions and 395 deletions
|
@ -110,7 +110,9 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
|
|||
var err error
|
||||
if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: issue.Repo.Link(),
|
||||
Links: markup.Links{
|
||||
Base: issue.Repo.Link(),
|
||||
},
|
||||
Metas: issue.Repo.ComposeMetas(ctx),
|
||||
}, comment.Content); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -585,7 +585,6 @@ func (repo *Repository) CanEnableEditor() bool {
|
|||
func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML {
|
||||
desc, err := markup.RenderDescriptionHTML(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: repo.HTMLURL(),
|
||||
// Don't use Metas to speedup requests
|
||||
}, repo.Description)
|
||||
if err != nil {
|
||||
|
|
13
modules/markup/external/external.go
vendored
13
modules/markup/external/external.go
vendored
|
@ -79,9 +79,10 @@ func envMark(envName string) string {
|
|||
// Render renders the data of the document to HTML via the external tool.
|
||||
func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
|
||||
var (
|
||||
urlRawPrefix = strings.Replace(ctx.URLPrefix, "/src/", "/raw/", 1)
|
||||
command = strings.NewReplacer(envMark("GITEA_PREFIX_SRC"), ctx.URLPrefix,
|
||||
envMark("GITEA_PREFIX_RAW"), urlRawPrefix).Replace(p.Command)
|
||||
command = strings.NewReplacer(
|
||||
envMark("GITEA_PREFIX_SRC"), ctx.Links.SrcLink(),
|
||||
envMark("GITEA_PREFIX_RAW"), ctx.Links.RawLink(),
|
||||
).Replace(p.Command)
|
||||
commands = strings.Fields(command)
|
||||
args = commands[1:]
|
||||
)
|
||||
|
@ -121,14 +122,14 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
|
|||
ctx.Ctx = graceful.GetManager().ShutdownContext()
|
||||
}
|
||||
|
||||
processCtx, _, finished := process.GetManager().AddContext(ctx.Ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.URLPrefix))
|
||||
processCtx, _, finished := process.GetManager().AddContext(ctx.Ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.Links.SrcLink()))
|
||||
defer finished()
|
||||
|
||||
cmd := exec.CommandContext(processCtx, commands[0], args...)
|
||||
cmd.Env = append(
|
||||
os.Environ(),
|
||||
"GITEA_PREFIX_SRC="+ctx.URLPrefix,
|
||||
"GITEA_PREFIX_RAW="+urlRawPrefix,
|
||||
"GITEA_PREFIX_SRC="+ctx.Links.SrcLink(),
|
||||
"GITEA_PREFIX_RAW="+ctx.Links.RawLink(),
|
||||
)
|
||||
if !p.IsInputFile {
|
||||
cmd.Stdin = input
|
||||
|
|
|
@ -80,15 +80,10 @@ const keywordClass = "issue-keyword"
|
|||
|
||||
// IsLink reports whether link fits valid format.
|
||||
func IsLink(link []byte) bool {
|
||||
return isLink(link)
|
||||
}
|
||||
|
||||
// isLink reports whether link fits valid format.
|
||||
func isLink(link []byte) bool {
|
||||
return validLinksPattern.Match(link)
|
||||
}
|
||||
|
||||
func isLinkStr(link string) bool {
|
||||
func IsLinkStr(link string) bool {
|
||||
return validLinksPattern.MatchString(link)
|
||||
}
|
||||
|
||||
|
@ -344,7 +339,7 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output
|
|||
node = node.FirstChild
|
||||
}
|
||||
|
||||
visitNode(ctx, procs, procs, node)
|
||||
visitNode(ctx, procs, node)
|
||||
|
||||
newNodes := make([]*html.Node, 0, 5)
|
||||
|
||||
|
@ -375,7 +370,7 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output
|
|||
return nil
|
||||
}
|
||||
|
||||
func visitNode(ctx *RenderContext, procs, textProcs []processor, node *html.Node) {
|
||||
func visitNode(ctx *RenderContext, procs []processor, node *html.Node) {
|
||||
// Add user-content- to IDs and "#" links if they don't already have them
|
||||
for idx, attr := range node.Attr {
|
||||
val := strings.TrimPrefix(attr.Val, "#")
|
||||
|
@ -390,35 +385,29 @@ func visitNode(ctx *RenderContext, procs, textProcs []processor, node *html.Node
|
|||
}
|
||||
|
||||
if attr.Key == "class" && attr.Val == "emoji" {
|
||||
textProcs = nil
|
||||
procs = nil
|
||||
}
|
||||
}
|
||||
|
||||
// We ignore code and pre.
|
||||
switch node.Type {
|
||||
case html.TextNode:
|
||||
textNode(ctx, textProcs, node)
|
||||
textNode(ctx, procs, node)
|
||||
case html.ElementNode:
|
||||
if node.Data == "img" {
|
||||
for i, attr := range node.Attr {
|
||||
if attr.Key != "src" {
|
||||
continue
|
||||
}
|
||||
if len(attr.Val) > 0 && !isLinkStr(attr.Val) && !strings.HasPrefix(attr.Val, "data:image/") {
|
||||
prefix := ctx.URLPrefix
|
||||
if ctx.IsWiki {
|
||||
prefix = util.URLJoin(prefix, "wiki", "raw")
|
||||
}
|
||||
prefix = strings.Replace(prefix, "/src/", "/media/", 1)
|
||||
|
||||
attr.Val = util.URLJoin(prefix, attr.Val)
|
||||
if len(attr.Val) > 0 && !IsLinkStr(attr.Val) && !strings.HasPrefix(attr.Val, "data:image/") {
|
||||
attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val)
|
||||
}
|
||||
attr.Val = camoHandleLink(attr.Val)
|
||||
node.Attr[i] = attr
|
||||
}
|
||||
} else if node.Data == "a" {
|
||||
// Restrict text in links to emojis
|
||||
textProcs = emojiProcessors
|
||||
procs = emojiProcessors
|
||||
} else if node.Data == "code" || node.Data == "pre" {
|
||||
return
|
||||
} else if node.Data == "i" {
|
||||
|
@ -444,7 +433,7 @@ func visitNode(ctx *RenderContext, procs, textProcs []processor, node *html.Node
|
|||
}
|
||||
}
|
||||
for n := node.FirstChild; n != nil; n = n.NextSibling {
|
||||
visitNode(ctx, procs, textProcs, n)
|
||||
visitNode(ctx, procs, n)
|
||||
}
|
||||
}
|
||||
// ignore everything else
|
||||
|
@ -641,10 +630,6 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
|
|||
}
|
||||
|
||||
func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
|
||||
shortLinkProcessorFull(ctx, node, false)
|
||||
}
|
||||
|
||||
func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) {
|
||||
next := node.NextSibling
|
||||
for node != nil && node != next {
|
||||
m := shortLinkPattern.FindStringSubmatchIndex(node.Data)
|
||||
|
@ -665,7 +650,7 @@ func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) {
|
|||
if equalPos := strings.IndexByte(v, '='); equalPos == -1 {
|
||||
// There is no equal in this argument; this is a mandatory arg
|
||||
if props["name"] == "" {
|
||||
if isLinkStr(v) {
|
||||
if IsLinkStr(v) {
|
||||
// If we clearly see it is a link, we save it so
|
||||
|
||||
// But first we need to ensure, that if both mandatory args provided
|
||||
|
@ -740,7 +725,7 @@ func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) {
|
|||
DataAtom: atom.A,
|
||||
}
|
||||
childNode.Parent = linkNode
|
||||
absoluteLink := isLinkStr(link)
|
||||
absoluteLink := IsLinkStr(link)
|
||||
if !absoluteLink {
|
||||
if image {
|
||||
link = strings.ReplaceAll(link, " ", "+")
|
||||
|
@ -751,16 +736,9 @@ func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) {
|
|||
link = url.PathEscape(link)
|
||||
}
|
||||
}
|
||||
urlPrefix := ctx.URLPrefix
|
||||
if image {
|
||||
if !absoluteLink {
|
||||
if IsSameDomain(urlPrefix) {
|
||||
urlPrefix = strings.Replace(urlPrefix, "/src/", "/raw/", 1)
|
||||
}
|
||||
if ctx.IsWiki {
|
||||
link = util.URLJoin("wiki", "raw", link)
|
||||
}
|
||||
link = util.URLJoin(urlPrefix, link)
|
||||
link = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), link)
|
||||
}
|
||||
title := props["title"]
|
||||
if title == "" {
|
||||
|
@ -789,18 +767,15 @@ func shortLinkProcessorFull(ctx *RenderContext, node *html.Node, noLink bool) {
|
|||
} else {
|
||||
if !absoluteLink {
|
||||
if ctx.IsWiki {
|
||||
link = util.URLJoin("wiki", link)
|
||||
link = util.URLJoin(ctx.Links.WikiLink(), link)
|
||||
} else {
|
||||
link = util.URLJoin(ctx.Links.SrcLink(), link)
|
||||
}
|
||||
link = util.URLJoin(urlPrefix, link)
|
||||
}
|
||||
childNode.Type = html.TextNode
|
||||
childNode.Data = name
|
||||
}
|
||||
if noLink {
|
||||
linkNode = childNode
|
||||
} else {
|
||||
linkNode.Attr = []html.Attribute{{Key: "href", Val: link}}
|
||||
}
|
||||
replaceContent(node, m[0], m[1], linkNode)
|
||||
node = node.NextSibling.NextSibling
|
||||
}
|
||||
|
|
|
@ -287,8 +287,8 @@ func TestRender_IssueIndexPattern_Document(t *testing.T) {
|
|||
}
|
||||
|
||||
func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) {
|
||||
if ctx.URLPrefix == "" {
|
||||
ctx.URLPrefix = TestAppURL
|
||||
if ctx.Links.Base == "" {
|
||||
ctx.Links.Base = TestRepoURL
|
||||
}
|
||||
|
||||
var buf strings.Builder
|
||||
|
@ -304,7 +304,9 @@ func TestRender_AutoLink(t *testing.T) {
|
|||
var buffer strings.Builder
|
||||
err := PostProcess(&RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: TestRepoURL,
|
||||
Links: Links{
|
||||
Base: TestRepoURL,
|
||||
},
|
||||
Metas: localMetas,
|
||||
}, strings.NewReader(input), &buffer)
|
||||
assert.Equal(t, err, nil)
|
||||
|
@ -313,7 +315,9 @@ func TestRender_AutoLink(t *testing.T) {
|
|||
buffer.Reset()
|
||||
err = PostProcess(&RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: TestRepoURL,
|
||||
Links: Links{
|
||||
Base: TestRepoURL,
|
||||
},
|
||||
Metas: localMetas,
|
||||
IsWiki: true,
|
||||
}, strings.NewReader(input), &buffer)
|
||||
|
@ -343,7 +347,9 @@ func TestRender_FullIssueURLs(t *testing.T) {
|
|||
var result strings.Builder
|
||||
err := postProcess(&RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: TestRepoURL,
|
||||
Links: Links{
|
||||
Base: TestRepoURL,
|
||||
},
|
||||
Metas: localMetas,
|
||||
}, []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result)
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -42,7 +42,9 @@ func TestRender_Commits(t *testing.T) {
|
|||
buffer, err := markup.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
RelativePath: ".md",
|
||||
URLPrefix: markup.TestRepoURL,
|
||||
Links: markup.Links{
|
||||
Base: markup.TestRepoURL,
|
||||
},
|
||||
Metas: localMetas,
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
|
@ -93,7 +95,9 @@ func TestRender_CrossReferences(t *testing.T) {
|
|||
buffer, err := markup.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
RelativePath: "a.md",
|
||||
URLPrefix: setting.AppSubURL,
|
||||
Links: markup.Links{
|
||||
Base: setting.AppSubURL,
|
||||
},
|
||||
Metas: localMetas,
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
|
@ -138,7 +142,9 @@ func TestRender_links(t *testing.T) {
|
|||
buffer, err := markup.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
RelativePath: "a.md",
|
||||
URLPrefix: markup.TestRepoURL,
|
||||
Links: markup.Links{
|
||||
Base: markup.TestRepoURL,
|
||||
},
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
|
@ -238,7 +244,9 @@ func TestRender_email(t *testing.T) {
|
|||
res, err := markup.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
RelativePath: "a.md",
|
||||
URLPrefix: markup.TestRepoURL,
|
||||
Links: markup.Links{
|
||||
Base: markup.TestRepoURL,
|
||||
},
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
|
||||
|
@ -309,7 +317,9 @@ func TestRender_emoji(t *testing.T) {
|
|||
buffer, err := markup.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
RelativePath: "a.md",
|
||||
URLPrefix: markup.TestRepoURL,
|
||||
Links: markup.Links{
|
||||
Base: markup.TestRepoURL,
|
||||
},
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
|
@ -372,13 +382,18 @@ func TestRender_ShortLinks(t *testing.T) {
|
|||
test := func(input, expected, expectedWiki string) {
|
||||
buffer, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: tree,
|
||||
Links: markup.Links{
|
||||
Base: markup.TestRepoURL,
|
||||
BranchPath: "master",
|
||||
},
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
buffer, err = markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: markup.TestRepoURL,
|
||||
Links: markup.Links{
|
||||
Base: markup.TestRepoURL,
|
||||
},
|
||||
Metas: localMetas,
|
||||
IsWiki: true,
|
||||
}, input)
|
||||
|
@ -386,14 +401,14 @@ func TestRender_ShortLinks(t *testing.T) {
|
|||
assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(buffer))
|
||||
}
|
||||
|
||||
rawtree := util.URLJoin(markup.TestRepoURL, "raw", "master")
|
||||
mediatree := util.URLJoin(markup.TestRepoURL, "media", "master")
|
||||
url := util.URLJoin(tree, "Link")
|
||||
otherURL := util.URLJoin(tree, "Other-Link")
|
||||
encodedURL := util.URLJoin(tree, "Link%3F")
|
||||
imgurl := util.URLJoin(rawtree, "Link.jpg")
|
||||
otherImgurl := util.URLJoin(rawtree, "Link+Other.jpg")
|
||||
encodedImgurl := util.URLJoin(rawtree, "Link+%23.jpg")
|
||||
notencodedImgurl := util.URLJoin(rawtree, "some", "path", "Link+#.jpg")
|
||||
imgurl := util.URLJoin(mediatree, "Link.jpg")
|
||||
otherImgurl := util.URLJoin(mediatree, "Link+Other.jpg")
|
||||
encodedImgurl := util.URLJoin(mediatree, "Link+%23.jpg")
|
||||
notencodedImgurl := util.URLJoin(mediatree, "some", "path", "Link+#.jpg")
|
||||
urlWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Link")
|
||||
otherURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Other-Link")
|
||||
encodedURLWiki := util.URLJoin(markup.TestRepoURL, "wiki", "Link%3F")
|
||||
|
@ -475,19 +490,23 @@ func TestRender_ShortLinks(t *testing.T) {
|
|||
|
||||
func TestRender_RelativeImages(t *testing.T) {
|
||||
setting.AppURL = markup.TestAppURL
|
||||
tree := util.URLJoin(markup.TestRepoURL, "src", "master")
|
||||
|
||||
test := func(input, expected, expectedWiki string) {
|
||||
buffer, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: tree,
|
||||
Links: markup.Links{
|
||||
Base: markup.TestRepoURL,
|
||||
BranchPath: "master",
|
||||
},
|
||||
Metas: localMetas,
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
buffer, err = markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: markup.TestRepoURL,
|
||||
Links: markup.Links{
|
||||
Base: markup.TestRepoURL,
|
||||
},
|
||||
Metas: localMetas,
|
||||
IsWiki: true,
|
||||
}, input)
|
||||
|
@ -522,7 +541,9 @@ func Test_ParseClusterFuzz(t *testing.T) {
|
|||
var res strings.Builder
|
||||
err := markup.PostProcess(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Links: markup.Links{
|
||||
Base: "https://example.com",
|
||||
},
|
||||
Metas: localMetas,
|
||||
}, strings.NewReader(data), &res)
|
||||
assert.NoError(t, err)
|
||||
|
@ -533,7 +554,9 @@ func Test_ParseClusterFuzz(t *testing.T) {
|
|||
res.Reset()
|
||||
err = markup.PostProcess(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Links: markup.Links{
|
||||
Base: "https://example.com",
|
||||
},
|
||||
Metas: localMetas,
|
||||
}, strings.NewReader(data), &res)
|
||||
|
||||
|
@ -543,6 +566,7 @@ func Test_ParseClusterFuzz(t *testing.T) {
|
|||
|
||||
func TestPostProcess_RenderDocument(t *testing.T) {
|
||||
setting.AppURL = markup.TestAppURL
|
||||
setting.StaticURLPrefix = markup.TestAppURL // can't run standalone
|
||||
|
||||
localMetas := map[string]string{
|
||||
"user": "go-gitea",
|
||||
|
@ -554,7 +578,9 @@ func TestPostProcess_RenderDocument(t *testing.T) {
|
|||
var res strings.Builder
|
||||
err := markup.PostProcess(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Links: markup.Links{
|
||||
Base: "https://example.com",
|
||||
},
|
||||
Metas: localMetas,
|
||||
}, strings.NewReader(input), &res)
|
||||
assert.NoError(t, err)
|
||||
|
@ -595,7 +621,6 @@ func TestIssue16020(t *testing.T) {
|
|||
var res strings.Builder
|
||||
err := markup.PostProcess(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Metas: localMetas,
|
||||
}, strings.NewReader(data), &res)
|
||||
assert.NoError(t, err)
|
||||
|
@ -612,7 +637,6 @@ func BenchmarkEmojiPostprocess(b *testing.B) {
|
|||
var res strings.Builder
|
||||
err := markup.PostProcess(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Metas: localMetas,
|
||||
}, strings.NewReader(data), &res)
|
||||
assert.NoError(b, err)
|
||||
|
@ -623,7 +647,9 @@ func TestFuzz(t *testing.T) {
|
|||
s := "t/l/issues/8#/../../a"
|
||||
renderContext := markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com/go-gitea/gitea",
|
||||
Links: markup.Links{
|
||||
Base: "https://example.com/go-gitea/gitea",
|
||||
},
|
||||
Metas: map[string]string{
|
||||
"user": "go-gitea",
|
||||
"repo": "gitea",
|
||||
|
@ -641,7 +667,6 @@ func TestIssue18471(t *testing.T) {
|
|||
var res strings.Builder
|
||||
err := markup.PostProcess(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: "https://example.com",
|
||||
Metas: localMetas,
|
||||
}, strings.NewReader(data), &res)
|
||||
|
||||
|
|
|
@ -87,18 +87,8 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
|||
// Check if the destination is a real link
|
||||
link := v.Destination
|
||||
if len(link) > 0 && !markup.IsLink(link) {
|
||||
prefix := pc.Get(urlPrefixKey).(string)
|
||||
if pc.Get(isWikiKey).(bool) {
|
||||
prefix = giteautil.URLJoin(prefix, "wiki", "raw")
|
||||
v.Destination = []byte(giteautil.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), string(link)))
|
||||
}
|
||||
prefix = strings.Replace(prefix, "/src/", "/media/", 1)
|
||||
|
||||
lnk := strings.TrimLeft(string(link), "/")
|
||||
|
||||
lnk = giteautil.URLJoin(prefix, lnk)
|
||||
link = []byte(lnk)
|
||||
}
|
||||
v.Destination = link
|
||||
|
||||
parent := n.Parent()
|
||||
// Create a link around image only if parent is not already a link
|
||||
|
@ -107,7 +97,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
|||
|
||||
// Create a link wrapper
|
||||
wrap := ast.NewLink()
|
||||
wrap.Destination = link
|
||||
wrap.Destination = v.Destination
|
||||
wrap.Title = v.Title
|
||||
wrap.SetAttributeString("target", []byte("_blank"))
|
||||
|
||||
|
@ -143,11 +133,15 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
|||
link[0] != '#' && !bytes.HasPrefix(link, byteMailto) {
|
||||
// special case: this is not a link, a hash link or a mailto:, so it's a
|
||||
// relative URL
|
||||
lnk := string(link)
|
||||
if pc.Get(isWikiKey).(bool) {
|
||||
lnk = giteautil.URLJoin("wiki", lnk)
|
||||
|
||||
var base string
|
||||
if ctx.IsWiki {
|
||||
base = ctx.Links.WikiLink()
|
||||
} else {
|
||||
base = ctx.Links.Base
|
||||
}
|
||||
link = []byte(giteautil.URLJoin(pc.Get(urlPrefixKey).(string), lnk))
|
||||
|
||||
link = []byte(giteautil.URLJoin(base, string(link)))
|
||||
}
|
||||
if len(link) > 0 && link[0] == '#' {
|
||||
link = []byte("#user-content-" + string(link)[1:])
|
||||
|
@ -188,9 +182,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
|||
applyElementDir(v)
|
||||
case *ast.Text:
|
||||
if v.SoftLineBreak() && !v.HardLineBreak() {
|
||||
renderMetas := pc.Get(renderMetasKey).(map[string]string)
|
||||
mode := renderMetas["mode"]
|
||||
if mode != "document" {
|
||||
if ctx.Metas["mode"] != "document" {
|
||||
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInComments)
|
||||
} else {
|
||||
v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInDocuments)
|
||||
|
|
|
@ -34,9 +34,6 @@ var (
|
|||
)
|
||||
|
||||
var (
|
||||
urlPrefixKey = parser.NewContextKey()
|
||||
isWikiKey = parser.NewContextKey()
|
||||
renderMetasKey = parser.NewContextKey()
|
||||
renderContextKey = parser.NewContextKey()
|
||||
renderConfigKey = parser.NewContextKey()
|
||||
)
|
||||
|
@ -66,9 +63,6 @@ func (l *limitWriter) Write(data []byte) (int, error) {
|
|||
// newParserContext creates a parser.Context with the render context set
|
||||
func newParserContext(ctx *markup.RenderContext) parser.Context {
|
||||
pc := parser.NewContext(parser.WithIDs(newPrefixedIDs()))
|
||||
pc.Set(urlPrefixKey, ctx.URLPrefix)
|
||||
pc.Set(isWikiKey, ctx.IsWiki)
|
||||
pc.Set(renderMetasKey, ctx.Metas)
|
||||
pc.Set(renderContextKey, ctx)
|
||||
return pc
|
||||
}
|
||||
|
|
|
@ -53,14 +53,18 @@ func TestRender_StandardLinks(t *testing.T) {
|
|||
test := func(input, expected, expectedWiki string) {
|
||||
buffer, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: setting.AppSubURL,
|
||||
Links: markup.Links{
|
||||
Base: setting.AppSubURL,
|
||||
},
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
|
||||
buffer, err = markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: setting.AppSubURL,
|
||||
Links: markup.Links{
|
||||
Base: setting.AppSubURL,
|
||||
},
|
||||
IsWiki: true,
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
|
@ -84,7 +88,9 @@ func TestRender_Images(t *testing.T) {
|
|||
test := func(input, expected string) {
|
||||
buffer, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: setting.AppSubURL,
|
||||
Links: markup.Links{
|
||||
Base: setting.AppSubURL,
|
||||
},
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
|
@ -107,7 +113,6 @@ func TestRender_Images(t *testing.T) {
|
|||
"[!["+title+"]("+url+")]("+href+")",
|
||||
`<p><a href="`+href+`" rel="nofollow"><img src="`+result+`" alt="`+title+`"/></a></p>`)
|
||||
|
||||
url = "/../../.images/src/02/train.jpg"
|
||||
test(
|
||||
"!["+title+"]("+url+")",
|
||||
`<p><a href="`+result+`" target="_blank" rel="nofollow noopener"><img src="`+result+`" alt="`+title+`"/></a></p>`)
|
||||
|
@ -286,12 +291,14 @@ func TestTotal_RenderWiki(t *testing.T) {
|
|||
setting.AppURL = AppURL
|
||||
setting.AppSubURL = AppSubURL
|
||||
|
||||
answers := testAnswers(util.URLJoin(AppSubURL, "wiki/"), util.URLJoin(AppSubURL, "wiki", "raw/"))
|
||||
answers := testAnswers(util.URLJoin(AppSubURL, "wiki"), util.URLJoin(AppSubURL, "wiki", "raw"))
|
||||
|
||||
for i := 0; i < len(sameCases); i++ {
|
||||
line, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: AppSubURL,
|
||||
Links: markup.Links{
|
||||
Base: setting.AppSubURL,
|
||||
},
|
||||
Metas: localMetas,
|
||||
IsWiki: true,
|
||||
}, sameCases[i])
|
||||
|
@ -315,7 +322,9 @@ func TestTotal_RenderWiki(t *testing.T) {
|
|||
for i := 0; i < len(testCases); i += 2 {
|
||||
line, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: AppSubURL,
|
||||
Links: markup.Links{
|
||||
Base: setting.AppSubURL,
|
||||
},
|
||||
IsWiki: true,
|
||||
}, testCases[i])
|
||||
assert.NoError(t, err)
|
||||
|
@ -327,12 +336,15 @@ func TestTotal_RenderString(t *testing.T) {
|
|||
setting.AppURL = AppURL
|
||||
setting.AppSubURL = AppSubURL
|
||||
|
||||
answers := testAnswers(util.URLJoin(AppSubURL, "src", "master/"), util.URLJoin(AppSubURL, "raw", "master/"))
|
||||
answers := testAnswers(util.URLJoin(AppSubURL, "src", "master"), util.URLJoin(AppSubURL, "media", "master"))
|
||||
|
||||
for i := 0; i < len(sameCases); i++ {
|
||||
line, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: util.URLJoin(AppSubURL, "src", "master/"),
|
||||
Links: markup.Links{
|
||||
Base: AppSubURL,
|
||||
BranchPath: "master",
|
||||
},
|
||||
Metas: localMetas,
|
||||
}, sameCases[i])
|
||||
assert.NoError(t, err)
|
||||
|
@ -344,7 +356,9 @@ func TestTotal_RenderString(t *testing.T) {
|
|||
for i := 0; i < len(testCases); i += 2 {
|
||||
line, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: AppSubURL,
|
||||
Links: markup.Links{
|
||||
Base: AppSubURL,
|
||||
},
|
||||
}, testCases[i])
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testCases[i+1], line)
|
||||
|
@ -556,3 +570,367 @@ foo: bar
|
|||
assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderLinks(t *testing.T) {
|
||||
input := ` space @mention-user
|
||||
/just/a/path.bin
|
||||
https://example.com/file.bin
|
||||
[local link](file.bin)
|
||||
[remote link](https://example.com)
|
||||
[[local link|file.bin]]
|
||||
[[remote link|https://example.com]]
|
||||
![local image](image.jpg)
|
||||
![remote image](https://example.com/image.jpg)
|
||||
[[local image|image.jpg]]
|
||||
[[remote link|https://example.com/image.jpg]]
|
||||
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
|
||||
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
|
||||
:+1:
|
||||
mail@domain.com
|
||||
@mention-user test
|
||||
#123
|
||||
space
|
||||
`
|
||||
cases := []struct {
|
||||
Links markup.Links
|
||||
IsWiki bool
|
||||
Expected string
|
||||
}{
|
||||
{ // 0
|
||||
Links: markup.Links{},
|
||||
IsWiki: false,
|
||||
Expected: `<p>space @mention-user<br/>
|
||||
/just/a/path.bin<br/>
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||
<a href="/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/src/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||
<a href="/image.jpg" rel="nofollow"><img src="/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||
@mention-user test<br/>
|
||||
#123<br/>
|
||||
space</p>
|
||||
`,
|
||||
},
|
||||
{ // 1
|
||||
Links: markup.Links{},
|
||||
IsWiki: true,
|
||||
Expected: `<p>space @mention-user<br/>
|
||||
/just/a/path.bin<br/>
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/wiki/raw/image.jpg" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||
<a href="/wiki/raw/image.jpg" rel="nofollow"><img src="/wiki/raw/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||
@mention-user test<br/>
|
||||
#123<br/>
|
||||
space</p>
|
||||
`,
|
||||
},
|
||||
{ // 2
|
||||
Links: markup.Links{
|
||||
Base: "https://gitea.io/",
|
||||
},
|
||||
IsWiki: false,
|
||||
Expected: `<p>space @mention-user<br/>
|
||||
/just/a/path.bin<br/>
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||
<a href="https://gitea.io/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="https://gitea.io/src/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="https://gitea.io/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/image.jpg" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||
<a href="https://gitea.io/image.jpg" rel="nofollow"><img src="https://gitea.io/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||
@mention-user test<br/>
|
||||
#123<br/>
|
||||
space</p>
|
||||
`,
|
||||
},
|
||||
{ // 3
|
||||
Links: markup.Links{
|
||||
Base: "https://gitea.io/",
|
||||
},
|
||||
IsWiki: true,
|
||||
Expected: `<p>space @mention-user<br/>
|
||||
/just/a/path.bin<br/>
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||
<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="https://gitea.io/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="https://gitea.io/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://gitea.io/wiki/raw/image.jpg" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||
<a href="https://gitea.io/wiki/raw/image.jpg" rel="nofollow"><img src="https://gitea.io/wiki/raw/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||
@mention-user test<br/>
|
||||
#123<br/>
|
||||
space</p>
|
||||
`,
|
||||
},
|
||||
{ // 4
|
||||
Links: markup.Links{
|
||||
Base: "/relative/path",
|
||||
},
|
||||
IsWiki: false,
|
||||
Expected: `<p>space @mention-user<br/>
|
||||
/just/a/path.bin<br/>
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||
<a href="/relative/path/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/relative/path/src/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/relative/path/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/image.jpg" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||
<a href="/relative/path/image.jpg" rel="nofollow"><img src="/relative/path/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||
@mention-user test<br/>
|
||||
#123<br/>
|
||||
space</p>
|
||||
`,
|
||||
},
|
||||
{ // 5
|
||||
Links: markup.Links{
|
||||
Base: "/relative/path",
|
||||
},
|
||||
IsWiki: true,
|
||||
Expected: `<p>space @mention-user<br/>
|
||||
/just/a/path.bin<br/>
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||
@mention-user test<br/>
|
||||
#123<br/>
|
||||
space</p>
|
||||
`,
|
||||
},
|
||||
{ // 6
|
||||
Links: markup.Links{
|
||||
Base: "/user/repo",
|
||||
BranchPath: "branch/main",
|
||||
},
|
||||
IsWiki: false,
|
||||
Expected: `<p>space @mention-user<br/>
|
||||
/just/a/path.bin<br/>
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||
<a href="/user/repo/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/user/repo/src/branch/main/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/user/repo/media/branch/main/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/image.jpg" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||
<a href="/user/repo/media/branch/main/image.jpg" rel="nofollow"><img src="/user/repo/media/branch/main/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||
@mention-user test<br/>
|
||||
#123<br/>
|
||||
space</p>
|
||||
`,
|
||||
},
|
||||
{ // 7
|
||||
Links: markup.Links{
|
||||
Base: "/relative/path",
|
||||
BranchPath: "branch/main",
|
||||
},
|
||||
IsWiki: true,
|
||||
Expected: `<p>space @mention-user<br/>
|
||||
/just/a/path.bin<br/>
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||
@mention-user test<br/>
|
||||
#123<br/>
|
||||
space</p>
|
||||
`,
|
||||
},
|
||||
{ // 8
|
||||
Links: markup.Links{
|
||||
Base: "/user/repo",
|
||||
TreePath: "sub/folder",
|
||||
},
|
||||
IsWiki: false,
|
||||
Expected: `<p>space @mention-user<br/>
|
||||
/just/a/path.bin<br/>
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||
<a href="/user/repo/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/user/repo/src/sub/folder/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/user/repo/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/image.jpg" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||
<a href="/user/repo/image.jpg" rel="nofollow"><img src="/user/repo/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||
@mention-user test<br/>
|
||||
#123<br/>
|
||||
space</p>
|
||||
`,
|
||||
},
|
||||
{ // 9
|
||||
Links: markup.Links{
|
||||
Base: "/relative/path",
|
||||
TreePath: "sub/folder",
|
||||
},
|
||||
IsWiki: true,
|
||||
Expected: `<p>space @mention-user<br/>
|
||||
/just/a/path.bin<br/>
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||
@mention-user test<br/>
|
||||
#123<br/>
|
||||
space</p>
|
||||
`,
|
||||
},
|
||||
{ // 10
|
||||
Links: markup.Links{
|
||||
Base: "/user/repo",
|
||||
BranchPath: "branch/main",
|
||||
TreePath: "sub/folder",
|
||||
},
|
||||
IsWiki: false,
|
||||
Expected: `<p>space @mention-user<br/>
|
||||
/just/a/path.bin<br/>
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||
<a href="/user/repo/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/user/repo/src/branch/main/sub/folder/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/user/repo/media/branch/main/sub/folder/image.jpg" target="_blank" rel="nofollow noopener"><img src="/user/repo/media/branch/main/sub/folder/image.jpg" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||
<a href="/user/repo/media/branch/main/sub/folder/image.jpg" rel="nofollow"><img src="/user/repo/media/branch/main/sub/folder/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||
@mention-user test<br/>
|
||||
#123<br/>
|
||||
space</p>
|
||||
`,
|
||||
},
|
||||
{ // 11
|
||||
Links: markup.Links{
|
||||
Base: "/relative/path",
|
||||
BranchPath: "branch/main",
|
||||
TreePath: "sub/folder",
|
||||
},
|
||||
IsWiki: true,
|
||||
Expected: `<p>space @mention-user<br/>
|
||||
/just/a/path.bin<br/>
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a><br/>
|
||||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/relative/path/wiki/file.bin" rel="nofollow">local link</a><br/>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a><br/>
|
||||
<a href="/relative/path/wiki/raw/image.jpg" target="_blank" rel="nofollow noopener"><img src="/relative/path/wiki/raw/image.jpg" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a><br/>
|
||||
<a href="/relative/path/wiki/raw/image.jpg" rel="nofollow"><img src="/relative/path/wiki/raw/image.jpg" title="local image" alt="local image"/></a><br/>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a><br/>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow">https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare<br/>
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow">https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb</a><br/>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit<br/>
|
||||
<span class="emoji" aria-label="thumbs up">👍</span><br/>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a><br/>
|
||||
@mention-user test<br/>
|
||||
#123<br/>
|
||||
space</p>
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
result, err := markdown.RenderString(&markup.RenderContext{Ctx: context.Background(), Links: c.Links, IsWiki: c.IsWiki}, input)
|
||||
assert.NoError(t, err, "Unexpected error in testcase: %v", i)
|
||||
assert.Equal(t, c.Expected, result, "Unexpected result in testcase %v", i)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
package markup
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
|
@ -101,8 +100,7 @@ func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
|
|||
|
||||
w := &Writer{
|
||||
HTMLWriter: htmlWriter,
|
||||
URLPrefix: ctx.URLPrefix,
|
||||
IsWiki: ctx.IsWiki,
|
||||
Ctx: ctx,
|
||||
}
|
||||
|
||||
htmlWriter.ExtendingWriter = w
|
||||
|
@ -132,63 +130,53 @@ func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Wri
|
|||
// Writer implements org.Writer
|
||||
type Writer struct {
|
||||
*org.HTMLWriter
|
||||
URLPrefix string
|
||||
IsWiki bool
|
||||
Ctx *markup.RenderContext
|
||||
}
|
||||
|
||||
var byteMailto = []byte("mailto:")
|
||||
const mailto = "mailto:"
|
||||
|
||||
// WriteRegularLink renders images, links or videos
|
||||
func (r *Writer) WriteRegularLink(l org.RegularLink) {
|
||||
link := []byte(html.EscapeString(l.URL))
|
||||
func (r *Writer) resolveLink(l org.RegularLink) string {
|
||||
link := html.EscapeString(l.URL)
|
||||
if l.Protocol == "file" {
|
||||
link = link[len("file:"):]
|
||||
}
|
||||
if len(link) > 0 && !markup.IsLink(link) &&
|
||||
link[0] != '#' && !bytes.HasPrefix(link, byteMailto) {
|
||||
lnk := string(link)
|
||||
if r.IsWiki {
|
||||
lnk = util.URLJoin("wiki", lnk)
|
||||
if len(link) > 0 && !markup.IsLinkStr(link) &&
|
||||
link[0] != '#' && !strings.HasPrefix(link, mailto) {
|
||||
base := r.Ctx.Links.Base
|
||||
switch l.Kind() {
|
||||
case "image", "video":
|
||||
base = r.Ctx.Links.ResolveMediaLink(r.Ctx.IsWiki)
|
||||
}
|
||||
link = []byte(util.URLJoin(r.URLPrefix, lnk))
|
||||
link = util.URLJoin(base, link)
|
||||
}
|
||||
return link
|
||||
}
|
||||
|
||||
// WriteRegularLink renders images, links or videos
|
||||
func (r *Writer) WriteRegularLink(l org.RegularLink) {
|
||||
link := r.resolveLink(l)
|
||||
|
||||
// Inspired by https://github.com/niklasfasching/go-org/blob/6eb20dbda93cb88c3503f7508dc78cbbc639378f/org/html_writer.go#L406-L427
|
||||
switch l.Kind() {
|
||||
case "image":
|
||||
if l.Description == nil {
|
||||
imageSrc := getMediaURL(link)
|
||||
fmt.Fprintf(r, `<img src="%s" alt="%s" />`, imageSrc, link)
|
||||
fmt.Fprintf(r, `<img src="%s" alt="%s" />`, link, link)
|
||||
} else {
|
||||
description := strings.TrimPrefix(org.String(l.Description...), "file:")
|
||||
imageSrc := getMediaURL([]byte(description))
|
||||
imageSrc := r.resolveLink(l.Description[0].(org.RegularLink))
|
||||
fmt.Fprintf(r, `<a href="%s"><img src="%s" alt="%s" /></a>`, link, imageSrc, imageSrc)
|
||||
}
|
||||
case "video":
|
||||
if l.Description == nil {
|
||||
imageSrc := getMediaURL(link)
|
||||
fmt.Fprintf(r, `<video src="%s">%s</video>`, imageSrc, link)
|
||||
fmt.Fprintf(r, `<video src="%s">%s</video>`, link, link)
|
||||
} else {
|
||||
description := strings.TrimPrefix(org.String(l.Description...), "file:")
|
||||
videoSrc := getMediaURL([]byte(description))
|
||||
videoSrc := r.resolveLink(l.Description[0].(org.RegularLink))
|
||||
fmt.Fprintf(r, `<a href="%s"><video src="%s">%s</video></a>`, link, videoSrc, videoSrc)
|
||||
}
|
||||
default:
|
||||
description := string(link)
|
||||
description := link
|
||||
if l.Description != nil {
|
||||
description = r.WriteNodesAsString(l.Description...)
|
||||
}
|
||||
fmt.Fprintf(r, `<a href="%s">%s</a>`, link, description)
|
||||
}
|
||||
}
|
||||
|
||||
func getMediaURL(l []byte) string {
|
||||
srcURL := string(l)
|
||||
|
||||
// Check if link is valid
|
||||
if len(srcURL) > 0 && !markup.IsLink(l) {
|
||||
srcURL = strings.Replace(srcURL, "/src/", "/media/", 1)
|
||||
}
|
||||
|
||||
return srcURL
|
||||
}
|
||||
|
|
|
@ -28,7 +28,9 @@ func TestRender_StandardLinks(t *testing.T) {
|
|||
test := func(input, expected string) {
|
||||
buffer, err := RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: setting.AppSubURL,
|
||||
Links: markup.Links{
|
||||
Base: setting.AppSubURL,
|
||||
},
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
|
@ -49,7 +51,9 @@ func TestRender_Media(t *testing.T) {
|
|||
test := func(input, expected string) {
|
||||
buffer, err := RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: setting.AppSubURL,
|
||||
Links: markup.Links{
|
||||
Base: setting.AppSubURL,
|
||||
},
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
|
@ -85,7 +89,6 @@ func TestRender_Source(t *testing.T) {
|
|||
test := func(input, expected string) {
|
||||
buffer, err := RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
URLPrefix: setting.AppSubURL,
|
||||
}, input)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/yuin/goldmark/ast"
|
||||
)
|
||||
|
@ -69,7 +70,7 @@ type RenderContext struct {
|
|||
RelativePath string // relative path from tree root of the branch
|
||||
Type string
|
||||
IsWiki bool
|
||||
URLPrefix string
|
||||
Links Links
|
||||
Metas map[string]string
|
||||
DefaultLink string
|
||||
GitRepo *git.Repository
|
||||
|
@ -80,6 +81,45 @@ type RenderContext struct {
|
|||
InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
|
||||
}
|
||||
|
||||
type Links struct {
|
||||
Base string
|
||||
BranchPath string
|
||||
TreePath string
|
||||
}
|
||||
|
||||
func (l *Links) HasBranchInfo() bool {
|
||||
return l.BranchPath != ""
|
||||
}
|
||||
|
||||
func (l *Links) SrcLink() string {
|
||||
return util.URLJoin(l.Base, "src", l.BranchPath, l.TreePath)
|
||||
}
|
||||
|
||||
func (l *Links) MediaLink() string {
|
||||
return util.URLJoin(l.Base, "media", l.BranchPath, l.TreePath)
|
||||
}
|
||||
|
||||
func (l *Links) RawLink() string {
|
||||
return util.URLJoin(l.Base, "raw", l.BranchPath, l.TreePath)
|
||||
}
|
||||
|
||||
func (l *Links) WikiLink() string {
|
||||
return util.URLJoin(l.Base, "wiki")
|
||||
}
|
||||
|
||||
func (l *Links) WikiRawLink() string {
|
||||
return util.URLJoin(l.Base, "wiki/raw")
|
||||
}
|
||||
|
||||
func (l *Links) ResolveMediaLink(isWiki bool) string {
|
||||
if isWiki {
|
||||
return l.WikiRawLink()
|
||||
} else if l.HasBranchInfo() {
|
||||
return l.MediaLink()
|
||||
}
|
||||
return l.Base
|
||||
}
|
||||
|
||||
// Cancel runs any cleanup functions that have been registered for this Ctx
|
||||
func (ctx *RenderContext) Cancel() {
|
||||
if ctx == nil {
|
||||
|
|
|
@ -161,7 +161,6 @@ func NewFuncMap() template.FuncMap {
|
|||
"RenderEmoji": RenderEmoji,
|
||||
"RenderEmojiPlain": emoji.ReplaceAliases,
|
||||
"ReactionToEmoji": ReactionToEmoji,
|
||||
"RenderNote": RenderNote,
|
||||
|
||||
"RenderMarkdownToHtml": RenderMarkdownToHtml,
|
||||
"RenderLabel": RenderLabel,
|
||||
|
|
|
@ -24,20 +24,12 @@ import (
|
|||
)
|
||||
|
||||
// RenderCommitMessage renders commit message with XSS-safe and special links.
|
||||
func RenderCommitMessage(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML {
|
||||
return RenderCommitMessageLink(ctx, msg, urlPrefix, "", metas)
|
||||
}
|
||||
|
||||
// RenderCommitMessageLink renders commit message as a XXS-safe link to the provided
|
||||
// default url, handling for special links.
|
||||
func RenderCommitMessageLink(ctx context.Context, msg, urlPrefix, urlDefault string, metas map[string]string) template.HTML {
|
||||
func RenderCommitMessage(ctx context.Context, msg string, metas map[string]string) template.HTML {
|
||||
cleanMsg := template.HTMLEscapeString(msg)
|
||||
// we can safely assume that it will not return any error, since there
|
||||
// shouldn't be any special HTML.
|
||||
fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: urlPrefix,
|
||||
DefaultLink: urlDefault,
|
||||
Metas: metas,
|
||||
}, cleanMsg)
|
||||
if err != nil {
|
||||
|
@ -51,9 +43,9 @@ func RenderCommitMessageLink(ctx context.Context, msg, urlPrefix, urlDefault str
|
|||
return template.HTML(msgLines[0])
|
||||
}
|
||||
|
||||
// RenderCommitMessageLinkSubject renders commit message as a XXS-safe link to
|
||||
// RenderCommitMessageLinkSubject renders commit message as a XSS-safe link to
|
||||
// the provided default url, handling for special links without email to links.
|
||||
func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlPrefix, urlDefault string, metas map[string]string) template.HTML {
|
||||
func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlDefault string, metas map[string]string) template.HTML {
|
||||
msgLine := strings.TrimLeftFunc(msg, unicode.IsSpace)
|
||||
lineEnd := strings.IndexByte(msgLine, '\n')
|
||||
if lineEnd > 0 {
|
||||
|
@ -68,7 +60,6 @@ func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlPrefix, urlDefa
|
|||
// shouldn't be any special HTML.
|
||||
renderedMessage, err := markup.RenderCommitMessageSubject(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: urlPrefix,
|
||||
DefaultLink: urlDefault,
|
||||
Metas: metas,
|
||||
}, template.HTMLEscapeString(msgLine))
|
||||
|
@ -80,7 +71,7 @@ func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlPrefix, urlDefa
|
|||
}
|
||||
|
||||
// RenderCommitBody extracts the body of a commit message without its title.
|
||||
func RenderCommitBody(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML {
|
||||
func RenderCommitBody(ctx context.Context, msg string, metas map[string]string) template.HTML {
|
||||
msgLine := strings.TrimSpace(msg)
|
||||
lineEnd := strings.IndexByte(msgLine, '\n')
|
||||
if lineEnd > 0 {
|
||||
|
@ -95,7 +86,6 @@ func RenderCommitBody(ctx context.Context, msg, urlPrefix string, metas map[stri
|
|||
|
||||
renderedMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: urlPrefix,
|
||||
Metas: metas,
|
||||
}, template.HTMLEscapeString(msgLine))
|
||||
if err != nil {
|
||||
|
@ -115,10 +105,9 @@ func RenderCodeBlock(htmlEscapedTextToRender template.HTML) template.HTML {
|
|||
}
|
||||
|
||||
// RenderIssueTitle renders issue/pull title with defined post processors
|
||||
func RenderIssueTitle(ctx context.Context, text, urlPrefix string, metas map[string]string) template.HTML {
|
||||
func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) template.HTML {
|
||||
renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: urlPrefix,
|
||||
Metas: metas,
|
||||
}, template.HTMLEscapeString(text))
|
||||
if err != nil {
|
||||
|
@ -211,25 +200,9 @@ func ReactionToEmoji(reaction string) template.HTML {
|
|||
return template.HTML(fmt.Sprintf(`<img alt=":%s:" src="%s/assets/img/emoji/%s.png"></img>`, reaction, setting.StaticURLPrefix, url.PathEscape(reaction)))
|
||||
}
|
||||
|
||||
// RenderNote renders the contents of a git-notes file as a commit message.
|
||||
func RenderNote(ctx context.Context, msg, urlPrefix string, metas map[string]string) template.HTML {
|
||||
cleanMsg := template.HTMLEscapeString(msg)
|
||||
fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: urlPrefix,
|
||||
Metas: metas,
|
||||
}, cleanMsg)
|
||||
if err != nil {
|
||||
log.Error("RenderNote: %v", err)
|
||||
return ""
|
||||
}
|
||||
return template.HTML(fullMessage)
|
||||
}
|
||||
|
||||
func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //nolint:revive
|
||||
output, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: setting.AppSubURL,
|
||||
Metas: map[string]string{"mode": "document"},
|
||||
}, input)
|
||||
if err != nil {
|
||||
|
|
|
@ -6,16 +6,63 @@ package templates
|
|||
import (
|
||||
"context"
|
||||
"html/template"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const testInput = ` space @mention-user
|
||||
/just/a/path.bin
|
||||
https://example.com/file.bin
|
||||
[local link](file.bin)
|
||||
[remote link](https://example.com)
|
||||
[[local link|file.bin]]
|
||||
[[remote link|https://example.com]]
|
||||
![local image](image.jpg)
|
||||
![remote image](https://example.com/image.jpg)
|
||||
[[local image|image.jpg]]
|
||||
[[remote link|https://example.com/image.jpg]]
|
||||
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
|
||||
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
|
||||
:+1:
|
||||
mail@domain.com
|
||||
@mention-user test
|
||||
#123
|
||||
space
|
||||
`
|
||||
|
||||
var testMetas = map[string]string{
|
||||
"user": "user13",
|
||||
"repo": "repo11",
|
||||
"repoPath": "../../tests/gitea-repositories-meta/user13/repo11.git/",
|
||||
"mode": "comment",
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
unittest.InitSettings()
|
||||
if err := git.InitSimple(context.Background()); err != nil {
|
||||
log.Fatal("git init failed, err: %v", err)
|
||||
}
|
||||
markup.Init(&markup.ProcessorHelper{
|
||||
IsUsernameMentionable: func(ctx context.Context, username string) bool {
|
||||
return username == "mention-user"
|
||||
},
|
||||
})
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestRenderCommitBody(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
msg string
|
||||
urlPrefix string
|
||||
metas map[string]string
|
||||
}
|
||||
tests := []struct {
|
||||
|
@ -50,7 +97,91 @@ func TestRenderCommitBody(t *testing.T) {
|
|||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, RenderCommitBody(tt.args.ctx, tt.args.msg, tt.args.urlPrefix, tt.args.metas), "RenderCommitBody(%v, %v, %v, %v)", tt.args.ctx, tt.args.msg, tt.args.urlPrefix, tt.args.metas)
|
||||
assert.Equalf(t, tt.want, RenderCommitBody(tt.args.ctx, tt.args.msg, tt.args.metas), "RenderCommitBody(%v, %v, %v)", tt.args.ctx, tt.args.msg, tt.args.metas)
|
||||
})
|
||||
}
|
||||
|
||||
expected := `/just/a/path.bin
|
||||
<a href="https://example.com/file.bin" class="link">https://example.com/file.bin</a>
|
||||
[local link](file.bin)
|
||||
[remote link](<a href="https://example.com" class="link">https://example.com</a>)
|
||||
[[local link|file.bin]]
|
||||
[[remote link|<a href="https://example.com" class="link">https://example.com</a>]]
|
||||
![local image](image.jpg)
|
||||
![remote image](<a href="https://example.com/image.jpg" class="link">https://example.com/image.jpg</a>)
|
||||
[[local image|image.jpg]]
|
||||
[[remote link|<a href="https://example.com/image.jpg" class="link">https://example.com/image.jpg</a>]]
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" class="compare"><code class="nohighlight">88fc37a3c0...12fc37a3c0 (hash)</code></a>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" class="commit"><code class="nohighlight">88fc37a3c0</code></a>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
|
||||
<span class="emoji" aria-label="thumbs up">👍</span>
|
||||
<a href="mailto:mail@domain.com" class="mailto">mail@domain.com</a>
|
||||
<a href="http://localhost:3000/mention-user" class="mention">@mention-user</a> test
|
||||
<a href="http://localhost:3000/user13/repo11/issues/123" class="ref-issue">#123</a>
|
||||
space`
|
||||
|
||||
assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput, testMetas))
|
||||
}
|
||||
|
||||
func TestRenderCommitMessage(t *testing.T) {
|
||||
expected := `space <a href="http://localhost:3000/mention-user" class="mention">@mention-user</a> `
|
||||
|
||||
assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput, testMetas))
|
||||
}
|
||||
|
||||
func TestRenderCommitMessageLinkSubject(t *testing.T) {
|
||||
expected := `<a href="https://example.com/link" class="default-link muted">space </a><a href="http://localhost:3000/mention-user" class="mention">@mention-user</a>`
|
||||
|
||||
assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput, "https://example.com/link", testMetas))
|
||||
}
|
||||
|
||||
func TestRenderIssueTitle(t *testing.T) {
|
||||
expected := ` space @mention-user
|
||||
/just/a/path.bin
|
||||
https://example.com/file.bin
|
||||
[local link](file.bin)
|
||||
[remote link](https://example.com)
|
||||
[[local link|file.bin]]
|
||||
[[remote link|https://example.com]]
|
||||
![local image](image.jpg)
|
||||
![remote image](https://example.com/image.jpg)
|
||||
[[local image|image.jpg]]
|
||||
[[remote link|https://example.com/image.jpg]]
|
||||
https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
|
||||
https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
|
||||
<span class="emoji" aria-label="thumbs up">👍</span>
|
||||
mail@domain.com
|
||||
@mention-user test
|
||||
<a href="http://localhost:3000/user13/repo11/issues/123" class="ref-issue">#123</a>
|
||||
space
|
||||
`
|
||||
assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput, testMetas))
|
||||
}
|
||||
|
||||
func TestRenderMarkdownToHtml(t *testing.T) {
|
||||
expected := `<p>space <a href="http://localhost:3000/mention-user" rel="nofollow">@mention-user</a><br/>
|
||||
/just/a/path.bin
|
||||
<a href="https://example.com/file.bin" rel="nofollow">https://example.com/file.bin</a>
|
||||
<a href="/file.bin" rel="nofollow">local link</a>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a>
|
||||
<a href="/src/file.bin" rel="nofollow">local link</a>
|
||||
<a href="https://example.com" rel="nofollow">remote link</a>
|
||||
<a href="/image.jpg" target="_blank" rel="nofollow noopener"><img src="/image.jpg" alt="local image"/></a>
|
||||
<a href="https://example.com/image.jpg" target="_blank" rel="nofollow noopener"><img src="https://example.com/image.jpg" alt="remote image"/></a>
|
||||
<a href="/image.jpg" rel="nofollow"><img src="/image.jpg" title="local image" alt="local image"/></a>
|
||||
<a href="https://example.com/image.jpg" rel="nofollow"><img src="https://example.com/image.jpg" title="remote link" alt="remote link"/></a>
|
||||
<a href="https://example.com/user/repo/compare/88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb#hash" rel="nofollow"><code>88fc37a3c0...12fc37a3c0 (hash)</code></a>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a58247f42fb pare
|
||||
<a href="https://example.com/user/repo/commit/88fc37a3c0a4dda553bdcfc80c178a58247f42fb" rel="nofollow"><code>88fc37a3c0</code></a>
|
||||
com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit
|
||||
<span class="emoji" aria-label="thumbs up">👍</span>
|
||||
<a href="mailto:mail@domain.com" rel="nofollow">mail@domain.com</a>
|
||||
<a href="http://localhost:3000/mention-user" rel="nofollow">@mention-user</a> test
|
||||
#123
|
||||
space</p>
|
||||
`
|
||||
assert.EqualValues(t, expected, RenderMarkdownToHtml(context.Background(), testInput))
|
||||
}
|
||||
|
|
|
@ -33,7 +33,9 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr
|
|||
// Raw markdown
|
||||
if err := markdown.RenderRaw(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: urlPrefix,
|
||||
Links: markup.Links{
|
||||
Base: urlPrefix,
|
||||
},
|
||||
}, strings.NewReader(text), ctx.Resp); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
@ -76,7 +78,9 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr
|
|||
|
||||
if err := markup.Render(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: urlPrefix,
|
||||
Links: markup.Links{
|
||||
Base: urlPrefix,
|
||||
},
|
||||
Metas: meta,
|
||||
IsWiki: wiki,
|
||||
Type: markupType,
|
||||
|
|
|
@ -52,7 +52,9 @@ func toReleaseLink(ctx *context.Context, act *activities_model.Action) string {
|
|||
func renderMarkdown(ctx *context.Context, act *activities_model.Action, content string) string {
|
||||
markdownCtx := &markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: act.GetRepoLink(ctx),
|
||||
Links: markup.Links{
|
||||
Base: act.GetRepoLink(ctx),
|
||||
},
|
||||
Type: markdown.MarkupName,
|
||||
Metas: map[string]string{
|
||||
"user": act.GetRepoUserName(ctx),
|
||||
|
@ -199,7 +201,6 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
|
|||
switch act.OpType {
|
||||
case activities_model.ActionCommitRepo, activities_model.ActionMirrorSyncPush:
|
||||
push := templates.ActionContent2Commits(act)
|
||||
repoLink := act.GetRepoAbsoluteLink(ctx)
|
||||
|
||||
for _, commit := range push.Commits {
|
||||
if len(desc) != 0 {
|
||||
|
@ -208,7 +209,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
|
|||
desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s",
|
||||
html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(ctx), commit.Sha1)),
|
||||
commit.Sha1,
|
||||
templates.RenderCommitMessage(ctx, commit.Message, repoLink, nil),
|
||||
templates.RenderCommitMessage(ctx, commit.Message, nil),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -289,7 +290,9 @@ func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release, i
|
|||
link := &feeds.Link{Href: rel.HTMLURL()}
|
||||
content, err = markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: rel.Repo.Link(),
|
||||
Links: markup.Links{
|
||||
Base: rel.Repo.Link(),
|
||||
},
|
||||
Metas: rel.Repo.ComposeMetas(ctx),
|
||||
}, rel.Note)
|
||||
|
||||
|
|
|
@ -43,7 +43,9 @@ func showUserFeed(ctx *context.Context, formatType string) {
|
|||
|
||||
ctxUserDescription, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: ctx.ContextUser.HTMLURL(),
|
||||
Links: markup.Links{
|
||||
Base: ctx.ContextUser.HTMLURL(),
|
||||
},
|
||||
Metas: map[string]string{
|
||||
"user": ctx.ContextUser.GetDisplayName(),
|
||||
},
|
||||
|
|
|
@ -5,6 +5,7 @@ package org
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
|
@ -48,9 +49,7 @@ func Home(ctx *context.Context) {
|
|||
if len(org.Description) != 0 {
|
||||
desc, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Metas: map[string]string{"mode": "document"},
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
}, org.Description)
|
||||
if err != nil {
|
||||
ctx.ServerError("RenderString", err)
|
||||
|
@ -173,13 +172,15 @@ func prepareOrgProfileReadme(ctx *context.Context, profileGitRepo *git.Repositor
|
|||
if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
|
||||
log.Error("failed to GetBlobContent: %v", err)
|
||||
} else {
|
||||
// Pass URLPrefix to markdown render for the full link of media elements.
|
||||
// The profile of default branch would be shown.
|
||||
prefix := profileDbRepo.Link() + "/src/branch/" + util.PathEscapeSegments(profileDbRepo.DefaultBranch)
|
||||
if profileContent, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
GitRepo: profileGitRepo,
|
||||
URLPrefix: prefix,
|
||||
Links: markup.Links{
|
||||
// Pass repo link to markdown render for the full link of media elements.
|
||||
// The profile of default branch would be shown.
|
||||
Base: profileDbRepo.Link(),
|
||||
BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)),
|
||||
},
|
||||
Metas: map[string]string{"mode": "document"},
|
||||
}, bytes); err != nil {
|
||||
log.Error("failed to RenderString: %v", err)
|
||||
|
|
|
@ -7,7 +7,9 @@ package repo
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
|
@ -21,7 +23,9 @@ import (
|
|||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitgraph"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
git_service "code.gitea.io/gitea/services/repository"
|
||||
)
|
||||
|
@ -370,9 +374,21 @@ func Diff(ctx *context.Context) {
|
|||
note := &git.Note{}
|
||||
err = git.GetNote(ctx, ctx.Repo.GitRepo, commitID, note)
|
||||
if err == nil {
|
||||
ctx.Data["Note"] = string(charset.ToUTF8WithFallback(note.Message))
|
||||
ctx.Data["NoteCommit"] = note.Commit
|
||||
ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(ctx, note.Commit)
|
||||
ctx.Data["NoteRendered"], err = markup.RenderCommitMessage(&markup.RenderContext{
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
BranchPath: path.Join("commit", util.PathEscapeSegments(commitID)),
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
}, template.HTMLEscapeString(string(charset.ToUTF8WithFallback(note.Message))))
|
||||
if err != nil {
|
||||
ctx.ServerError("RenderCommitMessage", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Data["BranchName"], err = commit.GetBranchName()
|
||||
|
|
|
@ -1436,9 +1436,10 @@ func ViewIssue(ctx *context.Context) {
|
|||
}
|
||||
}
|
||||
ctx.Data["IssueWatch"] = iw
|
||||
|
||||
issue.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
|
@ -1601,7 +1602,9 @@ func ViewIssue(ctx *context.Context) {
|
|||
}
|
||||
|
||||
comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
|
@ -1678,7 +1681,9 @@ func ViewIssue(ctx *context.Context) {
|
|||
}
|
||||
} else if comment.Type.HasContentSupport() {
|
||||
comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
|
@ -2234,7 +2239,9 @@ func UpdateIssueContent(ctx *context.Context) {
|
|||
}
|
||||
|
||||
content, err := markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
|
||||
Links: markup.Links{
|
||||
Base: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
|
@ -3143,7 +3150,9 @@ func UpdateCommentContent(ctx *context.Context) {
|
|||
}
|
||||
|
||||
content, err := markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
|
||||
Links: markup.Links{
|
||||
Base: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
|
|
|
@ -81,7 +81,9 @@ func Milestones(ctx *context.Context) {
|
|||
}
|
||||
for _, m := range miles {
|
||||
m.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
|
@ -275,7 +277,9 @@ func MilestoneIssuesAndPulls(ctx *context.Context) {
|
|||
}
|
||||
|
||||
milestone.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
|
|
|
@ -90,7 +90,9 @@ func Projects(ctx *context.Context) {
|
|||
|
||||
for i := range projects {
|
||||
projects[i].RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
|
@ -357,7 +359,9 @@ func ViewProject(ctx *context.Context) {
|
|||
ctx.Data["LinkedPRs"] = linkedPrsMap
|
||||
|
||||
project.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
|
|
|
@ -136,7 +136,9 @@ func Releases(ctx *context.Context) {
|
|||
}
|
||||
|
||||
r.Note, err = markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
|
@ -287,7 +289,9 @@ func SingleRelease(ctx *context.Context) {
|
|||
}
|
||||
}
|
||||
release.Note, err = markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
|
|
|
@ -57,16 +57,15 @@ func RenderFile(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
treeLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
if ctx.Repo.TreePath != "" {
|
||||
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
}
|
||||
|
||||
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts")
|
||||
err = markup.Render(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
RelativePath: ctx.Repo.TreePath,
|
||||
URLPrefix: path.Dir(treeLink),
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
BranchPath: ctx.Repo.BranchNameSubURL(),
|
||||
TreePath: path.Dir(ctx.Repo.TreePath),
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
InStandalonePage: true,
|
||||
|
|
|
@ -158,7 +158,7 @@ func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry, try
|
|||
return "", readmeFile, nil
|
||||
}
|
||||
|
||||
func renderDirectory(ctx *context.Context, treeLink string) {
|
||||
func renderDirectory(ctx *context.Context) {
|
||||
entries := renderDirectoryFiles(ctx, 1*time.Second)
|
||||
if ctx.Written() {
|
||||
return
|
||||
|
@ -175,7 +175,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
|
|||
return
|
||||
}
|
||||
|
||||
renderReadmeFile(ctx, subfolder, readmeFile, treeLink)
|
||||
renderReadmeFile(ctx, subfolder, readmeFile)
|
||||
}
|
||||
|
||||
// localizedExtensions prepends the provided language code with and without a
|
||||
|
@ -259,7 +259,7 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte,
|
|||
return buf, dataRc, &fileInfo{st.IsText(), true, meta.Size, &meta.Pointer, st}, nil
|
||||
}
|
||||
|
||||
func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry, readmeTreelink string) {
|
||||
func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry) {
|
||||
target := readmeFile
|
||||
if readmeFile != nil && readmeFile.IsLink() {
|
||||
target, _ = readmeFile.FollowLinks()
|
||||
|
@ -312,7 +312,11 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr
|
|||
ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
RelativePath: path.Join(ctx.Repo.TreePath, readmeFile.Name()), // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path).
|
||||
URLPrefix: path.Join(readmeTreelink, subfolder),
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
BranchPath: ctx.Repo.BranchNameSubURL(),
|
||||
TreePath: path.Join(ctx.Repo.TreePath, subfolder),
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
}, rd)
|
||||
|
@ -337,7 +341,7 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr
|
|||
}
|
||||
}
|
||||
|
||||
func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink string) {
|
||||
func renderFile(ctx *context.Context, entry *git.TreeEntry) {
|
||||
ctx.Data["IsViewFile"] = true
|
||||
ctx.Data["HideRepoInfo"] = true
|
||||
blob := entry.Blob()
|
||||
|
@ -351,7 +355,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
|||
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
|
||||
ctx.Data["FileIsSymlink"] = entry.IsLink()
|
||||
ctx.Data["FileName"] = blob.Name()
|
||||
ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
|
||||
if ctx.Repo.TreePath == ".editorconfig" {
|
||||
_, editorconfigWarning, editorconfigErr := ctx.Repo.GetEditorconfig(ctx.Repo.Commit)
|
||||
|
@ -479,7 +483,11 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
|||
Ctx: ctx,
|
||||
Type: markupType,
|
||||
RelativePath: ctx.Repo.TreePath,
|
||||
URLPrefix: path.Dir(treeLink),
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
BranchPath: ctx.Repo.BranchNameSubURL(),
|
||||
TreePath: path.Dir(ctx.Repo.TreePath),
|
||||
},
|
||||
Metas: metas,
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
}, rd)
|
||||
|
@ -585,7 +593,11 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
|||
ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
RelativePath: ctx.Repo.TreePath,
|
||||
URLPrefix: path.Dir(treeLink),
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
BranchPath: ctx.Repo.BranchNameSubURL(),
|
||||
TreePath: path.Dir(ctx.Repo.TreePath),
|
||||
},
|
||||
Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx),
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
}, rd)
|
||||
|
@ -945,14 +957,6 @@ func renderCode(ctx *context.Context) {
|
|||
}
|
||||
ctx.Data["Title"] = title
|
||||
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
treeLink := branchLink
|
||||
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
|
||||
|
||||
if len(ctx.Repo.TreePath) > 0 {
|
||||
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
}
|
||||
|
||||
// Get Topics of this repo
|
||||
renderRepoTopics(ctx)
|
||||
if ctx.Written() {
|
||||
|
@ -977,9 +981,9 @@ func renderCode(ctx *context.Context) {
|
|||
}
|
||||
|
||||
if entry.IsDir() {
|
||||
renderDirectory(ctx, treeLink)
|
||||
renderDirectory(ctx)
|
||||
} else {
|
||||
renderFile(ctx, entry, treeLink, rawLink)
|
||||
renderFile(ctx, entry)
|
||||
}
|
||||
if ctx.Written() {
|
||||
return
|
||||
|
@ -1020,6 +1024,12 @@ func renderCode(ctx *context.Context) {
|
|||
}
|
||||
|
||||
ctx.Data["Paths"] = paths
|
||||
|
||||
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||
treeLink := branchLink
|
||||
if len(ctx.Repo.TreePath) > 0 {
|
||||
treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
|
||||
}
|
||||
ctx.Data["TreeLink"] = treeLink
|
||||
ctx.Data["TreeNames"] = treeNames
|
||||
ctx.Data["BranchLink"] = branchLink
|
||||
|
|
|
@ -239,8 +239,10 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
|||
|
||||
rctx := &markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx),
|
||||
Links: markup.Links{
|
||||
Base: ctx.Repo.RepoLink,
|
||||
},
|
||||
IsWiki: true,
|
||||
}
|
||||
buf := &strings.Builder{}
|
||||
|
|
|
@ -47,9 +47,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
|
|||
|
||||
if len(ctx.ContextUser.Description) != 0 {
|
||||
content, err := markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: ctx.Repo.RepoLink,
|
||||
Metas: map[string]string{"mode": "document"},
|
||||
GitRepo: ctx.Repo.GitRepo,
|
||||
Ctx: ctx,
|
||||
}, ctx.ContextUser.Description)
|
||||
if err != nil {
|
||||
|
|
|
@ -258,7 +258,9 @@ func Milestones(ctx *context.Context) {
|
|||
}
|
||||
|
||||
milestones[i].RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||
URLPrefix: milestones[i].Repo.Link(),
|
||||
Links: markup.Links{
|
||||
Base: milestones[i].Repo.Link(),
|
||||
},
|
||||
Metas: milestones[i].Repo.ComposeMetas(ctx),
|
||||
Ctx: ctx,
|
||||
}, milestones[i].Content)
|
||||
|
|
|
@ -7,6 +7,7 @@ package user
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
|
@ -233,17 +234,18 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
|
|||
if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
|
||||
log.Error("failed to GetBlobContent: %v", err)
|
||||
} else {
|
||||
// Give the URLPrefix to the markdown render for the full link of media element.
|
||||
if profileContent, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
GitRepo: profileGitRepo,
|
||||
Links: markup.Links{
|
||||
// Give the repo link to the markdown render for the full link of media element.
|
||||
// the media link usually be like /[user]/[repoName]/media/branch/[branchName],
|
||||
// Eg. /Tom/.profile/media/branch/main
|
||||
// The branch shown on the profile page is the default branch, this need to be in sync with doc, see:
|
||||
// https://docs.gitea.com/usage/profile-readme
|
||||
|
||||
prefix := profileDbRepo.Link() + "/src/branch/" + util.PathEscapeSegments(profileDbRepo.DefaultBranch)
|
||||
if profileContent, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
GitRepo: profileGitRepo,
|
||||
URLPrefix: prefix,
|
||||
Base: profileDbRepo.Link(),
|
||||
BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)),
|
||||
},
|
||||
Metas: map[string]string{"mode": "document"},
|
||||
}, bytes); err != nil {
|
||||
log.Error("failed to RenderString: %v", err)
|
||||
|
|
|
@ -221,7 +221,9 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
|||
// This is the body of the new issue or comment, not the mail body
|
||||
body, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: ctx.Issue.Repo.HTMLURL(),
|
||||
Links: markup.Links{
|
||||
Base: ctx.Issue.Repo.HTMLURL(),
|
||||
},
|
||||
Metas: ctx.Issue.Repo.ComposeMetas(ctx),
|
||||
}, ctx.Content)
|
||||
if err != nil {
|
||||
|
|
|
@ -58,7 +58,9 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo
|
|||
var err error
|
||||
rel.RenderedNote, err = markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: rel.Repo.Link(),
|
||||
Links: markup.Links{
|
||||
Base: rel.Repo.HTMLURL(),
|
||||
},
|
||||
Metas: rel.Repo.ComposeMetas(ctx),
|
||||
}, rel.Note)
|
||||
if err != nil {
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<button class="btn interact-fg gt-px-2" data-clipboard-text="{{.DefaultBranchBranch.DBBranch.Name}}">{{svg "octicon-copy" 14}}</button>
|
||||
{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DefaultBranchBranch.DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DefaultBranchBranch.DBBranch.CommitID)}}
|
||||
</div>
|
||||
<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage .RepoLink (.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}} {{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
|
||||
<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DefaultBranchBranch.DBBranch.Pusher}} {{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
|
||||
</td>
|
||||
<td class="right aligned middle aligned overflow-visible">
|
||||
{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
|
||||
|
@ -101,7 +101,7 @@
|
|||
<button class="btn interact-fg gt-px-2" data-clipboard-text="{{.DBBranch.Name}}">{{svg "octicon-copy" 14}}</button>
|
||||
{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DBBranch.CommitID)}}
|
||||
</div>
|
||||
<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DBBranch.CommitMessage $.RepoLink ($.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DBBranch.Pusher}} {{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} {{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
|
||||
<p class="info gt-df gt-ac gt-my-2">{{svg "octicon-git-commit" 16 "gt-mr-2"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DBBranch.CommitMessage ($.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated"}} {{TimeSince .DBBranch.CommitTime.AsTime ctx.Locale}}{{if .DBBranch.Pusher}} {{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} {{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
|
||||
{{end}}
|
||||
</td>
|
||||
<td class="two wide ui">
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
{{end}}
|
||||
<div class="ui top attached header clearing segment gt-relative commit-header {{$class}}">
|
||||
<div class="gt-df gt-mb-4 gt-fw">
|
||||
<h3 class="gt-mb-0 gt-f1"><span class="commit-summary" title="{{.Commit.Summary}}">{{RenderCommitMessage $.Context .Commit.Message $.RepoLink ($.Repository.ComposeMetas ctx)}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}</h3>
|
||||
<h3 class="gt-mb-0 gt-f1"><span class="commit-summary" title="{{.Commit.Summary}}">{{RenderCommitMessage $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}</span>{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}</h3>
|
||||
{{if not $.PageIsWiki}}
|
||||
<div>
|
||||
<a class="ui primary tiny button" href="{{.SourcePath}}">
|
||||
|
@ -135,7 +135,7 @@
|
|||
{{end}}
|
||||
</div>
|
||||
{{if IsMultilineCommitMessage .Commit.Message}}
|
||||
<pre class="commit-body">{{RenderCommitBody $.Context .Commit.Message $.RepoLink ($.Repository.ComposeMetas ctx)}}</pre>
|
||||
<pre class="commit-body">{{RenderCommitBody $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}</pre>
|
||||
{{end}}
|
||||
{{template "repo/commit_load_branches_and_tags" .}}
|
||||
</div>
|
||||
|
@ -258,7 +258,7 @@
|
|||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Note}}
|
||||
{{if .NoteRendered}}
|
||||
<div class="ui top attached header segment git-notes">
|
||||
{{svg "octicon-note" 16 "gt-mr-3"}}
|
||||
{{ctx.Locale.Tr "repo.diff.git-notes"}}:
|
||||
|
@ -276,7 +276,7 @@
|
|||
<span class="text grey" id="note-authored-time">{{TimeSince .NoteCommit.Author.When ctx.Locale}}</span>
|
||||
</div>
|
||||
<div class="ui bottom attached info segment git-notes">
|
||||
<pre class="commit-body">{{RenderNote $.Context .Note $.RepoLink ($.Repository.ComposeMetas ctx)}}</pre>
|
||||
<pre class="commit-body">{{.NoteRendered | Str2html}}</pre>
|
||||
</div>
|
||||
{{end}}
|
||||
{{template "repo/diff/box" .}}
|
||||
|
|
|
@ -60,7 +60,7 @@
|
|||
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{.Summary | RenderEmoji $.Context}}</span>
|
||||
{{else}}
|
||||
{{$commitLink:= printf "%s/commit/%s" $commitRepoLink (PathEscape .ID.String)}}
|
||||
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.Context .Message $commitRepoLink $commitLink ($.Repository.ComposeMetas ctx)}}</span>
|
||||
<span class="commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.Context .Message $commitLink ($.Repository.ComposeMetas ctx)}}</span>
|
||||
{{end}}
|
||||
</span>
|
||||
{{if IsMultilineCommitMessage .Message}}
|
||||
|
@ -68,7 +68,7 @@
|
|||
{{end}}
|
||||
{{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}}
|
||||
{{if IsMultilineCommitMessage .Message}}
|
||||
<pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .Message $commitRepoLink ($.Repository.ComposeMetas ctx)}}</pre>
|
||||
<pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .Message ($.Repository.ComposeMetas ctx)}}</pre>
|
||||
{{end}}
|
||||
</td>
|
||||
{{if .Committer}}
|
||||
|
|
|
@ -38,12 +38,12 @@
|
|||
</a>
|
||||
</span>
|
||||
|
||||
<span class="gt-mono commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}</span>
|
||||
<span class="gt-mono commit-summary {{if gt .ParentCount 1}} grey text{{end}}" title="{{.Summary}}">{{RenderCommitMessageLinkSubject $.root.Context .Message $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}</span>
|
||||
{{if IsMultilineCommitMessage .Message}}
|
||||
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
|
||||
{{end}}
|
||||
{{if IsMultilineCommitMessage .Message}}
|
||||
<pre class="commit-body gt-hidden">{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.Link|Escape) ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}</pre>
|
||||
<pre class="commit-body gt-hidden">{{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}</pre>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
@ -194,7 +194,7 @@
|
|||
<div class="twelve wide column issue-title">
|
||||
{{ctx.Locale.Tr "repo.pulls.has_pull_request" (print (Escape $.RepoLink) "/pulls/" .PullRequest.Issue.Index) (Escape $.RepoRelPath) .PullRequest.Index | Safe}}
|
||||
<h1>
|
||||
<span id="issue-title">{{RenderIssueTitle $.Context .PullRequest.Issue.Title $.RepoLink ($.Repository.ComposeMetas ctx)}}</span>
|
||||
<span id="issue-title">{{RenderIssueTitle $.Context .PullRequest.Issue.Title ($.Repository.ComposeMetas ctx)}}</span>
|
||||
<span class="index">#{{.PullRequest.Issue.Index}}</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
</a>
|
||||
</span>
|
||||
<span class="message gt-dib gt-ellipsis gt-mr-3">
|
||||
<span>{{RenderCommitMessage $.Context $commit.Subject $.RepoLink ($.Repository.ComposeMetas ctx)}}</span>
|
||||
<span>{{RenderCommitMessage $.Context $commit.Subject ($.Repository.ComposeMetas ctx)}}</span>
|
||||
</span>
|
||||
<span class="commit-refs gt-df gt-ac gt-mr-2">
|
||||
{{range $commit.Refs}}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<div class="issue-title-header">
|
||||
<div class="issue-title" id="issue-title-wrapper">
|
||||
<h1 class="gt-word-break">
|
||||
<span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title $.RepoLink ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} <span class="index">#{{.Issue.Index}}</span>
|
||||
<span id="issue-title">{{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} <span class="index">#{{.Issue.Index}}</span>
|
||||
</span>
|
||||
<div id="edit-title-input" class="ui input gt-f1 gt-hidden">
|
||||
<input value="{{.Issue.Title}}" maxlength="255" autocomplete="off">
|
||||
|
|
|
@ -26,10 +26,10 @@
|
|||
</a>
|
||||
{{template "repo/commit_statuses" dict "Status" .LatestCommitStatus "Statuses" .LatestCommitStatuses}}
|
||||
{{$commitLink:= printf "%s/commit/%s" .RepoLink (PathEscape .LatestCommit.ID.String)}}
|
||||
<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $.RepoLink $commitLink ($.Repository.ComposeMetas ctx)}}</span>
|
||||
<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject $.Context .LatestCommit.Message $commitLink ($.Repository.ComposeMetas ctx)}}</span>
|
||||
{{if IsMultilineCommitMessage .LatestCommit.Message}}
|
||||
<button class="ui button js-toggle-commit-body ellipsis-button" aria-expanded="false">...</button>
|
||||
<pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .LatestCommit.Message $.RepoLink ($.Repository.ComposeMetas ctx)}}</pre>
|
||||
<pre class="commit-body gt-hidden">{{RenderCommitBody $.Context .LatestCommit.Message ($.Repository.ComposeMetas ctx)}}</pre>
|
||||
{{end}}
|
||||
</span>
|
||||
{{end}}
|
||||
|
@ -83,7 +83,7 @@
|
|||
<span class="truncate">
|
||||
{{if $commit}}
|
||||
{{$commitLink := printf "%s/commit/%s" $.RepoLink (PathEscape $commit.ID.String)}}
|
||||
{{RenderCommitMessageLinkSubject $.Context $commit.Message $.RepoLink $commitLink ($.Repository.ComposeMetas ctx)}}
|
||||
{{RenderCommitMessageLinkSubject $.Context $commit.Message $commitLink ($.Repository.ComposeMetas ctx)}}
|
||||
{{else}}
|
||||
<div class="ui active tiny slow centered inline">…</div>
|
||||
{{end}}
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<img class="ui avatar" src="{{$push.AvatarLink $.Context .AuthorEmail}}" title="{{.AuthorName}}" width="16" height="16">
|
||||
<a class="ui sha label" href="{{$commitLink}}">{{ShortSha .Sha1}}</a>
|
||||
<span class="text truncate">
|
||||
{{RenderCommitMessage $.Context .Message $repoLink ($repo.ComposeMetas ctx)}}
|
||||
{{RenderCommitMessage $.Context .Message ($repo.ComposeMetas ctx)}}
|
||||
</span>
|
||||
</div>
|
||||
{{end}}
|
||||
|
|
|
@ -16,7 +16,9 @@ import (
|
|||
|
||||
var renderContext = markup.RenderContext{
|
||||
Ctx: context.Background(),
|
||||
URLPrefix: "https://example.com/go-gitea/gitea",
|
||||
Links: markup.Links{
|
||||
Base: "https://example.com/go-gitea/gitea",
|
||||
},
|
||||
Metas: map[string]string{
|
||||
"user": "go-gitea",
|
||||
"repo": "gitea",
|
||||
|
|
Loading…
Reference in a new issue