mirror of
				https://github.com/go-gitea/gitea
				synced 2025-09-28 03:28:13 +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, | 			AbsolutePrefix: true, | ||||||
| 			RelativePath: ".md", | 			Base:           markup.TestRepoURL, | ||||||
| 			Links: markup.Links{ | 		}), input) | ||||||
| 				AbsolutePrefix: true, |  | ||||||
| 				Base:           markup.TestRepoURL, |  | ||||||
| 			}, |  | ||||||
| 			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