2022-09-02 10:58:49 +03:00
// Copyright 2022 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2022-09-02 10:58:49 +03:00
package template
import (
"fmt"
"io"
2022-12-02 02:56:51 +03:00
"path"
2022-09-02 10:58:49 +03:00
"strconv"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
2022-10-31 18:10:33 +03:00
"code.gitea.io/gitea/modules/util"
2022-09-02 10:58:49 +03:00
2022-11-19 18:22:15 +03:00
"gopkg.in/yaml.v3"
2022-09-02 10:58:49 +03:00
)
// CouldBe indicates a file with the filename could be a template,
// it is a low cost check before further processing.
func CouldBe ( filename string ) bool {
it := & api . IssueTemplate {
FileName : filename ,
}
return it . Type ( ) != ""
}
// Unmarshal parses out a valid template from the content
func Unmarshal ( filename string , content [ ] byte ) ( * api . IssueTemplate , error ) {
it , err := unmarshal ( filename , content )
if err != nil {
return nil , err
}
if err := Validate ( it ) ; err != nil {
return nil , err
}
return it , nil
}
// UnmarshalFromEntry parses out a valid template from the blob in entry
func UnmarshalFromEntry ( entry * git . TreeEntry , dir string ) ( * api . IssueTemplate , error ) {
2022-12-02 02:56:51 +03:00
return unmarshalFromEntry ( entry , path . Join ( dir , entry . Name ( ) ) ) // Filepaths in Git are ALWAYS '/' separated do not use filepath here
2022-09-02 10:58:49 +03:00
}
// UnmarshalFromCommit parses out a valid template from the commit
func UnmarshalFromCommit ( commit * git . Commit , filename string ) ( * api . IssueTemplate , error ) {
entry , err := commit . GetTreeEntryByPath ( filename )
if err != nil {
return nil , fmt . Errorf ( "get entry for %q: %w" , filename , err )
}
return unmarshalFromEntry ( entry , filename )
}
// UnmarshalFromRepo parses out a valid template from the head commit of the branch
func UnmarshalFromRepo ( repo * git . Repository , branch , filename string ) ( * api . IssueTemplate , error ) {
commit , err := repo . GetBranchCommit ( branch )
if err != nil {
return nil , fmt . Errorf ( "get commit on branch %q: %w" , branch , err )
}
return UnmarshalFromCommit ( commit , filename )
}
func unmarshalFromEntry ( entry * git . TreeEntry , filename string ) ( * api . IssueTemplate , error ) {
if size := entry . Blob ( ) . Size ( ) ; size > setting . UI . MaxDisplayFileSize {
return nil , fmt . Errorf ( "too large: %v > MaxDisplayFileSize" , size )
}
r , err := entry . Blob ( ) . DataAsync ( )
if err != nil {
return nil , fmt . Errorf ( "data async: %w" , err )
}
defer r . Close ( )
content , err := io . ReadAll ( r )
if err != nil {
return nil , fmt . Errorf ( "read all: %w" , err )
}
return Unmarshal ( filename , content )
}
func unmarshal ( filename string , content [ ] byte ) ( * api . IssueTemplate , error ) {
it := & api . IssueTemplate {
FileName : filename ,
}
// Compatible with treating description as about
compatibleTemplate := & struct {
About string ` yaml:"description" `
} { }
if typ := it . Type ( ) ; typ == api . IssueTemplateTypeMarkdown {
2022-10-31 18:10:33 +03:00
if templateBody , err := markdown . ExtractMetadata ( string ( content ) , it ) ; err != nil {
// The only thing we know here is that we can't extract metadata from the content,
// it's hard to tell if metadata doesn't exist or metadata isn't valid.
// There's an example template:
//
// ---
// # Title
// ---
// Content
//
// It could be a valid markdown with two horizontal lines, or an invalid markdown with wrong metadata.
it . Content = string ( content )
2022-12-02 02:56:51 +03:00
it . Name = path . Base ( it . FileName ) // paths in Git are always '/' separated - do not use filepath!
2022-10-31 18:10:33 +03:00
it . About , _ = util . SplitStringAtByteN ( it . Content , 80 )
} else {
it . Content = templateBody
if it . About == "" {
if _ , err := markdown . ExtractMetadata ( string ( content ) , compatibleTemplate ) ; err == nil && compatibleTemplate . About != "" {
it . About = compatibleTemplate . About
}
2022-09-02 10:58:49 +03:00
}
}
} else if typ == api . IssueTemplateTypeYaml {
if err := yaml . Unmarshal ( content , it ) ; err != nil {
return nil , fmt . Errorf ( "yaml unmarshal: %w" , err )
}
if it . About == "" {
if err := yaml . Unmarshal ( content , compatibleTemplate ) ; err == nil && compatibleTemplate . About != "" {
it . About = compatibleTemplate . About
}
}
for i , v := range it . Fields {
2024-03-04 03:37:00 +03:00
// set default id value
2022-09-02 10:58:49 +03:00
if v . ID == "" {
v . ID = strconv . Itoa ( i )
}
2024-03-04 03:37:00 +03:00
// set default visibility
if v . Visible == nil {
v . Visible = [ ] api . IssueFormFieldVisible { api . IssueFormFieldVisibleForm }
// markdown is not submitted by default
if v . Type != api . IssueFormFieldTypeMarkdown {
v . Visible = append ( v . Visible , api . IssueFormFieldVisibleContent )
}
}
2022-09-02 10:58:49 +03:00
}
}
return it , nil
}