added external directories for E-mail templates and dialog templates
This commit is contained in:
@@ -104,6 +104,8 @@ type AmConfig struct {
|
||||
} `yaml:"rendering"`
|
||||
Resources struct {
|
||||
ViewTemplateDir string `yaml:"viewTemplateDir"`
|
||||
DialogTemplateDir string `yaml:"dialogTemplateDir"`
|
||||
EmailTemplateDir string `yaml:"emailTemplateDir"`
|
||||
ExternalContentPath string `yaml:"externalContentPath"`
|
||||
ExternalResourcePath string `yaml:"externalResourcePath"`
|
||||
} `yaml:"resources"`
|
||||
|
||||
@@ -49,6 +49,8 @@ rendering:
|
||||
veniceCompatibleImageURLs: false
|
||||
resources:
|
||||
viewTemplateDir: ""
|
||||
dialogTemplateDir: ""
|
||||
emailTemplateDir: ""
|
||||
externalContentPath: ""
|
||||
externalResourcePath: ""
|
||||
posting:
|
||||
|
||||
+18
-4
@@ -26,6 +26,7 @@ import (
|
||||
"git.erbosoft.com/amy/amsterdam/database"
|
||||
"github.com/CloudyKit/jet/v6"
|
||||
"github.com/CloudyKit/jet/v6/loaders/embedfs"
|
||||
"github.com/CloudyKit/jet/v6/loaders/multi"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@@ -210,11 +211,24 @@ func SetupMailSender() func() {
|
||||
disclaimerLines = strings.Split(config.GlobalConfig.Email.Disclaimer, "\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.
|
||||
emailRenderer = jet.NewSet(
|
||||
embedfs.NewLoader("templates/", emailTemplates),
|
||||
jet.DevelopmentMode(true),
|
||||
)
|
||||
emailRenderer = jet.NewSet(multi.NewLoader(templateLoaders...), jet.DevelopmentMode(true))
|
||||
emailRenderer.AddGlobal("AmsterdamVersion", config.AMSTERDAM_VERSION)
|
||||
emailRenderer.AddGlobal("AmsterdamCopyright", config.AMSTERDAM_COPYRIGHT)
|
||||
emailRenderer.AddGlobal("GlobalConfig", config.GlobalConfig)
|
||||
|
||||
+89
-37
@@ -13,9 +13,12 @@ package ui
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"math"
|
||||
"net"
|
||||
"net/mail"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -24,6 +27,7 @@ import (
|
||||
"git.erbosoft.com/amy/amsterdam/config"
|
||||
"git.erbosoft.com/amy/amsterdam/database"
|
||||
"git.erbosoft.com/amy/amsterdam/util"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -78,51 +82,99 @@ func (vr *VRange) IsEmpty() bool {
|
||||
//go:embed dialogs/*
|
||||
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.
|
||||
* Parameters:
|
||||
* name - The name of the dialog to load
|
||||
*/
|
||||
func AmLoadDialog(name string) (*Dialog, error) {
|
||||
b, err := dialogs.ReadFile(fmt.Sprintf("dialogs/%s.yaml", name))
|
||||
if err == nil {
|
||||
var d Dialog
|
||||
err = yaml.Unmarshal(b, &d)
|
||||
if err == nil {
|
||||
// "nil-patch" certain fields and create the fast-lookup map
|
||||
if d.MenuSelector == "" {
|
||||
d.MenuSelector = "nochange"
|
||||
var f fs.File = nil
|
||||
var err error
|
||||
if extDialogs != nil {
|
||||
f, err = extDialogs.Open(fmt.Sprintf("%s.yaml", name))
|
||||
if err != nil {
|
||||
f = nil
|
||||
pe := err.(*fs.PathError)
|
||||
if pe.Err == os.ErrInvalid || pe.Err == os.ErrNotExist {
|
||||
err = nil
|
||||
}
|
||||
d.fldmap = make(map[string]*DialogItem)
|
||||
for i, fld := range d.Fields {
|
||||
d.fldmap[fld.Name] = &(d.Fields[i])
|
||||
if fld.Type == "button" && fld.Param == "" {
|
||||
d.Fields[i].Param = "blue"
|
||||
}
|
||||
if fld.Type == "date" && fld.Param == "" {
|
||||
d.Fields[i].Param = "year:-100"
|
||||
}
|
||||
if fld.Type == "integer" && fld.Size == 0 {
|
||||
vr := fld.ValueRange()
|
||||
if !vr.IsEmpty() {
|
||||
// compute the number of digits in each end of the range and take the maximum as the size
|
||||
dlow := int(math.Floor(math.Log10(float64(vr.Low)))) + 1
|
||||
dhigh := int(math.Floor(math.Log10(float64(vr.High)))) + 1
|
||||
d.Fields[i].Size = max(dlow, dhigh)
|
||||
d.Fields[i].MaxLength = d.Fields[i].Size
|
||||
}
|
||||
}
|
||||
if fld.Type == "ipaddress" {
|
||||
d.Fields[i].Size = 15 // max IPv4
|
||||
d.Fields[i].MaxLength = 39 // max IPv6
|
||||
}
|
||||
if fld.Type == "dropdown" && len(fld.Choices) == 0 {
|
||||
return nil, fmt.Errorf("dropdown field %s in dialog %s has no choices", fld.Name, name)
|
||||
}
|
||||
}
|
||||
return &d, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
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
|
||||
if d.MenuSelector == "" {
|
||||
d.MenuSelector = "nochange"
|
||||
}
|
||||
d.fldmap = make(map[string]*DialogItem)
|
||||
for i, fld := range d.Fields {
|
||||
d.fldmap[fld.Name] = &(d.Fields[i])
|
||||
if fld.Type == "button" && fld.Param == "" {
|
||||
d.Fields[i].Param = "blue"
|
||||
}
|
||||
if fld.Type == "date" && fld.Param == "" {
|
||||
d.Fields[i].Param = "year:-100"
|
||||
}
|
||||
if fld.Type == "integer" && fld.Size == 0 {
|
||||
vr := fld.ValueRange()
|
||||
if !vr.IsEmpty() {
|
||||
// compute the number of digits in each end of the range and take the maximum as the size
|
||||
dlow := int(math.Floor(math.Log10(float64(vr.Low)))) + 1
|
||||
dhigh := int(math.Floor(math.Log10(float64(vr.High)))) + 1
|
||||
d.Fields[i].Size = max(dlow, dhigh)
|
||||
d.Fields[i].MaxLength = d.Fields[i].Size
|
||||
}
|
||||
}
|
||||
if fld.Type == "ipaddress" {
|
||||
d.Fields[i].Size = 15 // max IPv4
|
||||
d.Fields[i].MaxLength = 39 // max IPv6
|
||||
}
|
||||
if fld.Type == "dropdown" && len(fld.Choices) == 0 {
|
||||
return nil, fmt.Errorf("dropdown field %s in dialog %s has no choices", fld.Name, name)
|
||||
}
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// DateValues returns the date values stored in a date field.
|
||||
|
||||
@@ -16,6 +16,7 @@ import "slices"
|
||||
func SetupUILayer() func() {
|
||||
exitfuncs := make([]func(), 0, 2)
|
||||
setupTemplates()
|
||||
setupDialogs()
|
||||
setupMenuCache()
|
||||
setupResources()
|
||||
exitfuncs = append(exitfuncs, setupSessionManager())
|
||||
|
||||
Reference in New Issue
Block a user