diff --git a/models/mail.go b/models/mail.go index e66e316ba0..16e8c9e2ef 100644 --- a/models/mail.go +++ b/models/mail.go @@ -150,7 +150,7 @@ func composeTplData(subject, body, link string) map[string]interface{} { func composeIssueMessage(issue *Issue, doer *User, tplName base.TplName, tos []string, info string) *mailer.Message { subject := issue.mailSubject() - body := string(markdown.RenderSpecialLink([]byte(issue.Content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas())) + body := string(markdown.RenderString(issue.Content, issue.Repo.HTMLURL(), issue.Repo.ComposeMetas())) data := composeTplData(subject, body, issue.HTMLURL()) data["Doer"] = doer diff --git a/modules/markdown/markdown.go b/modules/markdown/markdown.go index 5218af8f27..52459e360e 100644 --- a/modules/markdown/markdown.go +++ b/modules/markdown/markdown.go @@ -92,10 +92,10 @@ var ( ShortLinkPattern = regexp.MustCompile(`(\[\[.*\]\]\w*)`) // AnySHA1Pattern allows to split url containing SHA into parts - AnySHA1Pattern = regexp.MustCompile(`http\S+//(\S+)/(\S+)/(\S+)/(\S+)/([0-9a-f]{40})(?:/?([^#\s]+)?(?:#(\S+))?)?`) + 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`) + IssueFullPattern = regexp.MustCompile(`(?:^|\s|\()(http\S*)://((?:[^\s/]+/)+)((?:\w{1,10}-)?[1-9][0-9]*)([\?|#]\S+.(\S+)?)?\b`) validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`) ) @@ -126,10 +126,11 @@ type Renderer struct { func (r *Renderer) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) { if len(link) > 0 && !isLink(link) { if link[0] != '#' { - mLink := URLJoin(r.urlPrefix, string(link)) + lnk := string(link) if r.isWikiMarkdown { - mLink = URLJoin(r.urlPrefix, "wiki", string(link)) + lnk = URLJoin("wiki", lnk) } + mLink := URLJoin(r.urlPrefix, lnk) link = []byte(mLink) } } @@ -206,12 +207,10 @@ func (r *Renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byt return } } else { - if link[0] != '/' { - if !strings.HasSuffix(prefix, "/") { - prefix += "/" - } - } - link = []byte(url.QueryEscape(prefix + string(link))) + lnk := string(link) + lnk = URLJoin(prefix, lnk) + lnk = strings.Replace(lnk, " ", "+", -1) + link = []byte(lnk) } } @@ -246,10 +245,30 @@ func URLJoin(elem ...string) string { last := len(elem) - 1 for i, item := range elem { res += item - if !strings.HasSuffix(res, "/") && i != last { + if i != last && !strings.HasSuffix(res, "/") { res += "/" } } + cwdIndex := strings.Index(res, "/./") + for cwdIndex != -1 { + res = strings.Replace(res, "/./", "/", 1) + cwdIndex = strings.Index(res, "/./") + } + upIndex := strings.Index(res, "/..") + for upIndex != -1 { + res = strings.Replace(res, "/..", "", 1) + prevStart := -1 + for i := upIndex - 1; i >= 0; i-- { + if res[i] == '/' { + prevStart = i + break + } + } + if prevStart != -1 { + res = res[:prevStart] + res[upIndex:] + } + upIndex = strings.Index(res, "/..") + } return res } @@ -286,6 +305,9 @@ func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string, metas map[string // IsSameDomain checks if given url string has the same hostname as current Gitea instance func IsSameDomain(s string) bool { + if strings.HasPrefix(s, "/") { + return true + } if uapp, err := url.Parse(setting.AppURL); err == nil { if u, err := url.Parse(s); err == nil { return u.Host == uapp.Host @@ -300,26 +322,27 @@ func renderFullSha1Pattern(rawBytes []byte, urlPrefix string) []byte { ms := AnySHA1Pattern.FindAllSubmatch(rawBytes, -1) for _, m := range ms { all := m[0] - paths := string(m[1]) - var path = "//" + paths - author := string(m[2]) - repoName := string(m[3]) + protocol := string(m[1]) + paths := string(m[2]) + path := protocol + "://" + paths + author := string(m[3]) + repoName := string(m[4]) path = URLJoin(path, author, repoName) ltype := "src" - itemType := m[4] + itemType := m[5] if IsSameDomain(paths) { ltype = string(itemType) } else if string(itemType) == "commit" { ltype = "commit" } - sha := m[5] + sha := m[6] var subtree string - if len(m) > 6 && len(m[6]) > 0 { - subtree = string(m[6]) + if len(m) > 7 && len(m[7]) > 0 { + subtree = string(m[7]) } var line []byte - if len(m) > 7 && len(m[7]) > 0 { - line = m[7] + if len(m) > 8 && len(m[8]) > 0 { + line = m[8] } urlSuffix := "" text := base.ShortSha(string(sha)) @@ -346,23 +369,18 @@ func renderFullIssuePattern(rawBytes []byte, urlPrefix string) []byte { ms := IssueFullPattern.FindAllSubmatch(rawBytes, -1) for _, m := range ms { all := m[0] - paths := bytes.Split(m[1], []byte("/")) + protocol := string(m[1]) + paths := bytes.Split(m[2], []byte("/")) paths = paths[:len(paths)-1] if bytes.HasPrefix(paths[0], []byte("gist.")) { continue } - var path string - if len(paths) > 3 { - // Internal one - path = URLJoin(urlPrefix, "issues") - } else { - path = "//" + string(m[1]) - } - id := string(m[2]) + path := protocol + "://" + string(m[2]) + id := string(m[3]) path = URLJoin(path, id) var comment []byte if len(m) > 3 { - comment = m[3] + comment = m[4] } urlSuffix := "" text := "#" + id @@ -394,8 +412,13 @@ func lastIndexOfByte(sl []byte, target byte) int { return -1 } -// renderShortLinks processes [[syntax]] -func renderShortLinks(rawBytes []byte, urlPrefix string, noLink bool) []byte { +// RenderShortLinks processes [[syntax]] +// +// noLink flag disables making link tags when set to true +// so this function just replaces the whole [[...]] with the content text +// +// isWikiMarkdown is a flag to choose linking url prefix +func RenderShortLinks(rawBytes []byte, urlPrefix string, noLink bool, isWikiMarkdown bool) []byte { ms := ShortLinkPattern.FindAll(rawBytes, -1) for _, m := range ms { orig := bytes.TrimSpace(m) @@ -482,11 +505,17 @@ func renderShortLinks(rawBytes []byte, urlPrefix string, noLink bool) []byte { } absoluteLink := isLink([]byte(link)) if !absoluteLink { - link = url.QueryEscape(link) + link = strings.Replace(link, " ", "+", -1) } if image { if !absoluteLink { - link = URLJoin(urlPrefix, "wiki", "raw", link) + if IsSameDomain(urlPrefix) { + urlPrefix = strings.Replace(urlPrefix, "/src/", "/raw/", 1) + } + if isWikiMarkdown { + link = URLJoin("wiki", "raw", link) + } + link = URLJoin(urlPrefix, link) } title := props["title"] if title == "" { @@ -504,7 +533,10 @@ func renderShortLinks(rawBytes []byte, urlPrefix string, noLink bool) []byte { } name = fmt.Sprintf(``, link, alt, title) } else if !absoluteLink { - link = URLJoin(urlPrefix, "wiki", link) + if isWikiMarkdown { + link = URLJoin("wiki", link) + } + link = URLJoin(urlPrefix, link) } if noLink { rawBytes = bytes.Replace(rawBytes, orig, []byte(name), -1) @@ -527,7 +559,7 @@ func RenderCrossReferenceIssueIndexPattern(rawBytes []byte, urlPrefix string, me repo := string(bytes.Split(m, []byte("#"))[0]) issue := string(bytes.Split(m, []byte("#"))[1]) - link := fmt.Sprintf(`%s`, URLJoin(urlPrefix, repo, "issues", issue), m) + link := fmt.Sprintf(`%s`, URLJoin(setting.AppURL, repo, "issues", issue), m) rawBytes = bytes.Replace(rawBytes, m, []byte(link), 1) } return rawBytes @@ -548,7 +580,7 @@ func renderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte { } // RenderSpecialLink renders mentions, indexes and SHA1 strings to corresponding links. -func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]string) []byte { +func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]string, isWikiMarkdown bool) []byte { ms := MentionPattern.FindAll(rawBytes, -1) for _, m := range ms { m = m[bytes.Index(m, []byte("@")):] @@ -556,7 +588,7 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string, metas map[string]strin []byte(fmt.Sprintf(`%s`, URLJoin(setting.AppURL, string(m[1:])), m)), -1) } - rawBytes = renderShortLinks(rawBytes, urlPrefix, false) + rawBytes = RenderShortLinks(rawBytes, urlPrefix, false, isWikiMarkdown) rawBytes = RenderIssueIndexPattern(rawBytes, urlPrefix, metas) rawBytes = RenderCrossReferenceIssueIndexPattern(rawBytes, urlPrefix, metas) rawBytes = renderFullSha1Pattern(rawBytes, urlPrefix) @@ -601,7 +633,7 @@ var noEndTags = []string{"img", "input", "br", "hr"} // PostProcess treats different types of HTML differently, // and only renders special links for plain text blocks. -func PostProcess(rawHTML []byte, urlPrefix string, metas map[string]string) []byte { +func PostProcess(rawHTML []byte, urlPrefix string, metas map[string]string, isWikiMarkdown bool) []byte { startTags := make([]string, 0, 5) var buf bytes.Buffer tokenizer := html.NewTokenizer(bytes.NewReader(rawHTML)) @@ -611,7 +643,7 @@ OUTER_LOOP: token := tokenizer.Token() switch token.Type { case html.TextToken: - buf.Write(RenderSpecialLink([]byte(token.String()), urlPrefix, metas)) + buf.Write(RenderSpecialLink([]byte(token.String()), urlPrefix, metas, isWikiMarkdown)) case html.StartTagToken: buf.WriteString(token.String()) @@ -623,7 +655,7 @@ OUTER_LOOP: token = tokenizer.Token() // Copy the token to the output verbatim - buf.Write(renderShortLinks([]byte(token.String()), urlPrefix, true)) + buf.Write(RenderShortLinks([]byte(token.String()), urlPrefix, true, isWikiMarkdown)) if token.Type == html.StartTagToken { if !com.IsSliceContainsStr(noEndTags, token.Data) { @@ -673,9 +705,9 @@ OUTER_LOOP: // Render renders Markdown to HTML with all specific handling stuff. func render(rawBytes []byte, urlPrefix string, metas map[string]string, isWikiMarkdown bool) []byte { - urlPrefix = strings.Replace(urlPrefix, " ", "%20", -1) + urlPrefix = strings.Replace(urlPrefix, " ", "+", -1) result := RenderRaw(rawBytes, urlPrefix, isWikiMarkdown) - result = PostProcess(result, urlPrefix, metas) + result = PostProcess(result, urlPrefix, metas, isWikiMarkdown) result = Sanitizer.SanitizeBytes(result) return result } diff --git a/modules/markdown/markdown_test.go b/modules/markdown/markdown_test.go index 60dd0afde2..403035bdf0 100644 --- a/modules/markdown/markdown_test.go +++ b/modules/markdown/markdown_test.go @@ -55,7 +55,7 @@ func testRenderIssueIndexPattern(t *testing.T, input, expected string, metas map string(RenderIssueIndexPattern([]byte(input), AppSubURL, metas))) } -func TestRenderIssueIndexPattern(t *testing.T) { +func TestRender_IssueIndexPattern(t *testing.T) { // numeric: render inputs without valid mentions test := func(s string) { testRenderIssueIndexPattern(t, s, s, nil) @@ -82,7 +82,7 @@ func TestRenderIssueIndexPattern(t *testing.T) { test("test #54321issue") } -func TestRenderIssueIndexPattern2(t *testing.T) { +func TestRender_IssueIndexPattern2(t *testing.T) { setting.AppURL = AppURL setting.AppSubURL = AppSubURL @@ -119,7 +119,7 @@ func TestRenderIssueIndexPattern2(t *testing.T) { test("#1 (#4321) test", "%s (%s) test", 1, 4321) } -func TestRenderIssueIndexPattern3(t *testing.T) { +func TestRender_IssueIndexPattern3(t *testing.T) { setting.AppURL = AppURL setting.AppSubURL = AppSubURL @@ -146,7 +146,7 @@ func TestRenderIssueIndexPattern3(t *testing.T) { test("ABC-0123") // no leading zero } -func TestRenderIssueIndexPattern4(t *testing.T) { +func TestRender_IssueIndexPattern4(t *testing.T) { setting.AppURL = AppURL setting.AppSubURL = AppSubURL @@ -164,15 +164,15 @@ func TestRenderIssueIndexPattern4(t *testing.T) { test("test issue ABCDEFGHIJ-1234567890", "test issue %s", "ABCDEFGHIJ-1234567890") } -func TestRenderer_AutoLink(t *testing.T) { +func TestRender_AutoLink(t *testing.T) { setting.AppURL = AppURL setting.AppSubURL = AppSubURL - SubURLNoProtocol := setting.AppSubURL[5:] - test := func(input, expected string) { - buffer := RenderSpecialLink([]byte(input), setting.AppSubURL, map[string]string{}) - assert.Equal(t, expected, string(buffer)) + buffer := RenderSpecialLink([]byte(input), setting.AppSubURL, nil, false) + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer))) + buffer = RenderSpecialLink([]byte(input), setting.AppSubURL, nil, true) + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer))) } // render valid issue URLs @@ -180,54 +180,98 @@ func TestRenderer_AutoLink(t *testing.T) { numericIssueLink(URLJoin(setting.AppSubURL, "issues"), 3333)) // render external issue URLs - tmp := "//1111/2222/ssss-issues/3333?param=blah&blahh=333" - test("http:"+tmp, - "#3333 ") - test("http://test.com/issues/33333", numericIssueLink("//test.com/issues", 33333)) - test("https://issues/333", numericIssueLink("//issues", 333)) + tmp := "http://1111/2222/ssss-issues/3333?param=blah&blahh=333" + test(tmp, "#3333 ") + test("http://test.com/issues/33333", numericIssueLink("http://test.com/issues", 33333)) + test("https://issues/333", numericIssueLink("https://issues", 333)) // render valid commit URLs - tmp = URLJoin(SubURLNoProtocol, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae") - test("http://"+tmp, "d8a994ef24") + tmp = URLJoin(AppSubURL, "commit", "d8a994ef243349f321568f9e36d5c3f444b99cae") + test(tmp, "d8a994ef24") tmp += "#diff-2" - test("http://"+tmp, "d8a994ef24 (diff-2)") + test(tmp, "d8a994ef24 (diff-2)") // render other commit URLs - tmp = "//external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2" - test("https:"+tmp, "d8a994ef24 (diff-2)") + tmp = "https://external-link.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2" + test(tmp, "d8a994ef24 (diff-2)") +} + +func TestRender_StandardLinks(t *testing.T) { + setting.AppURL = AppURL + setting.AppSubURL = AppSubURL + + test := func(input, expected, expectedWiki string) { + buffer := RenderString(input, setting.AppSubURL, nil) + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer))) + bufferWiki := RenderWiki([]byte(input), setting.AppSubURL, nil) + assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(bufferWiki)) + } + + googleRendered := `

https://google.com/

` + test("", googleRendered, googleRendered) + + lnk := URLJoin(AppSubURL, "WikiPage") + lnkWiki := URLJoin(AppSubURL, "wiki", "WikiPage") + test("[WikiPage](WikiPage)", + `

WikiPage

`, + `

WikiPage

`) } func TestRender_ShortLinks(t *testing.T) { setting.AppURL = AppURL setting.AppSubURL = AppSubURL + tree := URLJoin(AppSubURL, "src", "master") - test := func(input, expected string) { - buffer := RenderString(input, setting.AppSubURL, nil) - assert.Equal(t, expected, string(buffer)) + test := func(input, expected, expectedWiki string) { + buffer := RenderString(input, tree, nil) + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer))) + buffer = RenderWiki([]byte(input), setting.AppSubURL, nil) + assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer))) } - var url = URLJoin(AppSubURL, "wiki", "Link") - var imgurl = URLJoin(AppSubURL, "wiki", "raw", "Link.jpg") - var favicon = "http://google.com/favicon.ico" + rawtree := URLJoin(AppSubURL, "raw", "master") + url := URLJoin(tree, "Link") + imgurl := URLJoin(rawtree, "Link.jpg") + urlWiki := URLJoin(AppSubURL, "wiki", "Link") + imgurlWiki := URLJoin(AppSubURL, "wiki", "raw", "Link.jpg") + favicon := "http://google.com/favicon.ico" - test("[[Link]]", `

Link

-`) - test("[[Link.jpg]]", `

Link.jpg

-`) - test("[["+favicon+"]]", `

-`) - test("[[Name|Link]]", `

Name

-`) - test("[[Name|Link.jpg]]", `

Name

-`) - test("[[Name|Link.jpg|alt=AltName]]", `

AltName

-`) - test("[[Name|Link.jpg|title=Title]]", `

Title

-`) - test("[[Name|Link.jpg|alt=AltName|title=Title]]", `

AltName

-`) - test("[[Name|Link.jpg|alt=\"AltName\"|title='Title']]", `

AltName

-`) + test( + "[[Link]]", + `

Link

`, + `

Link

`) + test( + "[[Link.jpg]]", + `

Link.jpg

`, + `

Link.jpg

`) + test( + "[["+favicon+"]]", + `

`, + `

`) + test( + "[[Name|Link]]", + `

Name

`, + `

Name

`) + test( + "[[Name|Link.jpg]]", + `

Name

`, + `

Name

`) + test( + "[[Name|Link.jpg|alt=AltName]]", + `

AltName

`, + `

AltName

`) + test( + "[[Name|Link.jpg|title=Title]]", + `

Title

`, + `

Title

`) + test( + "[[Name|Link.jpg|alt=AltName|title=Title]]", + `

AltName

`, + `

AltName

`) + test( + "[[Name|Link.jpg|alt=\"AltName\"|title='Title']]", + `

AltName

`, + `

AltName

`) } func TestRender_Commits(t *testing.T) { @@ -236,7 +280,7 @@ func TestRender_Commits(t *testing.T) { test := func(input, expected string) { buffer := RenderString(input, setting.AppSubURL, nil) - assert.Equal(t, expected, string(buffer)) + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer))) } var sha = "b6dd6210eaebc915fd5be5579c58cce4da2e2579" @@ -245,12 +289,45 @@ func TestRender_Commits(t *testing.T) { var tree = strings.Replace(subtree, "/commit/", "/tree/", -1) var src = strings.Replace(subtree, "/commit/", "/src/", -1) - test(sha, `

b6dd6210ea

-`) - test(commit, `

b6dd6210ea

-`) - test(tree, `

b6dd6210ea/src

-`) + test(sha, `

b6dd6210ea

`) + test(commit, `

b6dd6210ea

`) + test(tree, `

b6dd6210ea/src

`) +} + +func TestRender_Images(t *testing.T) { + setting.AppURL = AppURL + setting.AppSubURL = AppSubURL + + test := func(input, expected string) { + buffer := RenderString(input, setting.AppSubURL, nil) + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer))) + } + + url := "../../.images/src/02/train.jpg" + title := "Train" + result := URLJoin(AppSubURL, url) + + test( + "!["+title+"]("+url+")", + `

`+title+`

`) + + test( + "[["+title+"|"+url+"]]", + `

`+title+`

`) +} + +func TestRender_CrossReferences(t *testing.T) { + setting.AppURL = AppURL + setting.AppSubURL = AppSubURL + + test := func(input, expected string) { + buffer := RenderString(input, setting.AppSubURL, nil) + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer))) + } + + test( + "gogits/gogs#12345", + `

gogits/gogs#12345

`) } func TestRegExp_MentionPattern(t *testing.T) { @@ -387,6 +464,7 @@ func TestRegExp_ShortLinkPattern(t *testing.T) { func TestRegExp_AnySHA1Pattern(t *testing.T) { testCases := map[string][]string{ "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js#L2703": []string{ + "https", "github.com", "jquery", "jquery", @@ -396,6 +474,7 @@ func TestRegExp_AnySHA1Pattern(t *testing.T) { "L2703", }, "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js": []string{ + "https", "github.com", "jquery", "jquery", @@ -405,6 +484,7 @@ func TestRegExp_AnySHA1Pattern(t *testing.T) { "", }, "https://github.com/jquery/jquery/commit/0705be475092aede1eddae01319ec931fb9c65fc": []string{ + "https", "github.com", "jquery", "jquery", @@ -414,6 +494,7 @@ func TestRegExp_AnySHA1Pattern(t *testing.T) { "", }, "https://github.com/jquery/jquery/tree/0705be475092aede1eddae01319ec931fb9c65fc/src": []string{ + "https", "github.com", "jquery", "jquery", @@ -423,6 +504,7 @@ func TestRegExp_AnySHA1Pattern(t *testing.T) { "", }, "https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2": []string{ + "https", "try.gogs.io", "gogs", "gogs", @@ -441,30 +523,35 @@ func TestRegExp_AnySHA1Pattern(t *testing.T) { func TestRegExp_IssueFullPattern(t *testing.T) { testCases := map[string][]string{ "https://github.com/gogits/gogs/pull/3244": []string{ + "https", "github.com/gogits/gogs/pull/", "3244", "", "", }, "https://github.com/gogits/gogs/issues/3247#issuecomment-231517079": []string{ + "https", "github.com/gogits/gogs/issues/", "3247", "#issuecomment-231517079", "", }, "https://try.gogs.io/gogs/gogs/issues/4#issue-685": []string{ + "https", "try.gogs.io/gogs/gogs/issues/", "4", "#issue-685", "", }, "https://youtrack.jetbrains.com/issue/JT-36485": []string{ + "https", "youtrack.jetbrains.com/issue/", "JT-36485", "", "", }, "https://youtrack.jetbrains.com/issue/JT-36485#comment=27-1508676": []string{ + "https", "youtrack.jetbrains.com/issue/", "JT-36485", "#comment=27-1508676", @@ -549,23 +636,6 @@ Ideas and codes - Node graph editors https://github.com/ocornut/imgui/issues/306 - [[Memory Editor|memory_editor_example]] - [[Plot var helper|plot_var_example]]`, - // rendered - `

Wiki! Enjoy :)

- - - -

Ideas and codes

- - -`, // wine-staging wiki home extract: tables, special wiki syntax, images `## What is Wine Staging? **Wine Staging** on website [wine-staging.com](http://wine-staging.com). @@ -576,11 +646,35 @@ Here are some links to the most important topics. You can find the full list of | [[images/icon-install.png]] | [[Installation]] | |--------------------------------|----------------------------------------------------------| | [[images/icon-usage.png]] | [[Usage]] | -| [[images/icon-config.png]] | [[Configuration]] | -| [[images/icon-bug.png]] | [Bugs](http://bugs.wine-staging.com) | `, - // rendered - `

What is Wine Staging?

+ // libgdx wiki page: inline images with special syntax + `[Excelsior JET](http://www.excelsiorjet.com/) allows you to create native executables for Windows, Linux and Mac OS X. + +1. [Package your libGDX application](https://github.com/libgdx/libgdx/wiki/Gradle-on-the-Commandline#packaging-for-the-desktop) +[[images/1.png]] +2. Perform a test run by hitting the Run! button. +[[images/2.png]]`, +} + +func testAnswers(baseURLContent, baseURLImages string) []string { + return []string{ + `

Wiki! Enjoy :)

+ + + +

Ideas and codes

+ + +`, + `

What is Wine Staging?

Wine Staging on website wine-staging.com.

@@ -591,66 +685,53 @@ Here are some links to the most important topics. You can find the full list of - - + + - - - - - - - - - - - - + +
images/icon-install.pngInstallationimages/icon-install.pngInstallation
images/icon-usage.pngUsage
images/icon-config.pngConfiguration
images/icon-bug.pngBugsimages/icon-usage.pngUsage
`, - // libgdx wiki page: inline images with special syntax - `[Excelsior JET](http://www.excelsiorjet.com/) allows you to create native executables for Windows, Linux and Mac OS X. - -1. [Package your libGDX application](https://github.com/libgdx/libgdx/wiki/Gradle-on-the-Commandline#packaging-for-the-desktop) -[[images/1.png]] -2. Perform a test run by hitting the Run! button. -[[images/2.png]]`, - // rendered - `

Excelsior JET allows you to create native executables for Windows, Linux and Mac OS X.

+ `

Excelsior JET allows you to create native executables for Windows, Linux and Mac OS X.

  1. Package your libGDX application -images/1.png
  2. +images/1.png
  3. Perform a test run by hitting the Run! button. -images/2.png
  4. +images/2.png
`, + } } func TestTotal_RenderString(t *testing.T) { - for i := 0; i < len(sameCases); i += 2 { - line := RenderString(sameCases[i], AppSubURL, map[string]string{}) - assert.Equal(t, sameCases[i+1], line) + answers := testAnswers(URLJoin(AppSubURL, "src", "master/"), URLJoin(AppSubURL, "raw", "master/")) + + for i := 0; i < len(sameCases); i++ { + line := RenderString(sameCases[i], URLJoin(AppSubURL, "src", "master/"), nil) + assert.Equal(t, answers[i], line) } testCases := []string{} for i := 0; i < len(testCases); i += 2 { - line := RenderString(testCases[i], AppSubURL, map[string]string{}) + line := RenderString(testCases[i], AppSubURL, nil) assert.Equal(t, testCases[i+1], line) } } func TestTotal_RenderWiki(t *testing.T) { - for i := 0; i < len(sameCases); i += 2 { - line := RenderWiki([]byte(sameCases[i]), AppSubURL, map[string]string{}) - assert.Equal(t, sameCases[i+1], line) + answers := testAnswers(URLJoin(AppSubURL, "wiki/"), URLJoin(AppSubURL, "wiki", "raw/")) + + for i := 0; i < len(sameCases); i++ { + line := RenderWiki([]byte(sameCases[i]), AppSubURL, nil) + assert.Equal(t, answers[i], line) } testCases := []string{ @@ -667,7 +748,7 @@ func TestTotal_RenderWiki(t *testing.T) { } for i := 0; i < len(testCases); i += 2 { - line := RenderWiki([]byte(testCases[i]), AppSubURL, map[string]string{}) + line := RenderWiki([]byte(testCases[i]), AppSubURL, nil) assert.Equal(t, testCases[i+1], line) } } diff --git a/routers/api/v1/misc/markdown.go b/routers/api/v1/misc/markdown.go index 947924dbed..188594e83e 100644 --- a/routers/api/v1/misc/markdown.go +++ b/routers/api/v1/misc/markdown.go @@ -27,7 +27,13 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) { switch form.Mode { case "gfm": - ctx.Write(markdown.Render([]byte(form.Text), markdown.URLJoin(setting.AppURL, form.Context), nil)) + md := []byte(form.Text) + context := markdown.URLJoin(setting.AppURL, form.Context) + if form.Wiki { + ctx.Write([]byte(markdown.RenderWiki(md, context, nil))) + } else { + ctx.Write(markdown.Render(md, context, nil)) + } default: ctx.Write(markdown.RenderRaw([]byte(form.Text), "", false)) } diff --git a/routers/api/v1/misc/markdown_test.go b/routers/api/v1/misc/markdown_test.go index 398e652d21..ae7a445e86 100644 --- a/routers/api/v1/misc/markdown_test.go +++ b/routers/api/v1/misc/markdown_test.go @@ -53,6 +53,7 @@ func TestAPI_RenderGFM(t *testing.T) { Mode: "gfm", Text: "", Context: Repo, + Wiki: true, } requrl, _ := url.Parse(markdown.URLJoin(AppURL, "api", "v1", "markdown")) req := &http.Request{ @@ -74,7 +75,7 @@ func TestAPI_RenderGFM(t *testing.T) { `, // wine-staging wiki home extract: special wiki syntax, images @@ -97,7 +98,7 @@ Here are some links to the most important topics. You can find the full list of

Here are some links to the most important topics. You can find the full list of pages at the sidebar.

Configuration -images/icon-bug.png

+images/icon-bug.png

`, // Guard wiki sidebar: special syntax `[[Guardfile-DSL / Configuring-Guard|Guardfile-DSL---Configuring-Guard]]`, diff --git a/vendor/code.gitea.io/sdk/gitea/miscellaneous.go b/vendor/code.gitea.io/sdk/gitea/miscellaneous.go index ae12d6ec56..30aaee77c8 100644 --- a/vendor/code.gitea.io/sdk/gitea/miscellaneous.go +++ b/vendor/code.gitea.io/sdk/gitea/miscellaneous.go @@ -9,4 +9,5 @@ type MarkdownOption struct { Text string Mode string Context string + Wiki bool }