-
-
Notifications
You must be signed in to change notification settings - Fork 10
/
internals.go
169 lines (130 loc) · 4.6 KB
/
internals.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package assets
import (
"bytes"
"embed"
"errors"
"html/template"
"log"
"os"
"strings"
"github.com/blackfyre/wga/utils"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
)
//go:embed "reference/*" "views/*"
var InternalFiles embed.FS
type Renderable struct {
IsHtmx bool
Page string
Block string
Data map[string]any
}
// NewRenderData creates and returns a map containing render data for the given app.
// The render data includes the environment variable "WGA_ENV" and the contents of the "analytics.txt" file.
// If the "renderable:analytics" cache is not available, the file is read and stored in the cache.
// The "Analytics" key in the map contains the contents of the "analytics.txt" file.
func NewRenderData(app *pocketbase.PocketBase) map[string]any {
data := map[string]any{
"Env": os.Getenv("WGA_ENV"),
"Analytics": "",
}
if app != nil {
if !app.Store().Has("renderable:analytics") {
analytics, err := os.ReadFile("./analytics.txt")
if err != nil {
if errors.Is(err, os.ErrNotExist) {
app.Logger().Warn("analytics.txt file not found, using empty string as default", err)
analytics = []byte("") // Provide an empty string if file does not exist
} else {
app.Logger().Error("Failed to read file", err)
return nil
}
}
app.Store().Set("renderable:analytics", string(analytics))
data["Analytics"] = string(analytics)
} else {
data["Analytics"] = app.Store().Get("renderable:analytics")
}
}
return data
}
// renderPage renders the given template with the provided data and returns the resulting HTML string.
// The template is parsed from the views directory using the provided template name and the layout.html file.
// If the template cannot be parsed or there is an error rendering it, an error is returned.
func RenderPage(t string, data map[string]any) (string, error) {
patterns := []string{
"views/layouts/layout.html",
"views/partials/*.html",
}
patterns = append(patterns, "views/pages/"+t+".html")
return renderHtml(patterns, "layout", data)
}
func RenderPageWithLayout(t string, layout string, data map[string]any) (string, error) {
patterns := []string{
"views/layouts/*.html",
"views/partials/*.html",
}
patterns = append(patterns, "views/pages/"+t+".html")
return renderHtml(patterns, layout, data)
}
// renderBlock renders a given block of HTML using the provided data and returns the resulting HTML string.
// The function searches for HTML templates in the "views/pages" and "views/partials" directories of the InternalFiles filesystem.
// It uses the utils.TemplateFuncs map to provide additional functions to the templates.
// If an error occurs while parsing or rendering the template, the function returns an empty string and the error.
func RenderBlock(block string, data map[string]any) (string, error) {
patterns := []string{
"views/pages/*.html",
"views/pages/*/*.html",
"views/partials/*.html",
}
return renderHtml(patterns, block, data)
}
// RenderEmail renders an email template with the given data.
// The function takes a string `t` representing the template name and a map `data` containing the data to be rendered.
// It returns a string representing the rendered email and an error if any occurred.
func RenderEmail(t string, data map[string]any) (string, error) {
patterns := []string{
"views/emails/*.html",
}
return renderHtml(patterns, t, data)
}
// renderHtml renders an HTML template using the provided patterns, name and data.
// It returns the rendered HTML as a string and an error if any occurred.
func renderHtml(patterns []string, name string, data map[string]any) (string, error) {
ts, err := template.New("").Funcs(utils.TemplateFuncs).ParseFS(
InternalFiles,
patterns...,
)
if err != nil {
log.Println("Error parsing template")
log.Println(err)
return "", err
}
html := new(bytes.Buffer)
err = ts.ExecuteTemplate(html, name, data)
if err != nil {
// or redirect to a dedicated 404 HTML page
log.Println("Error rendering template")
log.Println(err)
return "", apis.NewNotFoundError("", err)
}
return html.String(), nil
}
// Render renders a Renderable object and returns the resulting HTML string.
// If the Renderable object is marked as htmx, it renders the block using RenderBlock.
// Otherwise, it renders the page using RenderPage.
func Render(r Renderable) (string, error) {
if r.IsHtmx {
return RenderBlock(r.Block, r.Data)
}
page := ""
if r.Page != "" {
page = r.Page
} else {
page = strings.Split(r.Block, ":")[0]
}
if page == "" {
return "", errors.New("Renderable " + page + " not found")
}
return RenderPage(page, r.Data)
}