2023-03-24 07:12:23 +01:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (
2024-11-14 13:02:11 +08:00
"errors"
2023-03-24 07:12:23 +01:00
"fmt"
"net/http"
2024-06-17 08:16:14 +02:00
"path"
2023-03-24 07:12:23 +01:00
"strings"
2024-06-17 08:16:14 +02:00
"code.gitea.io/gitea/modules/httplib"
2023-03-24 07:12:23 +01:00
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
2024-11-14 13:02:11 +08:00
"code.gitea.io/gitea/modules/util"
2024-02-27 15:12:22 +08:00
"code.gitea.io/gitea/services/context"
2023-03-24 07:12:23 +01:00
)
// RenderMarkup renders markup text for the /markup and /markdown endpoints
2024-11-14 13:02:11 +08:00
func RenderMarkup ( ctx * context . Base , repo * context . Repository , mode , text , urlPathContext , filePath string ) {
2024-06-17 08:16:14 +02:00
// urlPathContext format is "/subpath/{user}/{repo}/src/{branch, commit, tag}/{identifier/path}/{file/dir}"
// filePath is the path of the file to render if the end user is trying to preview a repo file (mode == "file")
// filePath will be used as RenderContext.RelativePath
2023-03-24 07:12:23 +01:00
2024-06-17 08:16:14 +02:00
// 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"
2024-11-22 13:48:09 +08:00
renderCtx := markup . NewRenderContext ( ctx ) .
WithLinks ( markup . Links { AbsolutePrefix : true } ) .
WithMarkupType ( markdown . MarkupName )
2024-06-17 08:16:14 +02:00
if urlPathContext != "" {
2024-11-22 13:48:09 +08:00
renderCtx . RenderOptions . Links . Base = fmt . Sprintf ( "%s%s" , httplib . GuessCurrentHostURL ( ctx ) , urlPathContext )
2023-03-24 07:12:23 +01:00
}
2024-11-14 13:02:11 +08:00
if mode == "" || mode == "markdown" {
// raw markdown doesn't need any special handling
if err := markdown . RenderRaw ( renderCtx , strings . NewReader ( text ) , ctx . Resp ) ; err != nil {
2023-03-24 07:12:23 +01:00
ctx . Error ( http . StatusInternalServerError , err . Error ( ) )
}
return
2024-11-14 13:02:11 +08:00
}
switch mode {
case "gfm" : // legacy mode, do nothing
2023-03-24 07:12:23 +01:00
case "comment" :
2024-11-22 13:48:09 +08:00
renderCtx = renderCtx . WithMetas ( map [ string ] string { "markdownLineBreakStyle" : "comment" } )
2024-11-14 13:02:11 +08:00
case "wiki" :
2024-11-22 13:48:09 +08:00
renderCtx = renderCtx . WithMetas ( map [ string ] string { "markdownLineBreakStyle" : "document" , "markupContentMode" : "wiki" } )
2023-03-24 07:12:23 +01:00
case "file" :
2024-11-14 13:02:11 +08:00
// render the repo file content by its extension
2024-11-22 13:48:09 +08:00
renderCtx = renderCtx . WithMetas ( map [ string ] string { "markdownLineBreakStyle" : "document" } ) .
WithMarkupType ( "" ) .
WithRelativePath ( filePath )
2023-03-24 07:12:23 +01:00
default :
ctx . Error ( http . StatusUnprocessableEntity , fmt . Sprintf ( "Unknown mode: %s" , mode ) )
return
}
2024-06-17 08:16:14 +02:00
fields := strings . SplitN ( strings . TrimPrefix ( urlPathContext , setting . AppSubURL + "/" ) , "/" , 5 )
if len ( fields ) == 5 && fields [ 2 ] == "src" && ( fields [ 3 ] == "branch" || fields [ 3 ] == "commit" || fields [ 3 ] == "tag" ) {
// absolute base prefix is something like "https://host/subpath/{user}/{repo}"
absoluteBasePrefix := fmt . Sprintf ( "%s%s/%s" , httplib . GuessCurrentAppURL ( ctx ) , fields [ 0 ] , fields [ 1 ] )
fileDir := path . Dir ( filePath ) // it is "doc" if filePath is "doc/CHANGE.md"
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"
2024-11-22 13:48:09 +08:00
renderCtx = renderCtx . WithLinks ( markup . Links { AbsolutePrefix : true , Base : absoluteBasePrefix , BranchPath : refPath , TreePath : fileDir } )
2023-03-24 07:12:23 +01:00
}
2023-05-21 09:50:53 +08:00
if repo != nil && repo . Repository != nil {
2024-11-22 13:48:09 +08:00
renderCtx = renderCtx . WithRepoFacade ( repo . Repository )
2024-11-16 16:41:44 +08:00
if mode == "file" {
2024-11-22 13:48:09 +08:00
renderCtx = renderCtx . WithMetas ( repo . Repository . ComposeDocumentMetas ( ctx ) )
2024-11-16 16:41:44 +08:00
} else if mode == "wiki" {
2024-11-22 13:48:09 +08:00
renderCtx = renderCtx . WithMetas ( repo . Repository . ComposeWikiMetas ( ctx ) )
2024-11-16 16:41:44 +08:00
} else if mode == "comment" {
2024-11-22 13:48:09 +08:00
renderCtx = renderCtx . WithMetas ( repo . Repository . ComposeMetas ( ctx ) )
2023-03-24 07:12:23 +01:00
}
}
2024-11-14 13:02:11 +08:00
if err := markup . Render ( renderCtx , strings . NewReader ( text ) , ctx . Resp ) ; err != nil {
if errors . Is ( err , util . ErrInvalidArgument ) {
2023-03-24 07:12:23 +01:00
ctx . Error ( http . StatusUnprocessableEntity , err . Error ( ) )
} else {
ctx . Error ( http . StatusInternalServerError , err . Error ( ) )
}
return
}
}