mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-04 01:34:27 +00:00 
			
		
		
		
	Refactor markup render system (#32589)
This PR mainly moves some code and introduces `RenderContext.WithXxx` functions
This commit is contained in:
		@@ -112,14 +112,12 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
 | 
							rctx := markup.NewRenderContext(ctx).
 | 
				
			||||||
			Ctx:  ctx,
 | 
								WithRepoFacade(issue.Repo).
 | 
				
			||||||
			Repo: issue.Repo,
 | 
								WithLinks(markup.Links{Base: issue.Repo.Link()}).
 | 
				
			||||||
			Links: markup.Links{
 | 
								WithMetas(issue.Repo.ComposeMetas(ctx))
 | 
				
			||||||
				Base: issue.Repo.Link(),
 | 
							if comment.RenderedContent, err = markdown.RenderString(rctx,
 | 
				
			||||||
			},
 | 
								comment.Content); err != nil {
 | 
				
			||||||
			Metas: issue.Repo.ComposeMetas(ctx),
 | 
					 | 
				
			||||||
		}, comment.Content); err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -617,10 +617,7 @@ func (repo *Repository) CanEnableEditor() bool {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// DescriptionHTML does special handles to description and return HTML string.
 | 
					// DescriptionHTML does special handles to description and return HTML string.
 | 
				
			||||||
func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML {
 | 
					func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML {
 | 
				
			||||||
	desc, err := markup.RenderDescriptionHTML(&markup.RenderContext{
 | 
						desc, err := markup.RenderDescriptionHTML(markup.NewRenderContext(ctx), repo.Description)
 | 
				
			||||||
		Ctx: ctx,
 | 
					 | 
				
			||||||
		// Don't use Metas to speedup requests
 | 
					 | 
				
			||||||
	}, repo.Description)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("Failed to render description for %s (ID: %d): %v", repo.Name, repo.ID, err)
 | 
							log.Error("Failed to render description for %s (ID: %d): %v", repo.Name, repo.ID, err)
 | 
				
			||||||
		return template.HTML(markup.SanitizeDescription(repo.Description))
 | 
							return template.HTML(markup.SanitizeDescription(repo.Description))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@ import (
 | 
				
			|||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	stdcsv "encoding/csv"
 | 
						stdcsv "encoding/csv"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"path/filepath"
 | 
						"path"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -53,7 +53,7 @@ func CreateReaderAndDetermineDelimiter(ctx *markup.RenderContext, rd io.Reader)
 | 
				
			|||||||
func determineDelimiter(ctx *markup.RenderContext, data []byte) rune {
 | 
					func determineDelimiter(ctx *markup.RenderContext, data []byte) rune {
 | 
				
			||||||
	extension := ".csv"
 | 
						extension := ".csv"
 | 
				
			||||||
	if ctx != nil {
 | 
						if ctx != nil {
 | 
				
			||||||
		extension = strings.ToLower(filepath.Ext(ctx.RelativePath))
 | 
							extension = strings.ToLower(path.Ext(ctx.RenderOptions.RelativePath))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var delimiter rune
 | 
						var delimiter rune
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,13 +5,13 @@ package csv
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"encoding/csv"
 | 
						"encoding/csv"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/translation"
 | 
						"code.gitea.io/gitea/modules/translation"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -231,10 +231,7 @@ John Doe	john@doe.com	This,note,had,a,lot,of,commas,to,test,delimiters`,
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for n, c := range cases {
 | 
						for n, c := range cases {
 | 
				
			||||||
		delimiter := determineDelimiter(&markup.RenderContext{
 | 
							delimiter := determineDelimiter(markup.NewRenderContext(context.Background()).WithRelativePath(c.filename), []byte(decodeSlashes(t, c.csv)))
 | 
				
			||||||
			Ctx:          git.DefaultContext,
 | 
					 | 
				
			||||||
			RelativePath: c.filename,
 | 
					 | 
				
			||||||
		}, []byte(decodeSlashes(t, c.csv)))
 | 
					 | 
				
			||||||
		assert.EqualValues(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter)
 | 
							assert.EqualValues(t, c.expectedDelimiter, delimiter, "case %d: delimiter should be equal, expected '%c' got '%c'", n, c.expectedDelimiter, delimiter)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,10 +44,10 @@ func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule {
 | 
				
			|||||||
func (Renderer) Render(ctx *markup.RenderContext, _ io.Reader, output io.Writer) error {
 | 
					func (Renderer) Render(ctx *markup.RenderContext, _ io.Reader, output io.Writer) error {
 | 
				
			||||||
	rawURL := fmt.Sprintf("%s/%s/%s/raw/%s/%s",
 | 
						rawURL := fmt.Sprintf("%s/%s/%s/raw/%s/%s",
 | 
				
			||||||
		setting.AppSubURL,
 | 
							setting.AppSubURL,
 | 
				
			||||||
		url.PathEscape(ctx.Metas["user"]),
 | 
							url.PathEscape(ctx.RenderOptions.Metas["user"]),
 | 
				
			||||||
		url.PathEscape(ctx.Metas["repo"]),
 | 
							url.PathEscape(ctx.RenderOptions.Metas["repo"]),
 | 
				
			||||||
		ctx.Metas["BranchNameSubURL"],
 | 
							ctx.RenderOptions.Metas["BranchNameSubURL"],
 | 
				
			||||||
		url.PathEscape(ctx.RelativePath),
 | 
							url.PathEscape(ctx.RenderOptions.RelativePath),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	return ctx.RenderInternal.FormatWithSafeAttrs(output, `<div class="%s" %s="%s"></div>`, playerClassName, playerSrcAttr, rawURL)
 | 
						return ctx.RenderInternal.FormatWithSafeAttrs(output, `<div class="%s" %s="%s"></div>`, playerClassName, playerSrcAttr, rawURL)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,10 @@
 | 
				
			|||||||
package console
 | 
					package console
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
@@ -24,8 +24,7 @@ func TestRenderConsole(t *testing.T) {
 | 
				
			|||||||
		canRender := render.CanRender("test", strings.NewReader(k))
 | 
							canRender := render.CanRender("test", strings.NewReader(k))
 | 
				
			||||||
		assert.True(t, canRender)
 | 
							assert.True(t, canRender)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		err := render.Render(&markup.RenderContext{Ctx: git.DefaultContext},
 | 
							err := render.Render(markup.NewRenderContext(context.Background()), strings.NewReader(k), &buf)
 | 
				
			||||||
			strings.NewReader(k), &buf)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.EqualValues(t, v, buf.String())
 | 
							assert.EqualValues(t, v, buf.String())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -133,10 +133,10 @@ func (r Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.W
 | 
				
			|||||||
	// Check if maxRows or maxSize is reached, and if true, warn.
 | 
						// Check if maxRows or maxSize is reached, and if true, warn.
 | 
				
			||||||
	if (row >= maxRows && maxRows != 0) || (rd.InputOffset() >= maxSize && maxSize != 0) {
 | 
						if (row >= maxRows && maxRows != 0) || (rd.InputOffset() >= maxSize && maxSize != 0) {
 | 
				
			||||||
		warn := `<table class="data-table"><tr><td>`
 | 
							warn := `<table class="data-table"><tr><td>`
 | 
				
			||||||
		rawLink := ` <a href="` + ctx.Links.RawLink() + `/` + util.PathEscapeSegments(ctx.RelativePath) + `">`
 | 
							rawLink := ` <a href="` + ctx.RenderOptions.Links.RawLink() + `/` + util.PathEscapeSegments(ctx.RenderOptions.RelativePath) + `">`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Try to get the user translation
 | 
							// Try to get the user translation
 | 
				
			||||||
		if locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale); ok {
 | 
							if locale, ok := ctx.Value(translation.ContextKey).(translation.Locale); ok {
 | 
				
			||||||
			warn += locale.TrString("repo.file_too_large")
 | 
								warn += locale.TrString("repo.file_too_large")
 | 
				
			||||||
			rawLink += locale.TrString("repo.file_view_raw")
 | 
								rawLink += locale.TrString("repo.file_view_raw")
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,10 @@
 | 
				
			|||||||
package markup
 | 
					package markup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
@@ -24,8 +24,7 @@ func TestRenderCSV(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for k, v := range kases {
 | 
						for k, v := range kases {
 | 
				
			||||||
		var buf strings.Builder
 | 
							var buf strings.Builder
 | 
				
			||||||
		err := render.Render(&markup.RenderContext{Ctx: git.DefaultContext},
 | 
							err := render.Render(markup.NewRenderContext(context.Background()), strings.NewReader(k), &buf)
 | 
				
			||||||
			strings.NewReader(k), &buf)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.EqualValues(t, v, buf.String())
 | 
							assert.EqualValues(t, v, buf.String())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										19
									
								
								modules/markup/external/external.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								modules/markup/external/external.go
									
									
									
									
										vendored
									
									
								
							@@ -12,7 +12,6 @@ import (
 | 
				
			|||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/graceful"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/process"
 | 
						"code.gitea.io/gitea/modules/process"
 | 
				
			||||||
@@ -80,8 +79,8 @@ func envMark(envName string) string {
 | 
				
			|||||||
func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
 | 
					func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
		command = strings.NewReplacer(
 | 
							command = strings.NewReplacer(
 | 
				
			||||||
			envMark("GITEA_PREFIX_SRC"), ctx.Links.SrcLink(),
 | 
								envMark("GITEA_PREFIX_SRC"), ctx.RenderOptions.Links.SrcLink(),
 | 
				
			||||||
			envMark("GITEA_PREFIX_RAW"), ctx.Links.RawLink(),
 | 
								envMark("GITEA_PREFIX_RAW"), ctx.RenderOptions.Links.RawLink(),
 | 
				
			||||||
		).Replace(p.Command)
 | 
							).Replace(p.Command)
 | 
				
			||||||
		commands = strings.Fields(command)
 | 
							commands = strings.Fields(command)
 | 
				
			||||||
		args     = commands[1:]
 | 
							args     = commands[1:]
 | 
				
			||||||
@@ -113,22 +112,14 @@ func (p *Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.
 | 
				
			|||||||
		args = append(args, f.Name())
 | 
							args = append(args, f.Name())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.Ctx == nil {
 | 
						processCtx, _, finished := process.GetManager().AddContext(ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.RenderOptions.Links.SrcLink()))
 | 
				
			||||||
		if !setting.IsProd || setting.IsInTesting {
 | 
					 | 
				
			||||||
			panic("RenderContext did not provide context")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		log.Warn("RenderContext did not provide context, defaulting to Shutdown context")
 | 
					 | 
				
			||||||
		ctx.Ctx = graceful.GetManager().ShutdownContext()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	processCtx, _, finished := process.GetManager().AddContext(ctx.Ctx, fmt.Sprintf("Render [%s] for %s", commands[0], ctx.Links.SrcLink()))
 | 
					 | 
				
			||||||
	defer finished()
 | 
						defer finished()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cmd := exec.CommandContext(processCtx, commands[0], args...)
 | 
						cmd := exec.CommandContext(processCtx, commands[0], args...)
 | 
				
			||||||
	cmd.Env = append(
 | 
						cmd.Env = append(
 | 
				
			||||||
		os.Environ(),
 | 
							os.Environ(),
 | 
				
			||||||
		"GITEA_PREFIX_SRC="+ctx.Links.SrcLink(),
 | 
							"GITEA_PREFIX_SRC="+ctx.RenderOptions.Links.SrcLink(),
 | 
				
			||||||
		"GITEA_PREFIX_RAW="+ctx.Links.RawLink(),
 | 
							"GITEA_PREFIX_RAW="+ctx.RenderOptions.Links.RawLink(),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	if !p.IsInputFile {
 | 
						if !p.IsInputFile {
 | 
				
			||||||
		cmd.Stdin = input
 | 
							cmd.Stdin = input
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@ func renderCodeBlock(ctx *RenderContext, node *html.Node) (urlPosStart, urlPosSt
 | 
				
			|||||||
		CommitID:  node.Data[m[6]:m[7]],
 | 
							CommitID:  node.Data[m[6]:m[7]],
 | 
				
			||||||
		FilePath:  node.Data[m[8]:m[9]],
 | 
							FilePath:  node.Data[m[8]:m[9]],
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !httplib.IsCurrentGiteaSiteURL(ctx.Ctx, opts.FullURL) {
 | 
						if !httplib.IsCurrentGiteaSiteURL(ctx, opts.FullURL) {
 | 
				
			||||||
		return 0, 0, "", nil
 | 
							return 0, 0, "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	u, err := url.Parse(opts.FilePath)
 | 
						u, err := url.Parse(opts.FilePath)
 | 
				
			||||||
@@ -51,7 +51,7 @@ func renderCodeBlock(ctx *RenderContext, node *html.Node) (urlPosStart, urlPosSt
 | 
				
			|||||||
	lineStart, _ := strconv.Atoi(strings.TrimPrefix(lineStartStr, "L"))
 | 
						lineStart, _ := strconv.Atoi(strings.TrimPrefix(lineStartStr, "L"))
 | 
				
			||||||
	lineStop, _ := strconv.Atoi(strings.TrimPrefix(lineStopStr, "L"))
 | 
						lineStop, _ := strconv.Atoi(strings.TrimPrefix(lineStopStr, "L"))
 | 
				
			||||||
	opts.LineStart, opts.LineStop = lineStart, lineStop
 | 
						opts.LineStart, opts.LineStop = lineStart, lineStop
 | 
				
			||||||
	h, err := DefaultProcessorHelper.RenderRepoFileCodePreview(ctx.Ctx, opts)
 | 
						h, err := DefaultProcessorHelper.RenderRepoFileCodePreview(ctx, opts)
 | 
				
			||||||
	return m[0], m[1], h, err
 | 
						return m[0], m[1], h, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,6 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup/markdown"
 | 
						"code.gitea.io/gitea/modules/markup/markdown"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,10 +22,7 @@ func TestRenderCodePreview(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		buffer, err := markup.RenderString(&markup.RenderContext{
 | 
							buffer, err := markup.RenderString(markup.NewRenderContext(context.Background()).WithMarkupType(markdown.MarkupName), input)
 | 
				
			||||||
			Ctx:        git.DefaultContext,
 | 
					 | 
				
			||||||
			MarkupType: markdown.MarkupName,
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -84,7 +84,7 @@ func anyHashPatternExtract(s string) (ret anyHashPatternResult, ok bool) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// fullHashPatternProcessor renders SHA containing URLs
 | 
					// fullHashPatternProcessor renders SHA containing URLs
 | 
				
			||||||
func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
					func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			||||||
	if ctx.Metas == nil {
 | 
						if ctx.RenderOptions.Metas == nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	nodeStop := node.NextSibling
 | 
						nodeStop := node.NextSibling
 | 
				
			||||||
@@ -111,7 +111,7 @@ func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func comparePatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
					func comparePatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			||||||
	if ctx.Metas == nil {
 | 
						if ctx.RenderOptions.Metas == nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	nodeStop := node.NextSibling
 | 
						nodeStop := node.NextSibling
 | 
				
			||||||
@@ -163,14 +163,14 @@ func comparePatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
// hashCurrentPatternProcessor renders SHA1 strings to corresponding links that
 | 
					// hashCurrentPatternProcessor renders SHA1 strings to corresponding links that
 | 
				
			||||||
// are assumed to be in the same repository.
 | 
					// are assumed to be in the same repository.
 | 
				
			||||||
func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
					func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			||||||
	if ctx.Metas == nil || ctx.Metas["user"] == "" || ctx.Metas["repo"] == "" || (ctx.Repo == nil && ctx.GitRepo == nil) {
 | 
						if ctx.RenderOptions.Metas == nil || ctx.RenderOptions.Metas["user"] == "" || ctx.RenderOptions.Metas["repo"] == "" || (ctx.RenderHelper.repoFacade == nil && ctx.RenderHelper.gitRepo == nil) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	start := 0
 | 
						start := 0
 | 
				
			||||||
	next := node.NextSibling
 | 
						next := node.NextSibling
 | 
				
			||||||
	if ctx.ShaExistCache == nil {
 | 
						if ctx.RenderHelper.shaExistCache == nil {
 | 
				
			||||||
		ctx.ShaExistCache = make(map[string]bool)
 | 
							ctx.RenderHelper.shaExistCache = make(map[string]bool)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for node != nil && node != next && start < len(node.Data) {
 | 
						for node != nil && node != next && start < len(node.Data) {
 | 
				
			||||||
		m := globalVars().hashCurrentPattern.FindStringSubmatchIndex(node.Data[start:])
 | 
							m := globalVars().hashCurrentPattern.FindStringSubmatchIndex(node.Data[start:])
 | 
				
			||||||
@@ -191,25 +191,25 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
		// a commit in the repository before making it a link.
 | 
							// a commit in the repository before making it a link.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// check cache first
 | 
							// check cache first
 | 
				
			||||||
		exist, inCache := ctx.ShaExistCache[hash]
 | 
							exist, inCache := ctx.RenderHelper.shaExistCache[hash]
 | 
				
			||||||
		if !inCache {
 | 
							if !inCache {
 | 
				
			||||||
			if ctx.GitRepo == nil {
 | 
								if ctx.RenderHelper.gitRepo == nil {
 | 
				
			||||||
				var err error
 | 
									var err error
 | 
				
			||||||
				var closer io.Closer
 | 
									var closer io.Closer
 | 
				
			||||||
				ctx.GitRepo, closer, err = gitrepo.RepositoryFromContextOrOpen(ctx.Ctx, ctx.Repo)
 | 
									ctx.RenderHelper.gitRepo, closer, err = gitrepo.RepositoryFromContextOrOpen(ctx, ctx.RenderHelper.repoFacade)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					log.Error("unable to open repository: %s Error: %v", gitrepo.RepoGitURL(ctx.Repo), err)
 | 
										log.Error("unable to open repository: %s Error: %v", gitrepo.RepoGitURL(ctx.RenderHelper.repoFacade), err)
 | 
				
			||||||
					return
 | 
										return
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				ctx.AddCancel(func() {
 | 
									ctx.AddCancel(func() {
 | 
				
			||||||
					_ = closer.Close()
 | 
										_ = closer.Close()
 | 
				
			||||||
					ctx.GitRepo = nil
 | 
										ctx.RenderHelper.gitRepo = nil
 | 
				
			||||||
				})
 | 
									})
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Don't use IsObjectExist since it doesn't support short hashs with gogit edition.
 | 
								// Don't use IsObjectExist since it doesn't support short hashs with gogit edition.
 | 
				
			||||||
			exist = ctx.GitRepo.IsReferenceExist(hash)
 | 
								exist = ctx.RenderHelper.gitRepo.IsReferenceExist(hash)
 | 
				
			||||||
			ctx.ShaExistCache[hash] = exist
 | 
								ctx.RenderHelper.shaExistCache[hash] = exist
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if !exist {
 | 
							if !exist {
 | 
				
			||||||
@@ -217,7 +217,7 @@ func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		link := util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], "commit", hash)
 | 
							link := util.URLJoin(ctx.RenderOptions.Links.Prefix(), ctx.RenderOptions.Metas["user"], ctx.RenderOptions.Metas["repo"], "commit", hash)
 | 
				
			||||||
		replaceContent(node, m[2], m[3], createCodeLink(link, base.ShortSha(hash), "commit"))
 | 
							replaceContent(node, m[2], m[3], createCodeLink(link, base.ShortSha(hash), "commit"))
 | 
				
			||||||
		start = 0
 | 
							start = 0
 | 
				
			||||||
		node = node.NextSibling.NextSibling
 | 
							node = node.NextSibling.NextSibling
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,12 +4,12 @@
 | 
				
			|||||||
package markup
 | 
					package markup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	testModule "code.gitea.io/gitea/modules/test"
 | 
						testModule "code.gitea.io/gitea/modules/test"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
@@ -79,11 +79,11 @@ func TestRender_IssueIndexPattern(t *testing.T) {
 | 
				
			|||||||
	// numeric: render inputs without valid mentions
 | 
						// numeric: render inputs without valid mentions
 | 
				
			||||||
	test := func(s string) {
 | 
						test := func(s string) {
 | 
				
			||||||
		testRenderIssueIndexPattern(t, s, s, &RenderContext{
 | 
							testRenderIssueIndexPattern(t, s, s, &RenderContext{
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
								ctx: context.Background(),
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		testRenderIssueIndexPattern(t, s, s, &RenderContext{
 | 
							testRenderIssueIndexPattern(t, s, s, &RenderContext{
 | 
				
			||||||
			Ctx:   git.DefaultContext,
 | 
								ctx:           context.Background(),
 | 
				
			||||||
			Metas: numericMetas,
 | 
								RenderOptions: RenderOptions{Metas: numericMetas},
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -133,8 +133,8 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		expectedNil := fmt.Sprintf(expectedFmt, links...)
 | 
							expectedNil := fmt.Sprintf(expectedFmt, links...)
 | 
				
			||||||
		testRenderIssueIndexPattern(t, s, expectedNil, &RenderContext{
 | 
							testRenderIssueIndexPattern(t, s, expectedNil, &RenderContext{
 | 
				
			||||||
			Ctx:   git.DefaultContext,
 | 
								ctx:           context.Background(),
 | 
				
			||||||
			Metas: localMetas,
 | 
								RenderOptions: RenderOptions{Metas: localMetas},
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		class := "ref-issue"
 | 
							class := "ref-issue"
 | 
				
			||||||
@@ -147,8 +147,8 @@ func TestRender_IssueIndexPattern2(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		expectedNum := fmt.Sprintf(expectedFmt, links...)
 | 
							expectedNum := fmt.Sprintf(expectedFmt, links...)
 | 
				
			||||||
		testRenderIssueIndexPattern(t, s, expectedNum, &RenderContext{
 | 
							testRenderIssueIndexPattern(t, s, expectedNum, &RenderContext{
 | 
				
			||||||
			Ctx:   git.DefaultContext,
 | 
								ctx:           context.Background(),
 | 
				
			||||||
			Metas: numericMetas,
 | 
								RenderOptions: RenderOptions{Metas: numericMetas},
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -184,8 +184,8 @@ func TestRender_IssueIndexPattern3(t *testing.T) {
 | 
				
			|||||||
	// alphanumeric: render inputs without valid mentions
 | 
						// alphanumeric: render inputs without valid mentions
 | 
				
			||||||
	test := func(s string) {
 | 
						test := func(s string) {
 | 
				
			||||||
		testRenderIssueIndexPattern(t, s, s, &RenderContext{
 | 
							testRenderIssueIndexPattern(t, s, s, &RenderContext{
 | 
				
			||||||
			Ctx:   git.DefaultContext,
 | 
								ctx:           context.Background(),
 | 
				
			||||||
			Metas: alphanumericMetas,
 | 
								RenderOptions: RenderOptions{Metas: alphanumericMetas},
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	test("")
 | 
						test("")
 | 
				
			||||||
@@ -217,8 +217,8 @@ func TestRender_IssueIndexPattern4(t *testing.T) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		expected := fmt.Sprintf(expectedFmt, links...)
 | 
							expected := fmt.Sprintf(expectedFmt, links...)
 | 
				
			||||||
		testRenderIssueIndexPattern(t, s, expected, &RenderContext{
 | 
							testRenderIssueIndexPattern(t, s, expected, &RenderContext{
 | 
				
			||||||
			Ctx:   git.DefaultContext,
 | 
								ctx:           context.Background(),
 | 
				
			||||||
			Metas: alphanumericMetas,
 | 
								RenderOptions: RenderOptions{Metas: alphanumericMetas},
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	test("OTT-1234 test", "%s test", "OTT-1234")
 | 
						test("OTT-1234 test", "%s test", "OTT-1234")
 | 
				
			||||||
@@ -240,8 +240,8 @@ func TestRender_IssueIndexPattern5(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		expected := fmt.Sprintf(expectedFmt, links...)
 | 
							expected := fmt.Sprintf(expectedFmt, links...)
 | 
				
			||||||
		testRenderIssueIndexPattern(t, s, expected, &RenderContext{
 | 
							testRenderIssueIndexPattern(t, s, expected, &RenderContext{
 | 
				
			||||||
			Ctx:   git.DefaultContext,
 | 
								ctx:           context.Background(),
 | 
				
			||||||
			Metas: metas,
 | 
								RenderOptions: RenderOptions{Metas: metas},
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -264,8 +264,8 @@ func TestRender_IssueIndexPattern5(t *testing.T) {
 | 
				
			|||||||
	)
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testRenderIssueIndexPattern(t, "will not match", "will not match", &RenderContext{
 | 
						testRenderIssueIndexPattern(t, "will not match", "will not match", &RenderContext{
 | 
				
			||||||
		Ctx:   git.DefaultContext,
 | 
							ctx:           context.Background(),
 | 
				
			||||||
		Metas: regexpMetas,
 | 
							RenderOptions: RenderOptions{Metas: regexpMetas},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -279,16 +279,16 @@ func TestRender_IssueIndexPattern_NoShortPattern(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testRenderIssueIndexPattern(t, "#1", "#1", &RenderContext{
 | 
						testRenderIssueIndexPattern(t, "#1", "#1", &RenderContext{
 | 
				
			||||||
		Ctx:   git.DefaultContext,
 | 
							ctx:           context.Background(),
 | 
				
			||||||
		Metas: metas,
 | 
							RenderOptions: RenderOptions{Metas: metas},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	testRenderIssueIndexPattern(t, "#1312", "#1312", &RenderContext{
 | 
						testRenderIssueIndexPattern(t, "#1312", "#1312", &RenderContext{
 | 
				
			||||||
		Ctx:   git.DefaultContext,
 | 
							ctx:           context.Background(),
 | 
				
			||||||
		Metas: metas,
 | 
							RenderOptions: RenderOptions{Metas: metas},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	testRenderIssueIndexPattern(t, "!1", "!1", &RenderContext{
 | 
						testRenderIssueIndexPattern(t, "!1", "!1", &RenderContext{
 | 
				
			||||||
		Ctx:   git.DefaultContext,
 | 
							ctx:           context.Background(),
 | 
				
			||||||
		Metas: metas,
 | 
							RenderOptions: RenderOptions{Metas: metas},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -301,17 +301,17 @@ func TestRender_RenderIssueTitle(t *testing.T) {
 | 
				
			|||||||
		"style":  IssueNameStyleNumeric,
 | 
							"style":  IssueNameStyleNumeric,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	actual, err := RenderIssueTitle(&RenderContext{
 | 
						actual, err := RenderIssueTitle(&RenderContext{
 | 
				
			||||||
		Ctx:   git.DefaultContext,
 | 
							ctx:           context.Background(),
 | 
				
			||||||
		Metas: metas,
 | 
							RenderOptions: RenderOptions{Metas: metas},
 | 
				
			||||||
	}, "#1")
 | 
						}, "#1")
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.Equal(t, "#1", actual)
 | 
						assert.Equal(t, "#1", actual)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) {
 | 
					func testRenderIssueIndexPattern(t *testing.T, input, expected string, ctx *RenderContext) {
 | 
				
			||||||
	ctx.Links.AbsolutePrefix = true
 | 
						ctx.RenderOptions.Links.AbsolutePrefix = true
 | 
				
			||||||
	if ctx.Links.Base == "" {
 | 
						if ctx.RenderOptions.Links.Base == "" {
 | 
				
			||||||
		ctx.Links.Base = TestRepoURL
 | 
							ctx.RenderOptions.Links.Base = TestRepoURL
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var buf strings.Builder
 | 
						var buf strings.Builder
 | 
				
			||||||
@@ -326,22 +326,18 @@ func TestRender_AutoLink(t *testing.T) {
 | 
				
			|||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		var buffer strings.Builder
 | 
							var buffer strings.Builder
 | 
				
			||||||
		err := PostProcess(&RenderContext{
 | 
							err := PostProcess(&RenderContext{
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
								ctx: context.Background(),
 | 
				
			||||||
			Links: Links{
 | 
					
 | 
				
			||||||
				Base: TestRepoURL,
 | 
								RenderOptions: RenderOptions{Metas: localMetas, Links: Links{Base: TestRepoURL}},
 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			Metas: localMetas,
 | 
					 | 
				
			||||||
		}, strings.NewReader(input), &buffer)
 | 
							}, strings.NewReader(input), &buffer)
 | 
				
			||||||
		assert.Equal(t, err, nil)
 | 
							assert.Equal(t, err, nil)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		buffer.Reset()
 | 
							buffer.Reset()
 | 
				
			||||||
		err = PostProcess(&RenderContext{
 | 
							err = PostProcess(&RenderContext{
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
								ctx: context.Background(),
 | 
				
			||||||
			Links: Links{
 | 
					
 | 
				
			||||||
				Base: TestRepoURL,
 | 
								RenderOptions: RenderOptions{Metas: localWikiMetas, Links: Links{Base: TestRepoURL}},
 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			Metas: localWikiMetas,
 | 
					 | 
				
			||||||
		}, strings.NewReader(input), &buffer)
 | 
							}, strings.NewReader(input), &buffer)
 | 
				
			||||||
		assert.Equal(t, err, nil)
 | 
							assert.Equal(t, err, nil)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer.String()))
 | 
				
			||||||
@@ -368,11 +364,9 @@ func TestRender_FullIssueURLs(t *testing.T) {
 | 
				
			|||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		var result strings.Builder
 | 
							var result strings.Builder
 | 
				
			||||||
		err := postProcess(&RenderContext{
 | 
							err := postProcess(&RenderContext{
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
								ctx: context.Background(),
 | 
				
			||||||
			Links: Links{
 | 
					
 | 
				
			||||||
				Base: TestRepoURL,
 | 
								RenderOptions: RenderOptions{Metas: localMetas, Links: Links{Base: TestRepoURL}},
 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			Metas: localMetas,
 | 
					 | 
				
			||||||
		}, []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result)
 | 
							}, []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, expected, result.String())
 | 
							assert.Equal(t, expected, result.String())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,7 +19,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
					func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			||||||
	if ctx.Metas == nil {
 | 
						if ctx.RenderOptions.Metas == nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	next := node.NextSibling
 | 
						next := node.NextSibling
 | 
				
			||||||
@@ -36,14 +36,14 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		link := node.Data[m[0]:m[1]]
 | 
							link := node.Data[m[0]:m[1]]
 | 
				
			||||||
		if !httplib.IsCurrentGiteaSiteURL(ctx.Ctx, link) {
 | 
							if !httplib.IsCurrentGiteaSiteURL(ctx, link) {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		text := "#" + node.Data[m[2]:m[3]]
 | 
							text := "#" + node.Data[m[2]:m[3]]
 | 
				
			||||||
		// if m[4] and m[5] is not -1, then link is to a comment
 | 
							// if m[4] and m[5] is not -1, then link is to a comment
 | 
				
			||||||
		// indicate that in the text by appending (comment)
 | 
							// indicate that in the text by appending (comment)
 | 
				
			||||||
		if m[4] != -1 && m[5] != -1 {
 | 
							if m[4] != -1 && m[5] != -1 {
 | 
				
			||||||
			if locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale); ok {
 | 
								if locale, ok := ctx.Value(translation.ContextKey).(translation.Locale); ok {
 | 
				
			||||||
				text += " " + locale.TrString("repo.from_comment")
 | 
									text += " " + locale.TrString("repo.from_comment")
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				text += " (comment)"
 | 
									text += " (comment)"
 | 
				
			||||||
@@ -56,7 +56,7 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
		matchOrg := linkParts[len(linkParts)-4]
 | 
							matchOrg := linkParts[len(linkParts)-4]
 | 
				
			||||||
		matchRepo := linkParts[len(linkParts)-3]
 | 
							matchRepo := linkParts[len(linkParts)-3]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if matchOrg == ctx.Metas["user"] && matchRepo == ctx.Metas["repo"] {
 | 
							if matchOrg == ctx.RenderOptions.Metas["user"] && matchRepo == ctx.RenderOptions.Metas["repo"] {
 | 
				
			||||||
			replaceContent(node, m[0], m[1], createLink(ctx, link, text, "ref-issue"))
 | 
								replaceContent(node, m[0], m[1], createLink(ctx, link, text, "ref-issue"))
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			text = matchOrg + "/" + matchRepo + text
 | 
								text = matchOrg + "/" + matchRepo + text
 | 
				
			||||||
@@ -67,14 +67,14 @@ func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
					func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			||||||
	if ctx.Metas == nil {
 | 
						if ctx.RenderOptions.Metas == nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// crossLinkOnly: do not parse "#123", only parse "owner/repo#123"
 | 
						// crossLinkOnly: do not parse "#123", only parse "owner/repo#123"
 | 
				
			||||||
	// if there is no repo in the context, then the "#123" format can't be parsed
 | 
						// if there is no repo in the context, then the "#123" format can't be parsed
 | 
				
			||||||
	// old logic: crossLinkOnly := ctx.Metas["mode"] == "document" && !ctx.IsWiki
 | 
						// old logic: crossLinkOnly := ctx.RenderOptions.Metas["mode"] == "document" && !ctx.IsWiki
 | 
				
			||||||
	crossLinkOnly := ctx.Metas["markupAllowShortIssuePattern"] != "true"
 | 
						crossLinkOnly := ctx.RenderOptions.Metas["markupAllowShortIssuePattern"] != "true"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var (
 | 
						var (
 | 
				
			||||||
		found bool
 | 
							found bool
 | 
				
			||||||
@@ -84,20 +84,20 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
	next := node.NextSibling
 | 
						next := node.NextSibling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for node != nil && node != next {
 | 
						for node != nil && node != next {
 | 
				
			||||||
		_, hasExtTrackFormat := ctx.Metas["format"]
 | 
							_, hasExtTrackFormat := ctx.RenderOptions.Metas["format"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Repos with external issue trackers might still need to reference local PRs
 | 
							// Repos with external issue trackers might still need to reference local PRs
 | 
				
			||||||
		// We need to concern with the first one that shows up in the text, whichever it is
 | 
							// We need to concern with the first one that shows up in the text, whichever it is
 | 
				
			||||||
		isNumericStyle := ctx.Metas["style"] == "" || ctx.Metas["style"] == IssueNameStyleNumeric
 | 
							isNumericStyle := ctx.RenderOptions.Metas["style"] == "" || ctx.RenderOptions.Metas["style"] == IssueNameStyleNumeric
 | 
				
			||||||
		foundNumeric, refNumeric := references.FindRenderizableReferenceNumeric(node.Data, hasExtTrackFormat && !isNumericStyle, crossLinkOnly)
 | 
							foundNumeric, refNumeric := references.FindRenderizableReferenceNumeric(node.Data, hasExtTrackFormat && !isNumericStyle, crossLinkOnly)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		switch ctx.Metas["style"] {
 | 
							switch ctx.RenderOptions.Metas["style"] {
 | 
				
			||||||
		case "", IssueNameStyleNumeric:
 | 
							case "", IssueNameStyleNumeric:
 | 
				
			||||||
			found, ref = foundNumeric, refNumeric
 | 
								found, ref = foundNumeric, refNumeric
 | 
				
			||||||
		case IssueNameStyleAlphanumeric:
 | 
							case IssueNameStyleAlphanumeric:
 | 
				
			||||||
			found, ref = references.FindRenderizableReferenceAlphanumeric(node.Data)
 | 
								found, ref = references.FindRenderizableReferenceAlphanumeric(node.Data)
 | 
				
			||||||
		case IssueNameStyleRegexp:
 | 
							case IssueNameStyleRegexp:
 | 
				
			||||||
			pattern, err := regexplru.GetCompiled(ctx.Metas["regexp"])
 | 
								pattern, err := regexplru.GetCompiled(ctx.RenderOptions.Metas["regexp"])
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -121,9 +121,9 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
		var link *html.Node
 | 
							var link *html.Node
 | 
				
			||||||
		reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
 | 
							reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
 | 
				
			||||||
		if hasExtTrackFormat && !ref.IsPull {
 | 
							if hasExtTrackFormat && !ref.IsPull {
 | 
				
			||||||
			ctx.Metas["index"] = ref.Issue
 | 
								ctx.RenderOptions.Metas["index"] = ref.Issue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			res, err := vars.Expand(ctx.Metas["format"], ctx.Metas)
 | 
								res, err := vars.Expand(ctx.RenderOptions.Metas["format"], ctx.RenderOptions.Metas)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				// here we could just log the error and continue the rendering
 | 
									// here we could just log the error and continue the rendering
 | 
				
			||||||
				log.Error("unable to expand template vars for ref %s, err: %v", ref.Issue, err)
 | 
									log.Error("unable to expand template vars for ref %s, err: %v", ref.Issue, err)
 | 
				
			||||||
@@ -136,9 +136,9 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
			// Gitea will redirect on click as appropriate.
 | 
								// Gitea will redirect on click as appropriate.
 | 
				
			||||||
			issuePath := util.Iif(ref.IsPull, "pulls", "issues")
 | 
								issuePath := util.Iif(ref.IsPull, "pulls", "issues")
 | 
				
			||||||
			if ref.Owner == "" {
 | 
								if ref.Owner == "" {
 | 
				
			||||||
				link = createLink(ctx, util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], issuePath, ref.Issue), reftext, "ref-issue")
 | 
									link = createLink(ctx, util.URLJoin(ctx.RenderOptions.Links.Prefix(), ctx.RenderOptions.Metas["user"], ctx.RenderOptions.Metas["repo"], issuePath, ref.Issue), reftext, "ref-issue")
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				link = createLink(ctx, util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, issuePath, ref.Issue), reftext, "ref-issue")
 | 
									link = createLink(ctx, util.URLJoin(ctx.RenderOptions.Links.Prefix(), ref.Owner, ref.Name, issuePath, ref.Issue), reftext, "ref-issue")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -177,7 +177,7 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
 | 
							reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
 | 
				
			||||||
		link := createLink(ctx, util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit")
 | 
							link := createLink(ctx, util.URLJoin(ctx.RenderOptions.Links.Prefix(), ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
 | 
							replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
 | 
				
			||||||
		node = node.NextSibling.NextSibling
 | 
							node = node.NextSibling.NextSibling
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,15 +19,15 @@ import (
 | 
				
			|||||||
func ResolveLink(ctx *RenderContext, link, userContentAnchorPrefix string) (result string, resolved bool) {
 | 
					func ResolveLink(ctx *RenderContext, link, userContentAnchorPrefix string) (result string, resolved bool) {
 | 
				
			||||||
	isAnchorFragment := link != "" && link[0] == '#'
 | 
						isAnchorFragment := link != "" && link[0] == '#'
 | 
				
			||||||
	if !isAnchorFragment && !IsFullURLString(link) {
 | 
						if !isAnchorFragment && !IsFullURLString(link) {
 | 
				
			||||||
		linkBase := ctx.Links.Base
 | 
							linkBase := ctx.RenderOptions.Links.Base
 | 
				
			||||||
		if ctx.IsMarkupContentWiki() {
 | 
							if ctx.IsMarkupContentWiki() {
 | 
				
			||||||
			// no need to check if the link should be resolved as a wiki link or a wiki raw link
 | 
								// 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
 | 
								// just use wiki link here, and it will be redirected to a wiki raw link if necessary
 | 
				
			||||||
			linkBase = ctx.Links.WikiLink()
 | 
								linkBase = ctx.RenderOptions.Links.WikiLink()
 | 
				
			||||||
		} else if ctx.Links.BranchPath != "" || ctx.Links.TreePath != "" {
 | 
							} else if ctx.RenderOptions.Links.BranchPath != "" || ctx.RenderOptions.Links.TreePath != "" {
 | 
				
			||||||
			// if there is no BranchPath, then the link will be something like "/owner/repo/src/{the-file-path}"
 | 
								// if there is no BranchPath, then the link will be something like "/owner/repo/src/{the-file-path}"
 | 
				
			||||||
			// and then this link will be handled by the "legacy-ref" code and be redirected to the default branch like "/owner/repo/src/branch/main/{the-file-path}"
 | 
								// and then this link will be handled by the "legacy-ref" code and be redirected to the default branch like "/owner/repo/src/branch/main/{the-file-path}"
 | 
				
			||||||
			linkBase = ctx.Links.SrcLink()
 | 
								linkBase = ctx.RenderOptions.Links.SrcLink()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		link, resolved = util.URLJoin(linkBase, link), true
 | 
							link, resolved = util.URLJoin(linkBase, link), true
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -147,7 +147,7 @@ func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		if image {
 | 
							if image {
 | 
				
			||||||
			if !absoluteLink {
 | 
								if !absoluteLink {
 | 
				
			||||||
				link = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsMarkupContentWiki()), link)
 | 
									link = util.URLJoin(ctx.RenderOptions.Links.ResolveMediaLink(ctx.IsMarkupContentWiki()), link)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			title := props["title"]
 | 
								title := props["title"]
 | 
				
			||||||
			if title == "" {
 | 
								if title == "" {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,15 +25,15 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
		loc.Start += start
 | 
							loc.Start += start
 | 
				
			||||||
		loc.End += start
 | 
							loc.End += start
 | 
				
			||||||
		mention := node.Data[loc.Start:loc.End]
 | 
							mention := node.Data[loc.Start:loc.End]
 | 
				
			||||||
		teams, ok := ctx.Metas["teams"]
 | 
							teams, ok := ctx.RenderOptions.Metas["teams"]
 | 
				
			||||||
		// FIXME: util.URLJoin may not be necessary here:
 | 
							// FIXME: util.URLJoin may not be necessary here:
 | 
				
			||||||
		// - setting.AppURL is defined to have a terminal '/' so unless mention[1:]
 | 
							// - setting.AppURL is defined to have a terminal '/' so unless mention[1:]
 | 
				
			||||||
		// is an AppSubURL link we can probably fallback to concatenation.
 | 
							// is an AppSubURL link we can probably fallback to concatenation.
 | 
				
			||||||
		// team mention should follow @orgName/teamName style
 | 
							// team mention should follow @orgName/teamName style
 | 
				
			||||||
		if ok && strings.Contains(mention, "/") {
 | 
							if ok && strings.Contains(mention, "/") {
 | 
				
			||||||
			mentionOrgAndTeam := strings.Split(mention, "/")
 | 
								mentionOrgAndTeam := strings.Split(mention, "/")
 | 
				
			||||||
			if mentionOrgAndTeam[0][1:] == ctx.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") {
 | 
								if mentionOrgAndTeam[0][1:] == ctx.RenderOptions.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") {
 | 
				
			||||||
				replaceContent(node, loc.Start, loc.End, createLink(ctx, util.URLJoin(ctx.Links.Prefix(), "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "" /*mention*/))
 | 
									replaceContent(node, loc.Start, loc.End, createLink(ctx, util.URLJoin(ctx.RenderOptions.Links.Prefix(), "org", ctx.RenderOptions.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "" /*mention*/))
 | 
				
			||||||
				node = node.NextSibling.NextSibling
 | 
									node = node.NextSibling.NextSibling
 | 
				
			||||||
				start = 0
 | 
									start = 0
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
@@ -43,8 +43,8 @@ func mentionProcessor(ctx *RenderContext, node *html.Node) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		mentionedUsername := mention[1:]
 | 
							mentionedUsername := mention[1:]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
 | 
							if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx, mentionedUsername) {
 | 
				
			||||||
			replaceContent(node, loc.Start, loc.End, createLink(ctx, util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "" /*mention*/))
 | 
								replaceContent(node, loc.Start, loc.End, createLink(ctx, util.URLJoin(ctx.RenderOptions.Links.Prefix(), mentionedUsername), mention, "" /*mention*/))
 | 
				
			||||||
			node = node.NextSibling.NextSibling
 | 
								node = node.NextSibling.NextSibling
 | 
				
			||||||
			start = 0
 | 
								start = 0
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ func visitNodeImg(ctx *RenderContext, img *html.Node) (next *html.Node) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if IsNonEmptyRelativePath(attr.Val) {
 | 
							if IsNonEmptyRelativePath(attr.Val) {
 | 
				
			||||||
			attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsMarkupContentWiki()), attr.Val)
 | 
								attr.Val = util.URLJoin(ctx.RenderOptions.Links.ResolveMediaLink(ctx.IsMarkupContentWiki()), attr.Val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// By default, the "<img>" tag should also be clickable,
 | 
								// By default, the "<img>" tag should also be clickable,
 | 
				
			||||||
			// because frontend use `<img>` to paste the re-scaled image into the markdown,
 | 
								// 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
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if IsNonEmptyRelativePath(attr.Val) {
 | 
							if IsNonEmptyRelativePath(attr.Val) {
 | 
				
			||||||
			attr.Val = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsMarkupContentWiki()), attr.Val)
 | 
								attr.Val = util.URLJoin(ctx.RenderOptions.Links.ResolveMediaLink(ctx.IsMarkupContentWiki()), attr.Val)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		attr.Val = camoHandleLink(attr.Val)
 | 
							attr.Val = camoHandleLink(attr.Val)
 | 
				
			||||||
		node.Attr[i] = attr
 | 
							node.Attr[i] = attr
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,6 @@ import (
 | 
				
			|||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/emoji"
 | 
						"code.gitea.io/gitea/modules/emoji"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/gitrepo"
 | 
						"code.gitea.io/gitea/modules/gitrepo"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup/markdown"
 | 
						"code.gitea.io/gitea/modules/markup/markdown"
 | 
				
			||||||
@@ -57,16 +56,10 @@ func newMockRepo(ownerName, repoName string) gitrepo.Repository {
 | 
				
			|||||||
func TestRender_Commits(t *testing.T) {
 | 
					func TestRender_Commits(t *testing.T) {
 | 
				
			||||||
	setting.AppURL = markup.TestAppURL
 | 
						setting.AppURL = markup.TestAppURL
 | 
				
			||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		buffer, err := markup.RenderString(&markup.RenderContext{
 | 
							buffer, err := markup.RenderString(markup.NewTestRenderContext("a.md", localMetas, newMockRepo(testRepoOwnerName, testRepoName), markup.Links{
 | 
				
			||||||
			Ctx:          git.DefaultContext,
 | 
					 | 
				
			||||||
			RelativePath: ".md",
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
			AbsolutePrefix: true,
 | 
								AbsolutePrefix: true,
 | 
				
			||||||
			Base:           markup.TestRepoURL,
 | 
								Base:           markup.TestRepoURL,
 | 
				
			||||||
			},
 | 
							}), input)
 | 
				
			||||||
			Repo:  newMockRepo(testRepoOwnerName, testRepoName),
 | 
					 | 
				
			||||||
			Metas: localMetas,
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -112,15 +105,11 @@ func TestRender_CrossReferences(t *testing.T) {
 | 
				
			|||||||
	setting.AppURL = markup.TestAppURL
 | 
						setting.AppURL = markup.TestAppURL
 | 
				
			||||||
	defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
 | 
						defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
 | 
				
			||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		buffer, err := markup.RenderString(&markup.RenderContext{
 | 
							buffer, err := markup.RenderString(markup.NewTestRenderContext("a.md", localMetas,
 | 
				
			||||||
			Ctx:          git.DefaultContext,
 | 
								markup.Links{
 | 
				
			||||||
			RelativePath: "a.md",
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				AbsolutePrefix: true,
 | 
									AbsolutePrefix: true,
 | 
				
			||||||
				Base:           setting.AppSubURL,
 | 
									Base:           setting.AppSubURL,
 | 
				
			||||||
			},
 | 
								}), input)
 | 
				
			||||||
			Metas: localMetas,
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -154,13 +143,7 @@ func TestRender_links(t *testing.T) {
 | 
				
			|||||||
	setting.AppURL = markup.TestAppURL
 | 
						setting.AppURL = markup.TestAppURL
 | 
				
			||||||
	defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
 | 
						defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
 | 
				
			||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		buffer, err := markup.RenderString(&markup.RenderContext{
 | 
							buffer, err := markup.RenderString(markup.NewTestRenderContext("a.md", markup.Links{Base: markup.TestRepoURL}), input)
 | 
				
			||||||
			Ctx:          git.DefaultContext,
 | 
					 | 
				
			||||||
			RelativePath: "a.md",
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base: markup.TestRepoURL,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -265,13 +248,7 @@ func TestRender_email(t *testing.T) {
 | 
				
			|||||||
	setting.AppURL = markup.TestAppURL
 | 
						setting.AppURL = markup.TestAppURL
 | 
				
			||||||
	defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
 | 
						defer testModule.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
 | 
				
			||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		res, err := markup.RenderString(&markup.RenderContext{
 | 
							res, err := markup.RenderString(markup.NewTestRenderContext("a.md", markup.Links{Base: markup.TestRepoURL}), input)
 | 
				
			||||||
			Ctx:          git.DefaultContext,
 | 
					 | 
				
			||||||
			RelativePath: "a.md",
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base: markup.TestRepoURL,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -338,13 +315,7 @@ func TestRender_emoji(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		expected = strings.ReplaceAll(expected, "&", "&")
 | 
							expected = strings.ReplaceAll(expected, "&", "&")
 | 
				
			||||||
		buffer, err := markup.RenderString(&markup.RenderContext{
 | 
							buffer, err := markup.RenderString(markup.NewTestRenderContext("a.md", markup.Links{Base: markup.TestRepoURL}), input)
 | 
				
			||||||
			Ctx:          git.DefaultContext,
 | 
					 | 
				
			||||||
			RelativePath: "a.md",
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base: markup.TestRepoURL,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -404,22 +375,10 @@ func TestRender_ShortLinks(t *testing.T) {
 | 
				
			|||||||
	tree := util.URLJoin(markup.TestRepoURL, "src", "master")
 | 
						tree := util.URLJoin(markup.TestRepoURL, "src", "master")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	test := func(input, expected, expectedWiki string) {
 | 
						test := func(input, expected, expectedWiki string) {
 | 
				
			||||||
		buffer, err := markdown.RenderString(&markup.RenderContext{
 | 
							buffer, err := markdown.RenderString(markup.NewTestRenderContext(markup.Links{Base: markup.TestRepoURL, BranchPath: "master"}), input)
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base:       markup.TestRepoURL,
 | 
					 | 
				
			||||||
				BranchPath: "master",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
 | 
				
			||||||
		buffer, err = markdown.RenderString(&markup.RenderContext{
 | 
							buffer, err = markdown.RenderString(markup.NewTestRenderContext(markup.Links{Base: markup.TestRepoURL}, localWikiMetas), input)
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base: markup.TestRepoURL,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			Metas: localWikiMetas,
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer)))
 | 
							assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer)))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -529,11 +488,7 @@ func TestRender_ShortLinks(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestRender_RelativeMedias(t *testing.T) {
 | 
					func TestRender_RelativeMedias(t *testing.T) {
 | 
				
			||||||
	render := func(input string, isWiki bool, links markup.Links) string {
 | 
						render := func(input string, isWiki bool, links markup.Links) string {
 | 
				
			||||||
		buffer, err := markdown.RenderString(&markup.RenderContext{
 | 
							buffer, err := markdown.RenderString(markup.NewTestRenderContext(links, util.Iif(isWiki, localWikiMetas, localMetas)), input)
 | 
				
			||||||
			Ctx:   git.DefaultContext,
 | 
					 | 
				
			||||||
			Links: links,
 | 
					 | 
				
			||||||
			Metas: util.Iif(isWiki, localWikiMetas, localMetas),
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		return strings.TrimSpace(string(buffer))
 | 
							return strings.TrimSpace(string(buffer))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -574,26 +529,14 @@ func Test_ParseClusterFuzz(t *testing.T) {
 | 
				
			|||||||
	data := "<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
 | 
						data := "<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var res strings.Builder
 | 
						var res strings.Builder
 | 
				
			||||||
	err := markup.PostProcess(&markup.RenderContext{
 | 
						err := markup.PostProcess(markup.NewTestRenderContext(markup.Links{Base: "https://example.com"}, localMetas), strings.NewReader(data), &res)
 | 
				
			||||||
		Ctx: git.DefaultContext,
 | 
					 | 
				
			||||||
		Links: markup.Links{
 | 
					 | 
				
			||||||
			Base: "https://example.com",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Metas: localMetas,
 | 
					 | 
				
			||||||
	}, strings.NewReader(data), &res)
 | 
					 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.NotContains(t, res.String(), "<html")
 | 
						assert.NotContains(t, res.String(), "<html")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data = "<!DOCTYPE html>\n<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
 | 
						data = "<!DOCTYPE html>\n<A><maTH><tr><MN><bodY ÿ><temPlate></template><tH><tr></A><tH><d<bodY "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	res.Reset()
 | 
						res.Reset()
 | 
				
			||||||
	err = markup.PostProcess(&markup.RenderContext{
 | 
						err = markup.PostProcess(markup.NewTestRenderContext(markup.Links{Base: "https://example.com"}, localMetas), strings.NewReader(data), &res)
 | 
				
			||||||
		Ctx: git.DefaultContext,
 | 
					 | 
				
			||||||
		Links: markup.Links{
 | 
					 | 
				
			||||||
			Base: "https://example.com",
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Metas: localMetas,
 | 
					 | 
				
			||||||
	}, strings.NewReader(data), &res)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.NotContains(t, res.String(), "<html")
 | 
						assert.NotContains(t, res.String(), "<html")
 | 
				
			||||||
@@ -606,14 +549,13 @@ func TestPostProcess_RenderDocument(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		var res strings.Builder
 | 
							var res strings.Builder
 | 
				
			||||||
		err := markup.PostProcess(&markup.RenderContext{
 | 
							err := markup.PostProcess(markup.NewTestRenderContext(
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
								markup.Links{
 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				AbsolutePrefix: true,
 | 
									AbsolutePrefix: true,
 | 
				
			||||||
				Base:           "https://example.com",
 | 
									Base:           "https://example.com",
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Metas: map[string]string{"user": "go-gitea", "repo": "gitea"},
 | 
								map[string]string{"user": "go-gitea", "repo": "gitea"},
 | 
				
			||||||
		}, strings.NewReader(input), &res)
 | 
							), strings.NewReader(input), &res)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res.String()))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res.String()))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -650,10 +592,7 @@ func TestIssue16020(t *testing.T) {
 | 
				
			|||||||
	data := `<img src="data:image/png;base64,i//V"/>`
 | 
						data := `<img src="data:image/png;base64,i//V"/>`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var res strings.Builder
 | 
						var res strings.Builder
 | 
				
			||||||
	err := markup.PostProcess(&markup.RenderContext{
 | 
						err := markup.PostProcess(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
 | 
				
			||||||
		Ctx:   git.DefaultContext,
 | 
					 | 
				
			||||||
		Metas: localMetas,
 | 
					 | 
				
			||||||
	}, strings.NewReader(data), &res)
 | 
					 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.Equal(t, data, res.String())
 | 
						assert.Equal(t, data, res.String())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -666,29 +605,23 @@ func BenchmarkEmojiPostprocess(b *testing.B) {
 | 
				
			|||||||
	b.ResetTimer()
 | 
						b.ResetTimer()
 | 
				
			||||||
	for i := 0; i < b.N; i++ {
 | 
						for i := 0; i < b.N; i++ {
 | 
				
			||||||
		var res strings.Builder
 | 
							var res strings.Builder
 | 
				
			||||||
		err := markup.PostProcess(&markup.RenderContext{
 | 
							err := markup.PostProcess(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
 | 
				
			||||||
			Ctx:   git.DefaultContext,
 | 
					 | 
				
			||||||
			Metas: localMetas,
 | 
					 | 
				
			||||||
		}, strings.NewReader(data), &res)
 | 
					 | 
				
			||||||
		assert.NoError(b, err)
 | 
							assert.NoError(b, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestFuzz(t *testing.T) {
 | 
					func TestFuzz(t *testing.T) {
 | 
				
			||||||
	s := "t/l/issues/8#/../../a"
 | 
						s := "t/l/issues/8#/../../a"
 | 
				
			||||||
	renderContext := markup.RenderContext{
 | 
						renderContext := markup.NewTestRenderContext(
 | 
				
			||||||
		Ctx: git.DefaultContext,
 | 
							markup.Links{
 | 
				
			||||||
		Links: markup.Links{
 | 
					 | 
				
			||||||
			Base: "https://example.com/go-gitea/gitea",
 | 
								Base: "https://example.com/go-gitea/gitea",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Metas: map[string]string{
 | 
							map[string]string{
 | 
				
			||||||
			"user": "go-gitea",
 | 
								"user": "go-gitea",
 | 
				
			||||||
			"repo": "gitea",
 | 
								"repo": "gitea",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						)
 | 
				
			||||||
 | 
						err := markup.PostProcess(renderContext, strings.NewReader(s), io.Discard)
 | 
				
			||||||
	err := markup.PostProcess(&renderContext, strings.NewReader(s), io.Discard)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -696,10 +629,7 @@ func TestIssue18471(t *testing.T) {
 | 
				
			|||||||
	data := `http://domain/org/repo/compare/783b039...da951ce`
 | 
						data := `http://domain/org/repo/compare/783b039...da951ce`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var res strings.Builder
 | 
						var res strings.Builder
 | 
				
			||||||
	err := markup.PostProcess(&markup.RenderContext{
 | 
						err := markup.PostProcess(markup.NewTestRenderContext(localMetas), strings.NewReader(data), &res)
 | 
				
			||||||
		Ctx:   git.DefaultContext,
 | 
					 | 
				
			||||||
		Metas: localMetas,
 | 
					 | 
				
			||||||
	}, strings.NewReader(data), &res)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.Equal(t, `<a href="http://domain/org/repo/compare/783b039...da951ce" class="compare"><code class="nohighlight">783b039...da951ce</code></a>`, res.String())
 | 
						assert.Equal(t, `<a href="http://domain/org/repo/compare/783b039...da951ce" class="compare"><code class="nohighlight">783b039...da951ce</code></a>`, res.String())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -79,7 +79,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
 | 
				
			|||||||
				// TODO: this was a quite unclear part, old code: `if metas["mode"] != "document" { use comment link break setting }`
 | 
									// 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
 | 
									// 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.
 | 
									// especially in many tests.
 | 
				
			||||||
				markdownLineBreakStyle := ctx.Metas["markdownLineBreakStyle"]
 | 
									markdownLineBreakStyle := ctx.RenderOptions.Metas["markdownLineBreakStyle"]
 | 
				
			||||||
				if markup.RenderBehaviorForTesting.ForceHardLineBreak {
 | 
									if markup.RenderBehaviorForTesting.ForceHardLineBreak {
 | 
				
			||||||
					v.SetHardLineBreak(true)
 | 
										v.SetHardLineBreak(true)
 | 
				
			||||||
				} else if markdownLineBreakStyle == "comment" {
 | 
									} else if markdownLineBreakStyle == "comment" {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -182,7 +182,7 @@ func render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error
 | 
				
			|||||||
	bufWithMetadataLength := len(buf)
 | 
						bufWithMetadataLength := len(buf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc := &RenderConfig{
 | 
						rc := &RenderConfig{
 | 
				
			||||||
		Meta: renderMetaModeFromString(string(ctx.RenderMetaAs)),
 | 
							Meta: markup.RenderMetaAsDetails,
 | 
				
			||||||
		Icon: "table",
 | 
							Icon: "table",
 | 
				
			||||||
		Lang: "",
 | 
							Lang: "",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -241,7 +241,7 @@ func (Renderer) Render(ctx *markup.RenderContext, input io.Reader, output io.Wri
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Render renders Markdown to HTML with all specific handling stuff.
 | 
					// Render renders Markdown to HTML with all specific handling stuff.
 | 
				
			||||||
func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
 | 
					func Render(ctx *markup.RenderContext, input io.Reader, output io.Writer) error {
 | 
				
			||||||
	ctx.MarkupType = MarkupName
 | 
						ctx.RenderOptions.MarkupType = MarkupName
 | 
				
			||||||
	return markup.Render(ctx, input, output)
 | 
						return markup.Render(ctx, input, output)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,12 +4,10 @@
 | 
				
			|||||||
package markdown_test
 | 
					package markdown_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
					 | 
				
			||||||
	"html/template"
 | 
						"html/template"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/gitrepo"
 | 
						"code.gitea.io/gitea/modules/gitrepo"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
@@ -67,22 +65,11 @@ func TestRender_StandardLinks(t *testing.T) {
 | 
				
			|||||||
	setting.AppURL = AppURL
 | 
						setting.AppURL = AppURL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	test := func(input, expected, expectedWiki string) {
 | 
						test := func(input, expected, expectedWiki string) {
 | 
				
			||||||
		buffer, err := markdown.RenderString(&markup.RenderContext{
 | 
							buffer, err := markdown.RenderString(markup.NewTestRenderContext(markup.Links{Base: FullURL}), input)
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base: FullURL,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		buffer, err = markdown.RenderString(&markup.RenderContext{
 | 
							buffer, err = markdown.RenderString(markup.NewTestRenderContext(markup.Links{Base: FullURL}, localWikiMetas), input)
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base: FullURL,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			Metas: localWikiMetas,
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer)))
 | 
							assert.Equal(t, strings.TrimSpace(expectedWiki), strings.TrimSpace(string(buffer)))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -101,12 +88,7 @@ func TestRender_Images(t *testing.T) {
 | 
				
			|||||||
	setting.AppURL = AppURL
 | 
						setting.AppURL = AppURL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		buffer, err := markdown.RenderString(&markup.RenderContext{
 | 
							buffer, err := markdown.RenderString(markup.NewTestRenderContext(markup.Links{Base: FullURL}), input)
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base: FullURL,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -308,14 +290,11 @@ func TestTotal_RenderWiki(t *testing.T) {
 | 
				
			|||||||
	setting.AppURL = AppURL
 | 
						setting.AppURL = AppURL
 | 
				
			||||||
	answers := testAnswers(util.URLJoin(FullURL, "wiki"), util.URLJoin(FullURL, "wiki", "raw"))
 | 
						answers := testAnswers(util.URLJoin(FullURL, "wiki"), util.URLJoin(FullURL, "wiki", "raw"))
 | 
				
			||||||
	for i := 0; i < len(sameCases); i++ {
 | 
						for i := 0; i < len(sameCases); i++ {
 | 
				
			||||||
		line, err := markdown.RenderString(&markup.RenderContext{
 | 
							line, err := markdown.RenderString(markup.NewTestRenderContext(
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
								markup.Links{Base: FullURL},
 | 
				
			||||||
			Links: markup.Links{
 | 
								newMockRepo(testRepoOwnerName, testRepoName),
 | 
				
			||||||
				Base: FullURL,
 | 
								localWikiMetas,
 | 
				
			||||||
			},
 | 
							), sameCases[i])
 | 
				
			||||||
			Repo:  newMockRepo(testRepoOwnerName, testRepoName),
 | 
					 | 
				
			||||||
			Metas: localWikiMetas,
 | 
					 | 
				
			||||||
		}, sameCases[i])
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, answers[i], string(line))
 | 
							assert.Equal(t, answers[i], string(line))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -334,13 +313,7 @@ func TestTotal_RenderWiki(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := 0; i < len(testCases); i += 2 {
 | 
						for i := 0; i < len(testCases); i += 2 {
 | 
				
			||||||
		line, err := markdown.RenderString(&markup.RenderContext{
 | 
							line, err := markdown.RenderString(markup.NewTestRenderContext(markup.Links{Base: FullURL}, localWikiMetas), testCases[i])
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base: FullURL,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			Metas: localWikiMetas,
 | 
					 | 
				
			||||||
		}, testCases[i])
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.EqualValues(t, testCases[i+1], string(line))
 | 
							assert.EqualValues(t, testCases[i+1], string(line))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -352,15 +325,14 @@ func TestTotal_RenderString(t *testing.T) {
 | 
				
			|||||||
	setting.AppURL = AppURL
 | 
						setting.AppURL = AppURL
 | 
				
			||||||
	answers := testAnswers(util.URLJoin(FullURL, "src", "master"), util.URLJoin(FullURL, "media", "master"))
 | 
						answers := testAnswers(util.URLJoin(FullURL, "src", "master"), util.URLJoin(FullURL, "media", "master"))
 | 
				
			||||||
	for i := 0; i < len(sameCases); i++ {
 | 
						for i := 0; i < len(sameCases); i++ {
 | 
				
			||||||
		line, err := markdown.RenderString(&markup.RenderContext{
 | 
							line, err := markdown.RenderString(markup.NewTestRenderContext(
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
								markup.Links{
 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base:       FullURL,
 | 
									Base:       FullURL,
 | 
				
			||||||
				BranchPath: "master",
 | 
									BranchPath: "master",
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Repo:  newMockRepo(testRepoOwnerName, testRepoName),
 | 
								newMockRepo(testRepoOwnerName, testRepoName),
 | 
				
			||||||
			Metas: localMetas,
 | 
								localMetas,
 | 
				
			||||||
		}, sameCases[i])
 | 
							), sameCases[i])
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, answers[i], string(line))
 | 
							assert.Equal(t, answers[i], string(line))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -368,12 +340,7 @@ func TestTotal_RenderString(t *testing.T) {
 | 
				
			|||||||
	testCases := []string{}
 | 
						testCases := []string{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := 0; i < len(testCases); i += 2 {
 | 
						for i := 0; i < len(testCases); i += 2 {
 | 
				
			||||||
		line, err := markdown.RenderString(&markup.RenderContext{
 | 
							line, err := markdown.RenderString(markup.NewTestRenderContext(markup.Links{Base: FullURL}), testCases[i])
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base: FullURL,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		}, testCases[i])
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, template.HTML(testCases[i+1]), line)
 | 
							assert.Equal(t, template.HTML(testCases[i+1]), line)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -381,17 +348,17 @@ func TestTotal_RenderString(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func TestRender_RenderParagraphs(t *testing.T) {
 | 
					func TestRender_RenderParagraphs(t *testing.T) {
 | 
				
			||||||
	test := func(t *testing.T, str string, cnt int) {
 | 
						test := func(t *testing.T, str string, cnt int) {
 | 
				
			||||||
		res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, str)
 | 
							res, err := markdown.RenderRawString(markup.NewTestRenderContext(), str)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, cnt, strings.Count(res, "<p"), "Rendered result for unix should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
 | 
							assert.Equal(t, cnt, strings.Count(res, "<p"), "Rendered result for unix should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		mac := strings.ReplaceAll(str, "\n", "\r")
 | 
							mac := strings.ReplaceAll(str, "\n", "\r")
 | 
				
			||||||
		res, err = markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, mac)
 | 
							res, err = markdown.RenderRawString(markup.NewTestRenderContext(), mac)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, cnt, strings.Count(res, "<p"), "Rendered result for mac should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
 | 
							assert.Equal(t, cnt, strings.Count(res, "<p"), "Rendered result for mac should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dos := strings.ReplaceAll(str, "\n", "\r\n")
 | 
							dos := strings.ReplaceAll(str, "\n", "\r\n")
 | 
				
			||||||
		res, err = markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, dos)
 | 
							res, err = markdown.RenderRawString(markup.NewTestRenderContext(), dos)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, cnt, strings.Count(res, "<p"), "Rendered result for windows should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
 | 
							assert.Equal(t, cnt, strings.Count(res, "<p"), "Rendered result for windows should have %d paragraph(s) but has %d:\n%s\n", cnt, strings.Count(res, "<p"), res)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -419,7 +386,7 @@ func TestMarkdownRenderRaw(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for _, testcase := range testcases {
 | 
						for _, testcase := range testcases {
 | 
				
			||||||
		log.Info("Test markdown render error with fuzzy data: %x, the following errors can be recovered", testcase)
 | 
							log.Info("Test markdown render error with fuzzy data: %x, the following errors can be recovered", testcase)
 | 
				
			||||||
		_, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, string(testcase))
 | 
							_, err := markdown.RenderRawString(markup.NewTestRenderContext(), string(testcase))
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -432,7 +399,7 @@ func TestRenderSiblingImages_Issue12925(t *testing.T) {
 | 
				
			|||||||
<a href="/image2" target="_blank" rel="nofollow noopener"><img src="/image2" alt="image2"></a></p>
 | 
					<a href="/image2" target="_blank" rel="nofollow noopener"><img src="/image2" alt="image2"></a></p>
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
	defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)()
 | 
						defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)()
 | 
				
			||||||
	res, err := markdown.RenderRawString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase)
 | 
						res, err := markdown.RenderRawString(markup.NewTestRenderContext(), testcase)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.Equal(t, expected, res)
 | 
						assert.Equal(t, expected, res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -441,7 +408,7 @@ func TestRenderEmojiInLinks_Issue12331(t *testing.T) {
 | 
				
			|||||||
	testcase := `[Link with emoji :moon: in text](https://gitea.io)`
 | 
						testcase := `[Link with emoji :moon: in text](https://gitea.io)`
 | 
				
			||||||
	expected := `<p><a href="https://gitea.io" rel="nofollow">Link with emoji <span class="emoji" aria-label="waxing gibbous moon">🌔</span> in text</a></p>
 | 
						expected := `<p><a href="https://gitea.io" rel="nofollow">Link with emoji <span class="emoji" aria-label="waxing gibbous moon">🌔</span> in text</a></p>
 | 
				
			||||||
`
 | 
					`
 | 
				
			||||||
	res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, testcase)
 | 
						res, err := markdown.RenderString(markup.NewTestRenderContext(), testcase)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.Equal(t, template.HTML(expected), res)
 | 
						assert.Equal(t, template.HTML(expected), res)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -479,7 +446,7 @@ func TestColorPreview(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, test := range positiveTests {
 | 
						for _, test := range positiveTests {
 | 
				
			||||||
		res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
 | 
							res, err := markdown.RenderString(markup.NewTestRenderContext(), test.testcase)
 | 
				
			||||||
		assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
 | 
							assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
 | 
				
			||||||
		assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
 | 
							assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -498,7 +465,7 @@ func TestColorPreview(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, test := range negativeTests {
 | 
						for _, test := range negativeTests {
 | 
				
			||||||
		res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test)
 | 
							res, err := markdown.RenderString(markup.NewTestRenderContext(), test)
 | 
				
			||||||
		assert.NoError(t, err, "Unexpected error in testcase: %q", test)
 | 
							assert.NoError(t, err, "Unexpected error in testcase: %q", test)
 | 
				
			||||||
		assert.NotContains(t, res, `<span class="color-preview" style="background-color: `, "Unexpected result in testcase %q", test)
 | 
							assert.NotContains(t, res, `<span class="color-preview" style="background-color: `, "Unexpected result in testcase %q", test)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -573,7 +540,7 @@ func TestMathBlock(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, test := range testcases {
 | 
						for _, test := range testcases {
 | 
				
			||||||
		res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
 | 
							res, err := markdown.RenderString(markup.NewTestRenderContext(), test.testcase)
 | 
				
			||||||
		assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
 | 
							assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
 | 
				
			||||||
		assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
 | 
							assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -610,7 +577,7 @@ foo: bar
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, test := range testcases {
 | 
						for _, test := range testcases {
 | 
				
			||||||
		res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
 | 
							res, err := markdown.RenderString(markup.NewTestRenderContext(), test.testcase)
 | 
				
			||||||
		assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
 | 
							assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
 | 
				
			||||||
		assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
 | 
							assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1003,11 +970,7 @@ space</p>
 | 
				
			|||||||
	defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)()
 | 
						defer test.MockVariableValue(&markup.RenderBehaviorForTesting.ForceHardLineBreak, true)()
 | 
				
			||||||
	defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
 | 
						defer test.MockVariableValue(&markup.RenderBehaviorForTesting.DisableInternalAttributes, true)()
 | 
				
			||||||
	for i, c := range cases {
 | 
						for i, c := range cases {
 | 
				
			||||||
		result, err := markdown.RenderString(&markup.RenderContext{
 | 
							result, err := markdown.RenderString(markup.NewTestRenderContext(c.Links, util.Iif(c.IsWiki, map[string]string{"markupContentMode": "wiki"}, map[string]string{})), input)
 | 
				
			||||||
			Ctx:   context.Background(),
 | 
					 | 
				
			||||||
			Links: c.Links,
 | 
					 | 
				
			||||||
			Metas: util.Iif(c.IsWiki, map[string]string{"markupContentMode": "wiki"}, map[string]string{}),
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err, "Unexpected error in testcase: %v", i)
 | 
							assert.NoError(t, err, "Unexpected error in testcase: %v", i)
 | 
				
			||||||
		assert.Equal(t, c.Expected, string(result), "Unexpected result in testcase %v", i)
 | 
							assert.Equal(t, c.Expected, string(result), "Unexpected result in testcase %v", i)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1029,7 +992,7 @@ func TestAttention(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		result, err := markdown.RenderString(&markup.RenderContext{Ctx: context.Background()}, input)
 | 
							result, err := markdown.RenderString(markup.NewTestRenderContext(), input)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(result)))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(result)))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1062,6 +1025,6 @@ func BenchmarkSpecializedMarkdown(b *testing.B) {
 | 
				
			|||||||
func BenchmarkMarkdownRender(b *testing.B) {
 | 
					func BenchmarkMarkdownRender(b *testing.B) {
 | 
				
			||||||
	// 23202	     50840 ns/op
 | 
						// 23202	     50840 ns/op
 | 
				
			||||||
	for i := 0; i < b.N; i++ {
 | 
						for i := 0; i < b.N; i++ {
 | 
				
			||||||
		_, _ = markdown.RenderString(&markup.RenderContext{Ctx: context.Background()}, "https://example.com\n- a\n- b\n")
 | 
							_, _ = markdown.RenderString(markup.NewTestRenderContext(), "https://example.com\n- a\n- b\n")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image)
 | 
				
			|||||||
	// Check if the destination is a real link
 | 
						// Check if the destination is a real link
 | 
				
			||||||
	if len(v.Destination) > 0 && !markup.IsFullURLBytes(v.Destination) {
 | 
						if len(v.Destination) > 0 && !markup.IsFullURLBytes(v.Destination) {
 | 
				
			||||||
		v.Destination = []byte(giteautil.URLJoin(
 | 
							v.Destination = []byte(giteautil.URLJoin(
 | 
				
			||||||
			ctx.Links.ResolveMediaLink(ctx.IsMarkupContentWiki()),
 | 
								ctx.RenderOptions.Links.ResolveMediaLink(ctx.IsMarkupContentWiki()),
 | 
				
			||||||
			strings.TrimLeft(string(v.Destination), "/"),
 | 
								strings.TrimLeft(string(v.Destination), "/"),
 | 
				
			||||||
		))
 | 
							))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -143,15 +143,15 @@ func (r *Writer) resolveLink(kind, link string) string {
 | 
				
			|||||||
			kind = org.RegularLink{URL: link}.Kind()
 | 
								kind = org.RegularLink{URL: link}.Kind()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		base := r.Ctx.Links.Base
 | 
							base := r.Ctx.RenderOptions.Links.Base
 | 
				
			||||||
		if r.Ctx.IsMarkupContentWiki() {
 | 
							if r.Ctx.IsMarkupContentWiki() {
 | 
				
			||||||
			base = r.Ctx.Links.WikiLink()
 | 
								base = r.Ctx.RenderOptions.Links.WikiLink()
 | 
				
			||||||
		} else if r.Ctx.Links.HasBranchInfo() {
 | 
							} else if r.Ctx.RenderOptions.Links.HasBranchInfo() {
 | 
				
			||||||
			base = r.Ctx.Links.SrcLink()
 | 
								base = r.Ctx.RenderOptions.Links.SrcLink()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if kind == "image" || kind == "video" {
 | 
							if kind == "image" || kind == "video" {
 | 
				
			||||||
			base = r.Ctx.Links.ResolveMediaLink(r.Ctx.IsMarkupContentWiki())
 | 
								base = r.Ctx.RenderOptions.Links.ResolveMediaLink(r.Ctx.IsMarkupContentWiki())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		link = util.URLJoin(base, link)
 | 
							link = util.URLJoin(base, link)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,10 @@
 | 
				
			|||||||
package markup
 | 
					package markup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
@@ -15,20 +15,21 @@ import (
 | 
				
			|||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const AppURL = "http://localhost:3000/"
 | 
					func TestMain(m *testing.M) {
 | 
				
			||||||
 | 
						setting.AppURL = "http://localhost:3000/"
 | 
				
			||||||
 | 
						setting.IsInTesting = true
 | 
				
			||||||
 | 
						os.Exit(m.Run())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRender_StandardLinks(t *testing.T) {
 | 
					func TestRender_StandardLinks(t *testing.T) {
 | 
				
			||||||
	setting.AppURL = AppURL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	test := func(input, expected string, isWiki bool) {
 | 
						test := func(input, expected string, isWiki bool) {
 | 
				
			||||||
		buffer, err := RenderString(&markup.RenderContext{
 | 
							buffer, err := RenderString(markup.NewTestRenderContext(
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
								markup.Links{
 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base:       "/relative-path",
 | 
									Base:       "/relative-path",
 | 
				
			||||||
				BranchPath: "branch/main",
 | 
									BranchPath: "branch/main",
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			Metas: map[string]string{"markupContentMode": util.Iif(isWiki, "wiki", "")},
 | 
								map[string]string{"markupContentMode": util.Iif(isWiki, "wiki", "")},
 | 
				
			||||||
		}, input)
 | 
							), input)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -42,16 +43,13 @@ func TestRender_StandardLinks(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRender_InternalLinks(t *testing.T) {
 | 
					func TestRender_InternalLinks(t *testing.T) {
 | 
				
			||||||
	setting.AppURL = AppURL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		buffer, err := RenderString(&markup.RenderContext{
 | 
							buffer, err := RenderString(markup.NewTestRenderContext(
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
								markup.Links{
 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base:       "/relative-path",
 | 
									Base:       "/relative-path",
 | 
				
			||||||
				BranchPath: "branch/main",
 | 
									BranchPath: "branch/main",
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}, input)
 | 
							), input)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -67,15 +65,8 @@ func TestRender_InternalLinks(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRender_Media(t *testing.T) {
 | 
					func TestRender_Media(t *testing.T) {
 | 
				
			||||||
	setting.AppURL = AppURL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		buffer, err := RenderString(&markup.RenderContext{
 | 
							buffer, err := RenderString(markup.NewTestRenderContext(markup.Links{Base: "./relative-path"}), input)
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
					 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base: "./relative-path",
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -113,12 +104,8 @@ func TestRender_Media(t *testing.T) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestRender_Source(t *testing.T) {
 | 
					func TestRender_Source(t *testing.T) {
 | 
				
			||||||
	setting.AppURL = AppURL
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	test := func(input, expected string) {
 | 
						test := func(input, expected string) {
 | 
				
			||||||
		buffer, err := RenderString(&markup.RenderContext{
 | 
							buffer, err := RenderString(markup.NewTestRenderContext(), input)
 | 
				
			||||||
			Ctx: git.DefaultContext,
 | 
					 | 
				
			||||||
		}, input)
 | 
					 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
		assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
							assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ import (
 | 
				
			|||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/url"
 | 
						"net/url"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/gitrepo"
 | 
						"code.gitea.io/gitea/modules/gitrepo"
 | 
				
			||||||
@@ -42,16 +43,16 @@ var RenderBehaviorForTesting struct {
 | 
				
			|||||||
	DisableInternalAttributes bool
 | 
						DisableInternalAttributes bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RenderContext represents a render context
 | 
					type RenderOptions struct {
 | 
				
			||||||
type RenderContext struct {
 | 
						// relative path from tree root of the branch
 | 
				
			||||||
	Ctx          context.Context
 | 
						RelativePath string
 | 
				
			||||||
	RelativePath string // relative path from tree root of the branch
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// eg: "orgmode", "asciicast", "console"
 | 
						// eg: "orgmode", "asciicast", "console"
 | 
				
			||||||
	// for file mode, it could be left as empty, and will be detected by file extension in RelativePath
 | 
						// for file mode, it could be left as empty, and will be detected by file extension in RelativePath
 | 
				
			||||||
	MarkupType string
 | 
						MarkupType string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Links Links // special link references for rendering, especially when there is a branch/tree path
 | 
						// special link references for rendering, especially when there is a branch/tree path
 | 
				
			||||||
 | 
						Links Links
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// user&repo, format&style®exp (for external issue pattern), teams&org (for mention)
 | 
						// user&repo, format&style®exp (for external issue pattern), teams&org (for mention)
 | 
				
			||||||
	// BranchNameSubURL (for iframe&asciicast)
 | 
						// BranchNameSubURL (for iframe&asciicast)
 | 
				
			||||||
@@ -59,27 +60,95 @@ type RenderContext struct {
 | 
				
			|||||||
	// markdownLineBreakStyle (comment, document)
 | 
						// markdownLineBreakStyle (comment, document)
 | 
				
			||||||
	Metas map[string]string
 | 
						Metas map[string]string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GitRepo          *git.Repository
 | 
						// used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
 | 
				
			||||||
	Repo             gitrepo.Repository
 | 
						InStandalonePage bool
 | 
				
			||||||
	ShaExistCache    map[string]bool
 | 
					}
 | 
				
			||||||
	cancelFn         func()
 | 
					 | 
				
			||||||
	SidebarTocNode   ast.Node
 | 
					 | 
				
			||||||
	RenderMetaAs     RenderMetaMode
 | 
					 | 
				
			||||||
	InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type RenderHelper struct {
 | 
				
			||||||
 | 
						gitRepo       *git.Repository
 | 
				
			||||||
 | 
						repoFacade    gitrepo.Repository
 | 
				
			||||||
 | 
						shaExistCache map[string]bool
 | 
				
			||||||
 | 
						cancelFn      func()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RenderContext represents a render context
 | 
				
			||||||
 | 
					type RenderContext struct {
 | 
				
			||||||
 | 
						ctx context.Context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SidebarTocNode ast.Node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RenderHelper   RenderHelper
 | 
				
			||||||
 | 
						RenderOptions  RenderOptions
 | 
				
			||||||
	RenderInternal internal.RenderInternal
 | 
						RenderInternal internal.RenderInternal
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ctx *RenderContext) Deadline() (deadline time.Time, ok bool) {
 | 
				
			||||||
 | 
						return ctx.ctx.Deadline()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ctx *RenderContext) Done() <-chan struct{} {
 | 
				
			||||||
 | 
						return ctx.ctx.Done()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ctx *RenderContext) Err() error {
 | 
				
			||||||
 | 
						return ctx.ctx.Err()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ctx *RenderContext) Value(key any) any {
 | 
				
			||||||
 | 
						return ctx.ctx.Value(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var _ context.Context = (*RenderContext)(nil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewRenderContext(ctx context.Context) *RenderContext {
 | 
				
			||||||
 | 
						return &RenderContext{ctx: ctx}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ctx *RenderContext) WithMarkupType(typ string) *RenderContext {
 | 
				
			||||||
 | 
						ctx.RenderOptions.MarkupType = typ
 | 
				
			||||||
 | 
						return ctx
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ctx *RenderContext) WithRelativePath(path string) *RenderContext {
 | 
				
			||||||
 | 
						ctx.RenderOptions.RelativePath = path
 | 
				
			||||||
 | 
						return ctx
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ctx *RenderContext) WithLinks(links Links) *RenderContext {
 | 
				
			||||||
 | 
						ctx.RenderOptions.Links = links
 | 
				
			||||||
 | 
						return ctx
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ctx *RenderContext) WithMetas(metas map[string]string) *RenderContext {
 | 
				
			||||||
 | 
						ctx.RenderOptions.Metas = metas
 | 
				
			||||||
 | 
						return ctx
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ctx *RenderContext) WithInStandalonePage(v bool) *RenderContext {
 | 
				
			||||||
 | 
						ctx.RenderOptions.InStandalonePage = v
 | 
				
			||||||
 | 
						return ctx
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ctx *RenderContext) WithGitRepo(r *git.Repository) *RenderContext {
 | 
				
			||||||
 | 
						ctx.RenderHelper.gitRepo = r
 | 
				
			||||||
 | 
						return ctx
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (ctx *RenderContext) WithRepoFacade(r gitrepo.Repository) *RenderContext {
 | 
				
			||||||
 | 
						ctx.RenderHelper.repoFacade = r
 | 
				
			||||||
 | 
						return ctx
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Cancel runs any cleanup functions that have been registered for this Ctx
 | 
					// Cancel runs any cleanup functions that have been registered for this Ctx
 | 
				
			||||||
func (ctx *RenderContext) Cancel() {
 | 
					func (ctx *RenderContext) Cancel() {
 | 
				
			||||||
	if ctx == nil {
 | 
						if ctx == nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.ShaExistCache = map[string]bool{}
 | 
						ctx.RenderHelper.shaExistCache = map[string]bool{}
 | 
				
			||||||
	if ctx.cancelFn == nil {
 | 
						if ctx.RenderHelper.cancelFn == nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.cancelFn()
 | 
						ctx.RenderHelper.cancelFn()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddCancel adds the provided fn as a Cleanup for this Ctx
 | 
					// AddCancel adds the provided fn as a Cleanup for this Ctx
 | 
				
			||||||
@@ -87,38 +156,38 @@ func (ctx *RenderContext) AddCancel(fn func()) {
 | 
				
			|||||||
	if ctx == nil {
 | 
						if ctx == nil {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	oldCancelFn := ctx.cancelFn
 | 
						oldCancelFn := ctx.RenderHelper.cancelFn
 | 
				
			||||||
	if oldCancelFn == nil {
 | 
						if oldCancelFn == nil {
 | 
				
			||||||
		ctx.cancelFn = fn
 | 
							ctx.RenderHelper.cancelFn = fn
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.cancelFn = func() {
 | 
						ctx.RenderHelper.cancelFn = func() {
 | 
				
			||||||
		defer oldCancelFn()
 | 
							defer oldCancelFn()
 | 
				
			||||||
		fn()
 | 
							fn()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ctx *RenderContext) IsMarkupContentWiki() bool {
 | 
					func (ctx *RenderContext) IsMarkupContentWiki() bool {
 | 
				
			||||||
	return ctx.Metas != nil && ctx.Metas["markupContentMode"] == "wiki"
 | 
						return ctx.RenderOptions.Metas != nil && ctx.RenderOptions.Metas["markupContentMode"] == "wiki"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Render renders markup file to HTML with all specific handling stuff.
 | 
					// Render renders markup file to HTML with all specific handling stuff.
 | 
				
			||||||
func Render(ctx *RenderContext, input io.Reader, output io.Writer) error {
 | 
					func Render(ctx *RenderContext, input io.Reader, output io.Writer) error {
 | 
				
			||||||
	if ctx.MarkupType == "" && ctx.RelativePath != "" {
 | 
						if ctx.RenderOptions.MarkupType == "" && ctx.RenderOptions.RelativePath != "" {
 | 
				
			||||||
		ctx.MarkupType = DetectMarkupTypeByFileName(ctx.RelativePath)
 | 
							ctx.RenderOptions.MarkupType = DetectMarkupTypeByFileName(ctx.RenderOptions.RelativePath)
 | 
				
			||||||
		if ctx.MarkupType == "" {
 | 
							if ctx.RenderOptions.MarkupType == "" {
 | 
				
			||||||
			return util.NewInvalidArgumentErrorf("unsupported file to render: %q", ctx.RelativePath)
 | 
								return util.NewInvalidArgumentErrorf("unsupported file to render: %q", ctx.RenderOptions.RelativePath)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	renderer := renderers[ctx.MarkupType]
 | 
						renderer := renderers[ctx.RenderOptions.MarkupType]
 | 
				
			||||||
	if renderer == nil {
 | 
						if renderer == nil {
 | 
				
			||||||
		return util.NewInvalidArgumentErrorf("unsupported markup type: %q", ctx.MarkupType)
 | 
							return util.NewInvalidArgumentErrorf("unsupported markup type: %q", ctx.RenderOptions.MarkupType)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.RelativePath != "" {
 | 
						if ctx.RenderOptions.RelativePath != "" {
 | 
				
			||||||
		if externalRender, ok := renderer.(ExternalRenderer); ok && externalRender.DisplayInIFrame() {
 | 
							if externalRender, ok := renderer.(ExternalRenderer); ok && externalRender.DisplayInIFrame() {
 | 
				
			||||||
			if !ctx.InStandalonePage {
 | 
								if !ctx.RenderOptions.InStandalonePage {
 | 
				
			||||||
				// for an external "DisplayInIFrame" render, it could only output its content in a standalone page
 | 
									// 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
 | 
									// otherwise, a <iframe> should be outputted to embed the external rendered page
 | 
				
			||||||
				return renderIFrame(ctx, output)
 | 
									return renderIFrame(ctx, output)
 | 
				
			||||||
@@ -151,10 +220,10 @@ width="100%%" height="0" scrolling="no" frameborder="0" style="overflow: hidden"
 | 
				
			|||||||
sandbox="allow-scripts"
 | 
					sandbox="allow-scripts"
 | 
				
			||||||
></iframe>`,
 | 
					></iframe>`,
 | 
				
			||||||
		setting.AppSubURL,
 | 
							setting.AppSubURL,
 | 
				
			||||||
		url.PathEscape(ctx.Metas["user"]),
 | 
							url.PathEscape(ctx.RenderOptions.Metas["user"]),
 | 
				
			||||||
		url.PathEscape(ctx.Metas["repo"]),
 | 
							url.PathEscape(ctx.RenderOptions.Metas["repo"]),
 | 
				
			||||||
		ctx.Metas["BranchNameSubURL"],
 | 
							ctx.RenderOptions.Metas["BranchNameSubURL"],
 | 
				
			||||||
		url.PathEscape(ctx.RelativePath),
 | 
							url.PathEscape(ctx.RenderOptions.RelativePath),
 | 
				
			||||||
	))
 | 
						))
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -176,7 +245,7 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr
 | 
				
			|||||||
	pr1, pw1, close1 := pipes()
 | 
						pr1, pw1, close1 := pipes()
 | 
				
			||||||
	defer close1()
 | 
						defer close1()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	eg, _ := errgroup.WithContext(ctx.Ctx)
 | 
						eg, _ := errgroup.WithContext(ctx)
 | 
				
			||||||
	var pw2 io.WriteCloser = util.NopCloser{Writer: finalProcessor}
 | 
						var pw2 io.WriteCloser = util.NopCloser{Writer: finalProcessor}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if r, ok := renderer.(ExternalRenderer); !ok || !r.SanitizerDisabled() {
 | 
						if r, ok := renderer.(ExternalRenderer); !ok || !r.SanitizerDisabled() {
 | 
				
			||||||
@@ -230,3 +299,27 @@ func Init(ph *ProcessorHelper) {
 | 
				
			|||||||
func ComposeSimpleDocumentMetas() map[string]string {
 | 
					func ComposeSimpleDocumentMetas() map[string]string {
 | 
				
			||||||
	return map[string]string{"markdownLineBreakStyle": "document"}
 | 
						return map[string]string{"markdownLineBreakStyle": "document"}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewTestRenderContext is a helper function to create a RenderContext for testing purpose
 | 
				
			||||||
 | 
					// It accepts string (RelativePath), Links, map[string]string (Metas), gitrepo.Repository
 | 
				
			||||||
 | 
					func NewTestRenderContext(a ...any) *RenderContext {
 | 
				
			||||||
 | 
						if !setting.IsInTesting {
 | 
				
			||||||
 | 
							panic("NewTestRenderContext should only be used in testing")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx := NewRenderContext(context.Background())
 | 
				
			||||||
 | 
						for _, v := range a {
 | 
				
			||||||
 | 
							switch v := v.(type) {
 | 
				
			||||||
 | 
							case string:
 | 
				
			||||||
 | 
								ctx = ctx.WithRelativePath(v)
 | 
				
			||||||
 | 
							case Links:
 | 
				
			||||||
 | 
								ctx = ctx.WithLinks(v)
 | 
				
			||||||
 | 
							case map[string]string:
 | 
				
			||||||
 | 
								ctx = ctx.WithMetas(v)
 | 
				
			||||||
 | 
							case gitrepo.Repository:
 | 
				
			||||||
 | 
								ctx = ctx.WithRepoFacade(v)
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								panic(fmt.Sprintf("unknown type %T", v))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ctx
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,10 +38,7 @@ func (ut *RenderUtils) RenderCommitMessage(msg string, metas map[string]string)
 | 
				
			|||||||
	cleanMsg := template.HTMLEscapeString(msg)
 | 
						cleanMsg := template.HTMLEscapeString(msg)
 | 
				
			||||||
	// we can safely assume that it will not return any error, since there
 | 
						// we can safely assume that it will not return any error, since there
 | 
				
			||||||
	// shouldn't be any special HTML.
 | 
						// shouldn't be any special HTML.
 | 
				
			||||||
	fullMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
 | 
						fullMessage, err := markup.RenderCommitMessage(markup.NewRenderContext(ut.ctx).WithMetas(metas), cleanMsg)
 | 
				
			||||||
		Ctx:   ut.ctx,
 | 
					 | 
				
			||||||
		Metas: metas,
 | 
					 | 
				
			||||||
	}, cleanMsg)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("RenderCommitMessage: %v", err)
 | 
							log.Error("RenderCommitMessage: %v", err)
 | 
				
			||||||
		return ""
 | 
							return ""
 | 
				
			||||||
@@ -68,10 +65,7 @@ func (ut *RenderUtils) RenderCommitMessageLinkSubject(msg, urlDefault string, me
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// we can safely assume that it will not return any error, since there
 | 
						// we can safely assume that it will not return any error, since there
 | 
				
			||||||
	// shouldn't be any special HTML.
 | 
						// shouldn't be any special HTML.
 | 
				
			||||||
	renderedMessage, err := markup.RenderCommitMessageSubject(&markup.RenderContext{
 | 
						renderedMessage, err := markup.RenderCommitMessageSubject(markup.NewRenderContext(ut.ctx).WithMetas(metas), urlDefault, template.HTMLEscapeString(msgLine))
 | 
				
			||||||
		Ctx:   ut.ctx,
 | 
					 | 
				
			||||||
		Metas: metas,
 | 
					 | 
				
			||||||
	}, urlDefault, template.HTMLEscapeString(msgLine))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("RenderCommitMessageSubject: %v", err)
 | 
							log.Error("RenderCommitMessageSubject: %v", err)
 | 
				
			||||||
		return ""
 | 
							return ""
 | 
				
			||||||
@@ -93,10 +87,7 @@ func (ut *RenderUtils) RenderCommitBody(msg string, metas map[string]string) tem
 | 
				
			|||||||
		return ""
 | 
							return ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	renderedMessage, err := markup.RenderCommitMessage(&markup.RenderContext{
 | 
						renderedMessage, err := markup.RenderCommitMessage(markup.NewRenderContext(ut.ctx).WithMetas(metas), template.HTMLEscapeString(msgLine))
 | 
				
			||||||
		Ctx:   ut.ctx,
 | 
					 | 
				
			||||||
		Metas: metas,
 | 
					 | 
				
			||||||
	}, template.HTMLEscapeString(msgLine))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("RenderCommitMessage: %v", err)
 | 
							log.Error("RenderCommitMessage: %v", err)
 | 
				
			||||||
		return ""
 | 
							return ""
 | 
				
			||||||
@@ -115,10 +106,7 @@ func renderCodeBlock(htmlEscapedTextToRender template.HTML) template.HTML {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// RenderIssueTitle renders issue/pull title with defined post processors
 | 
					// RenderIssueTitle renders issue/pull title with defined post processors
 | 
				
			||||||
func (ut *RenderUtils) RenderIssueTitle(text string, metas map[string]string) template.HTML {
 | 
					func (ut *RenderUtils) RenderIssueTitle(text string, metas map[string]string) template.HTML {
 | 
				
			||||||
	renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{
 | 
						renderedText, err := markup.RenderIssueTitle(markup.NewRenderContext(ut.ctx).WithMetas(metas), template.HTMLEscapeString(text))
 | 
				
			||||||
		Ctx:   ut.ctx,
 | 
					 | 
				
			||||||
		Metas: metas,
 | 
					 | 
				
			||||||
	}, template.HTMLEscapeString(text))
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("RenderIssueTitle: %v", err)
 | 
							log.Error("RenderIssueTitle: %v", err)
 | 
				
			||||||
		return ""
 | 
							return ""
 | 
				
			||||||
@@ -186,7 +174,7 @@ func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// RenderEmoji renders html text with emoji post processors
 | 
					// RenderEmoji renders html text with emoji post processors
 | 
				
			||||||
func (ut *RenderUtils) RenderEmoji(text string) template.HTML {
 | 
					func (ut *RenderUtils) RenderEmoji(text string) template.HTML {
 | 
				
			||||||
	renderedText, err := markup.RenderEmoji(&markup.RenderContext{Ctx: ut.ctx}, template.HTMLEscapeString(text))
 | 
						renderedText, err := markup.RenderEmoji(markup.NewRenderContext(ut.ctx), template.HTMLEscapeString(text))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("RenderEmoji: %v", err)
 | 
							log.Error("RenderEmoji: %v", err)
 | 
				
			||||||
		return ""
 | 
							return ""
 | 
				
			||||||
@@ -208,10 +196,7 @@ func reactionToEmoji(reaction string) template.HTML {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ut *RenderUtils) MarkdownToHtml(input string) template.HTML { //nolint:revive
 | 
					func (ut *RenderUtils) MarkdownToHtml(input string) template.HTML { //nolint:revive
 | 
				
			||||||
	output, err := markdown.RenderString(&markup.RenderContext{
 | 
						output, err := markdown.RenderString(markup.NewRenderContext(ut.ctx).WithMetas(markup.ComposeSimpleDocumentMetas()), input)
 | 
				
			||||||
		Ctx:   ut.ctx,
 | 
					 | 
				
			||||||
		Metas: markup.ComposeSimpleDocumentMetas(),
 | 
					 | 
				
			||||||
	}, input)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("RenderString: %v", err)
 | 
							log.Error("RenderString: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -99,9 +99,7 @@ func MarkdownRaw(ctx *context.APIContext) {
 | 
				
			|||||||
	//   "422":
 | 
						//   "422":
 | 
				
			||||||
	//     "$ref": "#/responses/validationError"
 | 
						//     "$ref": "#/responses/validationError"
 | 
				
			||||||
	defer ctx.Req.Body.Close()
 | 
						defer ctx.Req.Body.Close()
 | 
				
			||||||
	if err := markdown.RenderRaw(&markup.RenderContext{
 | 
						if err := markdown.RenderRaw(markup.NewRenderContext(ctx), ctx.Req.Body, ctx.Resp); err != nil {
 | 
				
			||||||
		Ctx: ctx,
 | 
					 | 
				
			||||||
	}, ctx.Req.Body, ctx.Resp); err != nil {
 | 
					 | 
				
			||||||
		ctx.InternalServerError(err)
 | 
							ctx.InternalServerError(err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,13 +28,12 @@ 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"
 | 
						// 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"
 | 
						// and the urlPathContext is "/gitea/owner/repo/src/branch/features/feat-123/doc"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	renderCtx := &markup.RenderContext{
 | 
						renderCtx := markup.NewRenderContext(ctx).
 | 
				
			||||||
		Ctx:        ctx,
 | 
							WithLinks(markup.Links{AbsolutePrefix: true}).
 | 
				
			||||||
		Links:      markup.Links{AbsolutePrefix: true},
 | 
							WithMarkupType(markdown.MarkupName)
 | 
				
			||||||
		MarkupType: markdown.MarkupName,
 | 
					
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if urlPathContext != "" {
 | 
						if urlPathContext != "" {
 | 
				
			||||||
		renderCtx.Links.Base = fmt.Sprintf("%s%s", httplib.GuessCurrentHostURL(ctx), urlPathContext)
 | 
							renderCtx.RenderOptions.Links.Base = fmt.Sprintf("%s%s", httplib.GuessCurrentHostURL(ctx), urlPathContext)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if mode == "" || mode == "markdown" {
 | 
						if mode == "" || mode == "markdown" {
 | 
				
			||||||
@@ -47,15 +46,14 @@ func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPa
 | 
				
			|||||||
	switch mode {
 | 
						switch mode {
 | 
				
			||||||
	case "gfm": // legacy mode, do nothing
 | 
						case "gfm": // legacy mode, do nothing
 | 
				
			||||||
	case "comment":
 | 
						case "comment":
 | 
				
			||||||
		renderCtx.Metas = map[string]string{"markdownLineBreakStyle": "comment"}
 | 
							renderCtx = renderCtx.WithMetas(map[string]string{"markdownLineBreakStyle": "comment"})
 | 
				
			||||||
	case "wiki":
 | 
						case "wiki":
 | 
				
			||||||
		renderCtx.Metas = map[string]string{"markdownLineBreakStyle": "document", "markupContentMode": "wiki"}
 | 
							renderCtx = renderCtx.WithMetas(map[string]string{"markdownLineBreakStyle": "document", "markupContentMode": "wiki"})
 | 
				
			||||||
	case "file":
 | 
						case "file":
 | 
				
			||||||
		// render the repo file content by its extension
 | 
							// render the repo file content by its extension
 | 
				
			||||||
		renderCtx.Metas = map[string]string{"markdownLineBreakStyle": "document"}
 | 
							renderCtx = renderCtx.WithMetas(map[string]string{"markdownLineBreakStyle": "document"}).
 | 
				
			||||||
		renderCtx.MarkupType = ""
 | 
								WithMarkupType("").
 | 
				
			||||||
		renderCtx.RelativePath = filePath
 | 
								WithRelativePath(filePath)
 | 
				
			||||||
		renderCtx.InStandalonePage = true
 | 
					 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Unknown mode: %s", mode))
 | 
							ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("Unknown mode: %s", mode))
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -70,17 +68,17 @@ 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.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"
 | 
							refPath = strings.TrimSuffix(refPath, "/"+fileDir) // now we get the correct branch path: "branch/features/feat-12"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		renderCtx.Links = markup.Links{AbsolutePrefix: true, Base: absoluteBasePrefix, BranchPath: refPath, TreePath: fileDir}
 | 
							renderCtx = renderCtx.WithLinks(markup.Links{AbsolutePrefix: true, Base: absoluteBasePrefix, BranchPath: refPath, TreePath: fileDir})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if repo != nil && repo.Repository != nil {
 | 
						if repo != nil && repo.Repository != nil {
 | 
				
			||||||
		renderCtx.Repo = repo.Repository
 | 
							renderCtx = renderCtx.WithRepoFacade(repo.Repository)
 | 
				
			||||||
		if mode == "file" {
 | 
							if mode == "file" {
 | 
				
			||||||
			renderCtx.Metas = repo.Repository.ComposeDocumentMetas(ctx)
 | 
								renderCtx = renderCtx.WithMetas(repo.Repository.ComposeDocumentMetas(ctx))
 | 
				
			||||||
		} else if mode == "wiki" {
 | 
							} else if mode == "wiki" {
 | 
				
			||||||
			renderCtx.Metas = repo.Repository.ComposeWikiMetas(ctx)
 | 
								renderCtx = renderCtx.WithMetas(repo.Repository.ComposeWikiMetas(ctx))
 | 
				
			||||||
		} else if mode == "comment" {
 | 
							} else if mode == "comment" {
 | 
				
			||||||
			renderCtx.Metas = repo.Repository.ComposeMetas(ctx)
 | 
								renderCtx = renderCtx.WithMetas(repo.Repository.ComposeMetas(ctx))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err := markup.Render(renderCtx, strings.NewReader(text), ctx.Resp); err != nil {
 | 
						if err := markup.Render(renderCtx, strings.NewReader(text), ctx.Resp); err != nil {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,16 +51,14 @@ func toReleaseLink(ctx *context.Context, act *activities_model.Action) string {
 | 
				
			|||||||
// renderMarkdown creates a minimal markdown render context from an action.
 | 
					// renderMarkdown creates a minimal markdown render context from an action.
 | 
				
			||||||
// If rendering fails, the original markdown text is returned
 | 
					// If rendering fails, the original markdown text is returned
 | 
				
			||||||
func renderMarkdown(ctx *context.Context, act *activities_model.Action, content string) template.HTML {
 | 
					func renderMarkdown(ctx *context.Context, act *activities_model.Action, content string) template.HTML {
 | 
				
			||||||
	markdownCtx := &markup.RenderContext{
 | 
						markdownCtx := markup.NewRenderContext(ctx).
 | 
				
			||||||
		Ctx: ctx,
 | 
							WithLinks(markup.Links{
 | 
				
			||||||
		Links: markup.Links{
 | 
					 | 
				
			||||||
			Base: act.GetRepoLink(ctx),
 | 
								Base: act.GetRepoLink(ctx),
 | 
				
			||||||
		},
 | 
							}).
 | 
				
			||||||
		Metas: map[string]string{ // FIXME: not right here, it should use issue to compose the metas
 | 
							WithMetas(map[string]string{ // FIXME: not right here, it should use issue to compose the metas
 | 
				
			||||||
			"user": act.GetRepoUserName(ctx),
 | 
								"user": act.GetRepoUserName(ctx),
 | 
				
			||||||
			"repo": act.GetRepoName(ctx),
 | 
								"repo": act.GetRepoName(ctx),
 | 
				
			||||||
		},
 | 
							})
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	markdown, err := markdown.RenderString(markdownCtx, content)
 | 
						markdown, err := markdown.RenderString(markdownCtx, content)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return templates.SanitizeHTML(content) // old code did so: use SanitizeHTML to render in tmpl
 | 
							return templates.SanitizeHTML(content) // old code did so: use SanitizeHTML to render in tmpl
 | 
				
			||||||
@@ -296,14 +294,13 @@ func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release) (
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		link := &feeds.Link{Href: rel.HTMLURL()}
 | 
							link := &feeds.Link{Href: rel.HTMLURL()}
 | 
				
			||||||
		content, err = markdown.RenderString(&markup.RenderContext{
 | 
							content, err = markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
			Ctx:  ctx,
 | 
								WithRepoFacade(rel.Repo).
 | 
				
			||||||
			Repo: rel.Repo,
 | 
								WithLinks(markup.Links{
 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base: rel.Repo.Link(),
 | 
									Base: rel.Repo.Link(),
 | 
				
			||||||
			},
 | 
								}).
 | 
				
			||||||
			Metas: rel.Repo.ComposeMetas(ctx),
 | 
								WithMetas(rel.Repo.ComposeMetas(ctx)),
 | 
				
			||||||
		}, rel.Note)
 | 
								rel.Note)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,13 +41,10 @@ func showUserFeed(ctx *context.Context, formatType string) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctxUserDescription, err := markdown.RenderString(&markup.RenderContext{
 | 
						ctxUserDescription, err := markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
		Ctx: ctx,
 | 
							WithLinks(markup.Links{Base: ctx.ContextUser.HTMLURL()}).
 | 
				
			||||||
		Links: markup.Links{
 | 
							WithMetas(markup.ComposeSimpleDocumentMetas()),
 | 
				
			||||||
			Base: ctx.ContextUser.HTMLURL(),
 | 
							ctx.ContextUser.Description)
 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Metas: markup.ComposeSimpleDocumentMetas(),
 | 
					 | 
				
			||||||
	}, ctx.ContextUser.Description)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("RenderString", err)
 | 
							ctx.ServerError("RenderString", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -180,17 +180,16 @@ func prepareOrgProfileReadme(ctx *context.Context, viewRepositories bool) bool {
 | 
				
			|||||||
	if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
 | 
						if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
 | 
				
			||||||
		log.Error("failed to GetBlobContent: %v", err)
 | 
							log.Error("failed to GetBlobContent: %v", err)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if profileContent, err := markdown.RenderString(&markup.RenderContext{
 | 
							if profileContent, err := markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
			Ctx:     ctx,
 | 
								WithGitRepo(profileGitRepo).
 | 
				
			||||||
			GitRepo: profileGitRepo,
 | 
								WithLinks(markup.Links{
 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				// Pass repo link to markdown render for the full link of media elements.
 | 
									// Pass repo link to markdown render for the full link of media elements.
 | 
				
			||||||
				// The profile of default branch would be shown.
 | 
									// The profile of default branch would be shown.
 | 
				
			||||||
				Base:       profileDbRepo.Link(),
 | 
									Base:       profileDbRepo.Link(),
 | 
				
			||||||
				BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)),
 | 
									BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)),
 | 
				
			||||||
			},
 | 
								}).
 | 
				
			||||||
			Metas: markup.ComposeSimpleDocumentMetas(),
 | 
								WithMetas(markup.ComposeSimpleDocumentMetas()),
 | 
				
			||||||
		}, bytes); err != nil {
 | 
								bytes); err != nil {
 | 
				
			||||||
			log.Error("failed to RenderString: %v", err)
 | 
								log.Error("failed to RenderString: %v", err)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			ctx.Data["ProfileReadme"] = profileContent
 | 
								ctx.Data["ProfileReadme"] = profileContent
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -392,16 +392,15 @@ func Diff(ctx *context.Context) {
 | 
				
			|||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		ctx.Data["NoteCommit"] = note.Commit
 | 
							ctx.Data["NoteCommit"] = note.Commit
 | 
				
			||||||
		ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(ctx, note.Commit)
 | 
							ctx.Data["NoteAuthor"] = user_model.ValidateCommitWithEmail(ctx, note.Commit)
 | 
				
			||||||
		ctx.Data["NoteRendered"], err = markup.RenderCommitMessage(&markup.RenderContext{
 | 
							ctx.Data["NoteRendered"], err = markup.RenderCommitMessage(markup.NewRenderContext(ctx).
 | 
				
			||||||
			Links: markup.Links{
 | 
								WithLinks(markup.Links{
 | 
				
			||||||
				Base:       ctx.Repo.RepoLink,
 | 
									Base:       ctx.Repo.RepoLink,
 | 
				
			||||||
				BranchPath: path.Join("commit", util.PathEscapeSegments(commitID)),
 | 
									BranchPath: path.Join("commit", util.PathEscapeSegments(commitID)),
 | 
				
			||||||
			},
 | 
								}).
 | 
				
			||||||
			Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
 | 
								WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
 | 
				
			||||||
			GitRepo: ctx.Repo.GitRepo,
 | 
								WithGitRepo(ctx.Repo.GitRepo).
 | 
				
			||||||
			Repo:    ctx.Repo.Repository,
 | 
								WithRepoFacade(ctx.Repo.Repository),
 | 
				
			||||||
			Ctx:     ctx,
 | 
								template.HTMLEscapeString(string(charset.ToUTF8WithFallback(note.Message, charset.ConvertOpts{}))))
 | 
				
			||||||
		}, template.HTMLEscapeString(string(charset.ToUTF8WithFallback(note.Message, charset.ConvertOpts{}))))
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.ServerError("RenderCommitMessage", err)
 | 
								ctx.ServerError("RenderCommitMessage", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -149,7 +149,7 @@ func setCsvCompareContext(ctx *context.Context) {
 | 
				
			|||||||
			return csvReader, reader, err
 | 
								return csvReader, reader, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		baseReader, baseBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.OldName}, baseBlob)
 | 
							baseReader, baseBlobCloser, err := csvReaderFromCommit(markup.NewRenderContext(ctx).WithRelativePath(diffFile.OldName), baseBlob)
 | 
				
			||||||
		if baseBlobCloser != nil {
 | 
							if baseBlobCloser != nil {
 | 
				
			||||||
			defer baseBlobCloser.Close()
 | 
								defer baseBlobCloser.Close()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -161,7 +161,7 @@ func setCsvCompareContext(ctx *context.Context) {
 | 
				
			|||||||
			return CsvDiffResult{nil, "unable to load file"}
 | 
								return CsvDiffResult{nil, "unable to load file"}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		headReader, headBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.Name}, headBlob)
 | 
							headReader, headBlobCloser, err := csvReaderFromCommit(markup.NewRenderContext(ctx).WithRelativePath(diffFile.Name), headBlob)
 | 
				
			||||||
		if headBlobCloser != nil {
 | 
							if headBlobCloser != nil {
 | 
				
			||||||
			defer headBlobCloser.Close()
 | 
								defer headBlobCloser.Close()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -366,15 +366,12 @@ func UpdateIssueContent(ctx *context.Context) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	content, err := markdown.RenderString(&markup.RenderContext{
 | 
						content, err := markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
		Links: markup.Links{
 | 
							WithLinks(markup.Links{Base: ctx.FormString("context")}).
 | 
				
			||||||
			Base: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
 | 
							WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
 | 
				
			||||||
		},
 | 
							WithGitRepo(ctx.Repo.GitRepo).
 | 
				
			||||||
		Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
 | 
							WithRepoFacade(ctx.Repo.Repository),
 | 
				
			||||||
		GitRepo: ctx.Repo.GitRepo,
 | 
							issue.Content)
 | 
				
			||||||
		Repo:    ctx.Repo.Repository,
 | 
					 | 
				
			||||||
		Ctx:     ctx,
 | 
					 | 
				
			||||||
	}, issue.Content)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("RenderString", err)
 | 
							ctx.ServerError("RenderString", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -267,15 +267,12 @@ func UpdateCommentContent(ctx *context.Context) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	var renderedContent template.HTML
 | 
						var renderedContent template.HTML
 | 
				
			||||||
	if comment.Content != "" {
 | 
						if comment.Content != "" {
 | 
				
			||||||
		renderedContent, err = markdown.RenderString(&markup.RenderContext{
 | 
							renderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
			Links: markup.Links{
 | 
								WithLinks(markup.Links{Base: ctx.FormString("context")}).
 | 
				
			||||||
				Base: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
 | 
								WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
 | 
				
			||||||
			},
 | 
								WithGitRepo(ctx.Repo.GitRepo).
 | 
				
			||||||
			Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
 | 
								WithRepoFacade(ctx.Repo.Repository),
 | 
				
			||||||
			GitRepo: ctx.Repo.GitRepo,
 | 
								comment.Content)
 | 
				
			||||||
			Repo:    ctx.Repo.Repository,
 | 
					 | 
				
			||||||
			Ctx:     ctx,
 | 
					 | 
				
			||||||
		}, comment.Content)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.ServerError("RenderString", err)
 | 
								ctx.ServerError("RenderString", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -359,15 +359,12 @@ func ViewIssue(ctx *context.Context) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Data["IssueWatch"] = iw
 | 
						ctx.Data["IssueWatch"] = iw
 | 
				
			||||||
	issue.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
 | 
						issue.RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
		Links: markup.Links{
 | 
							WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
 | 
				
			||||||
			Base: ctx.Repo.RepoLink,
 | 
							WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
 | 
				
			||||||
		},
 | 
							WithGitRepo(ctx.Repo.GitRepo).
 | 
				
			||||||
		Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
 | 
							WithRepoFacade(ctx.Repo.Repository),
 | 
				
			||||||
		GitRepo: ctx.Repo.GitRepo,
 | 
							issue.Content)
 | 
				
			||||||
		Repo:    ctx.Repo.Repository,
 | 
					 | 
				
			||||||
		Ctx:     ctx,
 | 
					 | 
				
			||||||
	}, issue.Content)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("RenderString", err)
 | 
							ctx.ServerError("RenderString", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -467,15 +464,14 @@ func ViewIssue(ctx *context.Context) {
 | 
				
			|||||||
		comment.Issue = issue
 | 
							comment.Issue = issue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if comment.Type == issues_model.CommentTypeComment || comment.Type == issues_model.CommentTypeReview {
 | 
							if comment.Type == issues_model.CommentTypeComment || comment.Type == issues_model.CommentTypeReview {
 | 
				
			||||||
			comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
 | 
								comment.RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
				Links: markup.Links{
 | 
									WithLinks(markup.Links{
 | 
				
			||||||
					Base: ctx.Repo.RepoLink,
 | 
										Base: ctx.Repo.RepoLink,
 | 
				
			||||||
				},
 | 
									}).
 | 
				
			||||||
				Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
 | 
									WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
 | 
				
			||||||
				GitRepo: ctx.Repo.GitRepo,
 | 
									WithGitRepo(ctx.Repo.GitRepo).
 | 
				
			||||||
				Repo:    ctx.Repo.Repository,
 | 
									WithRepoFacade(ctx.Repo.Repository),
 | 
				
			||||||
				Ctx:     ctx,
 | 
									comment.Content)
 | 
				
			||||||
			}, comment.Content)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				ctx.ServerError("RenderString", err)
 | 
									ctx.ServerError("RenderString", err)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
@@ -550,15 +546,12 @@ func ViewIssue(ctx *context.Context) {
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if comment.Type.HasContentSupport() {
 | 
							} else if comment.Type.HasContentSupport() {
 | 
				
			||||||
			comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
 | 
								comment.RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
				Links: markup.Links{
 | 
									WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
 | 
				
			||||||
					Base: ctx.Repo.RepoLink,
 | 
									WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
 | 
				
			||||||
				},
 | 
									WithGitRepo(ctx.Repo.GitRepo).
 | 
				
			||||||
				Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
 | 
									WithRepoFacade(ctx.Repo.Repository),
 | 
				
			||||||
				GitRepo: ctx.Repo.GitRepo,
 | 
									comment.Content)
 | 
				
			||||||
				Repo:    ctx.Repo.Repository,
 | 
					 | 
				
			||||||
				Ctx:     ctx,
 | 
					 | 
				
			||||||
			}, comment.Content)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				ctx.ServerError("RenderString", err)
 | 
									ctx.ServerError("RenderString", err)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -79,15 +79,12 @@ func Milestones(ctx *context.Context) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, m := range miles {
 | 
						for _, m := range miles {
 | 
				
			||||||
		m.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
 | 
							m.RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
			Links: markup.Links{
 | 
								WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
 | 
				
			||||||
				Base: ctx.Repo.RepoLink,
 | 
								WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
 | 
				
			||||||
			},
 | 
								WithGitRepo(ctx.Repo.GitRepo).
 | 
				
			||||||
			Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
 | 
								WithRepoFacade(ctx.Repo.Repository),
 | 
				
			||||||
			GitRepo: ctx.Repo.GitRepo,
 | 
								m.Content)
 | 
				
			||||||
			Repo:    ctx.Repo.Repository,
 | 
					 | 
				
			||||||
			Ctx:     ctx,
 | 
					 | 
				
			||||||
		}, m.Content)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.ServerError("RenderString", err)
 | 
								ctx.ServerError("RenderString", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -268,15 +265,12 @@ func MilestoneIssuesAndPulls(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	milestone.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
 | 
						milestone.RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
		Links: markup.Links{
 | 
							WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
 | 
				
			||||||
			Base: ctx.Repo.RepoLink,
 | 
							WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
 | 
				
			||||||
		},
 | 
							WithGitRepo(ctx.Repo.GitRepo).
 | 
				
			||||||
		Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
 | 
							WithRepoFacade(ctx.Repo.Repository),
 | 
				
			||||||
		GitRepo: ctx.Repo.GitRepo,
 | 
							milestone.Content)
 | 
				
			||||||
		Repo:    ctx.Repo.Repository,
 | 
					 | 
				
			||||||
		Ctx:     ctx,
 | 
					 | 
				
			||||||
	}, milestone.Content)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("RenderString", err)
 | 
							ctx.ServerError("RenderString", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -92,15 +92,12 @@ func Projects(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := range projects {
 | 
						for i := range projects {
 | 
				
			||||||
		projects[i].RenderedContent, err = markdown.RenderString(&markup.RenderContext{
 | 
							projects[i].RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
			Links: markup.Links{
 | 
								WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
 | 
				
			||||||
				Base: ctx.Repo.RepoLink,
 | 
								WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
 | 
				
			||||||
			},
 | 
								WithGitRepo(ctx.Repo.GitRepo).
 | 
				
			||||||
			Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
 | 
								WithRepoFacade(ctx.Repo.Repository),
 | 
				
			||||||
			GitRepo: ctx.Repo.GitRepo,
 | 
								projects[i].Description)
 | 
				
			||||||
			Repo:    ctx.Repo.Repository,
 | 
					 | 
				
			||||||
			Ctx:     ctx,
 | 
					 | 
				
			||||||
		}, projects[i].Description)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.ServerError("RenderString", err)
 | 
								ctx.ServerError("RenderString", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
@@ -425,15 +422,12 @@ func ViewProject(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["SelectLabels"] = selectLabels
 | 
						ctx.Data["SelectLabels"] = selectLabels
 | 
				
			||||||
	ctx.Data["AssigneeID"] = assigneeID
 | 
						ctx.Data["AssigneeID"] = assigneeID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	project.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
 | 
						project.RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
		Links: markup.Links{
 | 
							WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
 | 
				
			||||||
			Base: ctx.Repo.RepoLink,
 | 
							WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
 | 
				
			||||||
		},
 | 
							WithGitRepo(ctx.Repo.GitRepo).
 | 
				
			||||||
		Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
 | 
							WithRepoFacade(ctx.Repo.Repository),
 | 
				
			||||||
		GitRepo: ctx.Repo.GitRepo,
 | 
							project.Description)
 | 
				
			||||||
		Repo:    ctx.Repo.Repository,
 | 
					 | 
				
			||||||
		Ctx:     ctx,
 | 
					 | 
				
			||||||
	}, project.Description)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("RenderString", err)
 | 
							ctx.ServerError("RenderString", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -114,15 +114,12 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
 | 
				
			|||||||
			cacheUsers[r.PublisherID] = r.Publisher
 | 
								cacheUsers[r.PublisherID] = r.Publisher
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		r.RenderedNote, err = markdown.RenderString(&markup.RenderContext{
 | 
							r.RenderedNote, err = markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
			Links: markup.Links{
 | 
								WithLinks(markup.Links{Base: ctx.Repo.RepoLink}).
 | 
				
			||||||
				Base: ctx.Repo.RepoLink,
 | 
								WithMetas(ctx.Repo.Repository.ComposeMetas(ctx)).
 | 
				
			||||||
			},
 | 
								WithGitRepo(ctx.Repo.GitRepo).
 | 
				
			||||||
			Metas:   ctx.Repo.Repository.ComposeMetas(ctx),
 | 
								WithRepoFacade(ctx.Repo.Repository),
 | 
				
			||||||
			GitRepo: ctx.Repo.GitRepo,
 | 
								r.Note)
 | 
				
			||||||
			Repo:    ctx.Repo.Repository,
 | 
					 | 
				
			||||||
			Ctx:     ctx,
 | 
					 | 
				
			||||||
		}, r.Note)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,18 +56,17 @@ func RenderFile(ctx *context.Context) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = markup.Render(&markup.RenderContext{
 | 
						err = markup.Render(markup.NewRenderContext(ctx).
 | 
				
			||||||
		Ctx:          ctx,
 | 
							WithRelativePath(ctx.Repo.TreePath).
 | 
				
			||||||
		RelativePath: ctx.Repo.TreePath,
 | 
							WithLinks(markup.Links{
 | 
				
			||||||
		Links: markup.Links{
 | 
					 | 
				
			||||||
			Base:       ctx.Repo.RepoLink,
 | 
								Base:       ctx.Repo.RepoLink,
 | 
				
			||||||
			BranchPath: ctx.Repo.BranchNameSubURL(),
 | 
								BranchPath: ctx.Repo.BranchNameSubURL(),
 | 
				
			||||||
			TreePath:   path.Dir(ctx.Repo.TreePath),
 | 
								TreePath:   path.Dir(ctx.Repo.TreePath),
 | 
				
			||||||
		},
 | 
							}).
 | 
				
			||||||
		Metas:            ctx.Repo.Repository.ComposeDocumentMetas(ctx),
 | 
							WithMetas(ctx.Repo.Repository.ComposeDocumentMetas(ctx)).
 | 
				
			||||||
		GitRepo:          ctx.Repo.GitRepo,
 | 
							WithGitRepo(ctx.Repo.GitRepo).
 | 
				
			||||||
		InStandalonePage: true,
 | 
							WithInStandalonePage(true),
 | 
				
			||||||
	}, rd, ctx.Resp)
 | 
							rd, ctx.Resp)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("Failed to render file %q: %v", ctx.Repo.TreePath, err)
 | 
							log.Error("Failed to render file %q: %v", ctx.Repo.TreePath, err)
 | 
				
			||||||
		http.Error(ctx.Resp, "Failed to render file", http.StatusInternalServerError)
 | 
							http.Error(ctx.Resp, "Failed to render file", http.StatusInternalServerError)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -310,18 +310,17 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr
 | 
				
			|||||||
		ctx.Data["IsMarkup"] = true
 | 
							ctx.Data["IsMarkup"] = true
 | 
				
			||||||
		ctx.Data["MarkupType"] = markupType
 | 
							ctx.Data["MarkupType"] = markupType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
 | 
							ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, markup.NewRenderContext(ctx).
 | 
				
			||||||
			Ctx:          ctx,
 | 
								WithMarkupType(markupType).
 | 
				
			||||||
			MarkupType:   markupType,
 | 
								WithRelativePath(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).
 | 
				
			||||||
			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).
 | 
								WithLinks(markup.Links{
 | 
				
			||||||
			Links: markup.Links{
 | 
					 | 
				
			||||||
				Base:       ctx.Repo.RepoLink,
 | 
									Base:       ctx.Repo.RepoLink,
 | 
				
			||||||
				BranchPath: ctx.Repo.BranchNameSubURL(),
 | 
									BranchPath: ctx.Repo.BranchNameSubURL(),
 | 
				
			||||||
				TreePath:   path.Join(ctx.Repo.TreePath, subfolder),
 | 
									TreePath:   path.Join(ctx.Repo.TreePath, subfolder),
 | 
				
			||||||
			},
 | 
								}).
 | 
				
			||||||
			Metas:   ctx.Repo.Repository.ComposeDocumentMetas(ctx),
 | 
								WithMetas(ctx.Repo.Repository.ComposeDocumentMetas(ctx)).
 | 
				
			||||||
			GitRepo: ctx.Repo.GitRepo,
 | 
								WithGitRepo(ctx.Repo.GitRepo),
 | 
				
			||||||
		}, rd)
 | 
								rd)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.Name(), ctx.Repo.Repository, err)
 | 
								log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.Name(), ctx.Repo.Repository, err)
 | 
				
			||||||
			delete(ctx.Data, "IsMarkup")
 | 
								delete(ctx.Data, "IsMarkup")
 | 
				
			||||||
@@ -514,18 +513,17 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
 | 
				
			|||||||
			ctx.Data["MarkupType"] = markupType
 | 
								ctx.Data["MarkupType"] = markupType
 | 
				
			||||||
			metas := ctx.Repo.Repository.ComposeDocumentMetas(ctx)
 | 
								metas := ctx.Repo.Repository.ComposeDocumentMetas(ctx)
 | 
				
			||||||
			metas["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
 | 
								metas["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
 | 
				
			||||||
			ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
 | 
								ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, markup.NewRenderContext(ctx).
 | 
				
			||||||
				Ctx:          ctx,
 | 
									WithMarkupType(markupType).
 | 
				
			||||||
				MarkupType:   markupType,
 | 
									WithRelativePath(ctx.Repo.TreePath).
 | 
				
			||||||
				RelativePath: ctx.Repo.TreePath,
 | 
									WithLinks(markup.Links{
 | 
				
			||||||
				Links: markup.Links{
 | 
					 | 
				
			||||||
					Base:       ctx.Repo.RepoLink,
 | 
										Base:       ctx.Repo.RepoLink,
 | 
				
			||||||
					BranchPath: ctx.Repo.BranchNameSubURL(),
 | 
										BranchPath: ctx.Repo.BranchNameSubURL(),
 | 
				
			||||||
					TreePath:   path.Dir(ctx.Repo.TreePath),
 | 
										TreePath:   path.Dir(ctx.Repo.TreePath),
 | 
				
			||||||
				},
 | 
									}).
 | 
				
			||||||
				Metas:   metas,
 | 
									WithMetas(metas).
 | 
				
			||||||
				GitRepo: ctx.Repo.GitRepo,
 | 
									WithGitRepo(ctx.Repo.GitRepo),
 | 
				
			||||||
			}, rd)
 | 
									rd)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				ctx.ServerError("Render", err)
 | 
									ctx.ServerError("Render", err)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
@@ -606,18 +604,17 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
 | 
				
			|||||||
			rd := io.MultiReader(bytes.NewReader(buf), dataRc)
 | 
								rd := io.MultiReader(bytes.NewReader(buf), dataRc)
 | 
				
			||||||
			ctx.Data["IsMarkup"] = true
 | 
								ctx.Data["IsMarkup"] = true
 | 
				
			||||||
			ctx.Data["MarkupType"] = markupType
 | 
								ctx.Data["MarkupType"] = markupType
 | 
				
			||||||
			ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
 | 
								ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, markup.NewRenderContext(ctx).
 | 
				
			||||||
				Ctx:          ctx,
 | 
									WithMarkupType(markupType).
 | 
				
			||||||
				MarkupType:   markupType,
 | 
									WithRelativePath(ctx.Repo.TreePath).
 | 
				
			||||||
				RelativePath: ctx.Repo.TreePath,
 | 
									WithLinks(markup.Links{
 | 
				
			||||||
				Links: markup.Links{
 | 
					 | 
				
			||||||
					Base:       ctx.Repo.RepoLink,
 | 
										Base:       ctx.Repo.RepoLink,
 | 
				
			||||||
					BranchPath: ctx.Repo.BranchNameSubURL(),
 | 
										BranchPath: ctx.Repo.BranchNameSubURL(),
 | 
				
			||||||
					TreePath:   path.Dir(ctx.Repo.TreePath),
 | 
										TreePath:   path.Dir(ctx.Repo.TreePath),
 | 
				
			||||||
				},
 | 
									}).
 | 
				
			||||||
				Metas:   ctx.Repo.Repository.ComposeDocumentMetas(ctx),
 | 
									WithMetas(ctx.Repo.Repository.ComposeDocumentMetas(ctx)).
 | 
				
			||||||
				GitRepo: ctx.Repo.GitRepo,
 | 
									WithGitRepo(ctx.Repo.GitRepo),
 | 
				
			||||||
			}, rd)
 | 
									rd)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				ctx.ServerError("Render", err)
 | 
									ctx.ServerError("Render", err)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -288,13 +288,9 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
 | 
				
			|||||||
		footerContent = data
 | 
							footerContent = data
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rctx := &markup.RenderContext{
 | 
						rctx := markup.NewRenderContext(ctx).
 | 
				
			||||||
		Ctx:   ctx,
 | 
							WithMetas(ctx.Repo.Repository.ComposeWikiMetas(ctx)).
 | 
				
			||||||
		Metas: ctx.Repo.Repository.ComposeWikiMetas(ctx),
 | 
							WithLinks(markup.Links{Base: ctx.Repo.RepoLink})
 | 
				
			||||||
		Links: markup.Links{
 | 
					 | 
				
			||||||
			Base: ctx.Repo.RepoLink,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	buf := &strings.Builder{}
 | 
						buf := &strings.Builder{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	renderFn := func(data []byte) (escaped *charset.EscapeStatus, output string, err error) {
 | 
						renderFn := func(data []byte) (escaped *charset.EscapeStatus, output string, err error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,10 +49,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.Data["OpenIDs"] = openIDs
 | 
						ctx.Data["OpenIDs"] = openIDs
 | 
				
			||||||
	if len(ctx.ContextUser.Description) != 0 {
 | 
						if len(ctx.ContextUser.Description) != 0 {
 | 
				
			||||||
		content, err := markdown.RenderString(&markup.RenderContext{
 | 
							content, err := markdown.RenderString(markup.NewRenderContext(ctx).WithMetas(markup.ComposeSimpleDocumentMetas()), ctx.ContextUser.Description)
 | 
				
			||||||
			Metas: markup.ComposeSimpleDocumentMetas(),
 | 
					 | 
				
			||||||
			Ctx:   ctx,
 | 
					 | 
				
			||||||
		}, ctx.ContextUser.Description)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.ServerError("RenderString", err)
 | 
								ctx.ServerError("RenderString", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -257,14 +257,11 @@ func Milestones(ctx *context.Context) {
 | 
				
			|||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		milestones[i].RenderedContent, err = markdown.RenderString(&markup.RenderContext{
 | 
							milestones[i].RenderedContent, err = markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
			Links: markup.Links{
 | 
								WithLinks(markup.Links{Base: milestones[i].Repo.Link()}).
 | 
				
			||||||
				Base: milestones[i].Repo.Link(),
 | 
								WithMetas(milestones[i].Repo.ComposeMetas(ctx)).
 | 
				
			||||||
			},
 | 
								WithRepoFacade(milestones[i].Repo),
 | 
				
			||||||
			Metas: milestones[i].Repo.ComposeMetas(ctx),
 | 
								milestones[i].Content)
 | 
				
			||||||
			Ctx:   ctx,
 | 
					 | 
				
			||||||
			Repo:  milestones[i].Repo,
 | 
					 | 
				
			||||||
		}, milestones[i].Content)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.ServerError("RenderString", err)
 | 
								ctx.ServerError("RenderString", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -246,10 +246,9 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
 | 
				
			|||||||
		if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
 | 
							if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil {
 | 
				
			||||||
			log.Error("failed to GetBlobContent: %v", err)
 | 
								log.Error("failed to GetBlobContent: %v", err)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			if profileContent, err := markdown.RenderString(&markup.RenderContext{
 | 
								if profileContent, err := markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
				Ctx:     ctx,
 | 
									WithGitRepo(profileGitRepo).
 | 
				
			||||||
				GitRepo: profileGitRepo,
 | 
									WithLinks(markup.Links{
 | 
				
			||||||
				Links: markup.Links{
 | 
					 | 
				
			||||||
					// Give the repo link to the markdown render for the full link of media element.
 | 
										// Give the repo link to the markdown render for the full link of media element.
 | 
				
			||||||
					// the media link usually be like /[user]/[repoName]/media/branch/[branchName],
 | 
										// the media link usually be like /[user]/[repoName]/media/branch/[branchName],
 | 
				
			||||||
					// 	Eg. /Tom/.profile/media/branch/main
 | 
										// 	Eg. /Tom/.profile/media/branch/main
 | 
				
			||||||
@@ -257,8 +256,8 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb
 | 
				
			|||||||
					//	https://docs.gitea.com/usage/profile-readme
 | 
										//	https://docs.gitea.com/usage/profile-readme
 | 
				
			||||||
					Base:       profileDbRepo.Link(),
 | 
										Base:       profileDbRepo.Link(),
 | 
				
			||||||
					BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)),
 | 
										BranchPath: path.Join("branch", util.PathEscapeSegments(profileDbRepo.DefaultBranch)),
 | 
				
			||||||
				},
 | 
									}),
 | 
				
			||||||
			}, bytes); err != nil {
 | 
									bytes); err != nil {
 | 
				
			||||||
				log.Error("failed to RenderString: %v", err)
 | 
									log.Error("failed to RenderString: %v", err)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				ctx.Data["ProfileReadme"] = profileContent
 | 
									ctx.Data["ProfileReadme"] = profileContent
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -259,9 +259,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
 | 
						ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
 | 
				
			||||||
	if len(ctx.ContextUser.Description) != 0 {
 | 
						if len(ctx.ContextUser.Description) != 0 {
 | 
				
			||||||
		content, err := markdown.RenderString(&markup.RenderContext{
 | 
							content, err := markdown.RenderString(markup.NewRenderContext(ctx), ctx.ContextUser.Description)
 | 
				
			||||||
			Ctx: ctx,
 | 
					 | 
				
			||||||
		}, ctx.ContextUser.Description)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			ctx.ServerError("RenderString", err)
 | 
								ctx.ServerError("RenderString", err)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -219,15 +219,11 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// This is the body of the new issue or comment, not the mail body
 | 
						// This is the body of the new issue or comment, not the mail body
 | 
				
			||||||
	body, err := markdown.RenderString(&markup.RenderContext{
 | 
						body, err := markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
		Ctx:  ctx,
 | 
							WithRepoFacade(ctx.Issue.Repo).
 | 
				
			||||||
		Repo: ctx.Issue.Repo,
 | 
							WithLinks(markup.Links{AbsolutePrefix: true, Base: ctx.Issue.Repo.HTMLURL()}).
 | 
				
			||||||
		Links: markup.Links{
 | 
							WithMetas(ctx.Issue.Repo.ComposeMetas(ctx)),
 | 
				
			||||||
			AbsolutePrefix: true,
 | 
							ctx.Content)
 | 
				
			||||||
			Base:           ctx.Issue.Repo.HTMLURL(),
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Metas: ctx.Issue.Repo.ComposeMetas(ctx),
 | 
					 | 
				
			||||||
	}, ctx.Content)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,14 +56,11 @@ func mailNewRelease(ctx context.Context, lang string, tos []*user_model.User, re
 | 
				
			|||||||
	locale := translation.NewLocale(lang)
 | 
						locale := translation.NewLocale(lang)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	rel.RenderedNote, err = markdown.RenderString(&markup.RenderContext{
 | 
						rel.RenderedNote, err = markdown.RenderString(markup.NewRenderContext(ctx).
 | 
				
			||||||
		Ctx:  ctx,
 | 
							WithRepoFacade(rel.Repo).
 | 
				
			||||||
		Repo: rel.Repo,
 | 
							WithLinks(markup.Links{Base: rel.Repo.HTMLURL()}).
 | 
				
			||||||
		Links: markup.Links{
 | 
							WithMetas(rel.Repo.ComposeMetas(ctx)),
 | 
				
			||||||
			Base: rel.Repo.HTMLURL(),
 | 
							rel.Note)
 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Metas: rel.Repo.ComposeMetas(ctx),
 | 
					 | 
				
			||||||
	}, rel.Note)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("markdown.RenderString(%d): %v", rel.RepoID, err)
 | 
							log.Error("markdown.RenderString(%d): %v", rel.RepoID, err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,27 +14,22 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var renderContext = markup.RenderContext{
 | 
					func newFuzzRenderContext() *markup.RenderContext {
 | 
				
			||||||
	Ctx: context.Background(),
 | 
						return markup.NewRenderContext(context.Background()).
 | 
				
			||||||
	Links: markup.Links{
 | 
							WithLinks(markup.Links{Base: "https://example.com/go-gitea/gitea"}).
 | 
				
			||||||
		Base: "https://example.com/go-gitea/gitea",
 | 
							WithMetas(map[string]string{"user": "go-gitea", "repo": "gitea"})
 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	Metas: map[string]string{
 | 
					 | 
				
			||||||
		"user": "go-gitea",
 | 
					 | 
				
			||||||
		"repo": "gitea",
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func FuzzMarkdownRenderRaw(f *testing.F) {
 | 
					func FuzzMarkdownRenderRaw(f *testing.F) {
 | 
				
			||||||
	f.Fuzz(func(t *testing.T, data []byte) {
 | 
						f.Fuzz(func(t *testing.T, data []byte) {
 | 
				
			||||||
		setting.AppURL = "http://localhost:3000/"
 | 
							setting.AppURL = "http://localhost:3000/"
 | 
				
			||||||
		markdown.RenderRaw(&renderContext, bytes.NewReader(data), io.Discard)
 | 
							markdown.RenderRaw(newFuzzRenderContext(), bytes.NewReader(data), io.Discard)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func FuzzMarkupPostProcess(f *testing.F) {
 | 
					func FuzzMarkupPostProcess(f *testing.F) {
 | 
				
			||||||
	f.Fuzz(func(t *testing.T, data []byte) {
 | 
						f.Fuzz(func(t *testing.T, data []byte) {
 | 
				
			||||||
		setting.AppURL = "http://localhost:3000/"
 | 
							setting.AppURL = "http://localhost:3000/"
 | 
				
			||||||
		markup.PostProcess(&renderContext, bytes.NewReader(data), io.Discard)
 | 
							markup.PostProcess(newFuzzRenderContext(), bytes.NewReader(data), io.Discard)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user