mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-02 20:06:06 +01:00 
			
		
		
		
	This PR removes (almost) all path tricks, and introduces "renderhelper" package. Now we can clearly see the rendering behaviors for comment/file/wiki, more details are in "renderhelper" tests. Fix #31411 , fix #18592, fix #25632 and maybe more problems. (ps: fix #32608 by the way)
		
			
				
	
	
		
			91 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			91 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2024 The Gitea Authors. All rights reserved.
 | 
						|
// SPDX-License-Identifier: MIT
 | 
						|
 | 
						|
package markup
 | 
						|
 | 
						|
import (
 | 
						|
	"html/template"
 | 
						|
	"net/url"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"code.gitea.io/gitea/modules/httplib"
 | 
						|
	"code.gitea.io/gitea/modules/log"
 | 
						|
 | 
						|
	"golang.org/x/net/html"
 | 
						|
)
 | 
						|
 | 
						|
type RenderCodePreviewOptions struct {
 | 
						|
	FullURL   string
 | 
						|
	OwnerName string
 | 
						|
	RepoName  string
 | 
						|
	CommitID  string
 | 
						|
	FilePath  string
 | 
						|
 | 
						|
	LineStart, LineStop int
 | 
						|
}
 | 
						|
 | 
						|
func renderCodeBlock(ctx *RenderContext, node *html.Node) (urlPosStart, urlPosStop int, htm template.HTML, err error) {
 | 
						|
	m := globalVars().codePreviewPattern.FindStringSubmatchIndex(node.Data)
 | 
						|
	if m == nil {
 | 
						|
		return 0, 0, "", nil
 | 
						|
	}
 | 
						|
 | 
						|
	opts := RenderCodePreviewOptions{
 | 
						|
		FullURL:   node.Data[m[0]:m[1]],
 | 
						|
		OwnerName: node.Data[m[2]:m[3]],
 | 
						|
		RepoName:  node.Data[m[4]:m[5]],
 | 
						|
		CommitID:  node.Data[m[6]:m[7]],
 | 
						|
		FilePath:  node.Data[m[8]:m[9]],
 | 
						|
	}
 | 
						|
	if !httplib.IsCurrentGiteaSiteURL(ctx, opts.FullURL) {
 | 
						|
		return 0, 0, "", nil
 | 
						|
	}
 | 
						|
	u, err := url.Parse(opts.FilePath)
 | 
						|
	if err != nil {
 | 
						|
		return 0, 0, "", err
 | 
						|
	}
 | 
						|
	opts.FilePath = strings.TrimPrefix(u.Path, "/")
 | 
						|
 | 
						|
	lineStartStr, lineStopStr, _ := strings.Cut(node.Data[m[10]:m[11]], "-")
 | 
						|
	lineStart, _ := strconv.Atoi(strings.TrimPrefix(lineStartStr, "L"))
 | 
						|
	lineStop, _ := strconv.Atoi(strings.TrimPrefix(lineStopStr, "L"))
 | 
						|
	opts.LineStart, opts.LineStop = lineStart, lineStop
 | 
						|
	h, err := DefaultRenderHelperFuncs.RenderRepoFileCodePreview(ctx, opts)
 | 
						|
	return m[0], m[1], h, err
 | 
						|
}
 | 
						|
 | 
						|
func codePreviewPatternProcessor(ctx *RenderContext, node *html.Node) {
 | 
						|
	nodeStop := node.NextSibling
 | 
						|
	for node != nodeStop {
 | 
						|
		if node.Type != html.TextNode {
 | 
						|
			node = node.NextSibling
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		urlPosStart, urlPosEnd, renderedCodeBlock, err := renderCodeBlock(ctx, node)
 | 
						|
		if err != nil || renderedCodeBlock == "" {
 | 
						|
			if err != nil {
 | 
						|
				log.Error("Unable to render code preview: %v", err)
 | 
						|
			}
 | 
						|
			node = node.NextSibling
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		next := node.NextSibling
 | 
						|
		textBefore := node.Data[:urlPosStart]
 | 
						|
		textAfter := node.Data[urlPosEnd:]
 | 
						|
		// "textBefore" could be empty if there is only a URL in the text node, then an empty node (p, or li) will be left here.
 | 
						|
		// However, the empty node can't be simply removed, because:
 | 
						|
		// 1. the following processors will still try to access it (need to double-check undefined behaviors)
 | 
						|
		// 2. the new node is inserted as "<p>{TextBefore}<div NewNode/>{TextAfter}</p>" (the parent could also be "li")
 | 
						|
		//    then it is resolved as: "<p>{TextBefore}</p><div NewNode/><p>{TextAfter}</p>",
 | 
						|
		//    so unless it could correctly replace the parent "p/li" node, it is very difficult to eliminate the "TextBefore" empty node.
 | 
						|
		node.Data = textBefore
 | 
						|
		renderedCodeNode := &html.Node{Type: html.RawNode, Data: string(ctx.RenderInternal.ProtectSafeAttrs(renderedCodeBlock))}
 | 
						|
		node.Parent.InsertBefore(renderedCodeNode, next)
 | 
						|
		if textAfter != "" {
 | 
						|
			node.Parent.InsertBefore(&html.Node{Type: html.TextNode, Data: textAfter}, next)
 | 
						|
		}
 | 
						|
		node = next
 | 
						|
	}
 | 
						|
}
 |