mirror of
https://github.com/go-gitea/gitea
synced 2025-07-22 18:28:37 +00:00
Use a general approach to access custom/static/builtin assets (#24022)
The idea is to use a Layered Asset File-system (modules/assetfs/layered.go) For example: when there are 2 layers: "custom", "builtin", when access to asset "my/page.tmpl", the Layered Asset File-system will first try to use "custom" assets, if not found, then use "builtin" assets. This approach will hugely simplify a lot of code, make them testable. Other changes: * Simplify the AssetsHandlerFunc code * Simplify the `gitea embedded` sub-command code --------- Co-authored-by: Jason Song <i@wolfogre.com> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
This commit is contained in:
@@ -6,114 +6,17 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
texttmpl "text/template"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/assetfs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
)
|
||||
|
||||
var (
|
||||
subjectTemplates = texttmpl.New("")
|
||||
bodyTemplates = template.New("")
|
||||
)
|
||||
|
||||
// GlobalModTime provide a global mod time for embedded asset files
|
||||
func GlobalModTime(filename string) time.Time {
|
||||
return timeutil.GetExecutableModTime()
|
||||
}
|
||||
|
||||
// GetAssetFilename returns the filename of the provided asset
|
||||
func GetAssetFilename(name string) (string, error) {
|
||||
filename := filepath.Join(setting.CustomPath, name)
|
||||
_, err := os.Stat(filename)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return name, err
|
||||
} else if err == nil {
|
||||
return filename, nil
|
||||
}
|
||||
return "(builtin) " + name, nil
|
||||
}
|
||||
|
||||
// GetAsset get a special asset, only for chi
|
||||
func GetAsset(name string) ([]byte, error) {
|
||||
bs, err := os.ReadFile(filepath.Join(setting.CustomPath, name))
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
} else if err == nil {
|
||||
return bs, nil
|
||||
}
|
||||
return BuiltinAsset(strings.TrimPrefix(name, "templates/"))
|
||||
}
|
||||
|
||||
// GetFiles calls a callback for each template asset
|
||||
func walkTemplateFiles(callback func(path, name string, d fs.DirEntry, err error) error) error {
|
||||
if err := walkAssetDir(filepath.Join(setting.CustomPath, "templates"), true, callback); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTemplateAssetNames only for chi
|
||||
func GetTemplateAssetNames() []string {
|
||||
realFS := Assets.(vfsgen۰FS)
|
||||
tmpls := make([]string, 0, len(realFS))
|
||||
for k := range realFS {
|
||||
if strings.HasPrefix(k, "/mail/") {
|
||||
continue
|
||||
}
|
||||
tmpls = append(tmpls, "templates/"+k[1:])
|
||||
}
|
||||
|
||||
customDir := path.Join(setting.CustomPath, "templates")
|
||||
customTmpls := getDirTemplateAssetNames(customDir)
|
||||
return append(tmpls, customTmpls...)
|
||||
}
|
||||
|
||||
func walkMailerTemplates(callback func(path, name string, d fs.DirEntry, err error) error) error {
|
||||
if err := walkAssetDir(filepath.Join(setting.CustomPath, "templates", "mail"), false, callback); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuiltinAsset reads the provided asset from the builtin embedded assets
|
||||
func BuiltinAsset(name string) ([]byte, error) {
|
||||
f, err := Assets.Open("/" + name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return io.ReadAll(f)
|
||||
}
|
||||
|
||||
// BuiltinAssetNames returns the names of the built-in embedded assets
|
||||
func BuiltinAssetNames() []string {
|
||||
realFS := Assets.(vfsgen۰FS)
|
||||
results := make([]string, 0, len(realFS))
|
||||
for k := range realFS {
|
||||
results = append(results, k[1:])
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// BuiltinAssetIsDir returns if a provided asset is a directory
|
||||
func BuiltinAssetIsDir(name string) (bool, error) {
|
||||
if f, err := Assets.Open("/" + name); err != nil {
|
||||
return false, err
|
||||
} else {
|
||||
defer f.Close()
|
||||
if fi, err := f.Stat(); err != nil {
|
||||
return false, err
|
||||
} else {
|
||||
return fi.IsDir(), nil
|
||||
}
|
||||
}
|
||||
func BuiltinAssets() *assetfs.Layer {
|
||||
return assetfs.Bindata("builtin(bindata)", Assets)
|
||||
}
|
||||
|
Reference in New Issue
Block a user