jordi fita mas 4f04d973c2 Dynamically set min and max to arrival and departure date inputs
The departure must be at list one day after the arrival, but no longer
than seven, due to campground’s policy.
2024-01-31 20:00:38 +01:00

170 lines
5.2 KiB
Go

/*
* SPDX-FileCopyrightText: 2023 jordi fita mas <jfita@peritasoft.com>
* SPDX-License-Identifier: AGPL-3.0-only
*/
package template
import (
"fmt"
"html/template"
"io"
"math"
"net/http"
"net/url"
"path"
"strconv"
"strings"
"time"
"golang.org/x/text/language"
"golang.org/x/text/message"
"golang.org/x/text/number"
"dev.tandem.ws/tandem/camper/pkg/auth"
"dev.tandem.ws/tandem/camper/pkg/build"
"dev.tandem.ws/tandem/camper/pkg/database"
httplib "dev.tandem.ws/tandem/camper/pkg/http"
)
func adminTemplateFile(name string) string {
return "web/templates/admin/" + name
}
func publicTemplateFile(name string) string {
return "web/templates/public/" + name
}
func MustRenderAdmin(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, filename string, data interface{}) {
layout := "layout.gohtml"
if httplib.IsHTMxRequest(r) {
layout = "htmx.gohtml"
}
mustRenderLayout(w, user, company, adminTemplateFile, data, layout, filename)
}
func MustRenderAdminFiles(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, data interface{}, filenames ...string) {
layout := "layout.gohtml"
if httplib.IsHTMxRequest(r) {
layout = "htmx.gohtml"
}
filenames = append([]string{layout}, filenames...)
mustRenderLayout(w, user, company, adminTemplateFile, data, filenames...)
}
func MustRenderAdminNoLayout(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, filename string, data interface{}) {
mustRenderLayout(w, user, company, adminTemplateFile, data, filename)
}
func MustRenderPublic(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, filename string, data interface{}) {
layout := "layout.gohtml"
mustRenderLayout(w, user, company, publicTemplateFile, data, layout, filename)
}
func MustRenderPublicNoLayout(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, filename string, data interface{}) {
mustRenderLayout(w, user, company, publicTemplateFile, data, filename)
}
func MustRenderPublicFiles(w io.Writer, r *http.Request, user *auth.User, company *auth.Company, data interface{}, filenames ...string) {
layout := "layout.gohtml"
filenames = append([]string{layout}, filenames...)
mustRenderLayout(w, user, company, publicTemplateFile, data, filenames...)
}
func mustRenderLayout(w io.Writer, user *auth.User, company *auth.Company, templateFile func(string) string, data interface{}, templates ...string) {
t := template.New(templates[len(templates)-1])
t.Funcs(template.FuncMap{
"camperVersion": func() string {
return build.Version
},
"gettext": user.Locale.Get,
"pgettext": user.Locale.GetC,
"currentLocale": func() string {
return user.Locale.Language.String()
},
"isLoggedIn": func() bool {
return user.LoggedIn
},
"isAdmin": user.IsAdmin,
"CSRFHeader": func() string {
return fmt.Sprintf(`"%s": "%s"`, auth.CSRFTokenHeader, user.CSRFToken)
},
"CSRFInput": func() template.HTML {
return template.HTML(fmt.Sprintf(`<input type="hidden" name="%s" value="%s">`, auth.CSRFTokenField, user.CSRFToken))
},
"raw": func(s string) template.HTML {
return template.HTML(s)
},
"replaceAll": func(s, old, new string) string {
return strings.ReplaceAll(s, old, new)
},
"humanizeBytes": func(bytes int64) string {
return humanizeBytes(bytes)
},
"formatPrice": func(price string) string {
return formatPrice(price, user.Locale.Language, user.Locale.CurrencyPattern, company.DecimalDigits, company.CurrencySymbol)
},
"formatDate": func(time time.Time) template.HTML {
return template.HTML(`<time datetime="` + time.Format(database.ISODateFormat) + `">` + time.Format("02/01/2006") + "</time>")
},
"tomorrow": func() string {
return time.Now().AddDate(0, 0, 1).Format(database.ISODateFormat)
},
"overmorrow": func() string {
return time.Now().AddDate(0, 0, 2).Format(database.ISODateFormat)
},
"queryEscape": func(s string) string {
return url.QueryEscape(s)
},
"inc": func(i int) int {
return i + 1
},
"dec": func(i int) int {
return i - 1
},
"int": func(v interface{}) int {
switch v := v.(type) {
case int:
return v
case time.Weekday:
return int(v)
case time.Month:
return int(v)
default:
panic(fmt.Errorf("Could not convert to integer"))
}
},
"hexToDec": func(s string) int {
num, _ := strconv.ParseInt(s, 16, 0)
return int(num)
},
})
templates = append(templates, "form.gohtml")
files := make([]string, len(templates))
for i, tmpl := range templates {
if len(tmpl) > 4 && tmpl[0] == 'w' && tmpl[1] == 'e' && tmpl[2] == 'b' && tmpl[3] == '/' {
files[i] = tmpl
} else {
files[i] = templateFile(tmpl)
}
}
if _, err := t.ParseFiles(files...); err != nil {
panic(err)
}
if rw, ok := w.(http.ResponseWriter); ok {
rw.Header().Set("Content-Type", "text/html; charset=utf-8")
}
if err := t.ExecuteTemplate(w, path.Base(templates[0]), data); err != nil {
panic(err)
}
}
func formatPrice(price string, language language.Tag, currencyPattern string, decimalDigits int, currencySymbol string) string {
p := message.NewPrinter(language)
f, err := strconv.ParseFloat(price, 64)
if err != nil {
f = math.NaN()
}
return p.Sprintf(currencyPattern, decimalDigits, number.Decimal(f), currencySymbol)
}