added external directories for E-mail templates and dialog templates

This commit is contained in:
2026-03-04 22:59:34 -07:00
parent 95aee6cf75
commit 2b7225c269
5 changed files with 112 additions and 41 deletions
+2
View File
@@ -104,6 +104,8 @@ type AmConfig struct {
} `yaml:"rendering"` } `yaml:"rendering"`
Resources struct { Resources struct {
ViewTemplateDir string `yaml:"viewTemplateDir"` ViewTemplateDir string `yaml:"viewTemplateDir"`
DialogTemplateDir string `yaml:"dialogTemplateDir"`
EmailTemplateDir string `yaml:"emailTemplateDir"`
ExternalContentPath string `yaml:"externalContentPath"` ExternalContentPath string `yaml:"externalContentPath"`
ExternalResourcePath string `yaml:"externalResourcePath"` ExternalResourcePath string `yaml:"externalResourcePath"`
} `yaml:"resources"` } `yaml:"resources"`
+2
View File
@@ -49,6 +49,8 @@ rendering:
veniceCompatibleImageURLs: false veniceCompatibleImageURLs: false
resources: resources:
viewTemplateDir: "" viewTemplateDir: ""
dialogTemplateDir: ""
emailTemplateDir: ""
externalContentPath: "" externalContentPath: ""
externalResourcePath: "" externalResourcePath: ""
posting: posting:
+18 -4
View File
@@ -26,6 +26,7 @@ import (
"git.erbosoft.com/amy/amsterdam/database" "git.erbosoft.com/amy/amsterdam/database"
"github.com/CloudyKit/jet/v6" "github.com/CloudyKit/jet/v6"
"github.com/CloudyKit/jet/v6/loaders/embedfs" "github.com/CloudyKit/jet/v6/loaders/embedfs"
"github.com/CloudyKit/jet/v6/loaders/multi"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@@ -210,11 +211,24 @@ func SetupMailSender() func() {
disclaimerLines = strings.Split(config.GlobalConfig.Email.Disclaimer, "\n") disclaimerLines = strings.Split(config.GlobalConfig.Email.Disclaimer, "\n")
signatureLines = strings.Split(config.GlobalConfig.Email.Signature, "\n") signatureLines = strings.Split(config.GlobalConfig.Email.Signature, "\n")
// Locate the external template directory and build the loaders.
templateLoaders := make([]jet.Loader, 0, 2)
if config.GlobalConfig.Resources.EmailTemplateDir != "" {
finfo, err := os.Stat(config.GlobalConfig.Resources.EmailTemplateDir)
if err == nil {
if finfo.IsDir() {
templateLoaders = append(templateLoaders, jet.NewOSFileSystemLoader(config.GlobalConfig.Resources.EmailTemplateDir))
} else {
log.Errorf("email template directory %s is not a directory, ignored", config.GlobalConfig.Resources.EmailTemplateDir)
}
} else {
log.Errorf("email template directory %s is not valid, ignored (%v)", config.GlobalConfig.Resources.EmailTemplateDir, err)
}
}
templateLoaders = append(templateLoaders, embedfs.NewLoader("templates/", emailTemplates))
// Initialize the template engine. // Initialize the template engine.
emailRenderer = jet.NewSet( emailRenderer = jet.NewSet(multi.NewLoader(templateLoaders...), jet.DevelopmentMode(true))
embedfs.NewLoader("templates/", emailTemplates),
jet.DevelopmentMode(true),
)
emailRenderer.AddGlobal("AmsterdamVersion", config.AMSTERDAM_VERSION) emailRenderer.AddGlobal("AmsterdamVersion", config.AMSTERDAM_VERSION)
emailRenderer.AddGlobal("AmsterdamCopyright", config.AMSTERDAM_COPYRIGHT) emailRenderer.AddGlobal("AmsterdamCopyright", config.AMSTERDAM_COPYRIGHT)
emailRenderer.AddGlobal("GlobalConfig", config.GlobalConfig) emailRenderer.AddGlobal("GlobalConfig", config.GlobalConfig)
+61 -9
View File
@@ -13,9 +13,12 @@ package ui
import ( import (
"embed" "embed"
"fmt" "fmt"
"io"
"io/fs"
"math" "math"
"net" "net"
"net/mail" "net/mail"
"os"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@@ -24,6 +27,7 @@ import (
"git.erbosoft.com/amy/amsterdam/config" "git.erbosoft.com/amy/amsterdam/config"
"git.erbosoft.com/amy/amsterdam/database" "git.erbosoft.com/amy/amsterdam/database"
"git.erbosoft.com/amy/amsterdam/util" "git.erbosoft.com/amy/amsterdam/util"
log "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -78,16 +82,67 @@ func (vr *VRange) IsEmpty() bool {
//go:embed dialogs/* //go:embed dialogs/*
var dialogs embed.FS var dialogs embed.FS
// extDialogs is the external dialogs directory filesystem.
var extDialogs fs.FS = nil
// setupDialogs sets up the external dialog filesystem.
func setupDialogs() {
// Open the external dialog path.
if config.GlobalConfig.Resources.DialogTemplateDir != "" {
finfo, err := os.Stat(config.GlobalConfig.Resources.DialogTemplateDir)
if err == nil {
if finfo.IsDir() {
root, err := os.OpenRoot(config.GlobalConfig.Resources.DialogTemplateDir)
if err != nil {
panic(err)
}
extDialogs = root.FS()
} else {
log.Errorf("external resource path \"%s\" is not a directory, ignored", config.GlobalConfig.Resources.DialogTemplateDir)
}
} else {
log.Errorf("external resource path \"%s\" is not valid, ignored (%v)", config.GlobalConfig.Resources.DialogTemplateDir, err)
}
}
}
/* AmLoadDialog loads a dialog definition. /* AmLoadDialog loads a dialog definition.
* Parameters: * Parameters:
* name - The name of the dialog to load * name - The name of the dialog to load
*/ */
func AmLoadDialog(name string) (*Dialog, error) { func AmLoadDialog(name string) (*Dialog, error) {
b, err := dialogs.ReadFile(fmt.Sprintf("dialogs/%s.yaml", name)) var f fs.File = nil
if err == nil { var err error
var d Dialog if extDialogs != nil {
err = yaml.Unmarshal(b, &d) f, err = extDialogs.Open(fmt.Sprintf("%s.yaml", name))
if err == nil { if err != nil {
f = nil
pe := err.(*fs.PathError)
if pe.Err == os.ErrInvalid || pe.Err == os.ErrNotExist {
err = nil
}
}
if err != nil {
return nil, err
}
}
if f == nil {
f, err = dialogs.Open(fmt.Sprintf("dialogs/%s.yaml", name))
if err != nil {
return nil, err
}
}
b, err := io.ReadAll(f)
f.Close()
if err != nil {
return nil, err
}
d := new(Dialog)
err = yaml.Unmarshal(b, d)
if err != nil {
return nil, err
}
// "nil-patch" certain fields and create the fast-lookup map // "nil-patch" certain fields and create the fast-lookup map
if d.MenuSelector == "" { if d.MenuSelector == "" {
d.MenuSelector = "nochange" d.MenuSelector = "nochange"
@@ -119,10 +174,7 @@ func AmLoadDialog(name string) (*Dialog, error) {
return nil, fmt.Errorf("dropdown field %s in dialog %s has no choices", fld.Name, name) return nil, fmt.Errorf("dropdown field %s in dialog %s has no choices", fld.Name, name)
} }
} }
return &d, nil return d, nil
}
}
return nil, err
} }
// DateValues returns the date values stored in a date field. // DateValues returns the date values stored in a date field.
+1
View File
@@ -16,6 +16,7 @@ import "slices"
func SetupUILayer() func() { func SetupUILayer() func() {
exitfuncs := make([]func(), 0, 2) exitfuncs := make([]func(), 0, 2)
setupTemplates() setupTemplates()
setupDialogs()
setupMenuCache() setupMenuCache()
setupResources() setupResources()
exitfuncs = append(exitfuncs, setupSessionManager()) exitfuncs = append(exitfuncs, setupSessionManager())