From 86639f865a9a38ced67eb9d80b915436e0819a77 Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Wed, 18 Oct 2023 12:43:51 -0700 Subject: echo: add some template functions --- echo/tplfuncs/embed_csp.go | 75 ++++++++++++++++++++++++++++++++++++++++++++ echo/tplfuncs/humanize.go | 23 ++++++++++++++ echo/tplfuncs/picture_tag.go | 16 ++++++++++ 3 files changed, 114 insertions(+) create mode 100644 echo/tplfuncs/embed_csp.go create mode 100644 echo/tplfuncs/humanize.go create mode 100644 echo/tplfuncs/picture_tag.go diff --git a/echo/tplfuncs/embed_csp.go b/echo/tplfuncs/embed_csp.go new file mode 100644 index 0000000..44930c4 --- /dev/null +++ b/echo/tplfuncs/embed_csp.go @@ -0,0 +1,75 @@ +package tplfuncs + +import ( + "fmt" + "html/template" + "io" + "io/fs" + "path" + + "code.crute.us/mcrute/golib/echo/middleware" + + "github.com/labstack/echo/v4" +) + +type TemplateEmbeder struct { + templateStore fs.FS +} + +func (t *TemplateEmbeder) ConfigureTemplateStore(store fs.FS) { + t.templateStore = store +} + +func (t *TemplateEmbeder) Embed(filename string) ([]byte, error) { + if t.templateStore == nil { + return nil, fmt.Errorf("EmbedWithCSP: has not been setup with template store") + } + + fd, err := t.templateStore.Open(filename) + if err != nil { + return nil, err + } + defer fd.Close() + + fc, err := io.ReadAll(fd) + if err != nil { + return nil, err + } + + return fc, nil +} + +func (t *TemplateEmbeder) EmbedHTML(filename string) (any, error) { + d, err := t.Embed(filename) + return template.HTML(d), err +} + +func (t *TemplateEmbeder) embedWithCSP(filename string, c echo.Context) ([]byte, error) { + fc, err := t.Embed(filename) + if err != nil { + return nil, err + } + + csp := &middleware.ContentSecurityPolicyConfig{} + switch path.Ext(filename) { + case ".js", ".json": + csp.ScriptSrc = []middleware.CSPDirective{ + middleware.CSPSha256FromBytes(fc), + } + case ".css": + csp.StyleSrc = []middleware.CSPDirective{ + middleware.CSPSha256FromBytes(fc), + } + default: + return nil, fmt.Errorf("EmbedWithCSP: file %s can not be embedded", filename) + } + + middleware.ExtendCSP(c, csp) + + return fc, nil +} + +func (t *TemplateEmbeder) EmbedJSWithCSP(filename string, c echo.Context) (any, error) { + d, err := t.embedWithCSP(filename, c) + return template.JS(d), err +} diff --git a/echo/tplfuncs/humanize.go b/echo/tplfuncs/humanize.go new file mode 100644 index 0000000..4fc47a3 --- /dev/null +++ b/echo/tplfuncs/humanize.go @@ -0,0 +1,23 @@ +package tplfuncs + +import ( + "fmt" + "strings" +) + +// JoinEnglish renders a list of strings with commas between them and if +// there are three or more words will include the word "and" before the +// final word. +func JoinEnglish(words []string) string { + switch len(words) { + case 0: + return "" + case 1: + return words[0] + case 2: + return fmt.Sprintf("%s and %s", words[0], words[1]) + default: + base := strings.Join(words[:len(words)-1], ", ") + return fmt.Sprintf("%s, and %s", base, words[len(words)-1]) + } +} diff --git a/echo/tplfuncs/picture_tag.go b/echo/tplfuncs/picture_tag.go new file mode 100644 index 0000000..ddec4aa --- /dev/null +++ b/echo/tplfuncs/picture_tag.go @@ -0,0 +1,16 @@ +package tplfuncs + +import ( + "fmt" + "html/template" +) + +// RenderPictureTag renders a picture tag with a webp and jpeg source +// and alt text. +func RenderPictureTag(img, alt string) template.HTML { + return template.HTML(fmt.Sprintf(` + + + %s +`, img, img, img, alt)) +} -- cgit v1.2.3