diff options
32 files changed, 237 insertions, 257 deletions
diff --git a/models/repo/repo.go b/models/repo/repo.go index 68f8e16a21..4776ff0b9c 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -479,7 +479,6 @@ func (repo *Repository) ComposeMetas(ctx context.Context) map[string]string { metas := map[string]string{ "user": repo.OwnerName, "repo": repo.Name, - "mode": "comment", } unit, err := repo.GetUnit(ctx, unit.TypeExternalTracker) @@ -521,7 +520,6 @@ func (repo *Repository) ComposeDocumentMetas(ctx context.Context) map[string]str for k, v := range repo.ComposeMetas(ctx) { metas[k] = v } - metas["mode"] = "document" repo.DocumentRenderingMetas = metas } return repo.DocumentRenderingMetas diff --git a/modules/markup/console/console.go b/modules/markup/console/console.go index 01653565fe..d991527b80 100644 --- a/modules/markup/console/console.go +++ b/modules/markup/console/console.go @@ -8,7 +8,6 @@ import ( "io" "path/filepath" "regexp" - "strings" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" @@ -17,9 +16,6 @@ import ( "github.com/go-enry/go-enry/v2" ) -// MarkupName describes markup's name -var MarkupName = "console" - func init() { markup.RegisterRenderer(Renderer{}) } @@ -29,7 +25,7 @@ type Renderer struct{} // Name implements markup.Renderer func (Renderer) Name() string { - return MarkupName + return "console" } // Extensions implements markup.Renderer @@ -67,20 +63,3 @@ func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Wri _, err = output.Write(buf) return err } - -// Render renders terminal colors to HTML with all specific handling stuff. -func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { - if ctx.Type == "" { - ctx.Type = MarkupName - } - return markup.Render(ctx, input, output) -} - -// RenderString renders terminal colors in string to HTML with all specific handling stuff and return string -func RenderString(ctx *markup.RenderContext, content string) (string, error) { - var buf strings.Builder - if err := Render(ctx, strings.NewReader(content), &buf); err != nil { - return "", err - } - return buf.String(), nil -} diff --git a/modules/markup/html.go b/modules/markup/html.go index e2eefefc4b..54c65c95d2 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -442,12 +442,11 @@ func createLink(href, content, class string) *html.Node { a := &html.Node{ Type: html.ElementNode, Data: atom.A.String(), - Attr: []html.Attribute{ - {Key: "href", Val: href}, - {Key: "data-markdown-generated-content"}, - }, + Attr: []html.Attribute{{Key: "href", Val: href}}, + } + if !RenderBehaviorForTesting.DisableInternalAttributes { + a.Attr = append(a.Attr, html.Attribute{Key: "data-markdown-generated-content"}) } - if class != "" { a.Attr = append(a.Attr, html.Attribute{Key: "class", Val: class}) } diff --git a/modules/markup/html_codepreview_test.go b/modules/markup/html_codepreview_test.go index a90de278f5..5054627dde 100644 --- a/modules/markup/html_codepreview_test.go +++ b/modules/markup/html_codepreview_test.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/markdown" "github.com/stretchr/testify/assert" ) @@ -23,8 +24,8 @@ func TestRenderCodePreview(t *testing.T) { }) test := func(input, expected string) { buffer, err := markup.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - Type: "markdown", + Ctx: git.DefaultContext, + MarkupType: markdown.MarkupName, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index 8f516751b0..2fb657f56b 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" + testModule "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" @@ -123,8 +124,9 @@ func TestRender_IssueIndexPattern2(t *testing.T) { } expectedNil := fmt.Sprintf(expectedFmt, links...) testRenderIssueIndexPattern(t, s, expectedNil, &RenderContext{ - Ctx: git.DefaultContext, - Metas: localMetas, + Ctx: git.DefaultContext, + Metas: localMetas, + ContentMode: RenderContentAsComment, }) class := "ref-issue" @@ -137,8 +139,9 @@ func TestRender_IssueIndexPattern2(t *testing.T) { } expectedNum := fmt.Sprintf(expectedFmt, links...) testRenderIssueIndexPattern(t, s, expectedNum, &RenderContext{ - Ctx: git.DefaultContext, - Metas: numericMetas, + Ctx: git.DefaultContext, + Metas: numericMetas, + ContentMode: RenderContentAsComment, }) } @@ -266,7 +269,6 @@ func TestRender_IssueIndexPattern_Document(t *testing.T) { "user": "someUser", "repo": "someRepo", "style": IssueNameStyleNumeric, - "mode": "document", } testRenderIssueIndexPattern(t, "#1", "#1", &RenderContext{ @@ -316,8 +318,8 @@ func TestRender_AutoLink(t *testing.T) { Links: Links{ Base: TestRepoURL, }, - Metas: localMetas, - IsWiki: true, + Metas: localMetas, + ContentMode: RenderContentAsWiki, }, strings.NewReader(input), &buffer) assert.Equal(t, err, nil) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String())) @@ -340,7 +342,7 @@ func TestRender_AutoLink(t *testing.T) { func TestRender_FullIssueURLs(t *testing.T) { setting.AppURL = TestAppURL - + defer testModule.MockVariableValue(&RenderBehaviorForTesting.DisableInternalAttributes, true)() test := func(input, expected string) { var result strings.Builder err := postProcess(&RenderContext{ @@ -351,9 +353,7 @@ func TestRender_FullIssueURLs(t *testing.T) { Metas: localMetas, }, []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result) assert.NoError(t, err) - actual := result.String() - actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "") - assert.Equal(t, expected, actual) + assert.Equal(t, expected, result.String()) } 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") diff --git a/modules/markup/html_issue.go b/modules/markup/html_issue.go index b6d4ed6a8e..fa630656ce 100644 --- a/modules/markup/html_issue.go +++ b/modules/markup/html_issue.go @@ -67,9 +67,8 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { return } - // FIXME: the use of "mode" is quite dirty and hacky, for example: what is a "document"? how should it be rendered? - // The "mode" approach should be refactored to some other more clear&reliable way. - crossLinkOnly := ctx.Metas["mode"] == "document" && !ctx.IsWiki + // crossLinkOnly if not comment and not wiki + crossLinkOnly := ctx.ContentMode != RenderContentAsTitle && ctx.ContentMode != RenderContentAsComment && ctx.ContentMode != RenderContentAsWiki var ( found bool diff --git a/modules/markup/html_link.go b/modules/markup/html_link.go index 9350634568..30564da548 100644 --- a/modules/markup/html_link.go +++ b/modules/markup/html_link.go @@ -20,7 +20,7 @@ func ResolveLink(ctx *RenderContext, link, userContentAnchorPrefix string) (resu isAnchorFragment := link != "" && link[0] == '#' if !isAnchorFragment && !IsFullURLString(link) { linkBase := ctx.Links.Base - if ctx.IsWiki { + if ctx.ContentMode == RenderContentAsWiki { // no need to check if the link should be resolved as a wiki link or a wiki raw link // just use wiki link here and it will be redirected to a wiki raw link if necessary linkBase = ctx.Links.WikiLink() @@ -147,7 +147,7 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) { } if image { if !absoluteLink { - link = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), link) + link = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.ContentMode == RenderContentAsWiki), link) } title := props["title"] if title == "" { diff --git a/modules/markup/html_node.go b/modules/markup/html_node.go index 6d784975b9..c499854053 100644 --- a/modules/markup/html_node.go +++ b/modules/markup/html_node.go @@ -17,7 +17,7 @@ func visitNodeImg(ctx *RenderContext, img *html.Node) (next *html.Node) { } if IsNonEmptyRelativePath(attr.Val) { - attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val) + attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.ContentMode == RenderContentAsWiki), attr.Val) // By default, the "<img>" tag should also be clickable, // because frontend use `<img>` to paste the re-scaled image into the markdown, @@ -53,7 +53,7 @@ func visitNodeVideo(ctx *RenderContext, node *html.Node) (next *html.Node) { continue } if IsNonEmptyRelativePath(attr.Val) { - attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), attr.Val) + attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.ContentMode == RenderContentAsWiki), attr.Val) } attr.Val = camoHandleLink(attr.Val) node.Attr[i] = attr diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 82aded4407..262d0fc4dd 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" + testModule "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" @@ -104,7 +105,7 @@ func TestRender_Commits(t *testing.T) { func TestRender_CrossReferences(t *testing.T) { setting.AppURL = markup.TestAppURL - + defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() test := func(input, expected string) { buffer, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, @@ -116,9 +117,7 @@ func TestRender_CrossReferences(t *testing.T) { Metas: localMetas, }, input) assert.NoError(t, err) - actual := strings.TrimSpace(buffer) - actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "") - assert.Equal(t, strings.TrimSpace(expected), actual) + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) } test( @@ -148,7 +147,7 @@ func TestRender_CrossReferences(t *testing.T) { func TestRender_links(t *testing.T) { setting.AppURL = markup.TestAppURL - + defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() test := func(input, expected string) { buffer, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, @@ -158,9 +157,7 @@ func TestRender_links(t *testing.T) { }, }, input) assert.NoError(t, err) - actual := strings.TrimSpace(buffer) - actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "") - assert.Equal(t, strings.TrimSpace(expected), actual) + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) } oldCustomURLSchemes := setting.Markdown.CustomURLSchemes @@ -261,7 +258,7 @@ func TestRender_links(t *testing.T) { func TestRender_email(t *testing.T) { setting.AppURL = markup.TestAppURL - + defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() test := func(input, expected string) { res, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, @@ -271,9 +268,7 @@ func TestRender_email(t *testing.T) { }, }, input) assert.NoError(t, err) - actual := strings.TrimSpace(res) - actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "") - assert.Equal(t, strings.TrimSpace(expected), actual) + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res)) } // Text that should be turned into email link @@ -302,10 +297,10 @@ func TestRender_email(t *testing.T) { j.doe@example.com; j.doe@example.com? j.doe@example.com!`, - `<p><a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>,<br/> -<a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>.<br/> -<a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>;<br/> -<a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>?<br/> + `<p><a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>, +<a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>. +<a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>; +<a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>? <a href="mailto:j.doe@example.com" rel="nofollow">j.doe@example.com</a>!</p>`) // Test that should *not* be turned into email links @@ -418,8 +413,8 @@ func TestRender_ShortLinks(t *testing.T) { Links: markup.Links{ Base: markup.TestRepoURL, }, - Metas: localMetas, - IsWiki: true, + Metas: localMetas, + ContentMode: markup.RenderContentAsWiki, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer))) @@ -531,10 +526,10 @@ func TestRender_ShortLinks(t *testing.T) { func TestRender_RelativeMedias(t *testing.T) { render := func(input string, isWiki bool, links markup.Links) string { buffer, err := markdown.RenderString(&markup.RenderContext{ - Ctx: git.DefaultContext, - Links: links, - Metas: localMetas, - IsWiki: isWiki, + Ctx: git.DefaultContext, + Links: links, + Metas: localMetas, + ContentMode: util.Iif(isWiki, markup.RenderContentAsWiki, markup.RenderContentAsComment), }, input) assert.NoError(t, err) return strings.TrimSpace(string(buffer)) @@ -604,12 +599,7 @@ func Test_ParseClusterFuzz(t *testing.T) { func TestPostProcess_RenderDocument(t *testing.T) { setting.AppURL = markup.TestAppURL setting.StaticURLPrefix = markup.TestAppURL // can't run standalone - - localMetas := map[string]string{ - "user": "go-gitea", - "repo": "gitea", - "mode": "document", - } + defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() test := func(input, expected string) { var res strings.Builder @@ -619,12 +609,10 @@ func TestPostProcess_RenderDocument(t *testing.T) { AbsolutePrefix: true, Base: "https://example.com", }, - Metas: localMetas, + Metas: map[string]string{"user": "go-gitea", "repo": "gitea"}, }, strings.NewReader(input), &res) assert.NoError(t, err) - actual := strings.TrimSpace(res.String()) - actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "") - assert.Equal(t, strings.TrimSpace(expected), actual) + assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res.String())) } // Issue index shouldn't be post processing in a document. diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index 0cd9dc5f30..c8488cfb50 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -72,7 +72,12 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa g.transformList(ctx, v, rc) case *ast.Text: if v.SoftLineBreak() && !v.HardLineBreak() { - if ctx.Metas["mode"] != "document" { + // TODO: this was a quite unclear part, old code: `if metas["mode"] != "document" { use comment link break setting }` + // many places render non-comment contents with no mode=document, then these contents also use comment's hard line break setting + // especially in many tests. + if markup.RenderBehaviorForTesting.ForceHardLineBreak { + v.SetHardLineBreak(true) + } else if ctx.ContentMode == markup.RenderContentAsComment { v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInComments) } else { v.SetHardLineBreak(setting.Markdown.EnableHardLineBreakInDocuments) diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index db4e5706f6..6af0deb27b 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -257,9 +257,7 @@ func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Wri // Render renders Markdown to HTML with all specific handling stuff. func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error { - if ctx.Type == "" { - ctx.Type = MarkupName - } + ctx.MarkupType = MarkupName return markup.Render(ctx, input, output) } diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index ad38e7a088..315eed2e62 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/svg" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" @@ -74,7 +75,7 @@ func TestRender_StandardLinks(t *testing.T) { Links: markup.Links{ Base: FullURL, }, - IsWiki: true, + ContentMode: markup.RenderContentAsWiki, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer))) @@ -296,23 +297,22 @@ This PR has been generated by [Renovate Bot](https://github.com/renovatebot/reno } func TestTotal_RenderWiki(t *testing.T) { + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)() + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() setting.AppURL = AppURL - answers := testAnswers(util.URLJoin(FullURL, "wiki"), util.URLJoin(FullURL, "wiki", "raw")) - for i := 0; i < len(sameCases); i++ { line, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, Links: markup.Links{ Base: FullURL, }, - Repo: newMockRepo(testRepoOwnerName, testRepoName), - Metas: localMetas, - IsWiki: true, + Repo: newMockRepo(testRepoOwnerName, testRepoName), + Metas: localMetas, + ContentMode: markup.RenderContentAsWiki, }, sameCases[i]) assert.NoError(t, err) - actual := strings.ReplaceAll(string(line), ` data-markdown-generated-content=""`, "") - assert.Equal(t, answers[i], actual) + assert.Equal(t, answers[i], string(line)) } testCases := []string{ @@ -334,19 +334,18 @@ func TestTotal_RenderWiki(t *testing.T) { Links: markup.Links{ Base: FullURL, }, - IsWiki: true, + ContentMode: markup.RenderContentAsWiki, }, testCases[i]) assert.NoError(t, err) - actual := strings.ReplaceAll(string(line), ` data-markdown-generated-content=""`, "") - assert.EqualValues(t, testCases[i+1], actual) + assert.EqualValues(t, testCases[i+1], string(line)) } } func TestTotal_RenderString(t *testing.T) { + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)() + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() setting.AppURL = AppURL - answers := testAnswers(util.URLJoin(FullURL, "src", "master"), util.URLJoin(FullURL, "media", "master")) - for i := 0; i < len(sameCases); i++ { line, err := markdown.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, @@ -358,8 +357,7 @@ func TestTotal_RenderString(t *testing.T) { Metas: localMetas, }, sameCases[i]) assert.NoError(t, err) - actual := strings.ReplaceAll(string(line), ` data-markdown-generated-content=""`, "") - assert.Equal(t, answers[i], actual) + assert.Equal(t, answers[i], string(line)) } testCases := []string{} @@ -428,6 +426,7 @@ func TestRenderSiblingImages_Issue12925(t *testing.T) { expected := `<p><a href="/image1" target="_blank" rel="nofollow noopener"><img src="/image1" alt="image1"></a><br> <a href="/image2" target="_blank" rel="nofollow noopener"><img src="/image2" alt="image2"></a></p> ` + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)() res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase) assert.NoError(t, err) assert.Equal(t, expected, res) @@ -996,11 +995,16 @@ space</p> }, } + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)() + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() for i, c := range cases { - result, err := markdown.RenderString(&markup.RenderContext{Ctx: context.Background(), Links: c.Links, IsWiki: c.IsWiki}, input) + result, err := markdown.RenderString(&markup.RenderContext{ + Ctx: context.Background(), + Links: c.Links, + ContentMode: util.Iif(c.IsWiki, markup.RenderContentAsWiki, markup.RenderContentAsDefault), + }, input) assert.NoError(t, err, "Unexpected error in testcase: %v", i) - actual := strings.ReplaceAll(string(result), ` data-markdown-generated-content=""`, "") - assert.Equal(t, c.Expected, actual, "Unexpected result in testcase %v", i) + assert.Equal(t, c.Expected, string(result), "Unexpected result in testcase %v", i) } } diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go index 812e24f0a2..4ed4118854 100644 --- a/modules/markup/markdown/transform_image.go +++ b/modules/markup/markdown/transform_image.go @@ -21,7 +21,7 @@ func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image) // Check if the destination is a real link if len(v.Destination) > 0 && !markup.IsFullURLBytes(v.Destination) { v.Destination = []byte(giteautil.URLJoin( - ctx.Links.ResolveMediaLink(ctx.IsWiki), + ctx.Links.ResolveMediaLink(ctx.ContentMode == markup.RenderContentAsWiki), strings.TrimLeft(string(v.Destination), "/"), )) } diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go index 25f8d15ef4..6b9c963157 100644 --- a/modules/markup/orgmode/orgmode.go +++ b/modules/markup/orgmode/orgmode.go @@ -144,14 +144,15 @@ func (r *Writer) resolveLink(kind, link string) string { } base := r.Ctx.Links.Base - if r.Ctx.IsWiki { + isWiki := r.Ctx.ContentMode == markup.RenderContentAsWiki + if isWiki { base = r.Ctx.Links.WikiLink() } else if r.Ctx.Links.HasBranchInfo() { base = r.Ctx.Links.SrcLink() } if kind == "image" || kind == "video" { - base = r.Ctx.Links.ResolveMediaLink(r.Ctx.IsWiki) + base = r.Ctx.Links.ResolveMediaLink(isWiki) } link = util.URLJoin(base, link) diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go index 75b60ed81f..b882678c7e 100644 --- a/modules/markup/orgmode/orgmode_test.go +++ b/modules/markup/orgmode/orgmode_test.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" ) @@ -26,7 +27,7 @@ func TestRender_StandardLinks(t *testing.T) { Base: "/relative-path", BranchPath: "branch/main", }, - IsWiki: isWiki, + ContentMode: util.Iif(isWiki, markup.RenderContentAsWiki, markup.RenderContentAsDefault), }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) diff --git a/modules/markup/render.go b/modules/markup/render.go index f2ce9229af..add50f4382 100644 --- a/modules/markup/render.go +++ b/modules/markup/render.go @@ -5,11 +5,9 @@ package markup import ( "context" - "errors" "fmt" "io" "net/url" - "path/filepath" "strings" "sync" @@ -29,15 +27,44 @@ const ( RenderMetaAsTable RenderMetaMode = "table" ) +type RenderContentMode string + +const ( + RenderContentAsDefault RenderContentMode = "" // empty means "default", no special handling, maybe just a simple "document" + RenderContentAsComment RenderContentMode = "comment" + RenderContentAsTitle RenderContentMode = "title" + RenderContentAsWiki RenderContentMode = "wiki" +) + +var RenderBehaviorForTesting struct { + // Markdown line break rendering has 2 default behaviors: + // * Use hard: replace "\n" with "<br>" for comments, setting.Markdown.EnableHardLineBreakInComments=true + // * Keep soft: "\n" for non-comments (a.k.a. documents), setting.Markdown.EnableHardLineBreakInDocuments=false + // In history, there was a mess: + // * The behavior was controlled by `Metas["mode"] != "document", + // * However, many places render the content without setting "mode" in Metas, all these places used comment line break setting incorrectly + ForceHardLineBreak bool + + // Gitea will emit some internal attributes for various purposes, these attributes don't affect rendering. + // But there are too many hard-coded test cases, to avoid changing all of them again and again, we can disable emitting these internal attributes. + DisableInternalAttributes bool +} + // RenderContext represents a render context type RenderContext struct { - Ctx context.Context - RelativePath string // relative path from tree root of the branch - Type string - IsWiki bool - Links Links - Metas map[string]string // user, repo, mode(comment/document) - DefaultLink string + Ctx context.Context + RelativePath string // relative path from tree root of the branch + + // eg: "orgmode", "asciicast", "console" + // for file mode, it could be left as empty, and will be detected by file extension in RelativePath + MarkupType string + + // what the content will be used for: eg: for comment or for wiki? or just render a file? + ContentMode RenderContentMode + + Links Links // special link references for rendering, especially when there is a branch/tree path + Metas map[string]string // user&repo, format&style®exp (for external issue pattern), teams&org (for mention), BranchNameSubURL(for iframe&asciicast) + DefaultLink string // TODO: need to figure out GitRepo *git.Repository Repo gitrepo.Repository ShaExistCache map[string]bool @@ -77,12 +104,29 @@ func (ctx *RenderContext) AddCancel(fn func()) { // Render renders markup file to HTML with all specific handling stuff. func Render(ctx *RenderContext, input io.Reader, output io.Writer) error { - if ctx.Type != "" { - return renderByType(ctx, input, output) - } else if ctx.RelativePath != "" { - return renderFile(ctx, input, output) + if ctx.MarkupType == "" && ctx.RelativePath != "" { + ctx.MarkupType = DetectMarkupTypeByFileName(ctx.RelativePath) + if ctx.MarkupType == "" { + return util.NewInvalidArgumentErrorf("unsupported file to render: %q", ctx.RelativePath) + } + } + + renderer := renderers[ctx.MarkupType] + if renderer == nil { + return util.NewInvalidArgumentErrorf("unsupported markup type: %q", ctx.MarkupType) + } + + if ctx.RelativePath != "" { + if externalRender, ok := renderer.(ExternalRenderer); ok && externalRender.DisplayInIFrame() { + if !ctx.InStandalonePage { + // for an external "DisplayInIFrame" render, it could only output its content in a standalone page + // otherwise, a <iframe> should be outputted to embed the external rendered page + return renderIFrame(ctx, output) + } + } } - return errors.New("render options both filename and type missing") + + return render(ctx, renderer, input, output) } // RenderString renders Markup string to HTML with all specific handling stuff and return string @@ -170,42 +214,6 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr return err } -func renderByType(ctx *RenderContext, input io.Reader, output io.Writer) error { - if renderer, ok := renderers[ctx.Type]; ok { - return render(ctx, renderer, input, output) - } - return fmt.Errorf("unsupported render type: %s", ctx.Type) -} - -// ErrUnsupportedRenderExtension represents the error when extension doesn't supported to render -type ErrUnsupportedRenderExtension struct { - Extension string -} - -func IsErrUnsupportedRenderExtension(err error) bool { - _, ok := err.(ErrUnsupportedRenderExtension) - return ok -} - -func (err ErrUnsupportedRenderExtension) Error() string { - return fmt.Sprintf("Unsupported render extension: %s", err.Extension) -} - -func renderFile(ctx *RenderContext, input io.Reader, output io.Writer) error { - extension := strings.ToLower(filepath.Ext(ctx.RelativePath)) - if renderer, ok := extRenderers[extension]; ok { - if r, ok := renderer.(ExternalRenderer); ok && r.DisplayInIFrame() { - if !ctx.InStandalonePage { - // for an external render, it could only output its content in a standalone page - // otherwise, a <iframe> should be outputted to embed the external rendered page - return renderIFrame(ctx, output) - } - } - return render(ctx, renderer, input, output) - } - return ErrUnsupportedRenderExtension{extension} -} - // Init initializes the render global variables func Init(ph *ProcessorHelper) { if ph != nil { diff --git a/modules/structs/miscellaneous.go b/modules/structs/miscellaneous.go index 3b206c1dd7..cfdb6db96c 100644 --- a/modules/structs/miscellaneous.go +++ b/modules/structs/miscellaneous.go @@ -21,7 +21,7 @@ type MarkupOption struct { // // in: body Text string - // Mode to render (comment, gfm, markdown, file) + // Mode to render (markdown, comment, wiki, file) // // in: body Mode string @@ -30,8 +30,9 @@ type MarkupOption struct { // // in: body Context string - // Is it a wiki page ? + // Is it a wiki page? (use mode=wiki instead) // + // Deprecated: true // in: body Wiki bool // File path for detecting extension in file mode @@ -50,7 +51,7 @@ type MarkdownOption struct { // // in: body Text string - // Mode to render (comment, gfm, markdown) + // Mode to render (markdown, comment, wiki, file) // // in: body Mode string @@ -59,8 +60,9 @@ type MarkdownOption struct { // // in: body Context string - // Is it a wiki page ? + // Is it a wiki page? (use mode=wiki instead) // + // Deprecated: true // in: body Wiki bool } diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index 1201828345..1db1e4a111 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -94,8 +94,9 @@ func (ut *RenderUtils) RenderCommitBody(msg string, metas map[string]string) tem } renderedMessage, err := markup.RenderCommitMessage(&markup.RenderContext{ - Ctx: ut.ctx, - Metas: metas, + Ctx: ut.ctx, + Metas: metas, + ContentMode: markup.RenderContentAsComment, }, template.HTMLEscapeString(msgLine)) if err != nil { log.Error("RenderCommitMessage: %v", err) @@ -116,8 +117,9 @@ func renderCodeBlock(htmlEscapedTextToRender template.HTML) template.HTML { // RenderIssueTitle renders issue/pull title with defined post processors func (ut *RenderUtils) RenderIssueTitle(text string, metas map[string]string) template.HTML { renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{ - Ctx: ut.ctx, - Metas: metas, + Ctx: ut.ctx, + ContentMode: markup.RenderContentAsTitle, + Metas: metas, }, template.HTMLEscapeString(text)) if err != nil { log.Error("RenderIssueTitle: %v", err) diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 0a6e89c5f2..2d331b8317 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation" "github.com/stretchr/testify/assert" @@ -72,6 +73,7 @@ func newTestRenderUtils() *RenderUtils { } func TestRenderCommitBody(t *testing.T) { + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() type args struct { msg string metas map[string]string @@ -129,23 +131,21 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit <a href="/mention-user" class="mention">@mention-user</a> test <a href="/user13/repo11/issues/123" class="ref-issue">#123</a> space` - actual := strings.ReplaceAll(string(newTestRenderUtils().RenderCommitBody(testInput(), testMetas)), ` data-markdown-generated-content=""`, "") - assert.EqualValues(t, expected, actual) + assert.EqualValues(t, expected, string(newTestRenderUtils().RenderCommitBody(testInput(), testMetas))) } func TestRenderCommitMessage(t *testing.T) { expected := `space <a href="/mention-user" data-markdown-generated-content="" class="mention">@mention-user</a> ` - assert.EqualValues(t, expected, newTestRenderUtils().RenderCommitMessage(testInput(), testMetas)) } func TestRenderCommitMessageLinkSubject(t *testing.T) { expected := `<a href="https://example.com/link" class="default-link muted">space </a><a href="/mention-user" data-markdown-generated-content="" class="mention">@mention-user</a>` - assert.EqualValues(t, expected, newTestRenderUtils().RenderCommitMessageLinkSubject(testInput(), "https://example.com/link", testMetas)) } func TestRenderIssueTitle(t *testing.T) { + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() expected := ` space @mention-user<SPACE><SPACE> /just/a/path.bin https://example.com/file.bin @@ -168,11 +168,11 @@ mail@domain.com space<SPACE><SPACE> ` expected = strings.ReplaceAll(expected, "<SPACE>", " ") - actual := strings.ReplaceAll(string(newTestRenderUtils().RenderIssueTitle(testInput(), testMetas)), ` data-markdown-generated-content=""`, "") - assert.EqualValues(t, expected, actual) + assert.EqualValues(t, expected, string(newTestRenderUtils().RenderIssueTitle(testInput(), testMetas))) } func TestRenderMarkdownToHtml(t *testing.T) { + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() 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> @@ -194,8 +194,7 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit #123 space</p> ` - actual := strings.ReplaceAll(string(newTestRenderUtils().MarkdownToHtml(testInput())), ` data-markdown-generated-content=""`, "") - assert.Equal(t, expected, actual) + assert.Equal(t, expected, string(newTestRenderUtils().MarkdownToHtml(testInput()))) } func TestRenderLabels(t *testing.T) { diff --git a/routers/api/v1/misc/markup.go b/routers/api/v1/misc/markup.go index 9699c79368..868ed92519 100644 --- a/routers/api/v1/misc/markup.go +++ b/routers/api/v1/misc/markup.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/context" @@ -41,7 +42,8 @@ func Markup(ctx *context.APIContext) { return } - common.RenderMarkup(ctx.Base, ctx.Repo, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki) + mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck + common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, form.FilePath) } // Markdown render markdown document to HTML @@ -71,12 +73,8 @@ func Markdown(ctx *context.APIContext) { return } - mode := "markdown" - if form.Mode == "comment" || form.Mode == "gfm" { - mode = form.Mode - } - - common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, "", form.Wiki) + mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck + common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, "") } // MarkdownRaw render raw markdown HTML diff --git a/routers/api/v1/misc/markup_test.go b/routers/api/v1/misc/markup_test.go index abffdf3516..6b8c09034a 100644 --- a/routers/api/v1/misc/markup_test.go +++ b/routers/api/v1/misc/markup_test.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/contexttest" @@ -24,6 +25,7 @@ const AppURL = "http://localhost:3000/" func testRenderMarkup(t *testing.T, mode string, wiki bool, filePath, text, expectedBody string, expectedCode int) { setting.AppURL = AppURL + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() context := "/gogits/gogs" if !wiki { context += path.Join("/src/branch/main", path.Dir(filePath)) @@ -38,13 +40,13 @@ func testRenderMarkup(t *testing.T, mode string, wiki bool, filePath, text, expe ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markup") web.SetForm(ctx, &options) Markup(ctx) - actual := strings.ReplaceAll(resp.Body.String(), ` data-markdown-generated-content=""`, "") - assert.Equal(t, expectedBody, actual) + assert.Equal(t, expectedBody, resp.Body.String()) assert.Equal(t, expectedCode, resp.Code) resp.Body.Reset() } func testRenderMarkdown(t *testing.T, mode string, wiki bool, text, responseBody string, responseCode int) { + defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)() setting.AppURL = AppURL context := "/gogits/gogs" if !wiki { @@ -59,8 +61,7 @@ func testRenderMarkdown(t *testing.T, mode string, wiki bool, text, responseBody ctx, resp := contexttest.MockAPIContext(t, "POST /api/v1/markdown") web.SetForm(ctx, &options) Markdown(ctx) - actual := strings.ReplaceAll(resp.Body.String(), ` data-markdown-generated-content=""`, "") - assert.Equal(t, responseBody, actual) + assert.Equal(t, responseBody, resp.Body.String()) assert.Equal(t, responseCode, resp.Code) resp.Body.Reset() } @@ -158,8 +159,8 @@ Here are some links to the most important topics. You can find the full list of <a href="http://localhost:3000/gogits/gogs/media/branch/main/path/image.png" target="_blank" rel="nofollow noopener"><img src="http://localhost:3000/gogits/gogs/media/branch/main/path/image.png" alt="Image"/></a></p> `, http.StatusOK) - testRenderMarkup(t, "file", true, "path/test.unknown", "## Test", "Unsupported render extension: .unknown\n", http.StatusUnprocessableEntity) - testRenderMarkup(t, "unknown", true, "", "## Test", "Unknown mode: unknown\n", http.StatusUnprocessableEntity) + testRenderMarkup(t, "file", false, "path/test.unknown", "## Test", "unsupported file to render: \"path/test.unknown\"\n", http.StatusUnprocessableEntity) + testRenderMarkup(t, "unknown", false, "", "## Test", "Unknown mode: unknown\n", http.StatusUnprocessableEntity) } var simpleCases = []string{ diff --git a/routers/common/markup.go b/routers/common/markup.go index 0a00eac7d4..c8cc1a5ff1 100644 --- a/routers/common/markup.go +++ b/routers/common/markup.go @@ -5,21 +5,22 @@ package common import ( + "errors" "fmt" "net/http" "path" "strings" - repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" ) // RenderMarkup renders markup text for the /markup and /markdown endpoints -func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPathContext, filePath string, wiki bool) { +func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPathContext, filePath string) { // urlPathContext format is "/subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}" // filePath is the path of the file to render if the end user is trying to preview a repo file (mode == "file") // filePath will be used as RenderContext.RelativePath @@ -27,32 +28,33 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPa // for example, when previewing file "/gitea/owner/repo/src/branch/features/feat-123/doc/CHANGE.md", then filePath is "doc/CHANGE.md" // and the urlPathContext is "/gitea/owner/repo/src/branch/features/feat-123/doc" - var markupType, relativePath string - - links := markup.Links{AbsolutePrefix: true} + renderCtx := &markup.RenderContext{ + Ctx: ctx, + Links: markup.Links{AbsolutePrefix: true}, + MarkupType: markdown.MarkupName, + } if urlPathContext != "" { - links.Base = fmt.Sprintf("%s%s", httplib.GuessCurrentHostURL(ctx), urlPathContext) + renderCtx.Links.Base = fmt.Sprintf("%s%s", httplib.GuessCurrentHostURL(ctx), urlPathContext) } - switch mode { - case "markdown": - // Raw markdown - if err := markdown.RenderRaw(&markup.RenderContext{ - Ctx: ctx, - Links: links, - }, strings.NewReader(text), ctx.Resp); err != nil { + if mode == "" || mode == "markdown" { + // raw markdown doesn't need any special handling + if err := markdown.RenderRaw(renderCtx, strings.NewReader(text), ctx.Resp); err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) } return + } + switch mode { + case "gfm": // legacy mode, do nothing case "comment": - // Issue & comment content - markupType = markdown.MarkupName - case "gfm": - // GitHub Flavored Markdown - markupType = markdown.MarkupName + renderCtx.ContentMode = markup.RenderContentAsComment + case "wiki": + renderCtx.ContentMode = markup.RenderContentAsWiki case "file": - markupType = "" // render the repo file content by its extension - relativePath = filePath + // render the repo file content by its extension + renderCtx.MarkupType = "" + renderCtx.RelativePath = filePath + renderCtx.InStandalonePage = true default: ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Unknown mode: %s", mode)) return @@ -67,33 +69,19 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPa refPath := strings.Join(fields[3:], "/") // it is "branch/features/feat-12/doc" refPath = strings.TrimSuffix(refPath, "/"+fileDir) // now we get the correct branch path: "branch/features/feat-12" - links = markup.Links{AbsolutePrefix: true, Base: absoluteBasePrefix, BranchPath: refPath, TreePath: fileDir} + renderCtx.Links = markup.Links{AbsolutePrefix: true, Base: absoluteBasePrefix, BranchPath: refPath, TreePath: fileDir} } - meta := map[string]string{} - var repoCtx *repo_model.Repository if repo != nil && repo.Repository != nil { - repoCtx = repo.Repository - if mode == "comment" { - meta = repo.Repository.ComposeMetas(ctx) + renderCtx.Repo = repo.Repository + if renderCtx.ContentMode == markup.RenderContentAsComment { + renderCtx.Metas = repo.Repository.ComposeMetas(ctx) } else { - meta = repo.Repository.ComposeDocumentMetas(ctx) + renderCtx.Metas = repo.Repository.ComposeDocumentMetas(ctx) } } - if mode != "comment" { - meta["mode"] = "document" - } - - if err := markup.Render(&markup.RenderContext{ - Ctx: ctx, - Repo: repoCtx, - Links: links, - Metas: meta, - IsWiki: wiki, - Type: markupType, - RelativePath: relativePath, - }, strings.NewReader(text), ctx.Resp); err != nil { - if markup.IsErrUnsupportedRenderExtension(err) { + if err := markup.Render(renderCtx, strings.NewReader(text), ctx.Resp); err != nil { + if errors.Is(err, util.ErrInvalidArgument) { ctx.Error(http.StatusUnprocessableEntity, err.Error()) } else { ctx.Error(http.StatusInternalServerError, err.Error()) diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go index 3f33e9933a..a273515c8a 100644 --- a/routers/web/feed/convert.go +++ b/routers/web/feed/convert.go @@ -56,7 +56,6 @@ func renderMarkdown(ctx *context.Context, act *activities_model.Action, content Links: markup.Links{ Base: act.GetRepoLink(ctx), }, - Type: markdown.MarkupName, Metas: map[string]string{ "user": act.GetRepoUserName(ctx), "repo": act.GetRepoName(ctx), diff --git a/routers/web/misc/markup.go b/routers/web/misc/markup.go index 2dbbd6fc09..0c7ec6c2eb 100644 --- a/routers/web/misc/markup.go +++ b/routers/web/misc/markup.go @@ -6,6 +6,7 @@ package misc import ( api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/context" @@ -14,5 +15,6 @@ import ( // Markup render markup document to HTML func Markup(ctx *context.Context) { form := web.GetForm(ctx).(*api.MarkupOption) - common.RenderMarkup(ctx.Base, ctx.Repo, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki) + mode := util.Iif(form.Wiki, "wiki", form.Mode) //nolint:staticcheck + common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, form.FilePath) } diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 7d9281b397..7030f6d8a9 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -312,6 +312,7 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{ Ctx: ctx, + MarkupType: markupType, RelativePath: path.Join(ctx.Repo.TreePath, readmeFile.Name()), // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path). Links: markup.Links{ Base: ctx.Repo.RepoLink, @@ -502,28 +503,20 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["ReadmeExist"] = readmeExist markupType := markup.DetectMarkupTypeByFileName(blob.Name()) - // If the markup is detected by custom markup renderer it should not be reset later on - // to not pass it down to the render context. - detected := false if markupType == "" { - detected = true markupType = markup.DetectRendererType(blob.Name(), bytes.NewReader(buf)) } if markupType != "" { ctx.Data["HasSourceRenderedToggle"] = true } - if markupType != "" && !shouldRenderSource { ctx.Data["IsMarkup"] = true ctx.Data["MarkupType"] = markupType - if !detected { - markupType = "" - } metas := ctx.Repo.Repository.ComposeDocumentMetas(ctx) metas["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL() ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{ Ctx: ctx, - Type: markupType, + MarkupType: markupType, RelativePath: ctx.Repo.TreePath, Links: markup.Links{ Base: ctx.Repo.RepoLink, @@ -615,6 +608,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["MarkupType"] = markupType ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{ Ctx: ctx, + MarkupType: markupType, RelativePath: ctx.Repo.TreePath, Links: markup.Links{ Base: ctx.Repo.RepoLink, diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index d2056353d8..2b8312f10a 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -289,12 +289,12 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { } rctx := &markup.RenderContext{ - Ctx: ctx, - Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx), + Ctx: ctx, + ContentMode: markup.RenderContentAsWiki, + Metas: ctx.Repo.Repository.ComposeDocumentMetas(ctx), Links: markup.Links{ Base: ctx.Repo.RepoLink, }, - IsWiki: true, } buf := &strings.Builder{} diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index d0abf603c3..4fbfc2bd17 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -258,7 +258,6 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb Base: profileDbRepo.Link(), BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)), }, - Metas: map[string]string{"mode": "document"}, }, bytes); err != nil { log.Error("failed to RenderString: %v", err) } else { diff --git a/services/context/org.go b/services/context/org.go index e420629372..132ce19a31 100644 --- a/services/context/org.go +++ b/services/context/org.go @@ -260,8 +260,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) if len(ctx.ContextUser.Description) != 0 { content, err := markdown.RenderString(&markup.RenderContext{ - Metas: map[string]string{"mode": "document"}, - Ctx: ctx, + Ctx: ctx, }, ctx.ContextUser.Description) if err != nil { ctx.ServerError("RenderString", err) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 8fed15b516..a6dcba4f19 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -22615,7 +22615,7 @@ "type": "string" }, "Mode": { - "description": "Mode to render (comment, gfm, markdown)\n\nin: body", + "description": "Mode to render (markdown, comment, wiki, file)\n\nin: body", "type": "string" }, "Text": { @@ -22623,7 +22623,7 @@ "type": "string" }, "Wiki": { - "description": "Is it a wiki page ?\n\nin: body", + "description": "Is it a wiki page? (use mode=wiki instead)\n\nDeprecated: true\nin: body", "type": "boolean" } }, @@ -22642,7 +22642,7 @@ "type": "string" }, "Mode": { - "description": "Mode to render (comment, gfm, markdown, file)\n\nin: body", + "description": "Mode to render (markdown, comment, wiki, file)\n\nin: body", "type": "string" }, "Text": { @@ -22650,7 +22650,7 @@ "type": "string" }, "Wiki": { - "description": "Is it a wiki page ?\n\nin: body", + "description": "Is it a wiki page? (use mode=wiki instead)\n\nDeprecated: true\nin: body", "type": "boolean" } }, diff --git a/tests/integration/markup_external_test.go b/tests/integration/markup_external_test.go index e50f5c1356..2d713b0eb9 100644 --- a/tests/integration/markup_external_test.go +++ b/tests/integration/markup_external_test.go @@ -10,6 +10,8 @@ import ( "strings" "testing" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/external" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/tests" @@ -23,10 +25,9 @@ func TestExternalMarkupRenderer(t *testing.T) { return } - const repoURL = "user30/renderer" - req := NewRequest(t, "GET", repoURL+"/src/branch/master/README.html") + req := NewRequest(t, "GET", "/user30/renderer/src/branch/master/README.html") resp := MakeRequest(t, req, http.StatusOK) - assert.EqualValues(t, "text/html; charset=utf-8", resp.Header()["Content-Type"][0]) + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header().Get("Content-Type")) bs, err := io.ReadAll(resp.Body) assert.NoError(t, err) @@ -36,4 +37,24 @@ func TestExternalMarkupRenderer(t *testing.T) { data, err := div.Html() assert.NoError(t, err) assert.EqualValues(t, "<div>\n\ttest external renderer\n</div>", strings.TrimSpace(data)) + + r := markup.GetRendererByFileName("a.html").(*external.Renderer) + r.RenderContentMode = setting.RenderContentModeIframe + + req = NewRequest(t, "GET", "/user30/renderer/src/branch/master/README.html") + resp = MakeRequest(t, req, http.StatusOK) + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header().Get("Content-Type")) + bs, err = io.ReadAll(resp.Body) + assert.NoError(t, err) + doc = NewHTMLParser(t, bytes.NewBuffer(bs)) + iframe := doc.Find("iframe") + assert.EqualValues(t, "/user30/renderer/render/branch/master/README.html", iframe.AttrOr("src", "")) + + req = NewRequest(t, "GET", "/user30/renderer/render/branch/master/README.html") + resp = MakeRequest(t, req, http.StatusOK) + assert.EqualValues(t, "text/html; charset=utf-8", resp.Header().Get("Content-Type")) + bs, err = io.ReadAll(resp.Body) + assert.NoError(t, err) + assert.EqualValues(t, "frame-src 'self'; sandbox allow-scripts", resp.Header().Get("Content-Security-Policy")) + assert.EqualValues(t, "<div>\n\ttest external renderer\n</div>\n", string(bs)) } diff --git a/web_src/js/features/comp/ComboMarkdownEditor.ts b/web_src/js/features/comp/ComboMarkdownEditor.ts index 576c1bccd6..7117952fa3 100644 --- a/web_src/js/features/comp/ComboMarkdownEditor.ts +++ b/web_src/js/features/comp/ComboMarkdownEditor.ts @@ -74,7 +74,6 @@ export class ComboMarkdownEditor { previewUrl: string; previewContext: string; previewMode: string; - previewWiki: boolean; constructor(container, options = {}) { container._giteaComboMarkdownEditor = this; @@ -213,13 +212,11 @@ export class ComboMarkdownEditor { this.previewUrl = this.tabPreviewer.getAttribute('data-preview-url'); this.previewContext = this.tabPreviewer.getAttribute('data-preview-context'); this.previewMode = this.options.previewMode ?? 'comment'; - this.previewWiki = this.options.previewWiki ?? false; this.tabPreviewer.addEventListener('click', async () => { const formData = new FormData(); formData.append('mode', this.previewMode); formData.append('context', this.previewContext); formData.append('text', this.value()); - formData.append('wiki', String(this.previewWiki)); const response = await POST(this.previewUrl, {data: formData}); const data = await response.text(); renderPreviewPanelContent($(panelPreviewer), data); diff --git a/web_src/js/features/repo-wiki.ts b/web_src/js/features/repo-wiki.ts index 1a62427b73..0e72b87109 100644 --- a/web_src/js/features/repo-wiki.ts +++ b/web_src/js/features/repo-wiki.ts @@ -26,7 +26,6 @@ async function initRepoWikiFormEditor() { formData.append('mode', editor.previewMode); formData.append('context', editor.previewContext); formData.append('text', newContent); - formData.append('wiki', editor.previewWiki); try { const response = await POST(editor.previewUrl, {data: formData}); const data = await response.text(); @@ -51,8 +50,7 @@ async function initRepoWikiFormEditor() { // And another benefit is that we only need to write the style once for both editors. // TODO: Move height style to CSS after EasyMDE removal. editorHeights: {minHeight: '300px', height: 'calc(100vh - 600px)'}, - previewMode: 'gfm', - previewWiki: true, + previewMode: 'wiki', easyMDEOptions: { previewRender: (_content, previewTarget) => previewTarget.innerHTML, // disable builtin preview render toolbar: ['bold', 'italic', 'strikethrough', '|', |