revamped left menus entirely to a new menu definition system
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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"
|
||||
+80
@@ -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]
|
||||
}
|
||||
+2
-3
@@ -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)
|
||||
}
|
||||
|
||||
+15
-35
@@ -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
|
||||
|
||||
+5
-12
@@ -81,18 +81,11 @@
|
||||
<div class="flex">
|
||||
<!-- LEFT SIDEBAR -->
|
||||
<div class="w-48 bg-blue-400 p-2">
|
||||
{{ range amsterdam_leftMenus }}
|
||||
<div class="mb-2 mt-2">
|
||||
<div class="font-bold mb-1">{{ .Title }}</div>
|
||||
{{ range .Items }}
|
||||
{{ if .Link != "" }}
|
||||
<a href="{{ .Link }}" class="text-blue-700 hover:text-blue-900">{{ .Text }}</a>
|
||||
{{ else }}
|
||||
<div class="text-gray-500 mb-1">{{ .Text }}</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ .SetScratch("__menu", AmMenu("top")) }}
|
||||
{{ .SubRender("menu_left.jet") | raw }}
|
||||
<div class="mb-2 mt-2"> </div>
|
||||
{{ .SetScratch("__menu", AmMenu("fixed")) }}
|
||||
{{ .SubRender("menu_left.jet") | raw }}
|
||||
</div>
|
||||
|
||||
<!-- MAIN CONTENT -->
|
||||
|
||||
@@ -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") }}
|
||||
<div class="mb-2 mt-2">
|
||||
<div class="font-bold mb-1">{{ menu.Title }}</div>
|
||||
{{ ctxt := . }}
|
||||
{{ range menu.Items }}
|
||||
{{ if .Show(ctxt) }}
|
||||
{{ if .Disabled }}
|
||||
<div class="text-gray-500 mb-1">{{ .Text }}</div>
|
||||
{{ else }}
|
||||
<a href="{{ .Link }}" class="text-blue-700 hover:text-blue-900">{{ .Text }}</a>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
</div>
|
||||
Reference in New Issue
Block a user