diff --git a/internal/dis/component/auth/auth.go b/internal/dis/component/auth/auth.go index d87bac5..9e4eabb 100644 --- a/internal/dis/component/auth/auth.go +++ b/internal/dis/component/auth/auth.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/FAU-CDI/wisski-distillery/internal/dis/component" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql" "github.com/FAU-CDI/wisski-distillery/pkg/lazy" "github.com/gorilla/sessions" @@ -17,7 +17,7 @@ type Auth struct { Dependencies struct { SQL *sql.SQL UserDeleteHooks []component.UserDeleteHook - Templating *templates.Templating + Templating *templating.Templating } store lazy.Lazy[sessions.Store] diff --git a/internal/dis/component/auth/login.html b/internal/dis/component/auth/login.html index 81a87ea..9ef48a4 100644 --- a/internal/dis/component/auth/login.html +++ b/internal/dis/component/auth/login.html @@ -1,5 +1,4 @@ -{{ template "_form.html" . }} -{{ define "form/title" }}Login Required{{ end }} +{{ template "form.html" . }} {{ define "form/button" }}Login{{ end }} {{ define "form/inside" }}
diff --git a/internal/dis/component/auth/panel/panel.go b/internal/dis/component/auth/panel/panel.go index 5eddfb3..2bda260 100644 --- a/internal/dis/component/auth/panel/panel.go +++ b/internal/dis/component/auth/panel/panel.go @@ -9,7 +9,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/next" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/policy" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/ssh2/sshkeys" "github.com/FAU-CDI/wisski-distillery/internal/models" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" @@ -20,7 +20,7 @@ type UserPanel struct { component.Base Dependencies struct { Auth *auth.Auth - Templating *templates.Templating + Templating *templating.Templating Policy *policy.Policy Instances *instances.Instances Next *next.Next @@ -106,30 +106,25 @@ func (panel *UserPanel) HandleRoute(ctx context.Context, route string) (http.Han } type userFormContext struct { - templates.BaseContext + templating.RuntimeFlags httpx.FormContext User *models.User } -func (panel *UserPanel) UserFormContext2(tpl *templates.Template[userFormContext], last component.MenuItem, gaps ...templates.BaseContextGaps) func(ctx httpx.FormContext, r *http.Request) any { - var g templates.BaseContextGaps - if len(gaps) > 1 { - panic("UserFormContext2: gaps must be of length 0 or 1") - } - if len(gaps) == 1 { - g = gaps[0] - } - g.Crumbs = []component.MenuItem{ - {Title: "User", Path: "/user/"}, - last, - } +func (panel *UserPanel) UserFormContext(tpl *templating.Template[userFormContext], last component.MenuItem, funcs ...templating.FlagFunc) func(ctx httpx.FormContext, r *http.Request) any { + funcs = append(funcs, func(flags templating.Flags, r *http.Request) templating.Flags { + flags.Crumbs = append(flags.Crumbs, component.MenuItem{}) + copy(flags.Crumbs[1:], flags.Crumbs) + flags.Crumbs[0] = component.MenuItem{Title: "User", Path: "/user/"} + return flags + }) - return templates.MappedHandler(tpl, func(ctx httpx.FormContext, r *http.Request) (userFormContext, templates.BaseContextGaps) { + return func(ctx httpx.FormContext, r *http.Request) any { uctx := userFormContext{FormContext: ctx} if user, err := panel.Dependencies.Auth.UserOf(r); err == nil { uctx.User = &user.User } - return uctx, g - }) + return tpl.Context(r, uctx, funcs...) + } } diff --git a/internal/dis/component/auth/panel/password.go b/internal/dis/component/auth/panel/password.go index adf3e79..6723d25 100644 --- a/internal/dis/component/auth/panel/password.go +++ b/internal/dis/component/auth/panel/password.go @@ -9,14 +9,19 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" - templating "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" "github.com/FAU-CDI/wisski-distillery/pkg/httpx/field" ) //go:embed "templates/password.html" var passwordHTML []byte -var passwordTemplate = templating.Parse[userFormContext]("password.html", passwordHTML, assets.AssetsUser) +var passwordTemplate = templating.Parse[userFormContext]( + "password.html", passwordHTML, httpx.FormTemplate, + + templating.Title("Change Password"), + templating.Assets(assets.AssetsUser), +) var ( errPasswordsNotIdentical = errors.New("passwords are not identical") @@ -40,7 +45,7 @@ func (panel *UserPanel) routePassword(ctx context.Context) http.Handler { FieldTemplate: field.PureCSSFieldTemplate, RenderTemplate: tpl.Template(), - RenderTemplateContext: panel.UserFormContext2(tpl, component.MenuItem{Title: "Change Password", Path: "/user/password/"}), + RenderTemplateContext: panel.UserFormContext(tpl, component.MenuItem{Title: "Change Password", Path: "/user/password/"}), Validate: func(r *http.Request, values map[string]string) (struct{}, error) { old, passcode, new, new2 := values["old"], values["otp"], values["new"], values["new2"] diff --git a/internal/dis/component/auth/panel/ssh.go b/internal/dis/component/auth/panel/ssh.go index e88c606..55bbd33 100644 --- a/internal/dis/component/auth/panel/ssh.go +++ b/internal/dis/component/auth/panel/ssh.go @@ -8,7 +8,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/internal/models" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" "github.com/FAU-CDI/wisski-distillery/pkg/httpx/field" @@ -22,10 +22,15 @@ import ( //go:embed "templates/ssh.html" var sshHTML []byte -var sshTemplate = templates.Parse[SSHTemplateContext]("ssh.html", sshHTML, assets.AssetsUser) +var sshTemplate = templating.Parse[SSHTemplateContext]( + "ssh.html", sshHTML, nil, + + templating.Title("SSH Keys"), + templating.Assets(assets.AssetsUser), +) type SSHTemplateContext struct { - templates.BaseContext + templating.RuntimeFlags Keys []models.Keys @@ -37,15 +42,16 @@ type SSHTemplateContext struct { } func (panel *UserPanel) sshRoute(ctx context.Context) http.Handler { - tpl := sshTemplate.Prepare(panel.Dependencies.Templating, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "User", Path: "/user/"}, - {Title: "SSH Keys", Path: "/user/ssh/"}, - }, - Actions: []component.MenuItem{ - {Title: "Add New Key", Path: "/user/ssh/add/"}, - }, - }) + tpl := sshTemplate.Prepare( + panel.Dependencies.Templating, + templating.Crumbs( + component.MenuItem{Title: "User", Path: "/user/"}, + component.MenuItem{Title: "SSH Keys", Path: "/user/ssh/"}, + ), + templating.Actions( + component.MenuItem{Title: "Add New Key", Path: "/user/ssh/add/"}, + ), + ) return tpl.HTMLHandler(func(r *http.Request) (sc SSHTemplateContext, err error) { user, err := panel.Dependencies.Auth.UserOf(r) @@ -114,7 +120,11 @@ func (panel *UserPanel) sshDeleteRoute(ctx context.Context) http.Handler { //go:embed "templates/ssh_add.html" var sshAddHTML []byte -var sshAddTemplate = templates.ParseForm("ssh_add.html", sshAddHTML, assets.AssetsUser) +var sshAddTemplate = templating.ParseForm( + "ssh_add.html", sshAddHTML, httpx.FormTemplate, + templating.Title("Add SSH Key"), + templating.Assets(assets.AssetsUser), +) type addKeyResult struct { User *auth.AuthUser @@ -123,13 +133,14 @@ type addKeyResult struct { } func (panel *UserPanel) sshAddRoute(ctx context.Context) http.Handler { - tpl := sshAddTemplate.Prepare(panel.Dependencies.Templating, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "User", Path: "/user/"}, - {Title: "SSH Keys", Path: "/user/ssh/"}, - {Title: "Add New Key", Path: "/user/ssh/add/"}, - }, - }) + tpl := sshAddTemplate.Prepare( + panel.Dependencies.Templating, + templating.Crumbs( + component.MenuItem{Title: "User", Path: "/user/"}, + component.MenuItem{Title: "SSH Keys", Path: "/user/ssh/"}, + component.MenuItem{Title: "Add New Key", Path: "/user/ssh/add/"}, + ), + ) return &httpx.Form[addKeyResult]{ Fields: []field.Field{ @@ -139,7 +150,7 @@ func (panel *UserPanel) sshAddRoute(ctx context.Context) http.Handler { FieldTemplate: field.PureCSSFieldTemplate, RenderTemplate: tpl.Template(), - RenderTemplateContext: templates.FormTemplateContext(tpl), + RenderTemplateContext: templating.FormTemplateContext(tpl), Validate: func(r *http.Request, values map[string]string) (ak addKeyResult, err error) { ak.User, err = panel.Dependencies.Auth.UserOf(r) diff --git a/internal/dis/component/auth/panel/templates/password.html b/internal/dis/component/auth/panel/templates/password.html index e85182d..e7c5cff 100644 --- a/internal/dis/component/auth/panel/templates/password.html +++ b/internal/dis/component/auth/panel/templates/password.html @@ -1,3 +1,2 @@ -{{ template "_form.html" . }} -{{ define "form/title" }}Change Password{{ end }} +{{ template "form.html" . }} {{ define "form/button" }}Update{{ end }} diff --git a/internal/dis/component/auth/panel/templates/ssh.html b/internal/dis/component/auth/panel/templates/ssh.html index 9f20edd..219ee29 100644 --- a/internal/dis/component/auth/panel/templates/ssh.html +++ b/internal/dis/component/auth/panel/templates/ssh.html @@ -1,8 +1,3 @@ -{{ template "_base.html" . }} -{{ define "title" }}SSH Keys{{ end }} - -{{ define "content" }} -

This page allows you to add, view and remove ssh keys to and from your distillery account. @@ -101,4 +96,3 @@ Host {{ .Domain }}.proxy

-{{ end }} \ No newline at end of file diff --git a/internal/dis/component/auth/panel/templates/ssh_add.html b/internal/dis/component/auth/panel/templates/ssh_add.html index 15ae52e..bb47420 100644 --- a/internal/dis/component/auth/panel/templates/ssh_add.html +++ b/internal/dis/component/auth/panel/templates/ssh_add.html @@ -1,7 +1,4 @@ -{{ template "_form.html" . }} -{{ define "form/title" }}Add SSH Key{{ end }} {{ define "form/button" }}Add{{ end }} - {{ define "form/inside" }}

diff --git a/internal/dis/component/auth/panel/templates/totp_disable.html b/internal/dis/component/auth/panel/templates/totp_disable.html index ef14439..8362d8b 100644 --- a/internal/dis/component/auth/panel/templates/totp_disable.html +++ b/internal/dis/component/auth/panel/templates/totp_disable.html @@ -1,5 +1,5 @@ -{{ template "_form.html" . }} -{{ define "form/title" }}Disable TOTP{{ end }} +{{ template "form.html" . }} + {{ define "form/button" }}Disable{{ end }} {{ define "form/inside" }} diff --git a/internal/dis/component/auth/panel/templates/totp_enable.html b/internal/dis/component/auth/panel/templates/totp_enable.html index dcb8363..0a6b2bb 100644 --- a/internal/dis/component/auth/panel/templates/totp_enable.html +++ b/internal/dis/component/auth/panel/templates/totp_enable.html @@ -1,5 +1,4 @@ -{{ template "_form.html" . }} -{{ define "form/title" }}Enable TOTP{{ end }} +{{ template "form.html" . }} {{ define "form/button" }}Enable{{ end }} {{ define "form/inside" }}

diff --git a/internal/dis/component/auth/panel/templates/totp_enroll.html b/internal/dis/component/auth/panel/templates/totp_enroll.html index 6625ad1..a0f08a3 100644 --- a/internal/dis/component/auth/panel/templates/totp_enroll.html +++ b/internal/dis/component/auth/panel/templates/totp_enroll.html @@ -1,5 +1,3 @@ -{{ template "_form.html" . }} -{{ define "form/title" }}Enable TOTP{{ end }} {{ define "form/button" }}Enable{{ end }} {{ define "form/inside" }}
diff --git a/internal/dis/component/auth/panel/templates/user.html b/internal/dis/component/auth/panel/templates/user.html index 00932d3..5e72c25 100644 --- a/internal/dis/component/auth/panel/templates/user.html +++ b/internal/dis/component/auth/panel/templates/user.html @@ -1,7 +1,3 @@ -{{ template "_base.html" . }} -{{ define "title" }}{{ .User.User }}{{ end }} - -{{ define "content" }}

    @@ -79,6 +75,4 @@
-
- -{{ end }} \ No newline at end of file +
\ No newline at end of file diff --git a/internal/dis/component/auth/panel/totp.go b/internal/dis/component/auth/panel/totp.go index 40c670b..02df299 100644 --- a/internal/dis/component/auth/panel/totp.go +++ b/internal/dis/component/auth/panel/totp.go @@ -8,7 +8,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" "github.com/FAU-CDI/wisski-distillery/pkg/httpx/field" @@ -17,7 +17,12 @@ import ( //go:embed "templates/totp_enable.html" var totpEnableHTML []byte -var totpEnable = templates.Parse[userFormContext]("totp_enable.html", totpEnableHTML, assets.AssetsUser) +var totpEnable = templating.Parse[userFormContext]( + "totp_enable.html", totpEnableHTML, httpx.FormTemplate, + + templating.Title("Enable TOTP"), + templating.Assets(assets.AssetsUser), +) func (panel *UserPanel) routeTOTPEnable(ctx context.Context) http.Handler { tpl := totpEnable.Prepare(panel.Dependencies.Templating) @@ -34,7 +39,7 @@ func (panel *UserPanel) routeTOTPEnable(ctx context.Context) http.Handler { }, RenderTemplate: tpl.Template(), - RenderTemplateContext: panel.UserFormContext2(tpl, component.MenuItem{Title: "Enable TOTP", Path: "/user/totp/enable/"}), + RenderTemplateContext: panel.UserFormContext(tpl, component.MenuItem{Title: "Enable TOTP", Path: "/user/totp/enable/"}), Validate: func(r *http.Request, values map[string]string) (struct{}, error) { password := values["password"] @@ -69,7 +74,12 @@ func (panel *UserPanel) routeTOTPEnable(ctx context.Context) http.Handler { //go:embed "templates/totp_enroll.html" var totpEnrollHTML []byte -var totpEnrollTemplate = templates.Parse[totpEnrollContext]("totp_enroll.html", totpEnrollHTML, assets.AssetsUser) +var totpEnrollTemplate = templating.Parse[totpEnrollContext]( + "totp_enroll.html", totpEnrollHTML, httpx.FormTemplate, + + templating.Title("Enable TOTP"), + templating.Assets(assets.AssetsUser), +) type totpEnrollContext struct { userFormContext @@ -80,12 +90,13 @@ type totpEnrollContext struct { } func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler { - tpl := totpEnrollTemplate.Prepare(panel.Dependencies.Templating, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "User", Path: "/user/"}, - {Title: "Enable TOTP", Path: "/user/totp/enable/"}, - }, - }) + tpl := totpEnrollTemplate.Prepare( + panel.Dependencies.Templating, + templating.Crumbs( + component.MenuItem{Title: "User", Path: "/user/"}, + component.MenuItem{Title: "Enable TOTP", Path: "/user/totp/enable/"}, + ), + ) return &httpx.Form[struct{}]{ Fields: []field.Field{ @@ -98,9 +109,7 @@ func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler { user, err := panel.Dependencies.Auth.UserOf(r) return struct{}{}, err == nil && user != nil && user.IsTOTPEnabled() }, - RenderForm: func(context httpx.FormContext, w http.ResponseWriter, r *http.Request) { - // TODO: Do we want to reuse the same function here? - + RenderTemplateContext: func(context httpx.FormContext, r *http.Request) any { user, err := panel.Dependencies.Auth.UserOf(r) ctx := totpEnrollContext{ @@ -120,8 +129,10 @@ func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler { ctx.TOTPURL = template.URL(secret.URL()) } } - tpl.Execute(w, r, ctx) + + return tpl.Context(r, ctx) }, + RenderTemplate: tpl.Template(), Validate: func(r *http.Request, values map[string]string) (struct{}, error) { password, otp := values["password"], values["otp"] @@ -156,7 +167,12 @@ func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler { //go:embed "templates/totp_disable.html" var totpDisableHTML []byte -var totpDisableTemplate = templates.Parse[userFormContext]("totp_disable.html", totpDisableHTML, assets.AssetsUser) +var totpDisableTemplate = templating.Parse[userFormContext]( + "totp_disable.html", totpDisableHTML, httpx.FormTemplate, + + templating.Title("Disable TOTP"), + templating.Assets(assets.AssetsUser), +) func (panel *UserPanel) routeTOTPDisable(ctx context.Context) http.Handler { tpl := totpDisableTemplate.Prepare(panel.Dependencies.Templating) @@ -173,7 +189,7 @@ func (panel *UserPanel) routeTOTPDisable(ctx context.Context) http.Handler { return struct{}{}, err == nil && user != nil && !user.IsTOTPEnabled() }, RenderTemplate: tpl.Template(), - RenderTemplateContext: panel.UserFormContext2(tpl, component.MenuItem{Title: "Disable TOTP", Path: "/user/totp/disable/"}), + RenderTemplateContext: panel.UserFormContext(tpl, component.MenuItem{Title: "Disable TOTP", Path: "/user/totp/disable/"}), Validate: func(r *http.Request, values map[string]string) (struct{}, error) { password, otp := values["password"], values["otp"] diff --git a/internal/dis/component/auth/panel/user.go b/internal/dis/component/auth/panel/user.go index d0650aa..066c6f0 100644 --- a/internal/dis/component/auth/panel/user.go +++ b/internal/dis/component/auth/panel/user.go @@ -10,16 +10,20 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/internal/models" ) //go:embed "templates/user.html" var userHTML []byte -var userTemplate = templates.Parse[userContext]("user.html", userHTML, assets.AssetsUser) +var userTemplate = templating.Parse[userContext]( + "user.html", userHTML, nil, + + templating.Assets(assets.AssetsUser), +) type userContext struct { - templates.BaseContext + templating.RuntimeFlags *auth.AuthUser Grants []GrantWithURL @@ -31,41 +35,47 @@ type GrantWithURL struct { } func (panel *UserPanel) routeUser(ctx context.Context) http.Handler { - tpl := userTemplate.Prepare(panel.Dependencies.Templating, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "User", Path: "/user/"}, - }, - Actions: []component.MenuItem{ - {Title: "Change Password", Path: "/user/password/"}, - {Title: "*to be replaced*", Path: ""}, - {Title: "SSH Keys", Path: "/user/ssh/"}, - }, - }) + tpl := userTemplate.Prepare( + panel.Dependencies.Templating, + templating.Crumbs( + component.MenuItem{Title: "User", Path: "/user/"}, + ), + templating.Actions( + component.MenuItem{Title: "Change Password", Path: "/user/password/"}, + component.DummyMenuItem, + component.MenuItem{Title: "SSH Keys", Path: "/user/ssh/"}, + ), + ) - return tpl.HTMLHandlerWithGaps(func(r *http.Request, gaps *templates.BaseContextGaps) (uc userContext, err error) { + return tpl.HTMLHandlerWithFlags(func(r *http.Request) (uc userContext, funcs []templating.FlagFunc, err error) { // find the user uc.AuthUser, err = panel.Dependencies.Auth.UserOf(r) if err != nil || uc.AuthUser == nil { - return uc, err + return uc, nil, err } - // build the gaps + // replace the totp action in the menu + var totpAction component.MenuItem if uc.AuthUser.IsTOTPEnabled() { - gaps.Actions[1] = component.MenuItem{ + totpAction = component.MenuItem{ Title: "Disable Passcode (TOTP)", Path: "/user/totp/disable/", } } else { - gaps.Actions[1] = component.MenuItem{ + totpAction = component.MenuItem{ Title: "Enable Passcode (TOTP)", Path: "/user/totp/enable/", } } + funcs = []templating.FlagFunc{ + templating.ReplaceAction(1, totpAction), + templating.Title(uc.AuthUser.User.User), + } // find the grants grants, err := panel.Dependencies.Policy.User(r.Context(), uc.AuthUser.User.User) if err != nil { - return uc, err + return uc, nil, err } uc.Grants = make([]GrantWithURL, len(grants)) @@ -74,11 +84,11 @@ func (panel *UserPanel) routeUser(ctx context.Context) http.Handler { url, err := panel.Dependencies.Next.Next(r.Context(), grant.Slug, "/") if err != nil { - return uc, err + return uc, nil, err } uc.Grants[i].URL = template.URL(url) } - return uc, err + return uc, funcs, err }) } diff --git a/internal/dis/component/auth/session.go b/internal/dis/component/auth/session.go index 46c7245..e8eada1 100644 --- a/internal/dis/component/auth/session.go +++ b/internal/dis/component/auth/session.go @@ -9,7 +9,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" "github.com/FAU-CDI/wisski-distillery/pkg/httpx/field" "github.com/gorilla/sessions" @@ -120,7 +120,12 @@ func (auth *Auth) Logout(w http.ResponseWriter, r *http.Request) error { //go:embed "login.html" var loginHTML []byte -var loginTemplate = templates.ParseForm("login.html", loginHTML, assets.AssetsUser) +var loginTemplate = templating.ParseForm( + "login.html", loginHTML, httpx.FormTemplate, + + templating.Title("Login Required"), + templating.Assets(assets.AssetsUser), +) var loginResponse = httpx.Response{ ContentType: "text/plain", @@ -131,7 +136,15 @@ var errLoginFailed = errors.New("Login failed") // authLogin implements a view to login a user func (auth *Auth) authLogin(ctx context.Context) http.Handler { - tpl := loginTemplate.Prepare(auth.Dependencies.Templating) + tpl := loginTemplate.Prepare( + auth.Dependencies.Templating, + func(flags templating.Flags, r *http.Request) templating.Flags { + flags.Crumbs = []component.MenuItem{ + {Title: "Login", Path: template.URL(r.URL.RequestURI())}, + } + return flags + }, + ) return &httpx.Form[*AuthUser]{ Fields: []field.Field{ @@ -141,16 +154,13 @@ func (auth *Auth) authLogin(ctx context.Context) http.Handler { }, FieldTemplate: field.PureCSSFieldTemplate, - RenderForm: func(context httpx.FormContext, w http.ResponseWriter, r *http.Request) { - if context.Err != nil { - context.Err = errLoginFailed + RenderTemplateContext: func(ctx httpx.FormContext, r *http.Request) any { + if ctx.Err != nil { + ctx.Err = errLoginFailed } - tpl.Execute(w, r, templates.BaseFormContext{FormContext: context}, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "Login", Path: template.URL(r.URL.RequestURI())}, - }, - }) + return tpl.Context(r, templating.NewFormContext(ctx)) }, + RenderTemplate: tpl.Template(), Validate: func(r *http.Request, values map[string]string) (*AuthUser, error) { username, password, passcode := values["username"], values["password"], values["otp"] diff --git a/internal/dis/component/menu.go b/internal/dis/component/menu.go index aded940..42da728 100644 --- a/internal/dis/component/menu.go +++ b/internal/dis/component/menu.go @@ -19,6 +19,12 @@ type MenuItem struct { Priority MenuPriority // menu priority } +// DummyMenuItem is a dummy menu item +// It should be replaced before being displayed to the user +var DummyMenuItem = MenuItem{ + Title: "* to be replaced *", +} + func MenuItemSort(a, b MenuItem) bool { return a.Priority < b.Priority } diff --git a/internal/dis/component/resolver/resolver.go b/internal/dis/component/resolver/resolver.go index 9460e2f..7be75a0 100644 --- a/internal/dis/component/resolver/resolver.go +++ b/internal/dis/component/resolver/resolver.go @@ -13,7 +13,8 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" + "github.com/FAU-CDI/wisski-distillery/pkg/httpx" "github.com/FAU-CDI/wisski-distillery/pkg/lazy" "github.com/rs/zerolog" @@ -24,7 +25,7 @@ type Resolver struct { component.Base Dependencies struct { Instances *instances.Instances - Templating *templates.Templating + Templating *templating.Templating Auth *auth.Auth } @@ -50,36 +51,30 @@ func (resolver *Resolver) Routes() component.Routes { //go:embed "resolver.html" var resolverHTML []byte -var resolverTemplate = templates.Parse[resolverContext]("resolver.html", resolverHTML, assets.AssetsDefault) +var resolverTemplate = templating.Parse[resolverContext]( + "resolver.html", resolverHTML, nil, + + templating.Title("Resolver"), + templating.Assets(assets.AssetsDefault), +) type resolverContext struct { - templates.BaseContext + templating.RuntimeFlags wdresolve.IndexContext } func (resolver *Resolver) HandleRoute(ctx context.Context, route string) (http.Handler, error) { - tpl := resolverTemplate.Prepare(resolver.Dependencies.Templating, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "Resolver", Path: "/wisski/get/"}, - }, - }) + // get the resolver template + tpl := resolverTemplate.Prepare( + resolver.Dependencies.Templating, + templating.Crumbs( + component.MenuItem{Title: "Resolver", Path: "/wisski/get/"}, + ), + ) + t := tpl.Template() + + // extract a logger and the fallback logger := zerolog.Ctx(ctx) - - var p wdresolve.ResolveHandler - var err error - - p.HandleIndex = func(context wdresolve.IndexContext, w http.ResponseWriter, r *http.Request) { - ctx := resolverContext{ - IndexContext: context, - } - if !resolver.Dependencies.Auth.Has(auth.User, r) { - ctx.IndexContext.Prefixes = nil - } - - tpl.Execute(w, r, ctx) - } - p.TrustXForwardedProto = true - fallback := &resolvers.Regexp{ Data: map[string]string{}, } @@ -97,12 +92,26 @@ func (resolver *Resolver) HandleRoute(ctx context.Context, route string) (http.H logger.Info().Str("name", domainName).Msg("registering legacy domain") } - // resolve the prefixes - p.Resolver = resolvers.InOrder{ - resolver, - fallback, + p := wdresolve.ResolveHandler{ + HandleIndex: func(context wdresolve.IndexContext, w http.ResponseWriter, r *http.Request) { + ctx := resolverContext{ + IndexContext: context, + } + if !resolver.Dependencies.Auth.Has(auth.User, r) { + ctx.IndexContext.Prefixes = nil + } + httpx.WriteHTML(tpl.Context(r, ctx), nil, t, "", w, r) + }, + + Resolver: resolvers.InOrder{ + resolver, + fallback, + }, + + TrustXForwardedProto: true, } - return p, err + + return p, nil } func (resolver *Resolver) Target(uri string) string { diff --git a/internal/dis/component/resolver/resolver.html b/internal/dis/component/resolver/resolver.html index d1b21db..9f5a012 100644 --- a/internal/dis/component/resolver/resolver.html +++ b/internal/dis/component/resolver/resolver.html @@ -1,7 +1,3 @@ -{{ template "_base.html" . }} -{{ define "title" }}Resolver{{ end }} - -{{ define "content" }}

This page contains the global distillery resolver. @@ -62,4 +58,3 @@

{{ end }} -{{ end }} \ No newline at end of file diff --git a/internal/dis/component/server/admin/admin.go b/internal/dis/component/server/admin/admin.go index 69f4633..de440c5 100644 --- a/internal/dis/component/server/admin/admin.go +++ b/internal/dis/component/server/admin/admin.go @@ -10,7 +10,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component/exporter" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/exporter/logger" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances/purger" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/julienschmidt/httprouter" "github.com/rs/zerolog" @@ -32,7 +32,7 @@ type Admin struct { Policy *policy.Policy - Templating *templates.Templating + Templating *templating.Templating Purger *purger.Purger } diff --git a/internal/dis/component/server/admin/components.go b/internal/dis/component/server/admin/components.go index 9ce9809..04a2dca 100644 --- a/internal/dis/component/server/admin/components.go +++ b/internal/dis/component/server/admin/components.go @@ -10,75 +10,71 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" - "github.com/FAU-CDI/wisski-distillery/internal/models" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" "github.com/FAU-CDI/wisski-distillery/pkg/lazy" "github.com/julienschmidt/httprouter" ) -//go:embed "html/components.html" -var componentsHTML []byte -var componentsTemplate = templates.Parse[componentContext]("components.html", componentsHTML, assets.AssetsAdmin) +//go:embed "html/anal.html" +var analHTML []byte +var analTemplate = templating.Parse[analContext]( + "anal.html", analHTML, nil, -type componentContext struct { - templates.BaseContext + templating.Assets(assets.AssetsAdmin), +) + +type analContext struct { + templating.RuntimeFlags Analytics lazy.PoolAnalytics } func (admin *Admin) components(ctx context.Context) http.Handler { - tpl := componentsTemplate.Prepare(admin.Dependencies.Templating, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "Admin", Path: "/admin/"}, - {Title: "Components", Path: "/admin/components/"}, - }, - }) + tpl := analTemplate.Prepare( + admin.Dependencies.Templating, + templating.Crumbs( + component.MenuItem{Title: "Admin", Path: "/admin/"}, + component.MenuItem{Title: "Components", Path: "/admin/components/"}, + ), + templating.Title("Components"), + ) - return tpl.HTMLHandler(func(r *http.Request) (cp componentContext, err error) { - cp.Analytics = *admin.Analytics + return tpl.HTMLHandler(func(r *http.Request) (ac analContext, err error) { + ac.Analytics = *admin.Analytics return }) } -//go:embed "html/ingredients.html" -var ingredientsHTML []byte -var ingredientsTemplate = templates.Parse[ingredientsContext]("ingredients.html", ingredientsHTML, assets.AssetsAdmin) - -type ingredientsContext struct { - templates.BaseContext - - Instance models.Instance - Analytics *lazy.PoolAnalytics -} - func (admin *Admin) ingredients(ctx context.Context) http.Handler { - tpl := ingredientsTemplate.Prepare(admin.Dependencies.Templating, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "Admin", Path: "/admin/"}, - {Title: "Instance", Path: "* to be updated *"}, - {Title: "Ingredients", Path: "* to be updated *"}, - }, - }) + tpl := analTemplate.Prepare( + admin.Dependencies.Templating, + templating.Crumbs( + component.MenuItem{Title: "Admin", Path: "/admin/"}, + component.DummyMenuItem, + component.DummyMenuItem, + ), + ) - return tpl.HTMLHandlerWithGaps(func(r *http.Request, gaps *templates.BaseContextGaps) (ic ingredientsContext, err error) { + return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ac analContext, funcs []templating.FlagFunc, err error) { slug := httprouter.ParamsFromContext(r.Context()).ByName("slug") - gaps.Crumbs[1] = component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + slug)} - gaps.Crumbs[2] = component.MenuItem{Title: "Ingredients", Path: template.URL("/admin/instance/" + slug + "/ingredients/")} - // find the instance itself! instance, err := admin.Dependencies.Instances.WissKI(r.Context(), slug) if err == instances.ErrWissKINotFound { - return ic, httpx.ErrNotFound + return ac, nil, httpx.ErrNotFound } if err != nil { - return ic, err + return ac, nil, err + } + funcs = []templating.FlagFunc{ + templating.ReplaceCrumb(1, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + slug)}), + templating.ReplaceCrumb(2, component.MenuItem{Title: "Ingredients", Path: template.URL("/admin/instance/" + slug + "/ingredients/")}), + templating.Title(instance.Name() + " - Ingredients"), } - ic.Instance = instance.Instance // and get the components - ic.Analytics = instance.Info().Analytics + ac.Analytics = *instance.Info().Analytics return }) diff --git a/internal/dis/component/server/admin/grants.go b/internal/dis/component/server/admin/grants.go index d8a0e78..cbc237f 100644 --- a/internal/dis/component/server/admin/grants.go +++ b/internal/dis/component/server/admin/grants.go @@ -10,7 +10,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/internal/models" "github.com/FAU-CDI/wisski-distillery/internal/wisski" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" @@ -22,10 +22,14 @@ import ( //go:embed "html/grants.html" var grantsHTML []byte -var grantsTemplate = templates.Parse[grantsContext]("grants.html", grantsHTML, assets.AssetsAdmin) +var grantsTemplate = templating.Parse[grantsContext]( + "grants.html", grantsHTML, nil, + + templating.Assets(assets.AssetsAdmin), +) type grantsContext struct { - templates.BaseContext + templating.RuntimeFlags Error string @@ -38,40 +42,43 @@ type grantsContext struct { } func (admin *Admin) grants(ctx context.Context) http.Handler { - tpl := grantsTemplate.Prepare(admin.Dependencies.Templating, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "Admin", Path: "/admin/"}, - {Title: "Instance", Path: "*to be updated*"}, - {Title: "Grants", Path: "*to be updated*"}, - }, - }) + tpl := grantsTemplate.Prepare( + admin.Dependencies.Templating, + templating.Crumbs( + component.MenuItem{Title: "Admin", Path: "/admin/"}, + component.DummyMenuItem, + component.DummyMenuItem, + ), + ) - return tpl.HTMLHandlerWithGaps(func(r *http.Request, gaps *templates.BaseContextGaps) (grantsContext, error) { + return tpl.HTMLHandlerWithFlags(func(r *http.Request) (grantsContext, []templating.FlagFunc, error) { if r.Method == http.MethodGet { - return admin.getGrants(r, gaps) + return admin.getGrants(r) } else { - return admin.postGrants(r, gaps) + return admin.postGrants(r) } }) } -func (admin *Admin) getGrants(r *http.Request, gaps *templates.BaseContextGaps) (gc grantsContext, err error) { +func (admin *Admin) getGrants(r *http.Request) (gc grantsContext, funcs []templating.FlagFunc, err error) { slug := httprouter.ParamsFromContext(r.Context()).ByName("slug") - if err := gc.use(r, gaps, slug, admin); err != nil { - return gc, err + + funcs, err = gc.use(r, slug, admin) + if err != nil { + return gc, nil, err } if err := gc.useGrants(r, admin); err != nil { - return gc, err + return gc, nil, err } - return gc, nil + return gc, funcs, nil } -func (admin *Admin) postGrants(r *http.Request, gaps *templates.BaseContextGaps) (gc grantsContext, err error) { +func (admin *Admin) postGrants(r *http.Request) (gc grantsContext, funcs []templating.FlagFunc, err error) { // parse the form if err := r.ParseForm(); err != nil { - return gc, err + return gc, nil, err } // read out the form values @@ -84,15 +91,16 @@ func (admin *Admin) postGrants(r *http.Request, gaps *templates.BaseContextGaps) ) // set the common fields - if err := gc.use(r, gaps, slug, admin); err != nil { - return gc, err + funcs, err = gc.use(r, slug, admin) + if err != nil { + return gc, nil, err } if delete { // delete the user grant err := admin.Dependencies.Policy.Remove(r.Context(), distilleryUser, slug) if err != nil { - return gc, err + return gc, nil, err } } else { // update the grant @@ -110,26 +118,29 @@ func (admin *Admin) postGrants(r *http.Request, gaps *templates.BaseContextGaps) // fetch the grants for the instance if err := gc.useGrants(r, admin); err != nil { - return gc, err + return gc, nil, err } - return gc, nil + return gc, funcs, nil } -func (gc *grantsContext) use(r *http.Request, gaps *templates.BaseContextGaps, slug string, admin *Admin) (err error) { - gaps.Crumbs[1] = component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + slug)} - gaps.Crumbs[2] = component.MenuItem{Title: "Grants", Path: template.URL("/admin/instance/" + slug + "/grants/")} - +func (gc *grantsContext) use(r *http.Request, slug string, admin *Admin) (funcs []templating.FlagFunc, err error) { // find the instance itself gc.instance, err = admin.Dependencies.Instances.WissKI(r.Context(), slug) if err == instances.ErrWissKINotFound { - return httpx.ErrNotFound + return nil, httpx.ErrNotFound } if err != nil { - return err + return nil, err } gc.Instance = gc.instance.Instance - return nil + // replace the functions + funcs = []templating.FlagFunc{ + templating.ReplaceCrumb(1, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + slug)}), + templating.ReplaceCrumb(2, component.MenuItem{Title: "Grants", Path: template.URL("/admin/instance/" + slug + "/grants/")}), + templating.Title(gc.Instance.Slug + " - Grants"), + } + return funcs, nil } func (gc *grantsContext) useGrants(r *http.Request, admin *Admin) (err error) { diff --git a/internal/dis/component/server/assets/templates/_anal.html b/internal/dis/component/server/admin/html/anal.html similarity index 100% rename from internal/dis/component/server/assets/templates/_anal.html rename to internal/dis/component/server/admin/html/anal.html diff --git a/internal/dis/component/server/admin/html/components.html b/internal/dis/component/server/admin/html/components.html deleted file mode 100644 index 51a1d66..0000000 --- a/internal/dis/component/server/admin/html/components.html +++ /dev/null @@ -1,6 +0,0 @@ -{{ template "_base.html" . }} -{{ define "title" }}Components{{ end }} - -{{ define "content" }} -{{ template "_anal.html" .Analytics }} -{{ end }} \ No newline at end of file diff --git a/internal/dis/component/server/admin/html/grants.html b/internal/dis/component/server/admin/html/grants.html index 77e8441..fa152aa 100644 --- a/internal/dis/component/server/admin/html/grants.html +++ b/internal/dis/component/server/admin/html/grants.html @@ -1,7 +1,3 @@ -{{ template "_base.html" . }} -{{ define "title" }}{{ .Instance.Slug }} - Grants{{ end }} - -{{ define "content" }} {{ $csrf := .CSRF }} {{ $slug := .Instance.Slug }}
@@ -123,5 +119,4 @@ {{ range $unused, $drupal := .Drupals }}
-{{end}} -{{ end }} \ No newline at end of file +{{end}} \ No newline at end of file diff --git a/internal/dis/component/server/admin/html/ingredients.html b/internal/dis/component/server/admin/html/ingredients.html deleted file mode 100644 index 45c543d..0000000 --- a/internal/dis/component/server/admin/html/ingredients.html +++ /dev/null @@ -1,6 +0,0 @@ -{{ template "_base.html" . }} -{{ define "title" }}{{ .Instance.Slug }} - Ingredients{{ end }} - -{{ define "content" }} -{{ template "_anal.html" .Analytics }} -{{ end }} \ No newline at end of file diff --git a/internal/dis/component/server/admin/html/instance.html b/internal/dis/component/server/admin/html/instance.html index 5bbf630..d102e13 100644 --- a/internal/dis/component/server/admin/html/instance.html +++ b/internal/dis/component/server/admin/html/instance.html @@ -1,7 +1,3 @@ -{{ template "_base.html" . }} -{{ define "title" }}{{ .Instance.Slug }}{{ end }} - -{{ define "content" }}

Info & Status

@@ -484,5 +480,4 @@ - -{{ end }} \ No newline at end of file + \ No newline at end of file diff --git a/internal/dis/component/server/admin/html/user_create.html b/internal/dis/component/server/admin/html/user_create.html index 6a11b9b..82d37cf 100644 --- a/internal/dis/component/server/admin/html/user_create.html +++ b/internal/dis/component/server/admin/html/user_create.html @@ -1,4 +1,2 @@ -{{ template "_form.html" . }} -{{ define "form/title" }}Create User{{ end }} +{{ template "form.html" . }} {{ define "form/button" }}Create{{ end }} - diff --git a/internal/dis/component/server/admin/html/users.html b/internal/dis/component/server/admin/html/users.html index 3ea1d2a..e51ca7b 100644 --- a/internal/dis/component/server/admin/html/users.html +++ b/internal/dis/component/server/admin/html/users.html @@ -1,8 +1,3 @@ -{{ template "_base.html" . }} -{{ define "title" }}Users{{ end }} - -{{ define "content" }} -
@@ -105,5 +100,3 @@
- -{{ end }} \ No newline at end of file diff --git a/internal/dis/component/server/admin/index.go b/internal/dis/component/server/admin/index.go index 34877ad..76fcba4 100644 --- a/internal/dis/component/server/admin/index.go +++ b/internal/dis/component/server/admin/index.go @@ -9,7 +9,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/internal/status" "golang.org/x/sync/errgroup" ) @@ -80,27 +80,33 @@ func (admin *Admin) Fetch(flags component.FetcherFlags, target *status.Distiller //go:embed "html/index.html" var indexHTML []byte -var indexTemplate = templates.Parse[indexContext]("index.html", indexHTML, assets.AssetsAdmin) +var indexTemplate = templating.Parse[indexContext]( + "index.html", indexHTML, nil, + + templating.Title("Admin"), + templating.Assets(assets.AssetsAdmin), +) type indexContext struct { - templates.BaseContext + templating.RuntimeFlags status.Distillery Instances []status.WissKI } func (admin *Admin) index(ctx context.Context) http.Handler { - tpl := indexTemplate.Prepare(admin.Dependencies.Templating, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "Admin", Path: "/admin/"}, - }, - Actions: []component.MenuItem{ - {Title: "Users", Path: "/admin/users/"}, - {Title: "Components", Path: "/admin/components/", Priority: component.SmallButton}, - }, - }) + tpl := indexTemplate.Prepare( + admin.Dependencies.Templating, + templating.Crumbs( + component.MenuItem{Title: "Admin", Path: "/admin/"}, + ), + templating.Actions( + component.MenuItem{Title: "Users", Path: "/admin/users/"}, + component.MenuItem{Title: "Components", Path: "/admin/components/", Priority: component.SmallButton}, + ), + ) - return tpl.HTMLHandlerWithGaps(func(r *http.Request, gaps *templates.BaseContextGaps) (idx indexContext, err error) { + return tpl.HTMLHandler(func(r *http.Request) (idx indexContext, err error) { idx.Distillery, idx.Instances, err = admin.Status(r.Context(), true) return }) diff --git a/internal/dis/component/server/admin/instance.go b/internal/dis/component/server/admin/instance.go index 539fa03..6f63aae 100644 --- a/internal/dis/component/server/admin/instance.go +++ b/internal/dis/component/server/admin/instance.go @@ -9,7 +9,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/internal/models" "github.com/FAU-CDI/wisski-distillery/internal/status" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" @@ -18,49 +18,57 @@ import ( //go:embed "html/instance.html" var instanceHTML []byte -var instanceTemplate = templates.Parse[instanceContext]("instance.html", instanceHTML, assets.AssetsAdmin) +var instanceTemplate = templating.Parse[instanceContext]( + "instance.html", instanceHTML, nil, + + templating.Assets(assets.AssetsAdmin), +) type instanceContext struct { - templates.BaseContext + templating.RuntimeFlags Instance models.Instance Info status.WissKI } func (admin *Admin) instance(ctx context.Context) http.Handler { - tpl := instanceTemplate.Prepare(admin.Dependencies.Templating, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "Admin", Path: "/admin/"}, - {Title: "Instance", Path: "*to be replaced*"}, - }, - Actions: []component.MenuItem{ - {Title: "Grants", Path: "*to be replaced*"}, - {Title: "Ingredients", Path: "*to be replaced*", Priority: component.SmallButton}, - }, - }) + tpl := instanceTemplate.Prepare( + admin.Dependencies.Templating, + templating.Crumbs( + component.MenuItem{Title: "Admin", Path: "/admin/"}, + component.DummyMenuItem, + ), + templating.Actions( + component.DummyMenuItem, + component.DummyMenuItem, + ), + ) - return tpl.HTMLHandlerWithGaps(func(r *http.Request, gaps *templates.BaseContextGaps) (ic instanceContext, err error) { + return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ic instanceContext, funcs []templating.FlagFunc, err error) { slug := httprouter.ParamsFromContext(r.Context()).ByName("slug") - gaps.Crumbs[1] = component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + slug)} - - gaps.Actions[0] = component.MenuItem{Title: "Grants", Path: template.URL("/admin/grants/" + slug)} - gaps.Actions[1] = component.MenuItem{Title: "Ingredients", Path: template.URL("/admin/ingredients/" + slug), Priority: component.SmallButton} - // find the instance itself! instance, err := admin.Dependencies.Instances.WissKI(r.Context(), slug) if err == instances.ErrWissKINotFound { - return ic, httpx.ErrNotFound + return ic, nil, httpx.ErrNotFound } if err != nil { - return ic, err + return ic, nil, err } ic.Instance = instance.Instance // get some more info about the wisski ic.Info, err = instance.Info().Information(r.Context(), false) if err != nil { - return ic, err + return ic, nil, err + } + + funcs = []templating.FlagFunc{ + templating.ReplaceCrumb(1, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + slug)}), + templating.ReplaceAction(0, component.MenuItem{Title: "Grants", Path: template.URL("/admin/grants/" + slug)}), + templating.ReplaceAction(1, component.MenuItem{Title: "Ingredients", Path: template.URL("/admin/ingredients/" + slug), Priority: component.SmallButton}), + + templating.Title(instance.Name()), } return diff --git a/internal/dis/component/server/admin/users.go b/internal/dis/component/server/admin/users.go index be77ba1..13db73e 100644 --- a/internal/dis/component/server/admin/users.go +++ b/internal/dis/component/server/admin/users.go @@ -11,7 +11,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templates" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" "github.com/FAU-CDI/wisski-distillery/pkg/httpx/field" "github.com/rs/zerolog" @@ -19,25 +19,30 @@ import ( //go:embed "html/users.html" var usersHTML []byte -var usersTemplate = templates.Parse[usersContext]("user.html", usersHTML, assets.AssetsAdmin) +var usersTemplate = templating.Parse[usersContext]( + "users.html", usersHTML, nil, + + templating.Title("Users"), + templating.Assets(assets.AssetsAdmin), +) type usersContext struct { - templates.BaseContext - + templating.RuntimeFlags Error string Users []*auth.AuthUser } func (admin *Admin) users(ctx context.Context) http.Handler { - tpl := usersTemplate.Prepare(admin.Dependencies.Templating, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "Admin", Path: "/admin/"}, - {Title: "Users", Path: "/admin/users/"}, - }, - Actions: []component.MenuItem{ - {Title: "Create New", Path: "/admin/users/create/"}, - }, - }) + tpl := usersTemplate.Prepare( + admin.Dependencies.Templating, + templating.Crumbs( + component.MenuItem{Title: "Admin", Path: "/admin/"}, + component.MenuItem{Title: "Users", Path: "/admin/users/"}, + ), + templating.Actions( + component.MenuItem{Title: "Create New", Path: "/admin/users/create/"}, + ), + ) return tpl.HTMLHandler(func(r *http.Request) (uc usersContext, err error) { uc.Error = r.URL.Query().Get("error") @@ -48,7 +53,12 @@ func (admin *Admin) users(ctx context.Context) http.Handler { //go:embed "html/user_create.html" var userCreateHTML []byte -var userCreateTemplate = templates.ParseForm("user_create.html", userCreateHTML, assets.AssetsAdmin) +var userCreateTemplate = templating.ParseForm( + "user_create.html", userCreateHTML, httpx.FormTemplate, + + templating.Title("Create User"), + templating.Assets(assets.AssetsAdmin), +) var ( errCreateInvalidUsername = errors.New("invalid username") @@ -62,13 +72,14 @@ type createUserResult struct { } func (admin *Admin) createUser(ctx context.Context) http.Handler { - tpl := userCreateTemplate.Prepare(admin.Dependencies.Templating, templates.BaseContextGaps{ - Crumbs: []component.MenuItem{ - {Title: "Admin", Path: "/admin/"}, - {Title: "Users", Path: "/admin/users"}, - {Title: "Create", Path: "/admin/users/create"}, - }, - }) + tpl := userCreateTemplate.Prepare( + admin.Dependencies.Templating, + templating.Crumbs( + component.MenuItem{Title: "Admin", Path: "/admin/"}, + component.MenuItem{Title: "Users", Path: "/admin/users"}, + component.MenuItem{Title: "Create", Path: "/admin/users/create"}, + ), + ) return &httpx.Form[createUserResult]{ Fields: []field.Field{ @@ -79,7 +90,7 @@ func (admin *Admin) createUser(ctx context.Context) http.Handler { FieldTemplate: field.PureCSSFieldTemplate, RenderTemplate: tpl.Template(), - RenderTemplateContext: templates.FormTemplateContext(tpl), + RenderTemplateContext: templating.FormTemplateContext(tpl), Validate: func(r *http.Request, values map[string]string) (cu createUserResult, err error) { cu.User, cu.Passsword, cu.Admin = values["username"], values["password"], values["admin"] == field.CheckboxChecked diff --git a/internal/dis/component/server/assets/assets.go b/internal/dis/component/server/assets/assets.go index 5d28cba..8d1b2d8 100644 --- a/internal/dis/component/server/assets/assets.go +++ b/internal/dis/component/server/assets/assets.go @@ -17,32 +17,8 @@ import ( // // Each asset group should be registered as a parameter to the 'go:generate' line. type Assets struct { - Scripts string //