Skip to content

Commit 3ff0a12

Browse files
mrsdizzielafriks
authored andcommitted
Improve issue autolinks (#6273)
* Improve issue autolinks Update autolinks to match what github does here: Issue in same repo: #1 Issue in different repo: org/repo#1 Fixes #6264 * Use setting.AppURL when parsing URL Using setting.AppURL here is a more reliable way of parsing the current URL and what other functions in this file seem to use. * Make ComposeMetas always return a valid context * Add per repository markdown renderers for better context * Update for use of context metas Now that we include the user and repo name inside context metas, update various code and tests for this new logic
1 parent 3186ef5 commit 3ff0a12

File tree

13 files changed

+94
-37
lines changed

13 files changed

+94
-37
lines changed

models/repo.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -469,19 +469,19 @@ func (repo *Repository) mustOwnerName(e Engine) string {
469469
return repo.OwnerName
470470
}
471471

472-
// ComposeMetas composes a map of metas for rendering external issue tracker URL.
472+
// ComposeMetas composes a map of metas for properly rendering issue links and external issue trackers.
473473
func (repo *Repository) ComposeMetas() map[string]string {
474-
unit, err := repo.GetUnit(UnitTypeExternalTracker)
475-
if err != nil {
476-
return nil
477-
}
478-
479474
if repo.ExternalMetas == nil {
480475
repo.ExternalMetas = map[string]string{
481-
"format": unit.ExternalTrackerConfig().ExternalTrackerFormat,
482-
"user": repo.MustOwner().Name,
483-
"repo": repo.Name,
476+
"user": repo.MustOwner().Name,
477+
"repo": repo.Name,
484478
}
479+
unit, err := repo.GetUnit(UnitTypeExternalTracker)
480+
if err != nil {
481+
return repo.ExternalMetas
482+
}
483+
484+
repo.ExternalMetas["format"] = unit.ExternalTrackerConfig().ExternalTrackerFormat
485485
switch unit.ExternalTrackerConfig().ExternalTrackerStyle {
486486
case markup.IssueNameStyleAlphanumeric:
487487
repo.ExternalMetas["style"] = markup.IssueNameStyleAlphanumeric

models/repo_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ func TestRepo(t *testing.T) {
2020
repo.Owner = &User{Name: "testOwner"}
2121

2222
repo.Units = nil
23-
assert.Nil(t, repo.ComposeMetas())
23+
24+
metas := repo.ComposeMetas()
25+
assert.Equal(t, "testRepo", metas["repo"])
26+
assert.Equal(t, "testOwner", metas["user"])
2427

2528
externalTracker := RepoUnit{
2629
Type: UnitTypeExternalTracker,

modules/markup/html.go

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -551,20 +551,37 @@ func shortLinkProcessorFull(ctx *postProcessCtx, node *html.Node, noLink bool) {
551551
}
552552

553553
func fullIssuePatternProcessor(ctx *postProcessCtx, node *html.Node) {
554+
if ctx.metas == nil {
555+
return
556+
}
554557
m := getIssueFullPattern().FindStringSubmatchIndex(node.Data)
555558
if m == nil {
556559
return
557560
}
558561
link := node.Data[m[0]:m[1]]
559562
id := "#" + node.Data[m[2]:m[3]]
560-
// TODO if m[4]:m[5] is not nil, then link is to a comment,
561-
// and we should indicate that in the text somehow
562-
replaceContent(node, m[0], m[1], createLink(link, id))
563+
564+
// extract repo and org name from matched link like
565+
// http://localhost:3000/gituser/myrepo/issues/1
566+
linkParts := strings.Split(path.Clean(link), "/")
567+
matchOrg := linkParts[len(linkParts)-4]
568+
matchRepo := linkParts[len(linkParts)-3]
569+
570+
if matchOrg == ctx.metas["user"] && matchRepo == ctx.metas["repo"] {
571+
// TODO if m[4]:m[5] is not nil, then link is to a comment,
572+
// and we should indicate that in the text somehow
573+
replaceContent(node, m[0], m[1], createLink(link, id))
574+
575+
} else {
576+
orgRepoID := matchOrg + "/" + matchRepo + id
577+
replaceContent(node, m[0], m[1], createLink(link, orgRepoID))
578+
}
563579
}
564580

565581
func issueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
566-
prefix := cutoutVerbosePrefix(ctx.urlPrefix)
567-
582+
if ctx.metas == nil {
583+
return
584+
}
568585
// default to numeric pattern, unless alphanumeric is requested.
569586
pattern := issueNumericPattern
570587
if ctx.metas["style"] == IssueNameStyleAlphanumeric {
@@ -575,18 +592,19 @@ func issueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
575592
if match == nil {
576593
return
577594
}
595+
578596
id := node.Data[match[2]:match[3]]
579597
var link *html.Node
580-
if ctx.metas == nil {
581-
link = createLink(util.URLJoin(prefix, "issues", id[1:]), id)
582-
} else {
598+
if _, ok := ctx.metas["format"]; ok {
583599
// Support for external issue tracker
584600
if ctx.metas["style"] == IssueNameStyleAlphanumeric {
585601
ctx.metas["index"] = id
586602
} else {
587603
ctx.metas["index"] = id[1:]
588604
}
589605
link = createLink(com.Expand(ctx.metas["format"], ctx.metas), id)
606+
} else {
607+
link = createLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], "issues", id[1:]), id)
590608
}
591609
replaceContent(node, match[2], match[3], link)
592610
}

modules/markup/html_internal_test.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ var alphanumericMetas = map[string]string{
5353
"style": IssueNameStyleAlphanumeric,
5454
}
5555

56+
// these values should match the Repo const above
57+
var localMetas = map[string]string{
58+
"user": "gogits",
59+
"repo": "gogs",
60+
}
61+
5662
func TestRender_IssueIndexPattern(t *testing.T) {
5763
// numeric: render inputs without valid mentions
5864
test := func(s string) {
@@ -91,7 +97,7 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
9197
links[i] = numericIssueLink(util.URLJoin(setting.AppSubURL, "issues"), index)
9298
}
9399
expectedNil := fmt.Sprintf(expectedFmt, links...)
94-
testRenderIssueIndexPattern(t, s, expectedNil, nil)
100+
testRenderIssueIndexPattern(t, s, expectedNil, &postProcessCtx{metas: localMetas})
95101

96102
for i, index := range indices {
97103
links[i] = numericIssueLink("https://someurl.com/someUser/someRepo/", index)
@@ -171,6 +177,7 @@ func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *post
171177
if ctx.urlPrefix == "" {
172178
ctx.urlPrefix = AppSubURL
173179
}
180+
174181
res, err := ctx.postProcess([]byte(input))
175182
assert.NoError(t, err)
176183
assert.Equal(t, expected, string(res))
@@ -181,10 +188,10 @@ func TestRender_AutoLink(t *testing.T) {
181188
setting.AppSubURL = AppSubURL
182189

183190
test := func(input, expected string) {
184-
buffer, err := PostProcess([]byte(input), setting.AppSubURL, nil, false)
191+
buffer, err := PostProcess([]byte(input), setting.AppSubURL, localMetas, false)
185192
assert.Equal(t, err, nil)
186193
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
187-
buffer, err = PostProcess([]byte(input), setting.AppSubURL, nil, true)
194+
buffer, err = PostProcess([]byte(input), setting.AppSubURL, localMetas, true)
188195
assert.Equal(t, err, nil)
189196
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
190197
}
@@ -214,16 +221,19 @@ func TestRender_FullIssueURLs(t *testing.T) {
214221
if ctx.urlPrefix == "" {
215222
ctx.urlPrefix = AppSubURL
216223
}
224+
ctx.metas = localMetas
217225
result, err := ctx.postProcess([]byte(input))
218226
assert.NoError(t, err)
219227
assert.Equal(t, expected, string(result))
220228
}
221229
test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6",
222230
"Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6")
223231
test("Look here http://localhost:3000/person/repo/issues/4",
224-
`Look here <a href="http://localhost:3000/person/repo/issues/4">#4</a>`)
232+
`Look here <a href="http://localhost:3000/person/repo/issues/4">person/repo#4</a>`)
225233
test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
226-
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234">#4</a>`)
234+
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234">person/repo#4</a>`)
235+
test("http://localhost:3000/gogits/gogs/issues/4",
236+
`<a href="http://localhost:3000/gogits/gogs/issues/4">#4</a>`)
227237
}
228238

229239
func TestRegExp_issueNumericPattern(t *testing.T) {

modules/markup/markdown/markdown_test.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ const AppURL = "http://localhost:3000/"
1919
const Repo = "gogits/gogs"
2020
const AppSubURL = AppURL + Repo + "/"
2121

22+
// these values should match the Repo const above
23+
var localMetas = map[string]string{
24+
"user": "gogits",
25+
"repo": "gogs",
26+
}
27+
2228
func TestRender_StandardLinks(t *testing.T) {
2329
setting.AppURL = AppURL
2430
setting.AppSubURL = AppSubURL
@@ -100,7 +106,8 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
100106
<p>Ideas and codes</p>
101107
102108
<ul>
103-
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" rel="nofollow">#786</a></li>
109+
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/ocornut/imgui/issues/786" rel="nofollow">ocornut/imgui#786</a></li>
110+
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) <a href="http://localhost:3000/gogits/gogs/issues/786" rel="nofollow">#786</a></li>
104111
<li>Node graph editors <a href="https://github.com/ocornut/imgui/issues/306" rel="nofollow">https://github.com/ocornut/imgui/issues/306</a></li>
105112
<li><a href="` + baseURLContent + `/memory_editor_example" rel="nofollow">Memory Editor</a></li>
106113
<li><a href="` + baseURLContent + `/plot_var_example" rel="nofollow">Plot var helper</a></li>
@@ -188,6 +195,7 @@ var sameCases = []string{
188195
Ideas and codes
189196
190197
- Bezier widget (by @r-lyeh) ` + AppURL + `ocornut/imgui/issues/786
198+
- Bezier widget (by @r-lyeh) ` + AppURL + `gogits/gogs/issues/786
191199
- Node graph editors https://github.com/ocornut/imgui/issues/306
192200
- [[Memory Editor|memory_editor_example]]
193201
- [[Plot var helper|plot_var_example]]`,
@@ -243,7 +251,7 @@ func TestTotal_RenderWiki(t *testing.T) {
243251
answers := testAnswers(util.URLJoin(AppSubURL, "wiki/"), util.URLJoin(AppSubURL, "wiki", "raw/"))
244252

245253
for i := 0; i < len(sameCases); i++ {
246-
line := RenderWiki([]byte(sameCases[i]), AppSubURL, nil)
254+
line := RenderWiki([]byte(sameCases[i]), AppSubURL, localMetas)
247255
assert.Equal(t, answers[i], line)
248256
}
249257

@@ -270,7 +278,7 @@ func TestTotal_RenderString(t *testing.T) {
270278
answers := testAnswers(util.URLJoin(AppSubURL, "src", "master/"), util.URLJoin(AppSubURL, "raw", "master/"))
271279

272280
for i := 0; i < len(sameCases); i++ {
273-
line := RenderString(sameCases[i], util.URLJoin(AppSubURL, "src", "master/"), nil)
281+
line := RenderString(sameCases[i], util.URLJoin(AppSubURL, "src", "master/"), localMetas)
274282
assert.Equal(t, answers[i], line)
275283
}
276284

routers/api/v1/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,8 @@ func RegisterRoutes(m *macaron.Macaron) {
584584
Patch(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.EditLabelOption{}), repo.EditLabel).
585585
Delete(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), repo.DeleteLabel)
586586
})
587+
m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown)
588+
m.Post("/markdown/raw", misc.MarkdownRaw)
587589
m.Group("/milestones", func() {
588590
m.Combo("").Get(repo.ListMilestones).
589591
Post(reqToken(), reqRepoWriter(models.UnitTypeIssues, models.UnitTypePullRequests), bind(api.CreateMilestoneOption{}), repo.CreateMilestone)

routers/api/v1/misc/markdown.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@
55
package misc
66

77
import (
8+
"strings"
9+
810
api "code.gitea.io/sdk/gitea"
911

1012
"code.gitea.io/gitea/modules/context"
1113
"code.gitea.io/gitea/modules/markup/markdown"
1214
"code.gitea.io/gitea/modules/setting"
1315
"code.gitea.io/gitea/modules/util"
16+
17+
"mvdan.cc/xurls/v2"
1418
)
1519

1620
// Markdown render markdown document to HTML
@@ -45,11 +49,23 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) {
4549
switch form.Mode {
4650
case "gfm":
4751
md := []byte(form.Text)
48-
context := util.URLJoin(setting.AppURL, form.Context)
52+
urlPrefix := form.Context
53+
var meta map[string]string
54+
if !strings.HasPrefix(setting.AppSubURL+"/", urlPrefix) {
55+
// check if urlPrefix is already set to a URL
56+
linkRegex, _ := xurls.StrictMatchingScheme("https?://")
57+
m := linkRegex.FindStringIndex(urlPrefix)
58+
if m == nil {
59+
urlPrefix = util.URLJoin(setting.AppURL, form.Context)
60+
}
61+
}
62+
if ctx.Repo != nil && ctx.Repo.Repository != nil {
63+
meta = ctx.Repo.Repository.ComposeMetas()
64+
}
4965
if form.Wiki {
50-
ctx.Write([]byte(markdown.RenderWiki(md, context, nil)))
66+
ctx.Write([]byte(markdown.RenderWiki(md, urlPrefix, meta)))
5167
} else {
52-
ctx.Write(markdown.Render(md, context, nil))
68+
ctx.Write(markdown.Render(md, urlPrefix, meta))
5369
}
5470
default:
5571
ctx.Write(markdown.RenderRaw([]byte(form.Text), "", false))

templates/repo/diff/box.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@
202202
<div class="ui comment form">
203203
<div class="ui top attached tabular menu">
204204
<a class="active write item">{{$.i18n.Tr "write"}}</a>
205-
<a class="preview item" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
205+
<a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
206206
</div>
207207
<div class="ui bottom attached active write tab segment">
208208
<textarea tabindex="1" name="content"></textarea>

templates/repo/diff/comment_form.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<input type="hidden" name="diff_base_cid">
1313
<div class="ui top attached tabular menu" {{if not $.hidden}}onload="assingMenuAttributes(this)" {{end}}data-write="write" data-preview="preview">
1414
<a class="active item" data-tab="write">{{$.root.i18n.Tr "write"}}</a>
15-
<a class="item" data-tab="preview" data-url="{{$.root.AppSubUrl}}/api/v1/markdown" data-context="{{$.root.RepoLink}}">{{$.root.i18n.Tr "preview"}}</a>
15+
<a class="item" data-tab="preview" data-url="{{$.root.Repository.APIURL}}/markdown" data-context="{{$.root.RepoLink}}">{{$.root.i18n.Tr "preview"}}</a>
1616
</div>
1717
<div class="ui bottom attached active tab segment" data-tab="write">
1818
<div class="field">

templates/repo/editor/edit.tmpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@
3030
<div class="ui top attached tabular menu" data-write="write" data-preview="preview" data-diff="diff">
3131
<a class="active item" data-tab="write"><i class="octicon octicon-code"></i> {{if .IsNewFile}}{{.i18n.Tr "repo.editor.new_file"}}{{else}}{{.i18n.Tr "repo.editor.edit_file"}}{{end}}</a>
3232
{{if not .IsNewFile}}
33-
<a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL | EscapePound}}" data-preview-file-modes="{{.PreviewableFileModes}}"><i class="octicon octicon-eye"></i> {{.i18n.Tr "preview"}}</a>
33+
<a class="item" data-tab="preview" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}/src/{{.BranchNameSubURL | EscapePound}}" data-preview-file-modes="{{.PreviewableFileModes}}"><i class="octicon octicon-eye"></i> {{.i18n.Tr "preview"}}</a>
3434
<a class="item" data-tab="diff" data-url="{{.RepoLink}}/_preview/{{.BranchName | EscapePound}}/{{.TreePath | EscapePound}}" data-context="{{.BranchLink}}"><i class="octicon octicon-diff"></i> {{.i18n.Tr "repo.editor.preview_changes"}}</a>
3535
{{end}}
3636
</div>
3737
<div class="ui bottom attached active tab segment" data-tab="write">
3838
<textarea id="edit_area" name="content" data-id="repo-{{.Repository.Name}}-{{.TreePath}}"
39-
data-url="{{AppSubUrl}}/api/v1/markdown"
39+
data-url="{{.Repository.APIURL}}/markdown"
4040
data-context="{{.RepoLink}}"
4141
data-markdown-file-exts="{{.MarkdownFileExts}}"
4242
data-line-wrap-extensions="{{.LineWrapExtensions}}"

templates/repo/issue/comment_tab.tmpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<div class="field">
22
<div class="ui top attached tabular menu" data-write="write" data-preview="preview">
33
<a class="active item" data-tab="write">{{.i18n.Tr "write"}}</a>
4-
<a class="item" data-tab="preview" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}">{{.i18n.Tr "preview"}}</a>
4+
<a class="item" data-tab="preview" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}">{{.i18n.Tr "preview"}}</a>
55
</div>
66
<div class="ui bottom attached active tab segment" data-tab="write">
7-
<textarea id="content" class="edit_area js-quick-submit" name="content" tabindex="4" data-id="issue-{{.RepoName}}" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.Repo.RepoLink}}">
7+
<textarea id="content" class="edit_area js-quick-submit" name="content" tabindex="4" data-id="issue-{{.RepoName}}" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.Repo.RepoLink}}">
88
{{if .BodyQuery}}{{.BodyQuery}}{{else if .IssueTemplate}}{{.IssueTemplate}}{{else if .PullRequestTemplate}}{{.PullRequestTemplate}}{{else}}{{.content}}{{end}}</textarea>
99
</div>
1010
<div class="ui bottom attached tab segment markdown" data-tab="preview">

templates/repo/issue/view_content.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@
156156
<div class="ui comment form">
157157
<div class="ui top attached tabular menu">
158158
<a class="active write item">{{$.i18n.Tr "write"}}</a>
159-
<a class="preview item" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
159+
<a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
160160
</div>
161161
<div class="ui bottom attached active write tab segment">
162162
<textarea tabindex="1" name="content"></textarea>

templates/repo/wiki/new.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<input name="title" value="{{.title}}" autofocus required>
1818
</div>
1919
<div class="field">
20-
<textarea class="js-quick-submit" id="edit_area" name="content" data-id="wiki-{{.title}}" data-url="{{AppSubUrl}}/api/v1/markdown" data-context="{{.RepoLink}}/wiki" required>{{if .PageIsWikiEdit}}{{.content}}{{else}}{{.i18n.Tr "repo.wiki.welcome"}}{{end}}</textarea>
20+
<textarea class="js-quick-submit" id="edit_area" name="content" data-id="wiki-{{.title}}" data-url="{{.Repository.APIURL}}/markdown" data-context="{{.RepoLink}}/wiki" required>{{if .PageIsWikiEdit}}{{.content}}{{else}}{{.i18n.Tr "repo.wiki.welcome"}}{{end}}</textarea>
2121
</div>
2222
<div class="field">
2323
<input name="message" placeholder="{{.i18n.Tr "repo.wiki.default_commit_message"}}">

0 commit comments

Comments
 (0)