diff --git a/main.go b/main.go index 3d340e7..7fcff4b 100644 --- a/main.go +++ b/main.go @@ -76,7 +76,6 @@ func main() { ui.SetupTemplates() closer = ui.SetupSessionManager() defer closer() - ui.SetupLeftMenus() // Set up Echo. e := setupEcho() diff --git a/ui/amcontext.go b/ui/amcontext.go index 73a916e..9c83610 100644 --- a/ui/amcontext.go +++ b/ui/amcontext.go @@ -40,7 +40,6 @@ type AmContext interface { OutputType() string Parameter(string) string RemoteIP() string - Render(string) error ReplaceUser(*database.User) SaveSession() error SubRender(string) ([]byte, error) @@ -163,16 +162,6 @@ func (c *amContext) RemoteIP() string { return c.echoContext.RealIP() } -/* Render renders a template to the output. Called at the top level only. - * Parameters: - * name = The name of the tempate to be rendered. - * Returns: - * Standard Go error status. - */ -func (c *amContext) Render(name string) error { - return c.echoContext.Render(c.httprc, name, c) -} - /* ReplaceUser replaces the current user in the context. * Parameters: * u - New user to associate with the context. diff --git a/ui/leftmenus.go b/ui/leftmenus.go deleted file mode 100644 index b6b4264..0000000 --- a/ui/leftmenus.go +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Amsterdam Web Communities System - * Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ -// Package ui holds the support for the Amsterdam user interface, wrapping Echo and Jet templates. -package ui - -// AmLeftMenuItem represents an item on a left menu. -type AmLeftMenuItem struct { - Text string - Link string -} - -// AmLeftMenu represents a left menu. -type AmLeftMenu struct { - Title string - Items []AmLeftMenuItem -} - -// leftMenusRepo holds the possible left menus. -var leftMenusRepo = map[string]AmLeftMenu{} - -// SetupLeftMenus parses the left menus into internal data structures. -func SetupLeftMenus() { - // "Front Page" menu (not community specific) - menu := AmLeftMenu{Title: "Front Page", Items: make([]AmLeftMenuItem, 2)} - menu.Items[0] = AmLeftMenuItem{Text: "Calendar", Link: ""} - menu.Items[1] = AmLeftMenuItem{Text: "Chat", Link: ""} - leftMenusRepo["fp"] = menu - - // "About" menu (global) - menu = AmLeftMenu{Title: "About This Site", Items: make([]AmLeftMenuItem, 2)} - menu.Items[0] = AmLeftMenuItem{Text: "Documentation", Link: ""} - menu.Items[1] = AmLeftMenuItem{Text: "About Amsterdam", Link: "/about"} - leftMenusRepo["about"] = menu -} - -/* augmentWithLeftMenus adds the left menus to the Amsterdam context. - * Parameters: - * ctxt - The context to add the menus to. - */ -func augmentWithLeftMenus(ctxt AmContext) { - mlist := make([]AmLeftMenu, 0, len(leftMenusRepo)) - // TODO: check for "in community" status and select menu - mlist = append(mlist, leftMenusRepo["fp"]) - mlist = append(mlist, leftMenusRepo["about"]) - ctxt.VarMap().Set("amsterdam_leftMenus", mlist) -} diff --git a/ui/menudefs.yaml b/ui/menudefs.yaml new file mode 100644 index 0000000..659b203 --- /dev/null +++ b/ui/menudefs.yaml @@ -0,0 +1,31 @@ +# +# Amsterdam Web Communities System +# Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. +# +menudefs: + - id: "top" + title: "Front Page" + permSet: "user" + items: + - text: "Calendar" + link: "/TODO/calendar" + disabled: true + - text: "Chat" + link: "/TODO/chat" + disabled: true + - id: "fixed" + title: "About This Site" + permSet: "user" + items: + - text: "Documentation" + link: "/TODO/documentation" + disabled: true + - text: "About Amsterdam" + link: "/about" + - text: "System Administration" + link: "/TODO/sysadmin" + permission: "Global.SysAdminAccess" diff --git a/ui/menus.go b/ui/menus.go new file mode 100644 index 0000000..e04b185 --- /dev/null +++ b/ui/menus.go @@ -0,0 +1,80 @@ +/* + * Amsterdam Web Communities System + * Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +// Package ui holds the support for the Amsterdam user interface, wrapping Echo and Jet templates. +package ui + +import ( + _ "embed" + + "git.erbosoft.com/amy/amsterdam/database" + "gopkg.in/yaml.v3" +) + +// MenuItem represents an itrem within a menu definition. +type MenuItem struct { + Text string `yaml:"text"` + Link string `yaml:"link"` + Disabled bool `yaml:"disabled"` + Permission string `yaml:"permission"` + P *MenuDefinition +} + +func (mi *MenuItem) Show(ctxt AmContext) bool { + if mi.Permission == "" { + return true + } + u := ctxt.CurrentUser() + var eperm uint16 + switch mi.P.PermSet { + case "user": + eperm = u.BaseLevel + default: + eperm = database.AmRole("NotInList").Level() + } + return database.AmTestPermission(mi.Permission, eperm) +} + +// MenuDefinition represents a full menu definition. +type MenuDefinition struct { + ID string `yaml:"id"` + Title string `yaml:"title"` + PermSet string `yaml:"permSet"` + Items []MenuItem `yaml:"items"` +} + +// MenuDefs represents the set of all menu definitions. +type MenuDefs struct { + D []MenuDefinition `yaml:"menudefs"` + table map[string]*MenuDefinition +} + +//go:embed menudefs.yaml +var initMenuData []byte + +// menuDefinitions gives the menu definitions. +var menuDefinitions MenuDefs + +// init loads the menu definitions. +func init() { + if err := yaml.Unmarshal(initMenuData, &menuDefinitions); err != nil { + panic(err) // can't happen + } + menuDefinitions.table = make(map[string]*MenuDefinition) + for i, d := range menuDefinitions.D { + menuDefinitions.table[d.ID] = &(menuDefinitions.D[i]) + for j := range menuDefinitions.D[i].Items { + menuDefinitions.D[i].Items[j].P = &(menuDefinitions.D[i]) + } + } +} + +// AmMenu returns a menu definition. +func AmMenu(name string) *MenuDefinition { + return menuDefinitions.table[name] +} diff --git a/ui/render_wrap.go b/ui/render_wrap.go index 4f553e3..fc83ebf 100644 --- a/ui/render_wrap.go +++ b/ui/render_wrap.go @@ -31,11 +31,10 @@ func sendPageData(ctxt echo.Context, amctxt AmContext, command string, data any) case "string": err = ctxt.String(amctxt.RC(), fmt.Sprintf("%v", data)) case "template": - err = amctxt.Render(fmt.Sprintf("%v", data)) + err = ctxt.Render(amctxt.RC(), fmt.Sprintf("%v", data), amctxt) case "framed_template": amctxt.VarMap().Set("amsterdam_innerPage", data) - augmentWithLeftMenus(amctxt) - err = amctxt.Render("frame.jet") + err = ctxt.Render(amctxt.RC(), "frame.jet", amctxt) default: err = fmt.Errorf("unknown rendering type: %s", command) } diff --git a/ui/templates.go b/ui/templates.go index 1119763..b36a2a3 100644 --- a/ui/templates.go +++ b/ui/templates.go @@ -21,9 +21,9 @@ import ( "strings" "sync" "time" - "unicode" "git.erbosoft.com/amy/amsterdam/config" + "git.erbosoft.com/amy/amsterdam/util" "github.com/CloudyKit/jet/v6" "github.com/CloudyKit/jet/v6/loaders/embedfs" "github.com/CloudyKit/jet/v6/loaders/multi" @@ -83,11 +83,6 @@ func internalGetLanguageList() []Language { return cachedLanguageList } -// getLanguageList is a wrapper around the list of languages that can be added to the template context. -func getLanguageList(a jet.Arguments) reflect.Value { - return reflect.ValueOf(internalGetLanguageList()) -} - // cachedTimeZoneList is a wrapper around timezone.Timezones() that produces it by timezone name. var cachedTimeZoneList []string = nil @@ -112,11 +107,6 @@ func internalGetTimeZoneList() []string { return cachedTimeZoneList } -// getTimeZoneList is a wrapper around internalGetTimeZoneList that can be added to the template context. -func getTimeZoneList(a jet.Arguments) reflect.Value { - return reflect.ValueOf(internalGetTimeZoneList()) -} - // cachedCountryList is the cached country list after sorting. var cachedCountryList []countries.CountryCode = nil @@ -148,11 +138,6 @@ func internalGetCountryList() []countries.CountryCode { return cachedCountryList } -// getCountryList is a wrapper around countries.All() to be added to the template context. -func getCountryList(a jet.Arguments) reflect.Value { - return reflect.ValueOf(internalGetCountryList()) -} - // getMonthList is a simple wrapper that returns the names of the months to the template context. func getMonthList(a jet.Arguments) reflect.Value { rc := make([]string, 12) @@ -221,21 +206,6 @@ func makeYearRange(a jet.Arguments) reflect.Value { } } -/* CapitalizeString changes the first character of the string to a capital. - * Parameters: - * s - The string to be capitalized. - * Returns: - * The capitalized string. - */ -func CapitalizeString(s string) string { - runes := []rune(s) - if len(runes) > 0 { - runes[0] = unicode.ToUpper(runes[0]) - return string(runes) - } - return "" -} - // SetupTemplates is called to set up the template renderer after the configuration is loaded. func SetupTemplates() { views = jet.NewSet( @@ -248,16 +218,26 @@ func SetupTemplates() { views.AddGlobal("AmsterdamVersion", config.AMSTERDAM_VERSION) views.AddGlobal("AmsterdamCopyright", config.AMSTERDAM_COPYRIGHT) views.AddGlobal("GlobalConfig", config.GlobalConfig) - views.AddGlobalFunc("GetCountryList", getCountryList) - views.AddGlobalFunc("GetLanguageList", getLanguageList) - views.AddGlobalFunc("GetTimeZoneList", getTimeZoneList) views.AddGlobalFunc("GetMonthList", getMonthList) views.AddGlobalFunc("MakeIntRange", makeIntRange) views.AddGlobalFunc("MakeYearRange", makeYearRange) + views.AddGlobalFunc("GetCountryList", func(a jet.Arguments) reflect.Value { + return reflect.ValueOf(internalGetCountryList()) + }) + views.AddGlobalFunc("GetLanguageList", func(a jet.Arguments) reflect.Value { + return reflect.ValueOf(internalGetLanguageList()) + }) + views.AddGlobalFunc("GetTimeZoneList", func(a jet.Arguments) reflect.Value { + return reflect.ValueOf(internalGetTimeZoneList()) + }) + views.AddGlobalFunc("AmMenu", func(a jet.Arguments) reflect.Value { + s := a.Get(0).Convert(reflect.TypeFor[string]()).String() + return reflect.ValueOf(AmMenu(s)) + }) views.AddGlobalFunc("CapitalizeString", func(a jet.Arguments) reflect.Value { s := a.Get(0).Convert(reflect.TypeFor[string]()).String() - return reflect.ValueOf(CapitalizeString(s)) + return reflect.ValueOf(util.CapitalizeString(s)) }) // preload the lists in the background diff --git a/ui/views/frame.jet b/ui/views/frame.jet index b7cd537..d03f3ba 100644 --- a/ui/views/frame.jet +++ b/ui/views/frame.jet @@ -81,18 +81,11 @@
- {{ range amsterdam_leftMenus }} -
-
{{ .Title }}
- {{ range .Items }} - {{ if .Link != "" }} - {{ .Text }} - {{ else }} -
{{ .Text }}
- {{ end }} - {{ end }} -
- {{ end }} + {{ .SetScratch("__menu", AmMenu("top")) }} + {{ .SubRender("menu_left.jet") | raw }} +
 
+ {{ .SetScratch("__menu", AmMenu("fixed")) }} + {{ .SubRender("menu_left.jet") | raw }}
diff --git a/ui/views/menu_left.jet b/ui/views/menu_left.jet new file mode 100644 index 0000000..5b2f89b --- /dev/null +++ b/ui/views/menu_left.jet @@ -0,0 +1,22 @@ +{* + * Amsterdam Web Communities System + * Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + *} +{{ menu := .GetScratch("__menu") }} +
+
{{ menu.Title }}
+ {{ ctxt := . }} + {{ range menu.Items }} + {{ if .Show(ctxt) }} + {{ if .Disabled }} +
{{ .Text }}
+ {{ else }} + {{ .Text }} + {{ end }} + {{ end }} + {{ end }} +
diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..1f7b66c --- /dev/null +++ b/util/util.go @@ -0,0 +1,28 @@ +/* + * Amsterdam Web Communities System + * Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +// Package util contains utility definitions. +package util + +import "unicode" + +/* CapitalizeString changes the first character of the string to a capital. + * Parameters: + * s - The string to be capitalized. + * Returns: + * The capitalized string. + */ +func CapitalizeString(s string) string { + runes := []rune(s) + if len(runes) > 0 { + runes[0] = unicode.ToUpper(runes[0]) + return string(runes) + } + return "" +}