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-11-24 16:18:57 +08:00
"code.gitea.io/gitea/models/renderhelper"
"code.gitea.io/gitea/models/repo"
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-24 16:18:57 +08:00
func RenderMarkup ( ctx * context . Base , ctxRepo * 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-14 13:02:11 +08:00
if mode == "" || mode == "markdown" {
// raw markdown doesn't need any special handling
2024-11-24 16:18:57 +08:00
baseLink := urlPathContext
if baseLink == "" {
baseLink = fmt . Sprintf ( "%s%s" , httplib . GuessCurrentHostURL ( ctx ) , urlPathContext )
}
rctx := renderhelper . NewRenderContextSimpleDocument ( ctx , baseLink ) . WithUseAbsoluteLink ( true ) .
WithMarkupType ( markdown . MarkupName )
if err := markdown . RenderRaw ( rctx , 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
}
2024-11-24 16:18:57 +08:00
// Ideally, this handler should be called with RepoAssigment and get the related repo from context "/owner/repo/markup"
// then render could use the repo to do various things (the permission check has passed)
//
// However, this handler is also exposed as "/markup" without any repo context,
// then since there is no permission check, so we can't use the repo from "context" parameter,
// in this case, only the "path" information could be used which doesn't cause security problems.
var repoModel * repo . Repository
if ctxRepo != nil {
repoModel = ctxRepo . Repository
}
var repoOwnerName , repoName , refPath , treePath string
repoLinkPath := strings . TrimPrefix ( urlPathContext , setting . AppSubURL + "/" )
fields := strings . SplitN ( repoLinkPath , "/" , 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}"
repoOwnerName , repoName = fields [ 0 ] , fields [ 1 ]
treePath = 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 , "/" + treePath ) // now we get the correct branch path: "branch/features/feat-12"
} else if fields = strings . SplitN ( repoLinkPath , "/" , 3 ) ; len ( fields ) == 2 {
repoOwnerName , repoName = fields [ 0 ] , fields [ 1 ]
}
var rctx * markup . RenderContext
2024-11-14 13:02:11 +08:00
switch mode {
2024-11-24 16:18:57 +08:00
case "gfm" : // legacy mode
rctx = renderhelper . NewRenderContextRepoFile ( ctx , repoModel , renderhelper . RepoFileOptions {
DeprecatedOwnerName : repoOwnerName , DeprecatedRepoName : repoName ,
CurrentRefPath : refPath , CurrentTreePath : treePath ,
} )
rctx = rctx . WithMarkupType ( markdown . MarkupName )
2023-03-24 07:12:23 +01:00
case "comment" :
2024-11-24 16:18:57 +08:00
rctx = renderhelper . NewRenderContextRepoComment ( ctx , repoModel , renderhelper . RepoCommentOptions { DeprecatedOwnerName : repoOwnerName , DeprecatedRepoName : repoName } )
2024-11-26 03:04:55 +08:00
rctx = rctx . WithMarkupType ( markdown . MarkupName )
2024-11-14 13:02:11 +08:00
case "wiki" :
2024-11-24 16:18:57 +08:00
rctx = renderhelper . NewRenderContextRepoWiki ( ctx , repoModel , renderhelper . RepoWikiOptions { DeprecatedOwnerName : repoOwnerName , DeprecatedRepoName : repoName } )
2024-11-26 03:04:55 +08:00
rctx = rctx . WithMarkupType ( markdown . MarkupName )
2023-03-24 07:12:23 +01:00
case "file" :
2024-11-24 16:18:57 +08:00
rctx = renderhelper . NewRenderContextRepoFile ( ctx , repoModel , renderhelper . RepoFileOptions {
DeprecatedOwnerName : repoOwnerName , DeprecatedRepoName : repoName ,
CurrentRefPath : refPath , CurrentTreePath : treePath ,
} )
rctx = rctx . WithMarkupType ( "" ) . WithRelativePath ( filePath ) // render the repo file content by its extension
2023-03-24 07:12:23 +01:00
default :
ctx . Error ( http . StatusUnprocessableEntity , fmt . Sprintf ( "Unknown mode: %s" , mode ) )
return
}
2024-11-24 16:18:57 +08:00
rctx = rctx . WithUseAbsoluteLink ( true )
if err := markup . Render ( rctx , strings . NewReader ( text ) , ctx . Resp ) ; err != nil {
2024-11-14 13:02:11 +08:00
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
}
}