Skip to content

Commit 8335b55

Browse files
ethantkoeniglunny
authored andcommitted
Fix rendering of external links (#2292) (#2315)
1 parent 09fff9e commit 8335b55

File tree

4 files changed

+56
-79
lines changed

4 files changed

+56
-79
lines changed

modules/markdown/markdown.go

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,29 @@ var (
7979
// AnySHA1Pattern allows to split url containing SHA into parts
8080
AnySHA1Pattern = regexp.MustCompile(`(http\S*)://(\S+)/(\S+)/(\S+)/(\S+)/([0-9a-f]{40})(?:/?([^#\s]+)?(?:#(\S+))?)?`)
8181

82-
// IssueFullPattern allows to split issue (and pull) URLs into parts
83-
IssueFullPattern = regexp.MustCompile(`(?:^|\s|\()(http\S*)://((?:[^\s/]+/)+)((?:\w{1,10}-)?[1-9][0-9]*)([\?|#]\S+.(\S+)?)?\b`)
84-
8582
validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`)
8683
)
8784

85+
// regexp for full links to issues/pulls
86+
var issueFullPattern *regexp.Regexp
87+
88+
// InitMarkdown initialize regexps for markdown parsing
89+
func InitMarkdown() {
90+
getIssueFullPattern()
91+
}
92+
93+
func getIssueFullPattern() *regexp.Regexp {
94+
if issueFullPattern == nil {
95+
appURL := setting.AppURL
96+
if len(appURL) > 0 && appURL[len(appURL)-1] != '/' {
97+
appURL += "/"
98+
}
99+
issueFullPattern = regexp.MustCompile(appURL +
100+
`\w+/\w+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#]\S+.(\S+)?)?\b`)
101+
}
102+
return issueFullPattern
103+
}
104+
88105
// isLink reports whether link fits valid format.
89106
func isLink(link []byte) bool {
90107
return validLinksPattern.Match(link)
@@ -352,32 +369,17 @@ func renderFullSha1Pattern(rawBytes []byte, urlPrefix string) []byte {
352369
return rawBytes
353370
}
354371

355-
// renderFullIssuePattern renders issues-like URLs
356-
func renderFullIssuePattern(rawBytes []byte, urlPrefix string) []byte {
357-
ms := IssueFullPattern.FindAllSubmatch(rawBytes, -1)
372+
// RenderFullIssuePattern renders issues-like URLs
373+
func RenderFullIssuePattern(rawBytes []byte) []byte {
374+
ms := getIssueFullPattern().FindAllSubmatch(rawBytes, -1)
358375
for _, m := range ms {
359376
all := m[0]
360-
protocol := string(m[1])
361-
paths := bytes.Split(m[2], []byte("/"))
362-
paths = paths[:len(paths)-1]
363-
if bytes.HasPrefix(paths[0], []byte("gist.")) {
364-
continue
365-
}
366-
path := protocol + "://" + string(m[2])
367-
id := string(m[3])
368-
path = URLJoin(path, id)
369-
var comment []byte
370-
if len(m) > 3 {
371-
comment = m[4]
372-
}
373-
urlSuffix := ""
377+
id := string(m[1])
374378
text := "#" + id
375-
if comment != nil {
376-
urlSuffix += string(comment)
377-
text += " <i class='comment icon'></i>"
378-
}
379+
// TODO if m[2] is not nil, then link is to a comment,
380+
// and we should indicate that in the text somehow
379381
rawBytes = bytes.Replace(rawBytes, all, []byte(fmt.Sprintf(
380-
`<a href="%s%s">%s</a>`, path, urlSuffix, text)), -1)
382+
`<a href="%s">%s</a>`, string(all), text)), -1)
381383
}
382384
return rawBytes
383385
}
@@ -579,12 +581,12 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]strin
579581
[]byte(fmt.Sprintf(`<a href="%s">%s</a>`, URLJoin(setting.AppURL, string(m[1:])), m)), -1)
580582
}
581583

584+
rawBytes = RenderFullIssuePattern(rawBytes)
582585
rawBytes = RenderShortLinks(rawBytes, urlPrefix, false, isWikiMarkdown)
583586
rawBytes = RenderIssueIndexPattern(rawBytes, urlPrefix, metas)
584587
rawBytes = RenderCrossReferenceIssueIndexPattern(rawBytes, urlPrefix, metas)
585588
rawBytes = renderFullSha1Pattern(rawBytes, urlPrefix)
586589
rawBytes = renderSha1CurrentPattern(rawBytes, urlPrefix)
587-
rawBytes = renderFullIssuePattern(rawBytes, urlPrefix)
588590
return rawBytes
589591
}
590592

modules/markdown/markdown_test.go

Lines changed: 26 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,15 @@ func TestRender_AutoLink(t *testing.T) {
180180
numericIssueLink(URLJoin(setting.AppSubURL, "issues"), 3333))
181181

182182
// render external issue URLs
183-
tmp := "http://1111/2222/ssss-issues/3333?param=blah&blahh=333"
184-
test(tmp, "<a href=\""+tmp+"\">#3333 <i class='comment icon'></i></a>")
185-
test("http://test.com/issues/33333", numericIssueLink("http://test.com/issues", 33333))
186-
test("https://issues/333", numericIssueLink("https://issues", 333))
183+
for _, externalURL := range []string{
184+
"http://1111/2222/ssss-issues/3333?param=blah&blahh=333",
185+
"http://test.com/issues/33333",
186+
"https://issues/333"} {
187+
test(externalURL, externalURL)
188+
}
187189

188190
// render valid commit URLs
189-
tmp = URLJoin(AppSubURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae")
191+
tmp := URLJoin(AppSubURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae")
190192
test(tmp, "<a href=\""+tmp+"\">d8a994ef24</a>")
191193
tmp += "#diff-2"
192194
test(tmp, "<a href=\""+tmp+"\">d8a994ef24 (diff-2)</a>")
@@ -332,6 +334,22 @@ func TestRender_CrossReferences(t *testing.T) {
332334
`<p><a href="`+URLJoin(AppURL, "gogits", "gogs", "issues", "12345")+`" rel="nofollow">gogits/gogs#12345</a></p>`)
333335
}
334336

337+
func TestRender_FullIssueURLs(t *testing.T) {
338+
setting.AppURL = AppURL
339+
setting.AppSubURL = AppSubURL
340+
341+
test := func(input, expected string) {
342+
result := RenderFullIssuePattern([]byte(input))
343+
assert.Equal(t, expected, string(result))
344+
}
345+
test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6",
346+
"Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6")
347+
test("Look here http://localhost:3000/person/repo/issues/4",
348+
`Look here <a href="http://localhost:3000/person/repo/issues/4">#4</a>`)
349+
test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
350+
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234">#4</a>`)
351+
}
352+
335353
func TestRegExp_MentionPattern(t *testing.T) {
336354
trueTestCases := []string{
337355
"@Unknwon",
@@ -522,50 +540,6 @@ func TestRegExp_AnySHA1Pattern(t *testing.T) {
522540
}
523541
}
524542

525-
func TestRegExp_IssueFullPattern(t *testing.T) {
526-
testCases := map[string][]string{
527-
"https://github.com/gogits/gogs/pull/3244": {
528-
"https",
529-
"github.com/gogits/gogs/pull/",
530-
"3244",
531-
"",
532-
"",
533-
},
534-
"https://github.com/gogits/gogs/issues/3247#issuecomment-231517079": {
535-
"https",
536-
"github.com/gogits/gogs/issues/",
537-
"3247",
538-
"#issuecomment-231517079",
539-
"",
540-
},
541-
"https://try.gogs.io/gogs/gogs/issues/4#issue-685": {
542-
"https",
543-
"try.gogs.io/gogs/gogs/issues/",
544-
"4",
545-
"#issue-685",
546-
"",
547-
},
548-
"https://youtrack.jetbrains.com/issue/JT-36485": {
549-
"https",
550-
"youtrack.jetbrains.com/issue/",
551-
"JT-36485",
552-
"",
553-
"",
554-
},
555-
"https://youtrack.jetbrains.com/issue/JT-36485#comment=27-1508676": {
556-
"https",
557-
"youtrack.jetbrains.com/issue/",
558-
"JT-36485",
559-
"#comment=27-1508676",
560-
"",
561-
},
562-
}
563-
564-
for k, v := range testCases {
565-
assert.Equal(t, IssueFullPattern.FindStringSubmatch(k)[1:], v)
566-
}
567-
}
568-
569543
func TestMisc_IsMarkdownFile(t *testing.T) {
570544
setting.Markdown.FileExtensions = []string{".md", ".markdown", ".mdown", ".mkd"}
571545
trueTestCases := []string{
@@ -634,7 +608,7 @@ var sameCases = []string{
634608
635609
Ideas and codes
636610
637-
- Bezier widget (by @r-lyeh) https://github.com/ocornut/imgui/issues/786
611+
- Bezier widget (by @r-lyeh) ` + AppURL + `ocornut/imgui/issues/786
638612
- Node graph editors https://github.com/ocornut/imgui/issues/306
639613
- [[Memory Editor|memory_editor_example]]
640614
- [[Plot var helper|plot_var_example]]`,
@@ -670,8 +644,8 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
670644
<p>Ideas and codes</p>
671645
672646
<ul>
673-
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>)<a href="https://github.com/ocornut/imgui/issues/786" rel="nofollow">#786</a></li>
674-
<li>Node graph editors<a href="https://github.com/ocornut/imgui/issues/306" rel="nofollow">#306</a></li>
647+
<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>
648+
<li>Node graph editors https://github.com/ocornut/imgui/issues/306</li>
675649
<li><a href="` + baseURLContent + `memory_editor_example" rel="nofollow">Memory Editor</a></li>
676650
<li><a href="` + baseURLContent + `plot_var_example" rel="nofollow">Plot var helper</a></li>
677651
</ul>

routers/api/v1/misc/markdown_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func TestAPI_RenderGFM(t *testing.T) {
7575
<ul>
7676
<li><a href="` + AppSubURL + `wiki/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
7777
<li><a href="` + AppSubURL + `wiki/Tips" rel="nofollow">Tips</a></li>
78-
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>)<a href="https://github.com/ocornut/imgui/issues/786" rel="nofollow">#786</a></li>
78+
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) https://github.com/ocornut/imgui/issues/786</li>
7979
</ul>
8080
`,
8181
// wine-staging wiki home extract: special wiki syntax, images

routers/init.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ func GlobalInit() {
4949

5050
if setting.InstallLock {
5151
highlight.NewContext()
52+
markdown.InitMarkdown()
5253
markdown.NewSanitizer()
5354
if err := models.NewEngine(); err != nil {
5455
log.Fatal(4, "Failed to initialize ORM engine: %v", err)

0 commit comments

Comments
 (0)