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
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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue