Use relative links for commits, mentions, and issues in markdown (#29427)
Fixes #29404 Use relative links for - commits - mentions - issues --------- Co-authored-by: silverwind <me@silverwind.io> (cherry picked from commit 85c59d6c21e10ef9d3ccf11713548f50e47e920f)
This commit is contained in:
		
							parent
							
								
									e2abfdc3a4
								
							
						
					
					
						commit
						024bfb7f34
					
				
					 9 changed files with 65 additions and 26 deletions
				
			
		| 
						 | 
				
			
			@ -609,7 +609,7 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
 | 
			
		|||
		if ok && strings.Contains(mention, "/") {
 | 
			
		||||
			mentionOrgAndTeam := strings.Split(mention, "/")
 | 
			
		||||
			if mentionOrgAndTeam[0][1:] == ctx.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") {
 | 
			
		||||
				replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "mention"))
 | 
			
		||||
				replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "mention"))
 | 
			
		||||
				node = node.NextSibling.NextSibling
 | 
			
		||||
				start = 0
 | 
			
		||||
				continue
 | 
			
		||||
| 
						 | 
				
			
			@ -620,7 +620,7 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
 | 
			
		|||
		mentionedUsername := mention[1:]
 | 
			
		||||
 | 
			
		||||
		if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
 | 
			
		||||
			replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mentionedUsername), mention, "mention"))
 | 
			
		||||
			replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "mention"))
 | 
			
		||||
			node = node.NextSibling.NextSibling
 | 
			
		||||
		} else {
 | 
			
		||||
			node = node.NextSibling
 | 
			
		||||
| 
						 | 
				
			
			@ -898,9 +898,9 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
			
		|||
				path = "pulls"
 | 
			
		||||
			}
 | 
			
		||||
			if ref.Owner == "" {
 | 
			
		||||
				link = createLink(util.URLJoin(setting.AppURL, ctx.Metas["user"], ctx.Metas["repo"], path, ref.Issue), reftext, "ref-issue")
 | 
			
		||||
				link = createLink(util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], path, ref.Issue), reftext, "ref-issue")
 | 
			
		||||
			} else {
 | 
			
		||||
				link = createLink(util.URLJoin(setting.AppURL, ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue")
 | 
			
		||||
				link = createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, path, ref.Issue), reftext, "ref-issue")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -939,7 +939,7 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
 | 
			
		||||
		link := createLink(util.URLJoin(setting.AppSubURL, ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit")
 | 
			
		||||
		link := createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit")
 | 
			
		||||
 | 
			
		||||
		replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
 | 
			
		||||
		node = node.NextSibling.NextSibling
 | 
			
		||||
| 
						 | 
				
			
			@ -1166,7 +1166,7 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
			
		|||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		link := util.URLJoin(setting.AppURL, ctx.Metas["user"], ctx.Metas["repo"], "commit", hash)
 | 
			
		||||
		link := util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], "commit", hash)
 | 
			
		||||
		replaceContent(node, m[2], m[3], createCodeLink(link, base.ShortSha(hash), "commit"))
 | 
			
		||||
		start = 0
 | 
			
		||||
		node = node.NextSibling.NextSibling
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -287,6 +287,7 @@ func TestRender_IssueIndexPattern_Document(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) {
 | 
			
		||||
	ctx.Links.AbsolutePrefix = true
 | 
			
		||||
	if ctx.Links.Base == "" {
 | 
			
		||||
		ctx.Links.Base = TestRepoURL
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,7 +43,8 @@ func TestRender_Commits(t *testing.T) {
 | 
			
		|||
			Ctx:          git.DefaultContext,
 | 
			
		||||
			RelativePath: ".md",
 | 
			
		||||
			Links: markup.Links{
 | 
			
		||||
				Base: markup.TestRepoURL,
 | 
			
		||||
				AbsolutePrefix: true,
 | 
			
		||||
				Base:           markup.TestRepoURL,
 | 
			
		||||
			},
 | 
			
		||||
			Metas: localMetas,
 | 
			
		||||
		}, input)
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +97,8 @@ func TestRender_CrossReferences(t *testing.T) {
 | 
			
		|||
			Ctx:          git.DefaultContext,
 | 
			
		||||
			RelativePath: "a.md",
 | 
			
		||||
			Links: markup.Links{
 | 
			
		||||
				Base: setting.AppSubURL,
 | 
			
		||||
				AbsolutePrefix: true,
 | 
			
		||||
				Base:           setting.AppSubURL,
 | 
			
		||||
			},
 | 
			
		||||
			Metas: localMetas,
 | 
			
		||||
		}, input)
 | 
			
		||||
| 
						 | 
				
			
			@ -579,7 +581,8 @@ func TestPostProcess_RenderDocument(t *testing.T) {
 | 
			
		|||
		err := markup.PostProcess(&markup.RenderContext{
 | 
			
		||||
			Ctx: git.DefaultContext,
 | 
			
		||||
			Links: markup.Links{
 | 
			
		||||
				Base: "https://example.com",
 | 
			
		||||
				AbsolutePrefix: true,
 | 
			
		||||
				Base:           "https://example.com",
 | 
			
		||||
			},
 | 
			
		||||
			Metas: localMetas,
 | 
			
		||||
		}, strings.NewReader(input), &res)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -131,11 +131,11 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
 | 
			
		|||
<li><a href="` + baseURLContent + `/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
 | 
			
		||||
<li><a href="` + baseURLContent + `/Tips" rel="nofollow">Tips</a></li>
 | 
			
		||||
</ul>
 | 
			
		||||
<p>See commit <a href="http://localhost:3000/gogits/gogs/commit/65f1bf27bc" rel="nofollow"><code>65f1bf27bc</code></a></p>
 | 
			
		||||
<p>See commit <a href="/gogits/gogs/commit/65f1bf27bc" rel="nofollow"><code>65f1bf27bc</code></a></p>
 | 
			
		||||
<p>Ideas and codes</p>
 | 
			
		||||
<ul>
 | 
			
		||||
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" class="ref-issue" rel="nofollow">ocornut/imgui#786</a></li>
 | 
			
		||||
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/gogits/gogs/issues/786" class="ref-issue" rel="nofollow">#786</a></li>
 | 
			
		||||
<li>Bezier widget (by <a href="/r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" class="ref-issue" rel="nofollow">ocornut/imgui#786</a></li>
 | 
			
		||||
<li>Bezier widget (by <a href="/r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/gogits/gogs/issues/786" class="ref-issue" rel="nofollow">#786</a></li>
 | 
			
		||||
<li>Node graph editors <a href="https://github.com/ocornut/imgui/issues/306" rel="nofollow">https://github.com/ocornut/imgui/issues/306</a></li>
 | 
			
		||||
<li><a href="` + baseURLContent + `/memory_editor_example" rel="nofollow">Memory Editor</a></li>
 | 
			
		||||
<li><a href="` + baseURLContent + `/plot_var_example" rel="nofollow">Plot var helper</a></li>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,9 +82,17 @@ type RenderContext struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
type Links struct {
 | 
			
		||||
	Base       string
 | 
			
		||||
	BranchPath string
 | 
			
		||||
	TreePath   string
 | 
			
		||||
	AbsolutePrefix bool
 | 
			
		||||
	Base           string
 | 
			
		||||
	BranchPath     string
 | 
			
		||||
	TreePath       string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *Links) Prefix() string {
 | 
			
		||||
	if l.AbsolutePrefix {
 | 
			
		||||
		return setting.AppURL
 | 
			
		||||
	}
 | 
			
		||||
	return setting.AppSubURL
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *Links) HasBranchInfo() bool {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -122,21 +122,21 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a582
 | 
			
		|||
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>
 | 
			
		||||
<a href="/mention-user" class="mention">@mention-user</a> test
 | 
			
		||||
<a href="/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>  `
 | 
			
		||||
	expected := `space <a href="/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>`
 | 
			
		||||
	expected := `<a href="https://example.com/link" class="default-link muted">space </a><a href="/mention-user" class="mention">@mention-user</a>`
 | 
			
		||||
 | 
			
		||||
	assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput, "https://example.com/link", testMetas))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -160,14 +160,14 @@ 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>
 | 
			
		||||
<a href="/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/>
 | 
			
		||||
	expected := `<p>space <a href="/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>
 | 
			
		||||
| 
						 | 
				
			
			@ -184,7 +184,7 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb...12fc37a3c0a4dda553bdcfc80c178a582
 | 
			
		|||
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
 | 
			
		||||
<a href="/mention-user" rel="nofollow">@mention-user</a> test
 | 
			
		||||
#123
 | 
			
		||||
space</p>
 | 
			
		||||
`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,8 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr
 | 
			
		|||
		if err := markdown.RenderRaw(&markup.RenderContext{
 | 
			
		||||
			Ctx: ctx,
 | 
			
		||||
			Links: markup.Links{
 | 
			
		||||
				Base: urlPrefix,
 | 
			
		||||
				AbsolutePrefix: true,
 | 
			
		||||
				Base:           urlPrefix,
 | 
			
		||||
			},
 | 
			
		||||
		}, strings.NewReader(text), ctx.Resp); err != nil {
 | 
			
		||||
			ctx.Error(http.StatusInternalServerError, err.Error())
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +80,8 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPr
 | 
			
		|||
	if err := markup.Render(&markup.RenderContext{
 | 
			
		||||
		Ctx: ctx,
 | 
			
		||||
		Links: markup.Links{
 | 
			
		||||
			Base: urlPrefix,
 | 
			
		||||
			AbsolutePrefix: true,
 | 
			
		||||
			Base:           urlPrefix,
 | 
			
		||||
		},
 | 
			
		||||
		Metas:        meta,
 | 
			
		||||
		IsWiki:       wiki,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -222,7 +222,8 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
 | 
			
		|||
	body, err := markdown.RenderString(&markup.RenderContext{
 | 
			
		||||
		Ctx: ctx,
 | 
			
		||||
		Links: markup.Links{
 | 
			
		||||
			Base: ctx.Issue.Repo.HTMLURL(),
 | 
			
		||||
			AbsolutePrefix: true,
 | 
			
		||||
			Base:           ctx.Issue.Repo.HTMLURL(),
 | 
			
		||||
		},
 | 
			
		||||
		Metas: ctx.Issue.Repo.ComposeMetas(ctx),
 | 
			
		||||
	}, ctx.Content)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,8 @@ import (
 | 
			
		|||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"html/template"
 | 
			
		||||
	"io"
 | 
			
		||||
	"mime/quotedprintable"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
| 
						 | 
				
			
			@ -19,6 +21,7 @@ import (
 | 
			
		|||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
			
		||||
	"code.gitea.io/gitea/models/unittest"
 | 
			
		||||
	user_model "code.gitea.io/gitea/models/user"
 | 
			
		||||
	"code.gitea.io/gitea/modules/markup"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +70,12 @@ func prepareMailerTest(t *testing.T) (doer *user_model.User, repo *repo_model.Re
 | 
			
		|||
func TestComposeIssueCommentMessage(t *testing.T) {
 | 
			
		||||
	doer, _, issue, comment := prepareMailerTest(t)
 | 
			
		||||
 | 
			
		||||
	markup.Init(&markup.ProcessorHelper{
 | 
			
		||||
		IsUsernameMentionable: func(ctx context.Context, username string) bool {
 | 
			
		||||
			return username == doer.Name
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	setting.IncomingEmail.Enabled = true
 | 
			
		||||
	defer func() { setting.IncomingEmail.Enabled = false }()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +86,8 @@ func TestComposeIssueCommentMessage(t *testing.T) {
 | 
			
		|||
	msgs, err := composeIssueCommentMessages(&mailCommentContext{
 | 
			
		||||
		Context: context.TODO(), // TODO: use a correct context
 | 
			
		||||
		Issue:   issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
 | 
			
		||||
		Content: "test body", Comment: comment,
 | 
			
		||||
		Content: fmt.Sprintf("test @%s %s#%d body", doer.Name, issue.Repo.FullName(), issue.Index),
 | 
			
		||||
		Comment: comment,
 | 
			
		||||
	}, "en-US", recipients, false, "issue comment")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Len(t, msgs, 2)
 | 
			
		||||
| 
						 | 
				
			
			@ -96,6 +106,20 @@ func TestComposeIssueCommentMessage(t *testing.T) {
 | 
			
		|||
	assert.Equal(t, "<user2/repo1/issues/1/comment/2@localhost>", gomailMsg.GetHeader("Message-ID")[0], "Message-ID header doesn't match")
 | 
			
		||||
	assert.Equal(t, "<mailto:"+replyTo+">", gomailMsg.GetHeader("List-Post")[0])
 | 
			
		||||
	assert.Len(t, gomailMsg.GetHeader("List-Unsubscribe"), 2) // url + mailto
 | 
			
		||||
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	gomailMsg.WriteTo(&buf)
 | 
			
		||||
 | 
			
		||||
	b, err := io.ReadAll(quotedprintable.NewReader(&buf))
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	// text/plain
 | 
			
		||||
	assert.Contains(t, string(b), fmt.Sprintf(`( %s )`, doer.HTMLURL()))
 | 
			
		||||
	assert.Contains(t, string(b), fmt.Sprintf(`( %s )`, issue.HTMLURL()))
 | 
			
		||||
 | 
			
		||||
	// text/html
 | 
			
		||||
	assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, doer.HTMLURL()))
 | 
			
		||||
	assert.Contains(t, string(b), fmt.Sprintf(`href="%s"`, issue.HTMLURL()))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestComposeIssueMessage(t *testing.T) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue