mirror of
				https://github.com/Theodor-Springmann-Stiftung/lenz-web.git
				synced 2025-10-30 01:35:32 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			149 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			149 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package templating
 | |
| 
 | |
| import (
 | |
| 	"html/template"
 | |
| 	"io/fs"
 | |
| 	"path/filepath"
 | |
| 	"slices"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| type TemplateContext struct {
 | |
| 	//  WARNING: Path is a URL path, NOT a filesystem path
 | |
| 	Path string
 | |
| 	//  WARNING: The keys of these maps are template names, NOT filesystem paths
 | |
| 	// The values are FS paths absolute from the root directory of the templates FS
 | |
| 	locals  map[string]string
 | |
| 	globals map[string]string
 | |
| }
 | |
| 
 | |
| func NewTemplateContext(path string) TemplateContext {
 | |
| 	return TemplateContext{
 | |
| 		Path:    path,
 | |
| 		locals:  make(map[string]string),
 | |
| 		globals: make(map[string]string),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (c *TemplateContext) Parse(fsys fs.FS) error {
 | |
| 	fspath := PathToFSPath(c.Path)
 | |
| 	entries, err := fs.ReadDir(fsys, fspath)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return NewError(InvalidPathError, c.Path)
 | |
| 	}
 | |
| 
 | |
| 	for _, e := range entries {
 | |
| 		if e.IsDir() {
 | |
| 			// INFO: components in the components directory can be overwritten
 | |
| 			// by components in the base directory down below
 | |
| 			// TODO: Maybe allow for subdirectories in the components directory?
 | |
| 			if e.Name() == TEMPLATE_COMPONENT_DIRECTORY {
 | |
| 				entries, err := fs.ReadDir(fsys, filepath.Join(fspath, e.Name()))
 | |
| 				if err != nil {
 | |
| 					return NewError(FileAccessError, filepath.Join(fspath, e.Name()))
 | |
| 				}
 | |
| 
 | |
| 				for _, e := range entries {
 | |
| 					ext := filepath.Ext(e.Name())
 | |
| 
 | |
| 					if !slices.Contains(TEMPLATE_FORMATS, ext) {
 | |
| 						continue
 | |
| 					}
 | |
| 
 | |
| 					name := strings.TrimSuffix(e.Name(), ext)
 | |
| 					if strings.HasPrefix(e.Name(), TEMPLATE_GLOBAL_PREFIX) {
 | |
| 						c.globals[name] = filepath.Join(fspath, TEMPLATE_COMPONENT_DIRECTORY, e.Name())
 | |
| 					} else {
 | |
| 						c.locals[name] = filepath.Join(fspath, TEMPLATE_COMPONENT_DIRECTORY, e.Name())
 | |
| 					}
 | |
| 				}
 | |
| 				continue
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		ext := filepath.Ext(e.Name())
 | |
| 
 | |
| 		if !slices.Contains(TEMPLATE_FORMATS, ext) {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		name := strings.TrimSuffix(e.Name(), ext)
 | |
| 		if strings.HasPrefix(e.Name(), TEMPLATE_GLOBAL_PREFIX) {
 | |
| 			c.globals[name] = filepath.Join(fspath, e.Name())
 | |
| 		} else {
 | |
| 			c.locals[name] = filepath.Join(fspath, e.Name())
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *TemplateContext) SetGlobals(globals map[string]string) error {
 | |
| 	// INFO: this allows for overwriting of existing global keys.
 | |
| 	// Make sure to call this appopriately before or after Parse(), depending on your use case
 | |
| 	for k, v := range globals {
 | |
| 		c.globals[k] = v
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (c *TemplateContext) Globals() map[string]string {
 | |
| 	return c.globals
 | |
| }
 | |
| 
 | |
| func (c *TemplateContext) Template(fsys fs.FS, funcmap *template.FuncMap) (*template.Template, error) {
 | |
| 	// TODO: locals need to be in order: root, head, body
 | |
| 	t, err := readTemplates(fsys, nil, c.locals, funcmap)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	t, err = readTemplates(fsys, t, c.globals, funcmap)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return t, nil
 | |
| }
 | |
| 
 | |
| func readTemplates(fsys fs.FS, t *template.Template, paths map[string]string, funcmap *template.FuncMap) (*template.Template, error) {
 | |
| 	for k, v := range paths {
 | |
| 		text, err := fs.ReadFile(fsys, v)
 | |
| 		if err != nil {
 | |
| 			return nil, NewError(FileAccessError, v)
 | |
| 		}
 | |
| 
 | |
| 		temp := template.New(k)
 | |
| 
 | |
| 		if funcmap != nil {
 | |
| 			temp.Funcs(*funcmap)
 | |
| 		}
 | |
| 
 | |
| 		temp, err = temp.Parse(string(text))
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 		if t == nil {
 | |
| 			t = temp
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		for _, template := range temp.Templates() {
 | |
| 			_, err = t.AddParseTree(template.Name(), template.Tree)
 | |
| 			if err != nil {
 | |
| 				return nil, err
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		_, err = t.AddParseTree(temp.Name(), temp.Tree)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	return t, nil
 | |
| }
 | 
