1
0
Fork 0

Adding upstream version 2.52.6.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-17 06:50:16 +02:00
parent a960158181
commit 6d002e9543
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
441 changed files with 95392 additions and 0 deletions

View file

@ -0,0 +1,206 @@
package html
import (
"fmt"
"html/template"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"sync"
"github.com/gofiber/fiber/v2/internal/template/utils"
"github.com/gofiber/fiber/v2/log"
)
// Engine struct
type Engine struct {
// delimiters
left string
right string
// views folder
directory string
// http.FileSystem supports embedded files
fileSystem http.FileSystem
// views extension
extension string
// layout variable name that incapsulates the template
layout string
// determines if the engine parsed all templates
loaded bool
// reload on each render
reload bool
// debug prints the parsed templates
debug bool
// lock for funcmap and templates
mutex sync.RWMutex
// template funcmap
funcmap map[string]interface{}
// templates
Templates *template.Template
}
// New returns a HTML render engine for Fiber
func New(directory, extension string) *Engine {
engine := &Engine{
left: "{{",
right: "}}",
directory: directory,
extension: extension,
layout: "embed",
funcmap: make(map[string]interface{}),
}
engine.AddFunc(engine.layout, func() error {
return fmt.Errorf("layout called unexpectedly.")
})
return engine
}
// NewFileSystem ...
func NewFileSystem(fs http.FileSystem, extension string) *Engine {
engine := &Engine{
left: "{{",
right: "}}",
directory: "/",
fileSystem: fs,
extension: extension,
layout: "embed",
funcmap: make(map[string]interface{}),
}
engine.AddFunc(engine.layout, func() error {
return fmt.Errorf("layout called unexpectedly.")
})
return engine
}
// Layout defines the variable name that will incapsulate the template
func (e *Engine) Layout(key string) *Engine {
e.layout = key
return e
}
// Delims sets the action delimiters to the specified strings, to be used in
// templates. An empty delimiter stands for the
// corresponding default: {{ or }}.
func (e *Engine) Delims(left, right string) *Engine {
e.left, e.right = left, right
return e
}
// AddFunc adds the function to the template's function map.
// It is legal to overwrite elements of the default actions
func (e *Engine) AddFunc(name string, fn interface{}) *Engine {
e.mutex.Lock()
e.funcmap[name] = fn
e.mutex.Unlock()
return e
}
// Reload if set to true the templates are reloading on each render,
// use it when you're in development and you don't want to restart
// the application when you edit a template file.
func (e *Engine) Reload(enabled bool) *Engine {
e.reload = enabled
return e
}
// Debug will print the parsed templates when Load is triggered.
func (e *Engine) Debug(enabled bool) *Engine {
e.debug = enabled
return e
}
// Parse is deprecated, please use Load() instead
func (e *Engine) Parse() error {
log.Warn("Parse() is deprecated, please use Load() instead.")
return e.Load()
}
// Load parses the templates to the engine.
func (e *Engine) Load() error {
if e.loaded {
return nil
}
// race safe
e.mutex.Lock()
defer e.mutex.Unlock()
e.Templates = template.New(e.directory)
// Set template settings
e.Templates.Delims(e.left, e.right)
e.Templates.Funcs(e.funcmap)
walkFn := func(path string, info os.FileInfo, err error) error {
// Return error if exist
if err != nil {
return err
}
// Skip file if it's a directory or has no file info
if info == nil || info.IsDir() {
return nil
}
// Skip file if it does not equal the given template extension
if len(e.extension) >= len(path) || path[len(path)-len(e.extension):] != e.extension {
return nil
}
// Get the relative file path
// ./views/html/index.tmpl -> index.tmpl
rel, err := filepath.Rel(e.directory, path)
if err != nil {
return err
}
// Reverse slashes '\' -> '/' and
// partials\footer.tmpl -> partials/footer.tmpl
name := filepath.ToSlash(rel)
// Remove ext from name 'index.tmpl' -> 'index'
name = strings.TrimSuffix(name, e.extension)
// name = strings.Replace(name, e.extension, "", -1)
// Read the file
// #gosec G304
buf, err := utils.ReadFile(path, e.fileSystem)
if err != nil {
return err
}
// Create new template associated with the current one
// This enable use to invoke other templates {{ template .. }}
_, err = e.Templates.New(name).Parse(string(buf))
if err != nil {
return err
}
// Debugging
if e.debug {
log.Infof("views: parsed template: %s", name)
}
return err
}
// notify engine that we parsed all templates
e.loaded = true
if e.fileSystem != nil {
return utils.Walk(e.fileSystem, e.directory, walkFn)
}
return filepath.Walk(e.directory, walkFn)
}
// Render will execute the template name along with the given values.
func (e *Engine) Render(out io.Writer, template string, binding interface{}, layout ...string) error {
tmpl := e.Templates.Lookup(template)
if tmpl == nil {
return fmt.Errorf("render: template %s does not exist", template)
}
if len(layout) > 0 && layout[0] != "" {
lay := e.Templates.Lookup(layout[0])
if lay == nil {
return fmt.Errorf("render: layout %s does not exist", layout[0])
}
e.mutex.Lock()
defer e.mutex.Unlock()
lay.Funcs(map[string]interface{}{
e.layout: func() error {
return tmpl.Execute(out, binding)
},
})
return lay.Execute(out, binding)
}
return tmpl.Execute(out, binding)
}

View file

@ -0,0 +1,110 @@
package utils
import (
"io"
"net/http"
"os"
pathpkg "path"
"path/filepath"
"sort"
)
// Walk walks the filesystem rooted at root, calling walkFn for each file or
// directory in the filesystem, including root. All errors that arise visiting files
// and directories are filtered by walkFn. The files are walked in lexical
// order.
func Walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error {
info, err := stat(fs, root)
if err != nil {
return walkFn(root, nil, err)
}
return walk(fs, root, info, walkFn)
}
// #nosec G304
// ReadFile returns the raw content of a file
func ReadFile(path string, fs http.FileSystem) ([]byte, error) {
if fs != nil {
file, err := fs.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
return io.ReadAll(file)
}
return os.ReadFile(path)
}
// readDirNames reads the directory named by dirname and returns
// a sorted list of directory entries.
func readDirNames(fs http.FileSystem, dirname string) ([]string, error) {
fis, err := readDir(fs, dirname)
if err != nil {
return nil, err
}
names := make([]string, len(fis))
for i := range fis {
names[i] = fis[i].Name()
}
sort.Strings(names)
return names, nil
}
// walk recursively descends path, calling walkFn.
func walk(fs http.FileSystem, path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
err := walkFn(path, info, nil)
if err != nil {
if info.IsDir() && err == filepath.SkipDir {
return nil
}
return err
}
if !info.IsDir() {
return nil
}
names, err := readDirNames(fs, path)
if err != nil {
return walkFn(path, info, err)
}
for _, name := range names {
filename := pathpkg.Join(path, name)
fileInfo, err := stat(fs, filename)
if err != nil {
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
return err
}
} else {
err = walk(fs, filename, fileInfo, walkFn)
if err != nil {
if !fileInfo.IsDir() || err != filepath.SkipDir {
return err
}
}
}
}
return nil
}
// readDir reads the contents of the directory associated with file and
// returns a slice of FileInfo values in directory order.
func readDir(fs http.FileSystem, name string) ([]os.FileInfo, error) {
f, err := fs.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
return f.Readdir(0)
}
// stat returns the FileInfo structure describing file.
func stat(fs http.FileSystem, name string) (os.FileInfo, error) {
f, err := fs.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
return f.Stat()
}