Fix rendering of external links (#2292)
This commit is contained in:
		
							parent
							
								
									2282e24028
								
							
						
					
					
						commit
						29f3a6e492
					
				| 
						 | 
				
			
			@ -69,12 +69,29 @@ var (
 | 
			
		|||
	// AnySHA1Pattern allows to split url containing SHA into parts
 | 
			
		||||
	AnySHA1Pattern = regexp.MustCompile(`(http\S*)://(\S+)/(\S+)/(\S+)/(\S+)/([0-9a-f]{40})(?:/?([^#\s]+)?(?:#(\S+))?)?`)
 | 
			
		||||
 | 
			
		||||
	// IssueFullPattern allows to split issue (and pull) URLs into parts
 | 
			
		||||
	IssueFullPattern = regexp.MustCompile(`(?:^|\s|\()(http\S*)://((?:[^\s/]+/)+)((?:\w{1,10}-)?[1-9][0-9]*)([\?|#]\S+.(\S+)?)?\b`)
 | 
			
		||||
 | 
			
		||||
	validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`)
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// regexp for full links to issues/pulls
 | 
			
		||||
var issueFullPattern *regexp.Regexp
 | 
			
		||||
 | 
			
		||||
// InitMarkdown initialize regexps for markdown parsing
 | 
			
		||||
func InitMarkdown() {
 | 
			
		||||
	getIssueFullPattern()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getIssueFullPattern() *regexp.Regexp {
 | 
			
		||||
	if issueFullPattern == nil {
 | 
			
		||||
		appURL := setting.AppURL
 | 
			
		||||
		if len(appURL) > 0 && appURL[len(appURL)-1] != '/' {
 | 
			
		||||
			appURL += "/"
 | 
			
		||||
		}
 | 
			
		||||
		issueFullPattern = regexp.MustCompile(appURL +
 | 
			
		||||
			`\w+/\w+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#]\S+.(\S+)?)?\b`)
 | 
			
		||||
	}
 | 
			
		||||
	return issueFullPattern
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// isLink reports whether link fits valid format.
 | 
			
		||||
func isLink(link []byte) bool {
 | 
			
		||||
	return validLinksPattern.Match(link)
 | 
			
		||||
| 
						 | 
				
			
			@ -323,32 +340,17 @@ func renderFullSha1Pattern(rawBytes []byte, urlPrefix string) []byte {
 | 
			
		|||
	return rawBytes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// renderFullIssuePattern renders issues-like URLs
 | 
			
		||||
func renderFullIssuePattern(rawBytes []byte, urlPrefix string) []byte {
 | 
			
		||||
	ms := IssueFullPattern.FindAllSubmatch(rawBytes, -1)
 | 
			
		||||
// RenderFullIssuePattern renders issues-like URLs
 | 
			
		||||
func RenderFullIssuePattern(rawBytes []byte) []byte {
 | 
			
		||||
	ms := getIssueFullPattern().FindAllSubmatch(rawBytes, -1)
 | 
			
		||||
	for _, m := range ms {
 | 
			
		||||
		all := m[0]
 | 
			
		||||
		protocol := string(m[1])
 | 
			
		||||
		paths := bytes.Split(m[2], []byte("/"))
 | 
			
		||||
		paths = paths[:len(paths)-1]
 | 
			
		||||
		if bytes.HasPrefix(paths[0], []byte("gist.")) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		path := protocol + "://" + string(m[2])
 | 
			
		||||
		id := string(m[3])
 | 
			
		||||
		path = URLJoin(path, id)
 | 
			
		||||
		var comment []byte
 | 
			
		||||
		if len(m) > 3 {
 | 
			
		||||
			comment = m[4]
 | 
			
		||||
		}
 | 
			
		||||
		urlSuffix := ""
 | 
			
		||||
		id := string(m[1])
 | 
			
		||||
		text := "#" + id
 | 
			
		||||
		if comment != nil {
 | 
			
		||||
			urlSuffix += string(comment)
 | 
			
		||||
			text += " <i class='comment icon'></i>"
 | 
			
		||||
		}
 | 
			
		||||
		// TODO if m[2] is not nil, then link is to a comment,
 | 
			
		||||
		// and we should indicate that in the text somehow
 | 
			
		||||
		rawBytes = bytes.Replace(rawBytes, all, []byte(fmt.Sprintf(
 | 
			
		||||
			`<a href="%s%s">%s</a>`, path, urlSuffix, text)), -1)
 | 
			
		||||
			`<a href="%s">%s</a>`, string(all), text)), -1)
 | 
			
		||||
	}
 | 
			
		||||
	return rawBytes
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -550,12 +552,12 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]strin
 | 
			
		|||
			[]byte(fmt.Sprintf(`<a href="%s">%s</a>`, URLJoin(setting.AppURL, string(m[1:])), m)), -1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rawBytes = RenderFullIssuePattern(rawBytes)
 | 
			
		||||
	rawBytes = RenderShortLinks(rawBytes, urlPrefix, false, isWikiMarkdown)
 | 
			
		||||
	rawBytes = RenderIssueIndexPattern(rawBytes, urlPrefix, metas)
 | 
			
		||||
	rawBytes = RenderCrossReferenceIssueIndexPattern(rawBytes, urlPrefix, metas)
 | 
			
		||||
	rawBytes = renderFullSha1Pattern(rawBytes, urlPrefix)
 | 
			
		||||
	rawBytes = renderSha1CurrentPattern(rawBytes, urlPrefix)
 | 
			
		||||
	rawBytes = renderFullIssuePattern(rawBytes, urlPrefix)
 | 
			
		||||
	return rawBytes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -209,13 +209,15 @@ func TestRender_AutoLink(t *testing.T) {
 | 
			
		|||
		numericIssueLink(URLJoin(setting.AppSubURL, "issues"), 3333))
 | 
			
		||||
 | 
			
		||||
	// render external issue URLs
 | 
			
		||||
	tmp := "http://1111/2222/ssss-issues/3333?param=blah&blahh=333"
 | 
			
		||||
	test(tmp, "<a href=\""+tmp+"\">#3333 <i class='comment icon'></i></a>")
 | 
			
		||||
	test("http://test.com/issues/33333", numericIssueLink("http://test.com/issues", 33333))
 | 
			
		||||
	test("https://issues/333", numericIssueLink("https://issues", 333))
 | 
			
		||||
	for _, externalURL := range []string{
 | 
			
		||||
		"http://1111/2222/ssss-issues/3333?param=blah&blahh=333",
 | 
			
		||||
		"http://test.com/issues/33333",
 | 
			
		||||
		"https://issues/333"} {
 | 
			
		||||
		test(externalURL, externalURL)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// render valid commit URLs
 | 
			
		||||
	tmp = URLJoin(AppSubURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae")
 | 
			
		||||
	tmp := URLJoin(AppSubURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae")
 | 
			
		||||
	test(tmp, "<a href=\""+tmp+"\">d8a994ef24</a>")
 | 
			
		||||
	tmp += "#diff-2"
 | 
			
		||||
	test(tmp, "<a href=\""+tmp+"\">d8a994ef24 (diff-2)</a>")
 | 
			
		||||
| 
						 | 
				
			
			@ -368,6 +370,22 @@ func TestRender_CrossReferences(t *testing.T) {
 | 
			
		|||
		`<p><a href="`+URLJoin(AppURL, "gogits", "gogs", "issues", "12345")+`" rel="nofollow">gogits/gogs#12345</a></p>`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRender_FullIssueURLs(t *testing.T) {
 | 
			
		||||
	setting.AppURL = AppURL
 | 
			
		||||
	setting.AppSubURL = AppSubURL
 | 
			
		||||
 | 
			
		||||
	test := func(input, expected string) {
 | 
			
		||||
		result := RenderFullIssuePattern([]byte(input))
 | 
			
		||||
		assert.Equal(t, expected, string(result))
 | 
			
		||||
	}
 | 
			
		||||
	test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6",
 | 
			
		||||
		"Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6")
 | 
			
		||||
	test("Look here http://localhost:3000/person/repo/issues/4",
 | 
			
		||||
		`Look here <a href="http://localhost:3000/person/repo/issues/4">#4</a>`)
 | 
			
		||||
	test("http://localhost:3000/person/repo/issues/4#issuecomment-1234",
 | 
			
		||||
		`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234">#4</a>`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRegExp_MentionPattern(t *testing.T) {
 | 
			
		||||
	trueTestCases := []string{
 | 
			
		||||
		"@Unknwon",
 | 
			
		||||
| 
						 | 
				
			
			@ -558,50 +576,6 @@ func TestRegExp_AnySHA1Pattern(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRegExp_IssueFullPattern(t *testing.T) {
 | 
			
		||||
	testCases := map[string][]string{
 | 
			
		||||
		"https://github.com/gogits/gogs/pull/3244": {
 | 
			
		||||
			"https",
 | 
			
		||||
			"github.com/gogits/gogs/pull/",
 | 
			
		||||
			"3244",
 | 
			
		||||
			"",
 | 
			
		||||
			"",
 | 
			
		||||
		},
 | 
			
		||||
		"https://github.com/gogits/gogs/issues/3247#issuecomment-231517079": {
 | 
			
		||||
			"https",
 | 
			
		||||
			"github.com/gogits/gogs/issues/",
 | 
			
		||||
			"3247",
 | 
			
		||||
			"#issuecomment-231517079",
 | 
			
		||||
			"",
 | 
			
		||||
		},
 | 
			
		||||
		"https://try.gogs.io/gogs/gogs/issues/4#issue-685": {
 | 
			
		||||
			"https",
 | 
			
		||||
			"try.gogs.io/gogs/gogs/issues/",
 | 
			
		||||
			"4",
 | 
			
		||||
			"#issue-685",
 | 
			
		||||
			"",
 | 
			
		||||
		},
 | 
			
		||||
		"https://youtrack.jetbrains.com/issue/JT-36485": {
 | 
			
		||||
			"https",
 | 
			
		||||
			"youtrack.jetbrains.com/issue/",
 | 
			
		||||
			"JT-36485",
 | 
			
		||||
			"",
 | 
			
		||||
			"",
 | 
			
		||||
		},
 | 
			
		||||
		"https://youtrack.jetbrains.com/issue/JT-36485#comment=27-1508676": {
 | 
			
		||||
			"https",
 | 
			
		||||
			"youtrack.jetbrains.com/issue/",
 | 
			
		||||
			"JT-36485",
 | 
			
		||||
			"#comment=27-1508676",
 | 
			
		||||
			"",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for k, v := range testCases {
 | 
			
		||||
		assert.Equal(t, IssueFullPattern.FindStringSubmatch(k)[1:], v)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestMisc_IsMarkdownFile(t *testing.T) {
 | 
			
		||||
	setting.Markdown.FileExtensions = []string{".md", ".markdown", ".mdown", ".mkd"}
 | 
			
		||||
	trueTestCases := []string{
 | 
			
		||||
| 
						 | 
				
			
			@ -645,7 +619,7 @@ var sameCases = []string{
 | 
			
		|||
 | 
			
		||||
Ideas and codes
 | 
			
		||||
 | 
			
		||||
- Bezier widget (by @r-lyeh) https://github.com/ocornut/imgui/issues/786
 | 
			
		||||
- Bezier widget (by @r-lyeh) ` + AppURL + `ocornut/imgui/issues/786
 | 
			
		||||
- Node graph editors https://github.com/ocornut/imgui/issues/306
 | 
			
		||||
- [[Memory Editor|memory_editor_example]]
 | 
			
		||||
- [[Plot var helper|plot_var_example]]`,
 | 
			
		||||
| 
						 | 
				
			
			@ -681,8 +655,8 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
 | 
			
		|||
<p>Ideas and codes</p>
 | 
			
		||||
 | 
			
		||||
<ul>
 | 
			
		||||
<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>
 | 
			
		||||
<li>Node graph editors<a href="https://github.com/ocornut/imgui/issues/306" rel="nofollow">#306</a></li>
 | 
			
		||||
<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>
 | 
			
		||||
<li>Node graph editors https://github.com/ocornut/imgui/issues/306</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>
 | 
			
		||||
</ul>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,7 +75,7 @@ func TestAPI_RenderGFM(t *testing.T) {
 | 
			
		|||
<ul>
 | 
			
		||||
<li><a href="` + AppSubURL + `wiki/Links" rel="nofollow">Links, Language bindings, Engine bindings</a></li>
 | 
			
		||||
<li><a href="` + AppSubURL + `wiki/Tips" rel="nofollow">Tips</a></li>
 | 
			
		||||
<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>
 | 
			
		||||
<li>Bezier widget (by <a href="` + AppURL + `r-lyeh" rel="nofollow">@r-lyeh</a>) https://github.com/ocornut/imgui/issues/786</li>
 | 
			
		||||
</ul>
 | 
			
		||||
`,
 | 
			
		||||
		// wine-staging wiki home extract: special wiki syntax, images
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,6 +50,7 @@ func GlobalInit() {
 | 
			
		|||
 | 
			
		||||
	if setting.InstallLock {
 | 
			
		||||
		highlight.NewContext()
 | 
			
		||||
		markdown.InitMarkdown()
 | 
			
		||||
		markdown.NewSanitizer()
 | 
			
		||||
		if err := models.NewEngine(migrations.Migrate); err != nil {
 | 
			
		||||
			log.Fatal(4, "Failed to initialize ORM engine: %v", err)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue