diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 5814c0d69d9ff..f9eea4fc7a578 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -2181,8 +2181,11 @@ PATH =
;RENDER_COMMAND = "asciidoc --out-file=- -"
;; Don't pass the file on STDIN, pass the filename as argument instead.
;IS_INPUT_FILE = false
-; Don't filter html tags and attributes if true
+;; Don't filter html tags and attributes if true
;DISABLE_SANITIZER = false
+;; Display the HTML with an embed iframe instead of rendering inside the page. When a renderer uses iframe
+;; it suppresses DISABLE_SANITIZER option and there will be no sanitizer.
+;USE_IFRAME=false
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
index c4c81d58ace80..cee27dc57172c 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -1027,7 +1027,7 @@ IS_INPUT_FILE = false
- RENDER\_COMMAND: External command to render all matching extensions.
- IS\_INPUT\_FILE: **false** Input is not a standard input but a file param followed `RENDER_COMMAND`.
- DISABLE_SANITIZER: **false** Don't filter html tags and attributes if true. This is insecure. Don't change this to true except you know what that means.
-- USE_IFRAME: **false** Display the HTML with an embed iframe but not directly renderer.
+- USE_IFRAME: **false** Display the HTML with an embed iframe instead of rendering inside the page. When a renderer uses iframe, it suppresses DISABLE_SANITIZER option and there will be no sanitizer.
Two special environment variables are passed to the render command:
- `GITEA_PREFIX_SRC`, which contains the current URL prefix in the `src` path tree. To be used as prefix for links.
diff --git a/modules/markup/console/console.go b/modules/markup/console/console.go
index 06298c72c4160..597593eee153c 100644
--- a/modules/markup/console/console.go
+++ b/modules/markup/console/console.go
@@ -26,9 +26,7 @@ func init() {
}
// Renderer implements markup.Renderer
-type Renderer struct {
- markup.BaseRenderer
-}
+type Renderer struct{}
// Name implements markup.Renderer
func (Renderer) Name() string {
diff --git a/modules/markup/csv/csv.go b/modules/markup/csv/csv.go
index 269816278d51d..5095b854650d1 100644
--- a/modules/markup/csv/csv.go
+++ b/modules/markup/csv/csv.go
@@ -22,9 +22,7 @@ func init() {
}
// Renderer implements markup.Renderer for csv files
-type Renderer struct {
- markup.BaseRenderer
-}
+type Renderer struct{}
// Name implements markup.Renderer
func (Renderer) Name() string {
diff --git a/modules/markup/external/external.go b/modules/markup/external/external.go
index 8354c676ecc46..7cd9ab4401515 100644
--- a/modules/markup/external/external.go
+++ b/modules/markup/external/external.go
@@ -34,6 +34,9 @@ type Renderer struct {
*setting.MarkupRenderer
}
+var _ markup.PostProcessRenderer = (*Renderer)(nil)
+var _ markup.ExternalRenderer = (*Renderer)(nil)
+
// Name returns the external tool name
func (p *Renderer) Name() string {
return p.MarkupName
diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go
index f52c5bc97bd0d..37e11e606fce5 100644
--- a/modules/markup/markdown/markdown.go
+++ b/modules/markup/markdown/markdown.go
@@ -203,16 +203,16 @@ func init() {
}
// Renderer implements markup.Renderer
-type Renderer struct {
- markup.BaseRenderer
-}
+type Renderer struct{}
+
+var _ markup.PostProcessRenderer = (*Renderer)(nil)
// Name implements markup.Renderer
func (Renderer) Name() string {
return MarkupName
}
-// NeedPostProcess implements markup.Renderer
+// NeedPostProcess implements markup.PostProcessRenderer
func (Renderer) NeedPostProcess() bool { return true }
// Extensions implements markup.Renderer
diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go
index 04b4b43196d04..8c9f3b3da736b 100644
--- a/modules/markup/orgmode/orgmode.go
+++ b/modules/markup/orgmode/orgmode.go
@@ -27,16 +27,16 @@ func init() {
}
// Renderer implements markup.Renderer for orgmode
-type Renderer struct {
- markup.BaseRenderer
-}
+type Renderer struct{}
+
+var _ markup.PostProcessRenderer = (*Renderer)(nil)
// Name implements markup.Renderer
func (Renderer) Name() string {
return "orgmode"
}
-// NeedPostProcess implements markup.Renderer
+// NeedPostProcess implements markup.PostProcessRenderer
func (Renderer) NeedPostProcess() bool { return true }
// Extensions implements markup.Renderer
diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go
index 7f5e9dcaef595..21f974b71b714 100644
--- a/modules/markup/renderer.go
+++ b/modules/markup/renderer.go
@@ -44,18 +44,18 @@ type Header struct {
// RenderContext represents a render context
type RenderContext struct {
- Ctx context.Context
- RelativePath string // relative path from tree root of the branch
- Type string
- IsWiki bool
- URLPrefix string
- Metas map[string]string
- DefaultLink string
- GitRepo *git.Repository
- ShaExistCache map[string]bool
- cancelFn func()
- TableOfContents []Header
- AllowIFrame bool
+ Ctx context.Context
+ RelativePath string // relative path from tree root of the branch
+ Type string
+ IsWiki bool
+ URLPrefix string
+ Metas map[string]string
+ DefaultLink string
+ GitRepo *git.Repository
+ ShaExistCache map[string]bool
+ cancelFn func()
+ TableOfContents []Header
+ InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
}
// Cancel runs any cleanup functions that have been registered for this Ctx
@@ -90,23 +90,23 @@ func (ctx *RenderContext) AddCancel(fn func()) {
type Renderer interface {
Name() string // markup format name
Extensions() []string
- NeedPostProcess() bool
SanitizerRules() []setting.MarkupSanitizerRule
- SanitizerDisabled() bool
- DisplayInIFrame() bool
Render(ctx *RenderContext, input io.Reader, output io.Writer) error
}
-type BaseRenderer struct{}
-
-// NeedPostProcess implements markup.Renderer
-func (BaseRenderer) NeedPostProcess() bool { return false }
+// PostProcessRenderer defines an interface for renderers who need post process
+type PostProcessRenderer interface {
+ NeedPostProcess() bool
+}
-// SanitizerDisabled disabled sanitize if return true
-func (BaseRenderer) SanitizerDisabled() bool { return false }
+// PostProcessRenderer defines an interface for external renderers
+type ExternalRenderer interface {
+ // SanitizerDisabled disabled sanitize if return true
+ SanitizerDisabled() bool
-// DisplayInIFrame represents whether render the content with an iframe
-func (BaseRenderer) DisplayInIFrame() bool { return false }
+ // DisplayInIFrame represents whether render the content with an iframe
+ DisplayInIFrame() bool
+}
// RendererContentDetector detects if the content can be rendered
// by specified renderer
@@ -177,10 +177,14 @@ type nopCloser struct {
func (nopCloser) Close() error { return nil }
-func renderIFrame(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Writer) error {
- _, err := io.WriteString(output, fmt.Sprintf(``,
+func renderIFrame(ctx *RenderContext, output io.Writer) error {
+ _, err := io.WriteString(output, fmt.Sprintf(`
+`,
setting.AppSubURL,
url.PathEscape(ctx.Metas["user"]),
url.PathEscape(ctx.Metas["repo"]),
@@ -202,7 +206,12 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr
var pr2 io.ReadCloser
var pw2 io.WriteCloser
- if !renderer.SanitizerDisabled() {
+ var sanitizerDisabled bool
+ if r, ok := renderer.(ExternalRenderer); ok {
+ sanitizerDisabled = r.SanitizerDisabled()
+ }
+
+ if !sanitizerDisabled {
pr2, pw2 = io.Pipe()
defer func() {
_ = pr2.Close()
@@ -221,7 +230,7 @@ func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Wr
wg.Add(1)
go func() {
- if renderer.NeedPostProcess() {
+ if r, ok := renderer.(PostProcessRenderer); ok && r.NeedPostProcess() {
err = PostProcess(ctx, pr, pw2)
} else {
_, err = io.Copy(pw2, pr)
@@ -268,8 +277,12 @@ func (err ErrUnsupportedRenderExtension) Error() string {
func renderFile(ctx *RenderContext, input io.Reader, output io.Writer) error {
extension := strings.ToLower(filepath.Ext(ctx.RelativePath))
if renderer, ok := extRenderers[extension]; ok {
- if renderer.DisplayInIFrame() && ctx.AllowIFrame {
- return renderIFrame(ctx, renderer, input, output)
+ if r, ok := renderer.(ExternalRenderer); ok && r.DisplayInIFrame() {
+ if !ctx.InStandalonePage {
+ // for an external render, it could only output its content in a standalone page
+ // otherwise, a