footer: Add a semi-flexible template system
This commit is contained in:
parent
bda763725e
commit
021fc3cc7e
26 changed files with 239 additions and 208 deletions
File diff suppressed because one or more lines are too long
|
|
@ -1,3 +1,20 @@
|
|||
{{ template "_form.html" . }}
|
||||
{{ define "form/title" }}Login Required{{ end }}
|
||||
{{ define "form/button" }}Login{{ end }}
|
||||
{{ define "form/inside" }}
|
||||
<div class="pure-form-group">
|
||||
<p>
|
||||
The site you are attempting to access requires you to login with your account.
|
||||
Simply fill out the form below and click <em>Login</em>.
|
||||
</p>
|
||||
<p>
|
||||
<small>
|
||||
This functionality requires you to have cookies enabled.
|
||||
A cookie to prevent fraudulent login attempts has already been set on your machine.
|
||||
When clicking the <em>Login</em> button, a further cookie will be set on your computer to keep track of your login session.
|
||||
If you do not wish to have futher cookie(s) set, do not click Login and do not attempt to access protected sites.
|
||||
See also <a href="/legal/#cookies">Legal Notices</a>.
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
|
@ -79,7 +79,7 @@ func (panel *UserPanel) UserFormContext(ctx httpx.FormContext, r *http.Request)
|
|||
user, err := panel.Dependencies.Auth.UserOf(r)
|
||||
|
||||
uctx := userFormContext{FormContext: ctx}
|
||||
panel.Dependencies.Custom.Update(&uctx)
|
||||
panel.Dependencies.Custom.Update(&uctx, r)
|
||||
if err == nil {
|
||||
uctx.User = &user.User
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler {
|
|||
FormContext: context,
|
||||
},
|
||||
}
|
||||
panel.Dependencies.Custom.Update(&ctx.userFormContext)
|
||||
panel.Dependencies.Custom.Update(&ctx.userFormContext, r)
|
||||
|
||||
if err == nil && user != nil {
|
||||
ctx.userFormContext.User = &user.User
|
||||
|
|
|
|||
|
|
@ -28,9 +28,9 @@ func (panel *UserPanel) routeUser(ctx context.Context) http.Handler {
|
|||
userTemplate := panel.Dependencies.Custom.Template(userTemplate)
|
||||
return &httpx.HTMLHandler[routeUserContext]{
|
||||
Handler: func(r *http.Request) (ruc routeUserContext, err error) {
|
||||
panel.Dependencies.Custom.Update(&ruc)
|
||||
panel.Dependencies.Custom.Update(&ruc, r)
|
||||
ruc.AuthUser, err = panel.Dependencies.Auth.UserOf(r)
|
||||
return routeUserContext{}, err
|
||||
return ruc, err
|
||||
},
|
||||
Template: userTemplate,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ func (auth *Auth) authLogin(ctx context.Context) http.Handler {
|
|||
if context.Err != nil {
|
||||
context.Err = errLoginFailed
|
||||
}
|
||||
httpx.WriteHTML(auth.Dependencies.Custom.NewForm(context), nil, loginTemplate, "", w, r)
|
||||
httpx.WriteHTML(auth.Dependencies.Custom.NewForm(context, r), nil, loginTemplate, "", w, r)
|
||||
},
|
||||
|
||||
Validate: func(r *http.Request, values map[string]string) (*AuthUser, error) {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ type componentContext struct {
|
|||
}
|
||||
|
||||
func (admin *Admin) components(r *http.Request) (cp componentContext, err error) {
|
||||
admin.Dependencies.Custom.Update(&cp)
|
||||
admin.Dependencies.Custom.Update(&cp, r)
|
||||
|
||||
cp.Analytics = *admin.Analytics
|
||||
return
|
||||
|
|
@ -49,7 +49,7 @@ type ingredientsContext struct {
|
|||
}
|
||||
|
||||
func (admin *Admin) ingredients(r *http.Request) (cp ingredientsContext, err error) {
|
||||
admin.Dependencies.Custom.Update(&cp)
|
||||
admin.Dependencies.Custom.Update(&cp, r)
|
||||
|
||||
// find the instance itself!
|
||||
instance, err := admin.Dependencies.Instances.WissKI(r.Context(), mux.Vars(r)["slug"])
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ type indexContext struct {
|
|||
}
|
||||
|
||||
func (admin *Admin) index(r *http.Request) (idx indexContext, err error) {
|
||||
admin.Dependencies.Custom.Update(&idx)
|
||||
admin.Dependencies.Custom.Update(&idx, r)
|
||||
idx.Distillery, idx.Instances, err = admin.Status(r.Context(), true)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package admin
|
|||
|
||||
import (
|
||||
_ "embed"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
||||
|
|
@ -25,13 +24,12 @@ var instanceTemplate = static.AssetsAdmin.MustParseShared(
|
|||
type instanceContext struct {
|
||||
custom.BaseContext
|
||||
|
||||
CSRF template.HTML
|
||||
Instance models.Instance
|
||||
Info status.WissKI
|
||||
}
|
||||
|
||||
func (admin *Admin) instance(r *http.Request) (is instanceContext, err error) {
|
||||
admin.Dependencies.Custom.Update(&is)
|
||||
admin.Dependencies.Custom.Update(&is, r)
|
||||
|
||||
is.CSRF = csrf.TemplateField(r)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package admin
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
_ "embed"
|
||||
|
|
@ -27,12 +26,11 @@ type userContext struct {
|
|||
custom.BaseContext
|
||||
httpx.FormContext
|
||||
|
||||
CSRF template.HTML
|
||||
Users []*auth.AuthUser
|
||||
}
|
||||
|
||||
func (admin *Admin) users(r *http.Request) (uc userContext, err error) {
|
||||
admin.Dependencies.Custom.Update(&uc)
|
||||
admin.Dependencies.Custom.Update(&uc, r)
|
||||
|
||||
uc.CSRF = csrf.TemplateField(r)
|
||||
uc.Users, err = admin.Dependencies.Auth.Users(r.Context())
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
{{ template "_base.html" . }}
|
||||
{{ define "title" }}WissKI Distillery{{ end }}
|
||||
|
||||
{{ define "header/time" }}
|
||||
<!-- no header/time -->
|
||||
{{ end }}
|
||||
{{ define "header"}}
|
||||
<!-- no header -->
|
||||
{{ end }}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ var homeTemplate = static.AssetsHome.MustParseShared("home.html", homeHTMLStr)
|
|||
|
||||
func (home *Home) homeRender(ctx context.Context) ([]byte, error) {
|
||||
var context homeContext
|
||||
home.Dependencies.Custom.Update(&context)
|
||||
home.Dependencies.Custom.Update(&context, nil)
|
||||
|
||||
// setup a couple of static things
|
||||
context.Time = time.Now().UTC()
|
||||
|
|
@ -60,10 +60,10 @@ func (home *Home) homeRender(ctx context.Context) ([]byte, error) {
|
|||
|
||||
// render the template
|
||||
var buffer bytes.Buffer
|
||||
home.homeTemplate.Get(func() *template.Template {
|
||||
err = home.homeTemplate.Get(func() *template.Template {
|
||||
return home.Dependencies.Custom.Template(homeTemplate)
|
||||
}).Execute(&buffer, context)
|
||||
return buffer.Bytes(), nil
|
||||
return buffer.Bytes(), err
|
||||
}
|
||||
|
||||
type homeContext struct {
|
||||
|
|
|
|||
|
|
@ -56,13 +56,13 @@ type legalContext struct {
|
|||
AssetsDisclaimer string
|
||||
}
|
||||
|
||||
func (legal *Legal) context(r *http.Request) (legalContext, error) {
|
||||
return legalContext{
|
||||
BaseContext: legal.Dependencies.Custom.New(),
|
||||
LegalNotices: cli.LegalNotices,
|
||||
func (legal *Legal) context(r *http.Request) (lc legalContext, err error) {
|
||||
legal.Dependencies.Custom.Update(&lc, r)
|
||||
|
||||
CSRFCookie: control.CSRFCookie,
|
||||
SessionCookie: control.SessionCookie,
|
||||
AssetsDisclaimer: static.AssetsDisclaimer,
|
||||
}, nil
|
||||
lc.LegalNotices = cli.LegalNotices
|
||||
|
||||
lc.CSRFCookie = control.CSRFCookie
|
||||
lc.SessionCookie = control.SessionCookie
|
||||
lc.AssetsDisclaimer = static.AssetsDisclaimer
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
{{ template "_base.html" . }}
|
||||
{{ define "title" }}Legal{{ end }}
|
||||
|
||||
{{ define "header/time" }}
|
||||
<!-- no header/time -->
|
||||
{{ end }}
|
||||
{{ define "header"}}
|
||||
<!-- no header -->
|
||||
{{ end }}
|
||||
|
|
|
|||
|
|
@ -10,23 +10,23 @@ var AssetsDisclaimer string
|
|||
// AssetsHome contains assets for the 'Home' entrypoint.
|
||||
var AssetsHome = Assets{
|
||||
Scripts: `<script type="module" src="/static/Home.38d394c2.js"></script><script src="/static/Home.38d394c2.js" nomodule="" defer></script><script type="module" src="/static/Home.38d394c2.js"></script><script src="/static/Home.38d394c2.js" nomodule="" defer></script>`,
|
||||
Styles: `<link rel="stylesheet" href="/static/Home.9f00501f.css"><link rel="stylesheet" href="/static/Home.2353e048.css">`,
|
||||
Styles: `<link rel="stylesheet" href="/static/Home.4b303448.css"><link rel="stylesheet" href="/static/Home.2353e048.css">`,
|
||||
}
|
||||
|
||||
// AssetsUser contains assets for the 'User' entrypoint.
|
||||
var AssetsUser = Assets{
|
||||
Scripts: `<script type="module" src="/static/Home.38d394c2.js"></script><script src="/static/Home.38d394c2.js" nomodule="" defer></script><script type="module" src="/static/User.4197014b.js"></script><script src="/static/User.30d54198.js" nomodule="" defer></script>`,
|
||||
Styles: `<link rel="stylesheet" href="/static/Home.9f00501f.css"><link rel="stylesheet" href="/static/User.38d394c2.css">`,
|
||||
Styles: `<link rel="stylesheet" href="/static/Home.4b303448.css"><link rel="stylesheet" href="/static/User.38d394c2.css">`,
|
||||
}
|
||||
|
||||
// AssetsAdmin contains assets for the 'Admin' entrypoint.
|
||||
var AssetsAdmin = Assets{
|
||||
Scripts: `<script nomodule="" defer src="/static/User.30d54198.js"></script><script type="module" src="/static/User.4197014b.js"></script><script type="module" src="/static/Home.38d394c2.js"></script><script src="/static/Home.38d394c2.js" nomodule="" defer></script><script type="module" src="/static/Admin.4ca3cb6f.js"></script><script src="/static/Admin.9750ba9c.js" nomodule="" defer></script>`,
|
||||
Styles: `<link rel="stylesheet" href="/static/Home.9f00501f.css"><link rel="stylesheet" href="/static/Admin.6d59e220.css"><link rel="stylesheet" href="/static/User.38d394c2.css"><link rel="stylesheet" href="/static/Admin.6d2ae968.css">`,
|
||||
Styles: `<link rel="stylesheet" href="/static/Home.4b303448.css"><link rel="stylesheet" href="/static/Admin.6d59e220.css"><link rel="stylesheet" href="/static/User.38d394c2.css"><link rel="stylesheet" href="/static/Admin.6d2ae968.css">`,
|
||||
}
|
||||
|
||||
// AssetsLegal contains assets for the 'Legal' entrypoint.
|
||||
var AssetsLegal = Assets{
|
||||
Scripts: `<script type="module" src="/static/Home.38d394c2.js"></script><script src="/static/Home.38d394c2.js" nomodule="" defer></script><script type="module" src="/static/Legal.38d394c2.js"></script><script src="/static/Legal.38d394c2.js" nomodule="" defer></script>`,
|
||||
Styles: `<link rel="stylesheet" href="/static/Home.9f00501f.css"><link rel="stylesheet" href="/static/Legal.d1531eba.css"><link rel="stylesheet" href="/static/Legal.20259812.css">`,
|
||||
Styles: `<link rel="stylesheet" href="/static/Home.4b303448.css"><link rel="stylesheet" href="/static/Legal.d1531eba.css"><link rel="stylesheet" href="/static/Legal.20259812.css">`,
|
||||
}
|
||||
|
|
|
|||
90
internal/dis/component/control/static/custom/context.go
Normal file
90
internal/dis/component/control/static/custom/context.go
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
package custom
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||
"github.com/gorilla/csrf"
|
||||
"github.com/tkw1536/goprogram/lib/reflectx"
|
||||
)
|
||||
|
||||
// baseContextName is the name of the [BaseContext] type
|
||||
var baseContextName = reflectx.TypeOf[BaseContext]().Name()
|
||||
|
||||
// BaseContext represents a context shared by all the templates.
|
||||
//
|
||||
// This context should always be initialized using the [custom.Update], [custom.New] or [custom.NewForm] functions.
|
||||
// Other invocations might cause an error at runtime.
|
||||
type BaseContext struct {
|
||||
inited bool // has this context been inited
|
||||
|
||||
GeneratedAt time.Time // time this page was generated at
|
||||
|
||||
CSRF template.HTML // CSRF Field
|
||||
}
|
||||
|
||||
// constants that are used in various parts of the template to render stuff
|
||||
const (
|
||||
errorPrefix template.HTML = `<div style="z-index:10000;position:fixed;top:0;left:0;width:100vh;height:100vw;background:red;text-align:center;padding:10vh 10vw;font-size:xx-large;font-weight:bold">`
|
||||
errorSuffix template.HTML = "</div>"
|
||||
|
||||
csrfError template.HTML = errorPrefix + "CSRF used but not provided" + errorSuffix
|
||||
initError template.HTML = errorPrefix + "BaseContext not initialized" + errorSuffix
|
||||
)
|
||||
|
||||
// Use updates this context to use the values from the given base.
|
||||
// For convenience the passed context is also returned.
|
||||
func (tc *BaseContext) use(base component.Base, r *http.Request) *BaseContext {
|
||||
tc.inited = true
|
||||
|
||||
tc.GeneratedAt = time.Now().UTC()
|
||||
|
||||
// setup the CSRF field
|
||||
tc.CSRF = csrfError
|
||||
if r != nil {
|
||||
tc.CSRF = csrf.TemplateField(r)
|
||||
}
|
||||
|
||||
return tc
|
||||
}
|
||||
|
||||
func (bc BaseContext) DoInitCheck() template.HTML {
|
||||
if !bc.inited {
|
||||
return initError
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// NewForm is like New, but returns a new BaseFormContext
|
||||
func (custom *Custom) NewForm(context httpx.FormContext, r *http.Request) (ctx BaseFormContext) {
|
||||
ctx.FormContext = context
|
||||
ctx.use(custom.Base, r)
|
||||
return
|
||||
}
|
||||
|
||||
// RenderContext is exactly like NewForm, but returns any to be used as httpx.Form.RenderTemplateContext
|
||||
func (custom *Custom) RenderContext(ctx httpx.FormContext, r *http.Request) any {
|
||||
return custom.NewForm(ctx, r)
|
||||
}
|
||||
|
||||
// Update updates an embedded BaseContext field in context.
|
||||
//
|
||||
// Assumes that context is a pointer to a struct type.
|
||||
// If this is not the case, might call panic().
|
||||
func (custom *Custom) Update(context any, r *http.Request) *BaseContext {
|
||||
ctx := reflect.ValueOf(context).
|
||||
Elem().FieldByName(baseContextName).Addr().
|
||||
Interface().(*BaseContext)
|
||||
ctx.use(custom.Base, r)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// BaseFormContext combines BaseContext and FormContext
|
||||
type BaseFormContext struct {
|
||||
BaseContext
|
||||
httpx.FormContext
|
||||
}
|
||||
|
|
@ -5,4 +5,7 @@ import "github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
|||
// Custom implements theme and page customization.
|
||||
type Custom struct {
|
||||
component.Base
|
||||
Dependencies struct {
|
||||
// nothing yet
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
internal/dis/component/control/static/custom/footer.html
Normal file
3
internal/dis/component/control/static/custom/footer.html
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<p>
|
||||
Generated <time format="{{ .GeneratedAt.Format "2006-01-02T15:04:05Z" }}">{{ .GeneratedAt.Format "2006-01-02T15:04:05Z07:00" }}</time> by <a href="https://github.com/FAU-CDI/wisski-distillery" target="_blank" rel="noopener noreferer">WissKI Distillery</a>. This site might use cookies for essential purposes, see also <a href="/legal/">Legal Notices</a>.
|
||||
</p>
|
||||
|
|
@ -1,20 +1,15 @@
|
|||
package custom
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||
"github.com/tkw1536/goprogram/lib/reflectx"
|
||||
)
|
||||
|
||||
const footerName = "footer"
|
||||
|
||||
// defaultTemplate is the default footer template
|
||||
var defaultTemplate = template.Must(template.New("footer.html").Parse(`<p>Powered By WissKI Distillery</p>`))
|
||||
//go:embed "footer.html"
|
||||
var defaultTemplateStr string
|
||||
var defaultTemplate = template.Must(template.New("footer.html").Parse(defaultTemplateStr))
|
||||
|
||||
// Template creates a copy of template with shared template parts updated accordingly.
|
||||
// Any template using this should use one of the template contexts in this package.
|
||||
|
|
@ -25,58 +20,3 @@ func (custom *Custom) Template(tpl *template.Template) *template.Template {
|
|||
template.Must(clone.AddParseTree(footerName, tree)) // add the parse tree to it
|
||||
return clone // and return the tree
|
||||
}
|
||||
|
||||
// NewContext returns a new BaseContext
|
||||
func (custom *Custom) New() (ctx BaseContext) {
|
||||
ctx.Use(custom.Base)
|
||||
return
|
||||
}
|
||||
|
||||
// NewForm is like New, but returns a new BaseFormContext
|
||||
func (custom *Custom) NewForm(context httpx.FormContext) (ctx BaseFormContext) {
|
||||
ctx.FormContext = context
|
||||
ctx.Use(custom.Base)
|
||||
return
|
||||
}
|
||||
|
||||
// RenderContext can be used as httpx.Form.RenderTemplateContext.
|
||||
// It returns a new [BaseFormContext].
|
||||
func (custom *Custom) RenderContext(ctx httpx.FormContext, r *http.Request) any {
|
||||
return BaseFormContext{
|
||||
FormContext: ctx,
|
||||
BaseContext: custom.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// Update updates an embedded BaseContext field in context.
|
||||
//
|
||||
// Assumes that context is a pointer to a struct type.
|
||||
// If this is not the case, might call panic().
|
||||
func (custom *Custom) Update(context any) *BaseContext {
|
||||
ctx := reflect.ValueOf(context).
|
||||
Elem().FieldByName(contextName).Addr().
|
||||
Interface().(*BaseContext)
|
||||
ctx.Use(custom.Base)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// contextName is the name of the [BaseContext] field.
|
||||
var contextName = reflectx.TypeOf[BaseContext]().Name()
|
||||
|
||||
// BaseContext is a context struct shared by all contexts
|
||||
type BaseContext struct {
|
||||
Time time.Time // time this page was generated at
|
||||
}
|
||||
|
||||
// Use updates this context to use the values from the given base.
|
||||
// For convenience the passed context is also returned.
|
||||
func (tc *BaseContext) Use(base component.Base) *BaseContext {
|
||||
tc.Time = time.Now().UTC()
|
||||
return tc
|
||||
}
|
||||
|
||||
// BaseFormContext combines BaseContext and FormContext
|
||||
type BaseFormContext struct {
|
||||
BaseContext
|
||||
httpx.FormContext
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -8,6 +8,20 @@ footer {
|
|||
margin: 2em;
|
||||
}
|
||||
|
||||
footer {
|
||||
font-size: small;
|
||||
border-top: 1px solid black;
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: blue !important;
|
||||
}
|
||||
|
||||
time {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.padding {
|
||||
padding: 1em;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,14 +9,10 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
{{ .BaseContext.DoInitCheck }}
|
||||
<header>
|
||||
<h1 id="top">{{ template "title" . }}</h1>
|
||||
{{ block "header/time" . }}
|
||||
{{ if .Time }}
|
||||
<small>Generated at <code class="date">{{ .Time.Format "2006-01-02T15:04:05Z07:00" }}</code></small>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ block "header" . }}header{{ end }}
|
||||
{{ block "header" . }}<!-- no header by default -->{{ end }}
|
||||
</header>
|
||||
<main>
|
||||
<div class="pure-g">
|
||||
|
|
@ -25,11 +21,8 @@
|
|||
</main>
|
||||
|
||||
<footer>
|
||||
{{ if .Time }}
|
||||
Generated at <code>{{ .Time }}</code>
|
||||
{{ end }}
|
||||
{{ block "footer" . }}
|
||||
<!-- no footer by default -->
|
||||
{{ block "footer" .BaseContext }}
|
||||
<!-- no footer -->
|
||||
{{ end }}
|
||||
</footer>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
{{ define "title" }}{{ block "form/title" . }}Form{{ end }}{{ end }}
|
||||
|
||||
{{ define "header" }}<!-- no header -->{{ end }}
|
||||
{{ define "header/time" }}<!-- no time -->{{ end }}
|
||||
{{ define "content" }}
|
||||
<div class="pure-u-1">
|
||||
{{ block "form/extra" . }}<!-- no extra -->{{ end }}
|
||||
|
|
@ -24,7 +23,7 @@
|
|||
|
||||
{{ block "form/inside" . }}<!-- no inside -->{{ end }}
|
||||
{{ .Form }}
|
||||
<input type="submit" value="{{ block " form/button" .}}Submit{{ end }}" class="pure-button">
|
||||
<input type="submit" value="{{ block "form/button" .}}Submit{{ end }}" class="pure-button">
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,14 +3,23 @@ package httpx
|
|||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// WriteHTML writes a html response of type T to w.
|
||||
// If an error occured, writes an error response instead.
|
||||
func WriteHTML[T any](result T, err error, template *template.Template, templateName string, w http.ResponseWriter, r *http.Request) {
|
||||
func WriteHTML[T any](result T, err error, template *template.Template, templateName string, w http.ResponseWriter, r *http.Request) (e error) {
|
||||
// log any error that occurs;
|
||||
defer func() {
|
||||
if e != nil {
|
||||
zerolog.Ctx(r.Context()).Err(e).Str("path", r.URL.String()).Msg("error rendering template")
|
||||
}
|
||||
}()
|
||||
|
||||
// intercept any errors
|
||||
if HTMLInterceptor.Intercept(w, r, err) {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
// write out the response as html
|
||||
|
|
@ -23,9 +32,9 @@ func WriteHTML[T any](result T, err error, template *template.Template, template
|
|||
|
||||
// and return the template
|
||||
if templateName != "" {
|
||||
template.ExecuteTemplate(minifier, templateName, result)
|
||||
return template.ExecuteTemplate(minifier, templateName, result)
|
||||
} else {
|
||||
template.Execute(minifier, result)
|
||||
return template.Execute(minifier, result)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
//go:build nominify
|
||||
|
||||
package httpx
|
||||
|
||||
import (
|
||||
|
|
|
|||
27
pkg/httpx/html_minify_off.go
Normal file
27
pkg/httpx/html_minify_off.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//go:build !nominify
|
||||
|
||||
package httpx
|
||||
|
||||
import "io"
|
||||
|
||||
// MinifyHTMLWriter wraps the given io.Writer to minify the given html instead.
|
||||
// The writer must be closed explicitly.
|
||||
//
|
||||
// Specific environments may chose to disable http minification, in which case MinifyHTMLWriter becomes the identity function.
|
||||
func MinifyHTMLWriter(dest io.Writer) io.WriteCloser {
|
||||
return noop{Writer: dest}
|
||||
}
|
||||
|
||||
type noop struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (noop) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinifyHTML minifies the html source.
|
||||
// If an error occurs, returns the unmodified source instead.
|
||||
func MinifyHTML(source []byte) []byte {
|
||||
return source
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue