templating: Move actions into template
This commit is contained in:
parent
313af2b9e3
commit
202599aaeb
23 changed files with 153 additions and 105 deletions
|
|
@ -84,8 +84,8 @@ type userFormContext struct {
|
||||||
User *models.User
|
User *models.User
|
||||||
}
|
}
|
||||||
|
|
||||||
func (panel *UserPanel) UserFormContext(last component.MenuItem) func(ctx httpx.FormContext, r *http.Request) any {
|
func (panel *UserPanel) UserFormContext(last component.MenuItem, gaps custom.BaseContextGaps) func(ctx httpx.FormContext, r *http.Request) any {
|
||||||
crumbs := []component.MenuItem{
|
gaps.Crumbs = []component.MenuItem{
|
||||||
{Title: "User", Path: "/user/"},
|
{Title: "User", Path: "/user/"},
|
||||||
last,
|
last,
|
||||||
}
|
}
|
||||||
|
|
@ -93,7 +93,7 @@ func (panel *UserPanel) UserFormContext(last component.MenuItem) func(ctx httpx.
|
||||||
user, err := panel.Dependencies.Auth.UserOf(r)
|
user, err := panel.Dependencies.Auth.UserOf(r)
|
||||||
|
|
||||||
uctx := userFormContext{FormContext: ctx}
|
uctx := userFormContext{FormContext: ctx}
|
||||||
panel.Dependencies.Custom.Update(&uctx, r, crumbs)
|
panel.Dependencies.Custom.Update(&uctx, r, gaps)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
uctx.User = &user.User
|
uctx.User = &user.User
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static/custom"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
|
||||||
)
|
)
|
||||||
|
|
@ -38,7 +39,7 @@ func (panel *UserPanel) routePassword(ctx context.Context) http.Handler {
|
||||||
FieldTemplate: field.PureCSSFieldTemplate,
|
FieldTemplate: field.PureCSSFieldTemplate,
|
||||||
|
|
||||||
RenderTemplate: passwordTemplate,
|
RenderTemplate: passwordTemplate,
|
||||||
RenderTemplateContext: panel.UserFormContext(component.MenuItem{Title: "Change Password", Path: "/user/password/"}),
|
RenderTemplateContext: panel.UserFormContext(component.MenuItem{Title: "Change Password", Path: "/user/password/"}, custom.BaseContextGaps{}),
|
||||||
|
|
||||||
Validate: func(r *http.Request, values map[string]string) (struct{}, error) {
|
Validate: func(r *http.Request, values map[string]string) (struct{}, error) {
|
||||||
old, passcode, new, new2 := values["old"], values["otp"], values["new"], values["new2"]
|
old, passcode, new, new2 := values["old"], values["otp"], values["new"], values["new2"]
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,6 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
</p>
|
</p>
|
||||||
<div class="pure-button-group" role="group" role="Actions">
|
|
||||||
<a class="pure-button" href="/user/password/">Change Password</a>
|
|
||||||
{{ if .User.IsTOTPEnabled }}
|
|
||||||
<a class="pure-button" href="/user/totp/disable/">Disable Passcode (TOTP)</a>
|
|
||||||
{{ else }}
|
|
||||||
<a class="pure-button" href="/user/totp/enable/">Enable Passcode (TOTP)</a>
|
|
||||||
{{ end }}
|
|
||||||
</div>
|
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -34,14 +26,11 @@
|
||||||
{{ if (not .User.IsTOTPEnabled) }}
|
{{ if (not .User.IsTOTPEnabled) }}
|
||||||
<div>
|
<div>
|
||||||
<p class="error-message">
|
<p class="error-message">
|
||||||
TOTP is required to access these.
|
You are an administrator, but do not have TOTP enabled.
|
||||||
|
Please turn it on to access the admin page.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="pure-button-group" role="group" role="Actions">
|
|
||||||
<a class="pure-button" href="/admin/">Distillery Admin Page</a>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
"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/auth"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static/custom"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
|
||||||
|
|
||||||
|
|
@ -33,7 +34,7 @@ func (panel *UserPanel) routeTOTPEnable(ctx context.Context) http.Handler {
|
||||||
},
|
},
|
||||||
|
|
||||||
RenderTemplate: totpEnableTemplate,
|
RenderTemplate: totpEnableTemplate,
|
||||||
RenderTemplateContext: panel.UserFormContext(component.MenuItem{Title: "Enable TOTP", Path: "/user/totp/enable/"}),
|
RenderTemplateContext: panel.UserFormContext(component.MenuItem{Title: "Enable TOTP", Path: "/user/totp/enable/"}, custom.BaseContextGaps{}),
|
||||||
|
|
||||||
Validate: func(r *http.Request, values map[string]string) (struct{}, error) {
|
Validate: func(r *http.Request, values map[string]string) (struct{}, error) {
|
||||||
password := values["password"]
|
password := values["password"]
|
||||||
|
|
@ -78,9 +79,11 @@ type totpEnrollContext struct {
|
||||||
|
|
||||||
func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler {
|
func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler {
|
||||||
totpEnrollTemplate := panel.Dependencies.Custom.Template(totpEnrollTemplate)
|
totpEnrollTemplate := panel.Dependencies.Custom.Template(totpEnrollTemplate)
|
||||||
crumbs := []component.MenuItem{
|
gaps := custom.BaseContextGaps{
|
||||||
{Title: "User", Path: "/user/"},
|
Crumbs: []component.MenuItem{
|
||||||
{Title: "Enable TOTP", Path: "/user/totp/enable/"},
|
{Title: "User", Path: "/user/"},
|
||||||
|
{Title: "Enable TOTP", Path: "/user/totp/enable/"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return &httpx.Form[struct{}]{
|
return &httpx.Form[struct{}]{
|
||||||
Fields: []field.Field{
|
Fields: []field.Field{
|
||||||
|
|
@ -103,7 +106,7 @@ func (panel *UserPanel) routeTOTPEnroll(ctx context.Context) http.Handler {
|
||||||
FormContext: context,
|
FormContext: context,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
panel.Dependencies.Custom.Update(&ctx.userFormContext, r, crumbs)
|
panel.Dependencies.Custom.Update(&ctx.userFormContext, r, gaps)
|
||||||
|
|
||||||
if err == nil && user != nil {
|
if err == nil && user != nil {
|
||||||
ctx.userFormContext.User = &user.User
|
ctx.userFormContext.User = &user.User
|
||||||
|
|
@ -168,7 +171,7 @@ func (panel *UserPanel) routeTOTPDisable(ctx context.Context) http.Handler {
|
||||||
return struct{}{}, err == nil && user != nil && !user.IsTOTPEnabled()
|
return struct{}{}, err == nil && user != nil && !user.IsTOTPEnabled()
|
||||||
},
|
},
|
||||||
RenderTemplate: totpDisableTemplate,
|
RenderTemplate: totpDisableTemplate,
|
||||||
RenderTemplateContext: panel.UserFormContext(component.MenuItem{Title: "Disable TOTP", Path: "/user/totp/disable/"}),
|
RenderTemplateContext: panel.UserFormContext(component.MenuItem{Title: "Disable TOTP", Path: "/user/totp/disable/"}, custom.BaseContextGaps{}),
|
||||||
|
|
||||||
Validate: func(r *http.Request, values map[string]string) (struct{}, error) {
|
Validate: func(r *http.Request, values map[string]string) (struct{}, error) {
|
||||||
password, otp := values["password"], values["otp"]
|
password, otp := values["password"], values["otp"]
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,17 @@ type GrantWithURL struct {
|
||||||
|
|
||||||
func (panel *UserPanel) routeUser(ctx context.Context) http.Handler {
|
func (panel *UserPanel) routeUser(ctx context.Context) http.Handler {
|
||||||
userTemplate := panel.Dependencies.Custom.Template(userTemplate)
|
userTemplate := panel.Dependencies.Custom.Template(userTemplate)
|
||||||
crumbs := []component.MenuItem{
|
gaps := custom.BaseContextGaps{
|
||||||
{Title: "User", Path: "/user/"},
|
Crumbs: []component.MenuItem{
|
||||||
|
{Title: "User", Path: "/user/"},
|
||||||
|
},
|
||||||
|
Actions: []component.MenuItem{
|
||||||
|
{Title: "Change Password", Path: "/user/password"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return &httpx.HTMLHandler[routeUserContext]{
|
return &httpx.HTMLHandler[routeUserContext]{
|
||||||
Handler: func(r *http.Request) (ruc routeUserContext, err error) {
|
Handler: func(r *http.Request) (ruc routeUserContext, err error) {
|
||||||
panel.Dependencies.Custom.Update(&ruc, r, crumbs)
|
|
||||||
|
|
||||||
// find the user
|
// find the user
|
||||||
ruc.AuthUser, err = panel.Dependencies.Auth.UserOf(r)
|
ruc.AuthUser, err = panel.Dependencies.Auth.UserOf(r)
|
||||||
|
|
@ -50,6 +54,21 @@ func (panel *UserPanel) routeUser(ctx context.Context) http.Handler {
|
||||||
return ruc, err
|
return ruc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// build the gaps
|
||||||
|
gaps := gaps.Clone()
|
||||||
|
if ruc.AuthUser.IsTOTPEnabled() {
|
||||||
|
gaps.Actions = append(gaps.Actions, component.MenuItem{
|
||||||
|
Title: "Disable Passcode (TOTP)",
|
||||||
|
Path: "/user/totp/disable/",
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
gaps.Actions = append(gaps.Actions, component.MenuItem{
|
||||||
|
Title: "Enable Passcode (TOTP)",
|
||||||
|
Path: "/user/totp/enable/",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
panel.Dependencies.Custom.Update(&ruc, r, gaps)
|
||||||
|
|
||||||
// find the grants
|
// find the grants
|
||||||
grants, err := panel.Dependencies.Policy.User(r.Context(), ruc.AuthUser.User.User)
|
grants, err := panel.Dependencies.Policy.User(r.Context(), ruc.AuthUser.User.User)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
||||||
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static/custom"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx/field"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
|
|
@ -144,8 +145,10 @@ func (auth *Auth) authLogin(ctx context.Context) http.Handler {
|
||||||
if context.Err != nil {
|
if context.Err != nil {
|
||||||
context.Err = errLoginFailed
|
context.Err = errLoginFailed
|
||||||
}
|
}
|
||||||
httpx.WriteHTML(auth.Dependencies.Custom.NewForm(context, r, []component.MenuItem{
|
httpx.WriteHTML(auth.Dependencies.Custom.NewForm(context, r, custom.BaseContextGaps{
|
||||||
{Title: "Login", Path: template.URL(r.URL.RequestURI())},
|
Crumbs: []component.MenuItem{
|
||||||
|
{Title: "Login", Path: template.URL(r.URL.RequestURI())},
|
||||||
|
},
|
||||||
}), nil, loginTemplate, "", w, r)
|
}), nil, loginTemplate, "", w, r)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,11 @@ type componentContext struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (admin *Admin) components(r *http.Request) (cp componentContext, err error) {
|
func (admin *Admin) components(r *http.Request) (cp componentContext, err error) {
|
||||||
admin.Dependencies.Custom.Update(&cp, r, []component.MenuItem{
|
admin.Dependencies.Custom.Update(&cp, r, custom.BaseContextGaps{
|
||||||
{Title: "Admin", Path: "/admin/"},
|
Crumbs: []component.MenuItem{
|
||||||
{Title: "Components", Path: "/admin/components/"},
|
{Title: "Admin", Path: "/admin/"},
|
||||||
|
{Title: "Components", Path: "/admin/components/"},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
cp.Analytics = *admin.Analytics
|
cp.Analytics = *admin.Analytics
|
||||||
|
|
@ -56,10 +58,12 @@ type ingredientsContext struct {
|
||||||
func (admin *Admin) ingredients(r *http.Request) (cp ingredientsContext, err error) {
|
func (admin *Admin) ingredients(r *http.Request) (cp ingredientsContext, err error) {
|
||||||
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||||
|
|
||||||
admin.Dependencies.Custom.Update(&cp, r, []component.MenuItem{
|
admin.Dependencies.Custom.Update(&cp, r, custom.BaseContextGaps{
|
||||||
{Title: "Admin", Path: "/admin/"},
|
Crumbs: []component.MenuItem{
|
||||||
{Title: "Instance", Path: template.URL("/admin/instance/" + slug)},
|
{Title: "Admin", Path: "/admin/"},
|
||||||
{Title: "Ingredients", Path: template.URL("/admin/instance/" + slug + "/ingredients/")},
|
{Title: "Instance", Path: template.URL("/admin/instance/" + slug)},
|
||||||
|
{Title: "Ingredients", Path: template.URL("/admin/instance/" + slug + "/ingredients/")},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// find the instance itself!
|
// find the instance itself!
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,12 @@ type grantsContext struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gc *grantsContext) use(r *http.Request, slug string, admin *Admin) (err error) {
|
func (gc *grantsContext) use(r *http.Request, slug string, admin *Admin) (err error) {
|
||||||
admin.Dependencies.Custom.Update(gc, r, []component.MenuItem{
|
admin.Dependencies.Custom.Update(gc, r, custom.BaseContextGaps{
|
||||||
{Title: "Admin", Path: "/admin/"},
|
Crumbs: []component.MenuItem{
|
||||||
{Title: "Instance", Path: template.URL("/admin/instance/" + slug)},
|
{Title: "Admin", Path: "/admin/"},
|
||||||
{Title: "Grants", Path: template.URL("/admin/instance/" + slug + "/grants/")},
|
{Title: "Instance", Path: template.URL("/admin/instance/" + slug)},
|
||||||
|
{Title: "Grants", Path: template.URL("/admin/instance/" + slug + "/grants/")},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// find the instance itself
|
// find the instance itself
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,6 @@
|
||||||
{{ template "_base.html" . }}
|
{{ template "_base.html" . }}
|
||||||
{{ define "title" }}Admin{{ end }}
|
{{ define "title" }}Admin{{ end }}
|
||||||
|
|
||||||
{{ define "header" }}
|
|
||||||
<p>
|
|
||||||
<div class="pure-button-group" role="group" aria-label="Actions">
|
|
||||||
<a class="pure-button" href="/admin/users">Users</a>
|
|
||||||
<a class="pure-button pure-button-small" href="/admin/components">Components</a>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="pure-u-1-1">
|
<div class="pure-u-1-1">
|
||||||
<h2 id="overview">Distillery Configuration</h2>
|
<h2 id="overview">Distillery Configuration</h2>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,6 @@
|
||||||
{{ template "_base.html" . }}
|
{{ template "_base.html" . }}
|
||||||
{{ define "title" }}{{ .Instance.Slug }}{{ end }}
|
{{ define "title" }}{{ .Instance.Slug }}{{ end }}
|
||||||
|
|
||||||
{{ define "header" }}
|
|
||||||
<p>
|
|
||||||
<div class="pure-button-group" role="group" aria-label="Actions">
|
|
||||||
<a class="pure-button" href="/admin/grants/{{ .Info.Slug }}">Grants</a>
|
|
||||||
<a class="pure-button pure-button-small" href="/admin/ingredients/{{ .Instance.Slug }}">Ingredients</a>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="pure-u-1-1">
|
<div class="pure-u-1-1">
|
||||||
<h2 id="overview">Info & Status</h2>
|
<h2 id="overview">Info & Status</h2>
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,5 @@
|
||||||
{{ template "_base.html" . }}
|
{{ template "_base.html" . }}
|
||||||
{{ define "title" }}Admin Users{{ end }}
|
{{ define "title" }}Users{{ end }}
|
||||||
|
|
||||||
{{ define "header" }}
|
|
||||||
<p>
|
|
||||||
<div class="pure-button-group" role="group" aria-label="Actions">
|
|
||||||
<a class="pure-button" href="/admin/users/create">Create New</a>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,14 @@ type indexContext struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (admin *Admin) index(r *http.Request) (idx indexContext, err error) {
|
func (admin *Admin) index(r *http.Request) (idx indexContext, err error) {
|
||||||
admin.Dependencies.Custom.Update(&idx, r, []component.MenuItem{
|
admin.Dependencies.Custom.Update(&idx, r, custom.BaseContextGaps{
|
||||||
{Title: "Admin", Path: "/admin/"},
|
Crumbs: []component.MenuItem{
|
||||||
|
{Title: "Admin", Path: "/admin/"},
|
||||||
|
},
|
||||||
|
Actions: []component.MenuItem{
|
||||||
|
{Title: "Users", Path: "/admin/users/"},
|
||||||
|
{Title: "Components", Path: "/admin/components/", Priority: component.SmallButton},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
idx.Distillery, idx.Instances, err = admin.Status(r.Context(), true)
|
idx.Distillery, idx.Instances, err = admin.Status(r.Context(), true)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,15 @@ type instanceContext struct {
|
||||||
func (admin *Admin) instance(r *http.Request) (is instanceContext, err error) {
|
func (admin *Admin) instance(r *http.Request) (is instanceContext, err error) {
|
||||||
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||||
|
|
||||||
admin.Dependencies.Custom.Update(&is, r, []component.MenuItem{
|
admin.Dependencies.Custom.Update(&is, r, custom.BaseContextGaps{
|
||||||
{Title: "Admin", Path: "/admin/"},
|
Crumbs: []component.MenuItem{
|
||||||
{Title: "Instance", Path: template.URL("/admin/instance/" + slug)},
|
{Title: "Admin", Path: "/admin/"},
|
||||||
|
{Title: "Instance", Path: template.URL("/admin/instance/" + slug)},
|
||||||
|
},
|
||||||
|
Actions: []component.MenuItem{
|
||||||
|
{Title: "Grants", Path: template.URL("/admin/grants/" + slug)},
|
||||||
|
{Title: "Ingredients", Path: template.URL("/admin/ingredients/" + slug), Priority: component.SmallButton},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// find the instance itself!
|
// find the instance itself!
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,11 @@ type userContext struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (admin *Admin) users(r *http.Request) (uc userContext, err error) {
|
func (admin *Admin) users(r *http.Request) (uc userContext, err error) {
|
||||||
admin.Dependencies.Custom.Update(&uc, r, []component.MenuItem{
|
admin.Dependencies.Custom.Update(&uc, r, custom.BaseContextGaps{
|
||||||
{Title: "Admin", Path: "/admin/"},
|
Crumbs: []component.MenuItem{
|
||||||
{Title: "Users", Path: "/admin/users/"},
|
{Title: "Admin", Path: "/admin/"},
|
||||||
|
{Title: "Users", Path: "/admin/users/"},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
uc.Error = r.URL.Query().Get("error")
|
uc.Error = r.URL.Query().Get("error")
|
||||||
|
|
@ -62,10 +64,15 @@ type createUserResult struct {
|
||||||
|
|
||||||
func (admin *Admin) createUser(ctx context.Context) http.Handler {
|
func (admin *Admin) createUser(ctx context.Context) http.Handler {
|
||||||
userCreateTemplate := admin.Dependencies.Custom.Template(userCreateTemplate)
|
userCreateTemplate := admin.Dependencies.Custom.Template(userCreateTemplate)
|
||||||
crumbs := []component.MenuItem{
|
gaps := custom.BaseContextGaps{
|
||||||
{Title: "Admin", Path: "/admin/"},
|
Crumbs: []component.MenuItem{
|
||||||
{Title: "Users", Path: "/admin/users"},
|
{Title: "Admin", Path: "/admin/"},
|
||||||
{Title: "Create", Path: "/admin/users/create"},
|
{Title: "Users", Path: "/admin/users"},
|
||||||
|
{Title: "Create", Path: "/admin/users/create"},
|
||||||
|
},
|
||||||
|
Actions: []component.MenuItem{
|
||||||
|
{Title: "Create New", Path: "/admin/users/create/"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return &httpx.Form[createUserResult]{
|
return &httpx.Form[createUserResult]{
|
||||||
|
|
@ -78,7 +85,7 @@ func (admin *Admin) createUser(ctx context.Context) http.Handler {
|
||||||
|
|
||||||
RenderTemplate: userCreateTemplate,
|
RenderTemplate: userCreateTemplate,
|
||||||
RenderTemplateContext: func(ctx httpx.FormContext, r *http.Request) any {
|
RenderTemplateContext: func(ctx httpx.FormContext, r *http.Request) any {
|
||||||
return admin.Dependencies.Custom.NewForm(ctx, r, crumbs)
|
return admin.Dependencies.Custom.NewForm(ctx, r, gaps)
|
||||||
},
|
},
|
||||||
|
|
||||||
Validate: func(r *http.Request, values map[string]string) (cu createUserResult, err error) {
|
Validate: func(r *http.Request, values map[string]string) (cu createUserResult, err error) {
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,10 @@ type publicContext struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (home *Home) publicHandler(ctx context.Context) http.Handler {
|
func (home *Home) publicHandler(ctx context.Context) http.Handler {
|
||||||
crumbs := []component.MenuItem{
|
gaps := custom.BaseContextGaps{
|
||||||
{Title: "WissKI Distillery", Path: "/"},
|
Crumbs: []component.MenuItem{
|
||||||
|
{Title: "WissKI Distillery", Path: "/"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return httpx.HTMLHandler[publicContext]{
|
return httpx.HTMLHandler[publicContext]{
|
||||||
Handler: func(r *http.Request) (pc publicContext, err error) {
|
Handler: func(r *http.Request) (pc publicContext, err error) {
|
||||||
|
|
@ -35,7 +37,7 @@ func (home *Home) publicHandler(ctx context.Context) http.Handler {
|
||||||
return pc, httpx.ErrNotFound
|
return pc, httpx.ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
home.Dependencies.Custom.Update(&pc, r, crumbs)
|
home.Dependencies.Custom.Update(&pc, r, gaps)
|
||||||
|
|
||||||
pc.Instances = home.homeInstances.Get(nil)
|
pc.Instances = home.homeInstances.Get(nil)
|
||||||
pc.SelfRedirect = home.Config.SelfRedirect.String()
|
pc.SelfRedirect = home.Config.SelfRedirect.String()
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,10 @@ type legalContext struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (legal *Legal) context(r *http.Request) (lc legalContext, err error) {
|
func (legal *Legal) context(r *http.Request) (lc legalContext, err error) {
|
||||||
legal.Dependencies.Custom.Update(&lc, r, []component.MenuItem{
|
legal.Dependencies.Custom.Update(&lc, r, custom.BaseContextGaps{
|
||||||
{Title: "Legal", Path: "/legal/"},
|
Crumbs: []component.MenuItem{
|
||||||
|
{Title: "Legal", Path: "/legal/"},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
lc.LegalNotices = cli.LegalNotices
|
lc.LegalNotices = cli.LegalNotices
|
||||||
|
|
|
||||||
|
|
@ -123,8 +123,10 @@ type newsContext struct {
|
||||||
|
|
||||||
// HandleRoute returns the handler for the requested path
|
// HandleRoute returns the handler for the requested path
|
||||||
func (news *News) HandleRoute(ctx context.Context, path string) (http.Handler, error) {
|
func (news *News) HandleRoute(ctx context.Context, path string) (http.Handler, error) {
|
||||||
crumbs := []component.MenuItem{
|
gaps := custom.BaseContextGaps{
|
||||||
{Title: "News", Path: "/news/"},
|
Crumbs: []component.MenuItem{
|
||||||
|
{Title: "News", Path: "/news/"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
items, itemsErr := Items()
|
items, itemsErr := Items()
|
||||||
|
|
@ -134,7 +136,7 @@ func (news *News) HandleRoute(ctx context.Context, path string) (http.Handler, e
|
||||||
|
|
||||||
return httpx.HTMLHandler[newsContext]{
|
return httpx.HTMLHandler[newsContext]{
|
||||||
Handler: func(r *http.Request) (nc newsContext, err error) {
|
Handler: func(r *http.Request) (nc newsContext, err error) {
|
||||||
news.Dependencies.Custom.Update(&nc, r, crumbs)
|
news.Dependencies.Custom.Update(&nc, r, gaps)
|
||||||
nc.Items, err = items, itemsErr
|
nc.Items, err = items, itemsErr
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ type BaseContext struct {
|
||||||
GeneratedAt time.Time // time this page was generated at
|
GeneratedAt time.Time // time this page was generated at
|
||||||
|
|
||||||
// Menu and breadcrumbs
|
// Menu and breadcrumbs
|
||||||
Menu []component.MenuItem
|
Menu []component.MenuItem
|
||||||
Crumbs []component.MenuItem
|
BaseContextGaps
|
||||||
|
|
||||||
CSRF template.HTML // CSRF Field
|
CSRF template.HTML // CSRF Field
|
||||||
}
|
}
|
||||||
|
|
@ -43,12 +43,24 @@ const (
|
||||||
requestNilError template.HTML = errorPrefix + "<code>BaseContext.use()</code> called with nil request" + errorSuffix
|
requestNilError template.HTML = errorPrefix + "<code>BaseContext.use()</code> called with nil request" + errorSuffix
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type BaseContextGaps struct {
|
||||||
|
Crumbs []component.MenuItem
|
||||||
|
Actions []component.MenuItem
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bcg BaseContextGaps) Clone() BaseContextGaps {
|
||||||
|
return BaseContextGaps{
|
||||||
|
Crumbs: slices.Clone(bcg.Crumbs),
|
||||||
|
Actions: slices.Clone(bcg.Actions),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Use updates this context to use the values from the given base.
|
// Use updates this context to use the values from the given base.
|
||||||
//
|
//
|
||||||
// The given request *must not* be nil.
|
// The given request *must not* be nil.
|
||||||
//
|
//
|
||||||
// For convenience the passed context is also returned.
|
// For convenience the passed context is also returned.
|
||||||
func (tc *BaseContext) use(custom *Custom, r *http.Request, crumbs []component.MenuItem) *BaseContext {
|
func (tc *BaseContext) use(custom *Custom, r *http.Request, gaps BaseContextGaps) *BaseContext {
|
||||||
// tc.custom = custom
|
// tc.custom = custom
|
||||||
tc.inited = true
|
tc.inited = true
|
||||||
tc.requestWasNil = r == nil
|
tc.requestWasNil = r == nil
|
||||||
|
|
@ -65,7 +77,7 @@ func (tc *BaseContext) use(custom *Custom, r *http.Request, crumbs []component.M
|
||||||
tc.Menu = custom.BuildMenu(r)
|
tc.Menu = custom.BuildMenu(r)
|
||||||
|
|
||||||
// build the breadcrumbs
|
// build the breadcrumbs
|
||||||
tc.Crumbs = slices.Clone(crumbs)
|
tc.BaseContextGaps = gaps.Clone()
|
||||||
last := len(tc.Crumbs) - 1
|
last := len(tc.Crumbs) - 1
|
||||||
for i := range tc.Crumbs {
|
for i := range tc.Crumbs {
|
||||||
tc.Crumbs[i].Active = i == last
|
tc.Crumbs[i].Active = i == last
|
||||||
|
|
@ -86,9 +98,9 @@ func (bc BaseContext) DoInitCheck() template.HTML {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewForm is like New, but returns a new BaseFormContext
|
// NewForm is like New, but returns a new BaseFormContext
|
||||||
func (custom *Custom) NewForm(context httpx.FormContext, r *http.Request, crumbs []component.MenuItem) (ctx BaseFormContext) {
|
func (custom *Custom) NewForm(context httpx.FormContext, r *http.Request, bcg BaseContextGaps) (ctx BaseFormContext) {
|
||||||
ctx.FormContext = context
|
ctx.FormContext = context
|
||||||
ctx.use(custom, r, crumbs)
|
ctx.use(custom, r, bcg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,11 +108,11 @@ func (custom *Custom) NewForm(context httpx.FormContext, r *http.Request, crumbs
|
||||||
//
|
//
|
||||||
// Assumes that context is a pointer to a struct type.
|
// Assumes that context is a pointer to a struct type.
|
||||||
// If this is not the case, might call panic().
|
// If this is not the case, might call panic().
|
||||||
func (custom *Custom) Update(context any, r *http.Request, crumbs []component.MenuItem) *BaseContext {
|
func (custom *Custom) Update(context any, r *http.Request, bcg BaseContextGaps) *BaseContext {
|
||||||
ctx := reflect.ValueOf(context).
|
ctx := reflect.ValueOf(context).
|
||||||
Elem().FieldByName(baseContextName).Addr().
|
Elem().FieldByName(baseContextName).Addr().
|
||||||
Interface().(*BaseContext)
|
Interface().(*BaseContext)
|
||||||
ctx.use(custom, r, crumbs)
|
ctx.use(custom, r, bcg)
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="pure-u-1-1">
|
<div class="pure-u-1-1">
|
||||||
<h2 id="components">Components</h2>
|
<h2 id="structs">Structs</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ range $name, $comp := .Components }}
|
{{ range $name, $comp := .Components }}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
<body>
|
<body>
|
||||||
{{ .BaseContext.DoInitCheck }}
|
{{ .BaseContext.DoInitCheck }}
|
||||||
<nav class="pure-menu pure-menu-horizontal">
|
<nav class="pure-menu pure-menu-horizontal">
|
||||||
<ul class="pure-menu-list">
|
<ul class="pure-menu-list" role="menubar">
|
||||||
{{ range .BaseContext.Menu }}
|
{{ range .BaseContext.Menu }}
|
||||||
<li class="pure-menu-item{{ if .Active }} pure-menu-selected{{ end }}">
|
<li class="pure-menu-item{{ if .Active }} pure-menu-selected{{ end }}">
|
||||||
<a href="{{ .Path }}" class="pure-menu-link">{{ .Title }}</a>
|
<a href="{{ .Path }}" class="pure-menu-link">{{ .Title }}</a>
|
||||||
|
|
@ -27,7 +27,13 @@
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<h1 id="top">{{ template "title" . }}</h1>
|
<h1 id="top">{{ template "title" . }}</h1>
|
||||||
{{ block "header" . }}<!-- no header by default -->{{ end }}
|
{{ if .BaseContext.Actions }}
|
||||||
|
<div class="pure-button-group" role="group" aria-label="Actions">
|
||||||
|
{{ range .BaseContext.Actions }}
|
||||||
|
<a href="{{ .Path }}" class="pure-button{{ if eq .Priority -1 }} pure-button-small{{end}}">{{ .Title }}</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
</header>
|
</header>
|
||||||
<main>
|
<main>
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
|
|
|
||||||
|
|
@ -34,3 +34,7 @@ const (
|
||||||
MenuAdmin
|
MenuAdmin
|
||||||
MenuAuth
|
MenuAuth
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SmallButton MenuPriority = -1
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/FAU-CDI/wdresolve"
|
"github.com/FAU-CDI/wdresolve"
|
||||||
"github.com/FAU-CDI/wdresolve/resolvers"
|
"github.com/FAU-CDI/wdresolve/resolvers"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
"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/control/static"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static/custom"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static/custom"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
||||||
|
|
@ -25,6 +26,7 @@ type Resolver struct {
|
||||||
Dependencies struct {
|
Dependencies struct {
|
||||||
Instances *instances.Instances
|
Instances *instances.Instances
|
||||||
Custom *custom.Custom
|
Custom *custom.Custom
|
||||||
|
Auth *auth.Auth
|
||||||
}
|
}
|
||||||
|
|
||||||
prefixes lazy.Lazy[map[string]string] // cached prefixes (from the server)
|
prefixes lazy.Lazy[map[string]string] // cached prefixes (from the server)
|
||||||
|
|
@ -58,8 +60,10 @@ type resolverContext struct {
|
||||||
|
|
||||||
func (resolver *Resolver) HandleRoute(ctx context.Context, route string) (http.Handler, error) {
|
func (resolver *Resolver) HandleRoute(ctx context.Context, route string) (http.Handler, error) {
|
||||||
resolverTemplate := resolver.Dependencies.Custom.Template(resolverTemplate)
|
resolverTemplate := resolver.Dependencies.Custom.Template(resolverTemplate)
|
||||||
crumbs := []component.MenuItem{
|
gaps := custom.BaseContextGaps{
|
||||||
{Title: "Resolver", Path: "/wisski/get/"},
|
Crumbs: []component.MenuItem{
|
||||||
|
{Title: "Resolver", Path: "/wisski/get/"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
logger := zerolog.Ctx(ctx)
|
logger := zerolog.Ctx(ctx)
|
||||||
|
|
@ -71,7 +75,11 @@ func (resolver *Resolver) HandleRoute(ctx context.Context, route string) (http.H
|
||||||
ctx := resolverContext{
|
ctx := resolverContext{
|
||||||
IndexContext: context,
|
IndexContext: context,
|
||||||
}
|
}
|
||||||
resolver.Dependencies.Custom.Update(&ctx, r, crumbs)
|
resolver.Dependencies.Custom.Update(&ctx, r, gaps)
|
||||||
|
|
||||||
|
if !resolver.Dependencies.Auth.Has(auth.User, r) {
|
||||||
|
ctx.IndexContext.Prefixes = nil
|
||||||
|
}
|
||||||
|
|
||||||
httpx.WriteHTML(ctx, nil, resolverTemplate, "", w, r)
|
httpx.WriteHTML(ctx, nil, resolverTemplate, "", w, r)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,14 @@
|
||||||
{{ template "_base.html" . }}
|
{{ template "_base.html" . }}
|
||||||
{{ define "title" }}WissKI Resolver{{ end }}
|
{{ define "title" }}Resolver{{ end }}
|
||||||
|
|
||||||
{{ define "content" }}
|
{{ define "content" }}
|
||||||
<div class="pure-u-1">
|
<div class="pure-u-1">
|
||||||
<h1>wdresolve</h1>
|
|
||||||
<p>
|
<p>
|
||||||
This page implements a resolver for the WissKI Distillery.
|
This page contains the global distillery resolver.
|
||||||
It takes a <b>RDF / Triplestore URI</b> that refers to a <b>WissKI Entity</b> and redirects to the page that displays the entity.
|
It takes a <b>RDF / Triplestore URI</b> that refers to a <b>WissKI Entity</b> and redirects to the page that displays the entity.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="pure-u-1">
|
<div class="pure-u-1">
|
||||||
<h2>Resolve URI</h2>
|
|
||||||
<form action="." method="GET" class="pure-form" autocomplete="off">
|
<form action="." method="GET" class="pure-form" autocomplete="off">
|
||||||
<label for="uri">Enter A URI To Resolve:</label>
|
<label for="uri">Enter A URI To Resolve:</label>
|
||||||
<input type="text" id="uri" name="uri" value="">
|
<input type="text" id="uri" name="uri" value="">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue