community profile and left menu implementation done, not quite working yet
This commit is contained in:
+8
-22
@@ -25,27 +25,12 @@ func ShowCommunity(ctxt ui.AmContext) (string, any, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ui.ErrorPage(ctxt, err)
|
return ui.ErrorPage(ctxt, err)
|
||||||
}
|
}
|
||||||
globals, err := database.AmGlobals()
|
err = ctxt.SetCommunityContext(ctxt.URLParam("cid"))
|
||||||
if err != nil {
|
|
||||||
return ui.ErrorPage(ctxt, err)
|
|
||||||
}
|
|
||||||
globalFlags, err := globals.Flags()
|
|
||||||
if err != nil {
|
|
||||||
return ui.ErrorPage(ctxt, err)
|
|
||||||
}
|
|
||||||
comm, err := database.AmGetCommunityFromParam(ctxt.URLParam("cid"))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctxt.SetRC(http.StatusNotFound)
|
ctxt.SetRC(http.StatusNotFound)
|
||||||
return ui.ErrorPage(ctxt, err)
|
return ui.ErrorPage(ctxt, err)
|
||||||
}
|
}
|
||||||
member, _, level, err := comm.Membership(me)
|
comm := ctxt.CurrentCommunity()
|
||||||
if err != nil {
|
|
||||||
return ui.ErrorPage(ctxt, err)
|
|
||||||
}
|
|
||||||
effectiveLevel := me.BaseLevel
|
|
||||||
if member && level > effectiveLevel {
|
|
||||||
effectiveLevel = level
|
|
||||||
}
|
|
||||||
ci, err := comm.ContactInfo()
|
ci, err := comm.ContactInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ui.ErrorPage(ctxt, err)
|
return ui.ErrorPage(ctxt, err)
|
||||||
@@ -55,14 +40,14 @@ func ShowCommunity(ctxt ui.AmContext) (string, any, error) {
|
|||||||
return ui.ErrorPage(ctxt, err)
|
return ui.ErrorPage(ctxt, err)
|
||||||
}
|
}
|
||||||
var cats []*database.Category
|
var cats []*database.Category
|
||||||
if !globalFlags.Get(database.GlobalFlagNoCategories) {
|
if !ctxt.GlobalFlags().Get(database.GlobalFlagNoCategories) {
|
||||||
cats, err = database.AmGetCategoryHierarchy(comm.CategoryId)
|
cats, err = database.AmGetCategoryHierarchy(comm.CategoryId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ui.ErrorPage(ctxt, err)
|
return ui.ErrorPage(ctxt, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var pvtAddr bool
|
var pvtAddr bool
|
||||||
if database.AmTestPermission("Global.SeeHiddenContactInfo", effectiveLevel) {
|
if ctxt.TestPermission("Global.SeeHiddenContactInfo") {
|
||||||
pvtAddr = false
|
pvtAddr = false
|
||||||
} else {
|
} else {
|
||||||
pvtAddr = ci.PrivateAddr
|
pvtAddr = ci.PrivateAddr
|
||||||
@@ -79,14 +64,14 @@ func ShowCommunity(ctxt ui.AmContext) (string, any, error) {
|
|||||||
if comm.LastUpdate != nil {
|
if comm.LastUpdate != nil {
|
||||||
ctxt.VarMap().Set("dateLastUpdate", loc.Strftime("%x %X", (*comm.LastUpdate).In(tz)))
|
ctxt.VarMap().Set("dateLastUpdate", loc.Strftime("%x %X", (*comm.LastUpdate).In(tz)))
|
||||||
}
|
}
|
||||||
if !member && effectiveLevel >= comm.JoinLevel {
|
if !ctxt.IsMember() && ctxt.EffectiveLevel() >= comm.JoinLevel {
|
||||||
ctxt.VarMap().Set("canJoin", true)
|
ctxt.VarMap().Set("canJoin", true)
|
||||||
}
|
}
|
||||||
if member && !me.IsAnon {
|
if ctxt.IsMember() && !me.IsAnon {
|
||||||
ctxt.VarMap().Set("canInvite", true)
|
ctxt.VarMap().Set("canInvite", true)
|
||||||
}
|
}
|
||||||
ctxt.VarMap().Set("public", comm.Public())
|
ctxt.VarMap().Set("public", comm.Public())
|
||||||
if !globalFlags.Get(database.GlobalFlagNoCategories) {
|
if !ctxt.GlobalFlags().Get(database.GlobalFlagNoCategories) {
|
||||||
ctxt.VarMap().Set("categories", cats)
|
ctxt.VarMap().Set("categories", cats)
|
||||||
}
|
}
|
||||||
if comm.Synopsis != nil && *comm.Synopsis != "" {
|
if comm.Synopsis != nil && *comm.Synopsis != "" {
|
||||||
@@ -131,6 +116,7 @@ func ShowCommunity(ctxt ui.AmContext) (string, any, error) {
|
|||||||
ctxt.VarMap().Set("homePage", *ci.URL)
|
ctxt.VarMap().Set("homePage", *ci.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctxt.SetLeftMenu("community")
|
||||||
ctxt.VarMap().Set("amsterdam_pageTitle", "Community Profile: "+comm.Name)
|
ctxt.VarMap().Set("amsterdam_pageTitle", "Community Profile: "+comm.Name)
|
||||||
return "framed_template", "comprofile.jet", nil
|
return "framed_template", "comprofile.jet", nil
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-8
@@ -165,16 +165,39 @@ func (c *Community) Membership(u *User) (bool, bool, uint16, error) {
|
|||||||
* true if the user has the permission, false if not.
|
* true if the user has the permission, false if not.
|
||||||
* Standard Go error status.
|
* Standard Go error status.
|
||||||
*/
|
*/
|
||||||
func (c *Community) TestPermission(user *User, perm string) (bool, error) {
|
func (c *Community) TestPermission(perm string, level uint16) bool {
|
||||||
member, _, level, err := c.Membership(user)
|
switch perm {
|
||||||
if err != nil {
|
case "Community.Read":
|
||||||
return false, err
|
return level >= c.ReadLevel
|
||||||
|
case "Community.Write":
|
||||||
|
return level >= c.WriteLevel
|
||||||
|
case "Community.Create":
|
||||||
|
return level >= c.CreateLevel
|
||||||
|
case "Community.Delete":
|
||||||
|
return level >= c.DeleteLevel
|
||||||
|
case "Community.Join":
|
||||||
|
return level >= c.JoinLevel
|
||||||
|
default:
|
||||||
|
return AmTestPermission(perm, level)
|
||||||
}
|
}
|
||||||
effectiveLevel := user.BaseLevel
|
|
||||||
if member && level > effectiveLevel {
|
|
||||||
effectiveLevel = level
|
|
||||||
}
|
}
|
||||||
return AmTestPermission(perm, effectiveLevel), nil
|
|
||||||
|
// PermissionLevel returns trhe permission level for a permission name.
|
||||||
|
func (c *Community) PermissionLevel(perm string) uint16 {
|
||||||
|
switch perm {
|
||||||
|
case "Community.Read":
|
||||||
|
return c.ReadLevel
|
||||||
|
case "Community.Write":
|
||||||
|
return c.WriteLevel
|
||||||
|
case "Community.Create":
|
||||||
|
return c.CreateLevel
|
||||||
|
case "Community.Delete":
|
||||||
|
return c.DeleteLevel
|
||||||
|
case "Community.Join":
|
||||||
|
return c.JoinLevel
|
||||||
|
default:
|
||||||
|
return AmPermissionLevel(perm)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AmGetCommunity returns a reference to the specified community.
|
/* AmGetCommunity returns a reference to the specified community.
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ import (
|
|||||||
type ContactInfo struct {
|
type ContactInfo struct {
|
||||||
Mutex sync.Mutex
|
Mutex sync.Mutex
|
||||||
ContactId int32 `db:"contactid"`
|
ContactId int32 `db:"contactid"`
|
||||||
GivenName string `db:"given_name"`
|
GivenName *string `db:"given_name"`
|
||||||
FamilyName string `db:"family_name"`
|
FamilyName *string `db:"family_name"`
|
||||||
MiddleInit *string `db:"middle_init"`
|
MiddleInit *string `db:"middle_init"`
|
||||||
Prefix *string `db:"prefix"`
|
Prefix *string `db:"prefix"`
|
||||||
Suffix *string `db:"suffix"`
|
Suffix *string `db:"suffix"`
|
||||||
|
|||||||
+22
-1
@@ -254,5 +254,26 @@ func AmRoleList(id string) RoleList {
|
|||||||
* true if the permission test is satisfied, false if not.
|
* true if the permission test is satisfied, false if not.
|
||||||
*/
|
*/
|
||||||
func AmTestPermission(id string, level uint16) bool {
|
func AmTestPermission(id string, level uint16) bool {
|
||||||
return securityRoot.permsMap[id].level < level
|
return securityRoot.permsMap[id].level <= level
|
||||||
|
}
|
||||||
|
|
||||||
|
// AmPermissionLevel returns a level value for a permission.
|
||||||
|
func AmPermissionLevel(id string) uint16 {
|
||||||
|
return securityRoot.permsMap[id].level
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AmCombinePermissionRole combines a permission and a role into a single permission level.
|
||||||
|
* Parameters:
|
||||||
|
* perm - Permission to use.
|
||||||
|
* role - Role to use.
|
||||||
|
* Returns:
|
||||||
|
* The combined permission level.
|
||||||
|
*/
|
||||||
|
func AmCombinePermissionRole(perm string, role string) uint16 {
|
||||||
|
p1 := securityRoot.permsMap[perm].level
|
||||||
|
p2 := securityRoot.roleMap[role].level
|
||||||
|
if p1 > p2 {
|
||||||
|
return p1
|
||||||
|
}
|
||||||
|
return p2
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ type ServiceDef struct {
|
|||||||
RequirePermission string `yaml:"requirePermission"`
|
RequirePermission string `yaml:"requirePermission"`
|
||||||
RequireRole string `yaml:"requireRole"`
|
RequireRole string `yaml:"requireRole"`
|
||||||
LinkSequence int `yaml:"linkSequence"`
|
LinkSequence int `yaml:"linkSequence"`
|
||||||
Link int `yaml:"link"`
|
Link string `yaml:"link"`
|
||||||
Title string `yaml:"title"`
|
Title string `yaml:"title"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
* Standard Go error status.
|
* Standard Go error status.
|
||||||
*/
|
*/
|
||||||
func NotImplPage(ctxt ui.AmContext) (string, any, error) {
|
func NotImplPage(ctxt ui.AmContext) (string, any, error) {
|
||||||
|
ctxt.SetLeftMenu("top")
|
||||||
ctxt.VarMap().Set("amsterdam_pageTitle", "Function Not Implemented")
|
ctxt.VarMap().Set("amsterdam_pageTitle", "Function Not Implemented")
|
||||||
ctxt.VarMap().Set("path", ctxt.URLPath())
|
ctxt.VarMap().Set("path", ctxt.URLPath())
|
||||||
return "framed_template", "notimpl.jet", nil
|
return "framed_template", "notimpl.jet", nil
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ func NewAccountUserAgreement(ctxt ui.AmContext) (string, any, error) {
|
|||||||
return ui.ErrorPage(ctxt, errors.New("you cannot create a new account while logged in on an existing one. You must log out first"))
|
return ui.ErrorPage(ctxt, errors.New("you cannot create a new account while logged in on an existing one. You must log out first"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctxt.SetLeftMenu("top")
|
||||||
ctxt.VarMap().Set("target", target)
|
ctxt.VarMap().Set("target", target)
|
||||||
ctxt.VarMap().Set("amsterdam_pageTitle", "New Account User Agreement")
|
ctxt.VarMap().Set("amsterdam_pageTitle", "New Account User Agreement")
|
||||||
ctxt.VarMap().Set("amsterdam_suppressLogin", true)
|
ctxt.VarMap().Set("amsterdam_suppressLogin", true)
|
||||||
@@ -370,13 +371,13 @@ func NewAccount(ctxt ui.AmContext) (string, any, error) {
|
|||||||
// create and save contact info
|
// create and save contact info
|
||||||
ci := database.AmNewUserContactInfo(user.Uid)
|
ci := database.AmNewUserContactInfo(user.Uid)
|
||||||
ci.Prefix = dlg.Field("prefix").ValPtr()
|
ci.Prefix = dlg.Field("prefix").ValPtr()
|
||||||
ci.GivenName = dlg.Field("first").Value
|
ci.GivenName = dlg.Field("first").ValPtr()
|
||||||
mid := dlg.Field("mid").Value
|
mid := dlg.Field("mid").Value
|
||||||
if mid == "" {
|
if mid == "" {
|
||||||
mid = " "
|
mid = " "
|
||||||
}
|
}
|
||||||
ci.MiddleInit = &mid
|
ci.MiddleInit = &mid
|
||||||
ci.FamilyName = dlg.Field("last").Value
|
ci.FamilyName = dlg.Field("last").ValPtr()
|
||||||
ci.Suffix = dlg.Field("suffix").ValPtr()
|
ci.Suffix = dlg.Field("suffix").ValPtr()
|
||||||
ci.Locality = dlg.Field("loc").ValPtr()
|
ci.Locality = dlg.Field("loc").ValPtr()
|
||||||
ci.Region = dlg.Field("reg").ValPtr()
|
ci.Region = dlg.Field("reg").ValPtr()
|
||||||
@@ -446,6 +447,7 @@ func PasswordRecovery(ctxt ui.AmContext) (string, any, error) {
|
|||||||
msg.AddVariable("username", user.Username)
|
msg.AddVariable("username", user.Username)
|
||||||
msg.AddVariable("password", newpass)
|
msg.AddVariable("password", newpass)
|
||||||
msg.Send()
|
msg.Send()
|
||||||
|
ctxt.SetLeftMenu("top")
|
||||||
ctxt.VarMap().Set("amsterdam_pageTitle", "Your Password Has Been Changed")
|
ctxt.VarMap().Set("amsterdam_pageTitle", "Your Password Has Been Changed")
|
||||||
return "framed_template", "password_changed.jet", nil
|
return "framed_template", "password_changed.jet", nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,8 @@ func main() {
|
|||||||
ui.SetupTemplates()
|
ui.SetupTemplates()
|
||||||
closer = ui.SetupSessionManager()
|
closer = ui.SetupSessionManager()
|
||||||
defer closer()
|
defer closer()
|
||||||
|
closer = ui.SetupAmContext()
|
||||||
|
defer closer()
|
||||||
|
|
||||||
// Set up Echo.
|
// Set up Echo.
|
||||||
e := setupEcho()
|
e := setupEcho()
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ func SysAdminMenu(ctxt ui.AmContext) (string, any, error) {
|
|||||||
ctxt.SetRC(http.StatusForbidden)
|
ctxt.SetRC(http.StatusForbidden)
|
||||||
return ui.ErrorPage(ctxt, errors.New("you are not authorized access to this page"))
|
return ui.ErrorPage(ctxt, errors.New("you are not authorized access to this page"))
|
||||||
}
|
}
|
||||||
|
ctxt.SetLeftMenu("top")
|
||||||
menu := ui.AmMenu("sysadmin")
|
menu := ui.AmMenu("sysadmin")
|
||||||
ctxt.VarMap().Set("menu", menu)
|
ctxt.VarMap().Set("menu", menu)
|
||||||
ctxt.VarMap().Set("amsterdam_pageTitle", menu.Title)
|
ctxt.VarMap().Set("amsterdam_pageTitle", menu.Title)
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ func buildCommunitiesSidebox(uid int32, out *RenderedSidebox, in *database.Sideb
|
|||||||
out.Items = make([]RenderedSideboxItem, len(l))
|
out.Items = make([]RenderedSideboxItem, len(l))
|
||||||
for i, c := range l {
|
for i, c := range l {
|
||||||
out.Items[i].Text = c.Name
|
out.Items[i].Text = c.Name
|
||||||
lk := fmt.Sprintf("/TODO/community/%s", c.Alias)
|
lk := fmt.Sprintf("/comm/%s/profile", c.Alias)
|
||||||
out.Items[i].Link = &lk
|
out.Items[i].Link = &lk
|
||||||
out.Items[i].Flags = make(map[string]bool)
|
out.Items[i].Flags = make(map[string]bool)
|
||||||
var level uint16
|
var level uint16
|
||||||
@@ -181,6 +181,7 @@ func TopPage(ctxt ui.AmContext) (string, any, error) {
|
|||||||
ctxt.VarMap().Set("sideboxes", rc)
|
ctxt.VarMap().Set("sideboxes", rc)
|
||||||
|
|
||||||
// Final data set.
|
// Final data set.
|
||||||
|
ctxt.SetLeftMenu("top")
|
||||||
ctxt.VarMap().Set("amsterdam_genRefresh", true)
|
ctxt.VarMap().Set("amsterdam_genRefresh", true)
|
||||||
return "framed_template", "top.jet", nil
|
return "framed_template", "top.jet", nil
|
||||||
}
|
}
|
||||||
|
|||||||
+164
-7
@@ -19,6 +19,7 @@ import (
|
|||||||
|
|
||||||
"git.erbosoft.com/amy/amsterdam/config"
|
"git.erbosoft.com/amy/amsterdam/config"
|
||||||
"git.erbosoft.com/amy/amsterdam/database"
|
"git.erbosoft.com/amy/amsterdam/database"
|
||||||
|
"git.erbosoft.com/amy/amsterdam/util"
|
||||||
"github.com/CloudyKit/jet/v6"
|
"github.com/CloudyKit/jet/v6"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/labstack/echo-contrib/session"
|
"github.com/labstack/echo-contrib/session"
|
||||||
@@ -30,12 +31,19 @@ import (
|
|||||||
type AmContext interface {
|
type AmContext interface {
|
||||||
ClearLoginCookie()
|
ClearLoginCookie()
|
||||||
ClearSession()
|
ClearSession()
|
||||||
|
CurrentCommunity() *database.Community
|
||||||
CurrentUser() *database.User
|
CurrentUser() *database.User
|
||||||
CurrentUserId() int32
|
CurrentUserId() int32
|
||||||
|
Done()
|
||||||
|
EffectiveLevel() uint16
|
||||||
FormField(string) string
|
FormField(string) string
|
||||||
FormFieldInt(string) (int, error)
|
FormFieldInt(string) (int, error)
|
||||||
FormFieldIsSet(string) bool
|
FormFieldIsSet(string) bool
|
||||||
FormFile(string) (*multipart.FileHeader, error)
|
FormFile(string) (*multipart.FileHeader, error)
|
||||||
|
Globals() *database.Globals
|
||||||
|
GlobalFlags() *util.OptionSet
|
||||||
|
IsMember() bool
|
||||||
|
LeftMenu() string
|
||||||
RC() int
|
RC() int
|
||||||
OutputType() string
|
OutputType() string
|
||||||
Parameter(string) string
|
Parameter(string) string
|
||||||
@@ -43,11 +51,14 @@ type AmContext interface {
|
|||||||
ReplaceUser(*database.User)
|
ReplaceUser(*database.User)
|
||||||
SaveSession() error
|
SaveSession() error
|
||||||
SubRender(string) ([]byte, error)
|
SubRender(string) ([]byte, error)
|
||||||
|
SetCommunityContext(string) error
|
||||||
|
SetLeftMenu(string)
|
||||||
SetLoginCookie(string)
|
SetLoginCookie(string)
|
||||||
SetOutputType(string)
|
SetOutputType(string)
|
||||||
SetRC(int)
|
SetRC(int)
|
||||||
GetScratch(string) any
|
GetScratch(string) any
|
||||||
SetScratch(string, any)
|
SetScratch(string, any)
|
||||||
|
TestPermission(string) bool
|
||||||
URLParam(string) string
|
URLParam(string) string
|
||||||
URLParamInt(string) (int, error)
|
URLParamInt(string) (int, error)
|
||||||
URLPath() string
|
URLPath() string
|
||||||
@@ -62,6 +73,12 @@ type amContext struct {
|
|||||||
outputType string
|
outputType string
|
||||||
scratchpad map[string]any
|
scratchpad map[string]any
|
||||||
session *sessions.Session
|
session *sessions.Session
|
||||||
|
globals *database.Globals
|
||||||
|
globalFlags *util.OptionSet
|
||||||
|
user *database.User
|
||||||
|
effectiveLevel uint16
|
||||||
|
community *database.Community
|
||||||
|
isMember bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClearLoginCookie overwrites and removes the login cookie.
|
// ClearLoginCookie overwrites and removes the login cookie.
|
||||||
@@ -77,15 +94,26 @@ func (c *amContext) ClearLoginCookie() {
|
|||||||
// ClearSession clears the current session.
|
// ClearSession clears the current session.
|
||||||
func (c *amContext) ClearSession() {
|
func (c *amContext) ClearSession() {
|
||||||
AmResetSession(c.session)
|
AmResetSession(c.session)
|
||||||
|
c.user = nil
|
||||||
|
c.effectiveLevel = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CurrentCommunity returns the current community, if one's been set.
|
||||||
|
func (c *amContext) CurrentCommunity() *database.Community {
|
||||||
|
return c.community
|
||||||
}
|
}
|
||||||
|
|
||||||
// CurrentUser returns the current user from the session.
|
// CurrentUser returns the current user from the session.
|
||||||
func (c *amContext) CurrentUser() *database.User {
|
func (c *amContext) CurrentUser() *database.User {
|
||||||
|
if c.user == nil {
|
||||||
u, err := database.AmGetUser(AmSessionUid(c.session))
|
u, err := database.AmGetUser(AmSessionUid(c.session))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("unable to retrieve current user")
|
log.Errorf("unable to retrieve current user")
|
||||||
}
|
}
|
||||||
return u
|
c.user = u
|
||||||
|
c.effectiveLevel = u.BaseLevel
|
||||||
|
}
|
||||||
|
return c.user
|
||||||
}
|
}
|
||||||
|
|
||||||
// CurrentUserId returns the current user ID.
|
// CurrentUserId returns the current user ID.
|
||||||
@@ -93,6 +121,16 @@ func (c *amContext) CurrentUserId() int32 {
|
|||||||
return AmSessionUid(c.session)
|
return AmSessionUid(c.session)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Done signals that we're done with this context and it can be recycled.
|
||||||
|
func (c *amContext) Done() {
|
||||||
|
amContextRecycleBin <- c
|
||||||
|
}
|
||||||
|
|
||||||
|
// EffectiveLevel returns the user's effective access level (in terms of current community, if any).
|
||||||
|
func (c *amContext) EffectiveLevel() uint16 {
|
||||||
|
return c.effectiveLevel
|
||||||
|
}
|
||||||
|
|
||||||
/* FormField returns the value of a form field from the request.
|
/* FormField returns the value of a form field from the request.
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* name - The name of the field to retrieve.
|
* name - The name of the field to retrieve.
|
||||||
@@ -133,6 +171,26 @@ func (c *amContext) FormFile(name string) (*multipart.FileHeader, error) {
|
|||||||
return c.echoContext.FormFile(name)
|
return c.echoContext.FormFile(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Globals returns a reference to the database globals.
|
||||||
|
func (c *amContext) Globals() *database.Globals {
|
||||||
|
return c.globals
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalFlags returns a reference to the database global flags.
|
||||||
|
func (c *amContext) GlobalFlags() *util.OptionSet {
|
||||||
|
return c.globalFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMember returns true if the user is a member of the current community.
|
||||||
|
func (c *amContext) IsMember() bool {
|
||||||
|
return c.isMember
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeftMenu returns the current left menu selector.
|
||||||
|
func (c *amContext) LeftMenu() string {
|
||||||
|
return c.session.Values["leftMenu"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
// RC returns the HTTP result code for the current operation.
|
// RC returns the HTTP result code for the current operation.
|
||||||
func (c *amContext) RC() int {
|
func (c *amContext) RC() int {
|
||||||
return c.httprc
|
return c.httprc
|
||||||
@@ -168,6 +226,8 @@ func (c *amContext) RemoteIP() string {
|
|||||||
*/
|
*/
|
||||||
func (c *amContext) ReplaceUser(u *database.User) {
|
func (c *amContext) ReplaceUser(u *database.User) {
|
||||||
AmSetSessionUser(c.session, u)
|
AmSetSessionUser(c.session, u)
|
||||||
|
c.user = u
|
||||||
|
c.effectiveLevel = u.BaseLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveSession saves the session link to cookies.
|
// SaveSession saves the session link to cookies.
|
||||||
@@ -201,6 +261,34 @@ func (c *amContext) SubRender(name string) ([]byte, error) {
|
|||||||
return buf.Bytes(), err
|
return buf.Bytes(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SetCommunityContext establishes the community context from a (ID or alias) parameter.
|
||||||
|
* Parameters:
|
||||||
|
* param - String parameter selecting the community.
|
||||||
|
* Returns:
|
||||||
|
* Standard Go error status.
|
||||||
|
*/
|
||||||
|
func (c *amContext) SetCommunityContext(param string) error {
|
||||||
|
comm, err := database.AmGetCommunityFromParam(param)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mbr, _, level, err := comm.Membership(c.CurrentUser())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.community = comm
|
||||||
|
c.isMember = mbr
|
||||||
|
if level > c.effectiveLevel {
|
||||||
|
c.effectiveLevel = level
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLeftMenu sets the current topmost left menu name value.
|
||||||
|
func (c *amContext) SetLeftMenu(name string) {
|
||||||
|
c.session.Values["leftMenu"] = name
|
||||||
|
}
|
||||||
|
|
||||||
/* SetLoginCookie adds the login cookie to the result output.
|
/* SetLoginCookie adds the login cookie to the result output.
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* auth - The auth string to set.
|
* auth - The auth string to set.
|
||||||
@@ -240,6 +328,11 @@ func (c *amContext) SetScratch(name string, val any) {
|
|||||||
c.scratchpad[name] = val
|
c.scratchpad[name] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPermission tests the current user against permissions.
|
||||||
|
func (c *amContext) TestPermission(perm string) bool {
|
||||||
|
return database.AmTestPermission(perm, c.effectiveLevel)
|
||||||
|
}
|
||||||
|
|
||||||
// URLParam returns the value of a URL parameter.
|
// URLParam returns the value of a URL parameter.
|
||||||
func (c *amContext) URLParam(name string) string {
|
func (c *amContext) URLParam(name string) string {
|
||||||
return c.echoContext.Param(name)
|
return c.echoContext.Param(name)
|
||||||
@@ -267,22 +360,42 @@ var defoptions *sessions.Options = &sessions.Options{
|
|||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* NewAmContext creates a new AmContext wrapping the Echo context.
|
// freeContext is a free list for amContext structures.
|
||||||
|
var freeContext util.FreeList[amContext]
|
||||||
|
|
||||||
|
// amContextRecycleBin is the channel we put contexts on to be recycled.
|
||||||
|
var amContextRecycleBin chan *amContext
|
||||||
|
|
||||||
|
/* AmCreateContext creates a new AmContext wrapping the Echo context.
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* ctxt - The Echo context to be wrapped.
|
* ctxt - The Echo context to be wrapped.
|
||||||
* Returns:
|
* Returns:
|
||||||
* A new Amsterdam context wrapping that context.
|
* A new Amsterdam context wrapping that context.
|
||||||
* Standard Go error status.
|
* Standard Go error status.
|
||||||
*/
|
*/
|
||||||
func NewAmContext(ctxt echo.Context) (AmContext, error) {
|
func AmCreateContext(ctxt echo.Context) (AmContext, error) {
|
||||||
rc := amContext{
|
rc := freeContext.Get()
|
||||||
echoContext: ctxt,
|
if rc == nil {
|
||||||
|
rc = &amContext{
|
||||||
httprc: http.StatusOK,
|
httprc: http.StatusOK,
|
||||||
rendervars: make(jet.VarMap),
|
rendervars: make(jet.VarMap),
|
||||||
outputType: "",
|
outputType: "",
|
||||||
scratchpad: nil,
|
scratchpad: nil,
|
||||||
}
|
}
|
||||||
ctxt.Set("amsterdam_context", &rc)
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if rc.globals, err = database.AmGlobals(); err != nil {
|
||||||
|
amContextRecycleBin <- rc
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if rc.globalFlags, err = rc.globals.Flags(); err != nil {
|
||||||
|
amContextRecycleBin <- rc
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rc.echoContext = ctxt
|
||||||
|
ctxt.Set("amsterdam_context", rc)
|
||||||
sess, err := session.Get("AMSTERDAM_SESSION", ctxt)
|
sess, err := session.Get("AMSTERDAM_SESSION", ctxt)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rc.session = sess
|
rc.session = sess
|
||||||
@@ -293,7 +406,14 @@ func NewAmContext(ctxt echo.Context) (AmContext, error) {
|
|||||||
AmHitSession(sess)
|
AmHitSession(sess)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &rc, err
|
rc.user, err = database.AmGetUser(AmSessionUid(sess))
|
||||||
|
if err == nil {
|
||||||
|
rc.effectiveLevel = rc.user.BaseLevel
|
||||||
|
} else {
|
||||||
|
rc.user = nil
|
||||||
|
rc.effectiveLevel = database.AmRole("NotInList").Level()
|
||||||
|
}
|
||||||
|
return rc, err
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AmContextFromEchoContext returns the AmContext associated with an Echo context.
|
/* AmContextFromEchoContext returns the AmContext associated with an Echo context.
|
||||||
@@ -312,3 +432,40 @@ func AmContextFromEchoContext(ctxt echo.Context) AmContext {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// contextRecycler is the task that recycles context blocks.
|
||||||
|
func contextRecycler(incoming chan *amContext, done chan bool) {
|
||||||
|
for c := range incoming {
|
||||||
|
c.echoContext = nil
|
||||||
|
c.httprc = http.StatusOK
|
||||||
|
for k := range c.rendervars {
|
||||||
|
delete(c.rendervars, k)
|
||||||
|
}
|
||||||
|
c.outputType = ""
|
||||||
|
if c.scratchpad != nil {
|
||||||
|
for k := range c.scratchpad {
|
||||||
|
delete(c.scratchpad, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.session = nil
|
||||||
|
c.globals = nil
|
||||||
|
c.globalFlags = nil
|
||||||
|
c.user = nil
|
||||||
|
c.effectiveLevel = 0
|
||||||
|
c.community = nil
|
||||||
|
c.isMember = false
|
||||||
|
freeContext.Put(c)
|
||||||
|
}
|
||||||
|
done <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetupAmContext starts the recycler for contexts.
|
||||||
|
func SetupAmContext() func() {
|
||||||
|
amContextRecycleBin = make(chan *amContext, 16)
|
||||||
|
done := make(chan bool)
|
||||||
|
go contextRecycler(amContextRecycleBin, done)
|
||||||
|
return func() {
|
||||||
|
close(amContextRecycleBin)
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -226,6 +226,9 @@ func (d *Dialog) Render(ctxt AmContext) (string, any, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if d.MenuSelector != "" && d.MenuSelector != "nochange" {
|
||||||
|
ctxt.SetLeftMenu(d.MenuSelector)
|
||||||
|
}
|
||||||
ctxt.VarMap().Set("amsterdam_required", required)
|
ctxt.VarMap().Set("amsterdam_required", required)
|
||||||
ctxt.VarMap().Set("amsterdam_dialog", d)
|
ctxt.VarMap().Set("amsterdam_dialog", d)
|
||||||
ctxt.VarMap().Set("amsterdam_pageTitle", d.Title)
|
ctxt.VarMap().Set("amsterdam_pageTitle", d.Title)
|
||||||
|
|||||||
+88
-2
@@ -11,12 +11,19 @@ package ui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"git.erbosoft.com/amy/amsterdam/database"
|
"git.erbosoft.com/amy/amsterdam/database"
|
||||||
|
"git.erbosoft.com/amy/amsterdam/util"
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MenuItem represents an itrem within a menu definition.
|
// MenuItem represents an item within a menu definition.
|
||||||
type MenuItem struct {
|
type MenuItem struct {
|
||||||
Text string `yaml:"text"`
|
Text string `yaml:"text"`
|
||||||
Link string `yaml:"link"`
|
Link string `yaml:"link"`
|
||||||
@@ -35,10 +42,23 @@ func (mi *MenuItem) Show(ctxt AmContext) bool {
|
|||||||
switch mi.P.PermSet {
|
switch mi.P.PermSet {
|
||||||
case "user":
|
case "user":
|
||||||
eperm = u.BaseLevel
|
eperm = u.BaseLevel
|
||||||
|
case "community":
|
||||||
|
eperm = ctxt.EffectiveLevel()
|
||||||
default:
|
default:
|
||||||
eperm = database.AmRole("NotInList").Level()
|
eperm = database.AmRole("NotInList").Level()
|
||||||
}
|
}
|
||||||
|
if util.IsNumeric(mi.Permission) {
|
||||||
|
v, _ := strconv.Atoi(mi.Permission)
|
||||||
|
return uint16(v) <= eperm
|
||||||
|
}
|
||||||
|
switch mi.P.PermSet {
|
||||||
|
case "user":
|
||||||
return database.AmTestPermission(mi.Permission, eperm)
|
return database.AmTestPermission(mi.Permission, eperm)
|
||||||
|
case "community":
|
||||||
|
return ctxt.CurrentCommunity().TestPermission(mi.Permission, eperm)
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MenuDefinition represents a full menu definition.
|
// MenuDefinition represents a full menu definition.
|
||||||
@@ -48,6 +68,7 @@ type MenuDefinition struct {
|
|||||||
PermSet string `yaml:"permSet"`
|
PermSet string `yaml:"permSet"`
|
||||||
Warning string `yaml:"warning"`
|
Warning string `yaml:"warning"`
|
||||||
Items []MenuItem `yaml:"items"`
|
Items []MenuItem `yaml:"items"`
|
||||||
|
Tag string
|
||||||
}
|
}
|
||||||
|
|
||||||
// MenuDefs represents the set of all menu definitions.
|
// MenuDefs represents the set of all menu definitions.
|
||||||
@@ -62,9 +83,19 @@ var initMenuData []byte
|
|||||||
// menuDefinitions gives the menu definitions.
|
// menuDefinitions gives the menu definitions.
|
||||||
var menuDefinitions MenuDefs
|
var menuDefinitions MenuDefs
|
||||||
|
|
||||||
|
// Cache of community menus.
|
||||||
|
var menuCache *lru.Cache
|
||||||
|
|
||||||
|
// Mutex controlling access to the cache.
|
||||||
|
var menuCacheMutex sync.Mutex
|
||||||
|
|
||||||
// init loads the menu definitions.
|
// init loads the menu definitions.
|
||||||
func init() {
|
func init() {
|
||||||
if err := yaml.Unmarshal(initMenuData, &menuDefinitions); err != nil {
|
var err error
|
||||||
|
if menuCache, err = lru.New(100); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if err = yaml.Unmarshal(initMenuData, &menuDefinitions); err != nil {
|
||||||
panic(err) // can't happen
|
panic(err) // can't happen
|
||||||
}
|
}
|
||||||
menuDefinitions.table = make(map[string]*MenuDefinition)
|
menuDefinitions.table = make(map[string]*MenuDefinition)
|
||||||
@@ -73,6 +104,7 @@ func init() {
|
|||||||
for j := range menuDefinitions.D[i].Items {
|
for j := range menuDefinitions.D[i].Items {
|
||||||
menuDefinitions.D[i].Items[j].P = &(menuDefinitions.D[i])
|
menuDefinitions.D[i].Items[j].P = &(menuDefinitions.D[i])
|
||||||
}
|
}
|
||||||
|
menuDefinitions.D[i].Tag = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,3 +112,57 @@ func init() {
|
|||||||
func AmMenu(name string) *MenuDefinition {
|
func AmMenu(name string) *MenuDefinition {
|
||||||
return menuDefinitions.table[name]
|
return menuDefinitions.table[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* AmBuildCommunityMenu buids a community menu for the specified community.
|
||||||
|
* Parameters:
|
||||||
|
* comm - The community to build the menu for.
|
||||||
|
* Returns:
|
||||||
|
* The new menu definition.
|
||||||
|
* Standard Go error status.
|
||||||
|
*/
|
||||||
|
func AmBuildCommunityMenu(comm *database.Community) (*MenuDefinition, error) {
|
||||||
|
menuCacheMutex.Lock()
|
||||||
|
defer menuCacheMutex.Unlock()
|
||||||
|
m, ok := menuCache.Get(comm.Id)
|
||||||
|
if ok {
|
||||||
|
return m.(*MenuDefinition), nil
|
||||||
|
}
|
||||||
|
sdef, err := database.AmGetCommunityServices(comm.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
slices.SortFunc(sdef, func(a, b *database.ServiceDef) int {
|
||||||
|
return a.LinkSequence - b.LinkSequence
|
||||||
|
})
|
||||||
|
mia := make([]MenuItem, len(sdef))
|
||||||
|
for i, sd := range sdef {
|
||||||
|
mia[i].Text = sd.Title
|
||||||
|
mia[i].Link = strings.ReplaceAll(sd.Link, "[CID]", comm.Alias)
|
||||||
|
mia[i].Disabled = false
|
||||||
|
if sd.RequirePermission == "" {
|
||||||
|
if sd.RequireRole == "" {
|
||||||
|
mia[i].Permission = ""
|
||||||
|
} else {
|
||||||
|
mia[i].Permission = fmt.Sprintf("%d", database.AmRole(sd.RequireRole).Level())
|
||||||
|
}
|
||||||
|
} else if sd.RequireRole == "" {
|
||||||
|
mia[i].Permission = sd.RequirePermission
|
||||||
|
} else {
|
||||||
|
v1 := comm.PermissionLevel(sd.RequirePermission)
|
||||||
|
v2 := database.AmRole(sd.RequireRole).Level()
|
||||||
|
if v2 > v1 {
|
||||||
|
v1 = v2
|
||||||
|
}
|
||||||
|
mia[i].Permission = fmt.Sprintf("%d", v1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
md := MenuDefinition{
|
||||||
|
ID: "community",
|
||||||
|
Title: comm.Name,
|
||||||
|
PermSet: "community",
|
||||||
|
Items: mia,
|
||||||
|
Tag: "community",
|
||||||
|
}
|
||||||
|
menuCache.Add(comm.Id, &md)
|
||||||
|
return &md, nil
|
||||||
|
}
|
||||||
|
|||||||
+17
-1
@@ -34,6 +34,21 @@ func sendPageData(ctxt echo.Context, amctxt AmContext, command string, data any)
|
|||||||
err = ctxt.Render(amctxt.RC(), fmt.Sprintf("%v", data), amctxt)
|
err = ctxt.Render(amctxt.RC(), fmt.Sprintf("%v", data), amctxt)
|
||||||
case "framed_template":
|
case "framed_template":
|
||||||
amctxt.VarMap().Set("amsterdam_innerPage", data)
|
amctxt.VarMap().Set("amsterdam_innerPage", data)
|
||||||
|
menus := make([]*MenuDefinition, 2)
|
||||||
|
switch amctxt.LeftMenu() {
|
||||||
|
case "top":
|
||||||
|
menus[0] = AmMenu("top")
|
||||||
|
case "community":
|
||||||
|
md, err := AmBuildCommunityMenu(amctxt.CurrentCommunity())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
menus[0] = md
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown left menu context: %s", amctxt.LeftMenu())
|
||||||
|
}
|
||||||
|
menus[1] = AmMenu("fixed")
|
||||||
|
amctxt.VarMap().Set("amsterdam_leftMenus", menus)
|
||||||
err = ctxt.Render(amctxt.RC(), "frame.jet", amctxt)
|
err = ctxt.Render(amctxt.RC(), "frame.jet", amctxt)
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unknown rendering type: %s", command)
|
err = fmt.Errorf("unknown rendering type: %s", command)
|
||||||
@@ -69,11 +84,12 @@ func ErrorPage(ctxt AmContext, input_err error) (string, any, error) {
|
|||||||
func AmWrap(myfunc func(AmContext) (string, any, error)) echo.HandlerFunc {
|
func AmWrap(myfunc func(AmContext) (string, any, error)) echo.HandlerFunc {
|
||||||
return func(ctxt echo.Context) error {
|
return func(ctxt echo.Context) error {
|
||||||
// Create the AmContext.
|
// Create the AmContext.
|
||||||
amctxt, aerr := NewAmContext(ctxt)
|
amctxt, aerr := AmCreateContext(ctxt)
|
||||||
if aerr != nil {
|
if aerr != nil {
|
||||||
ctxt.Logger().Errorf("Session creation error: %v", aerr)
|
ctxt.Logger().Errorf("Session creation error: %v", aerr)
|
||||||
return aerr
|
return aerr
|
||||||
}
|
}
|
||||||
|
defer amctxt.Done()
|
||||||
|
|
||||||
// Check IP banning.
|
// Check IP banning.
|
||||||
banmsg, banerr := database.AmTestIPBan(ctxt.RealIP())
|
banmsg, banerr := database.AmTestIPBan(ctxt.RealIP())
|
||||||
|
|||||||
+9
-3
@@ -81,11 +81,17 @@
|
|||||||
<div class="flex">
|
<div class="flex">
|
||||||
<!-- LEFT SIDEBAR -->
|
<!-- LEFT SIDEBAR -->
|
||||||
<div class="w-48 bg-blue-400 p-2">
|
<div class="w-48 bg-blue-400 p-2">
|
||||||
{{ .SetScratch("__menu", AmMenu("top")) }}
|
{{ range i, m := amsterdam_leftMenus }}
|
||||||
{{ .SubRender("menu_left.jet") | raw }}
|
{{ if i > 0 }}
|
||||||
<div class="mb-2 mt-2"> </div>
|
<div class="mb-2 mt-2"> </div>
|
||||||
{{ .SetScratch("__menu", AmMenu("fixed")) }}
|
{{ end }}
|
||||||
|
{{ .SetScratch("__menu", m) }}
|
||||||
|
{{ if m.Tag == "community" }}
|
||||||
|
{{ .SubRender("menu_left_comm.jet") | raw }}
|
||||||
|
{{ else }}
|
||||||
{{ .SubRender("menu_left.jet") | raw }}
|
{{ .SubRender("menu_left.jet") | raw }}
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- MAIN CONTENT -->
|
<!-- MAIN CONTENT -->
|
||||||
|
|||||||
@@ -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/.
|
||||||
|
*}
|
||||||
|
{{ menu := .GetScratch("__menu") }}
|
||||||
|
{{ comm := .CurrentCommunity() }}
|
||||||
|
<div class="mb-2 mt-2">
|
||||||
|
<div class="mb-1">
|
||||||
|
<img src="/img/builtin/default-community.jpg"
|
||||||
|
alt="{{ comm.Name }}" class="w-28 h-16 rounded">
|
||||||
|
</div>
|
||||||
|
<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 }}
|
||||||
|
{{ if .IsMember() }}
|
||||||
|
<div class="mb-1"> </div>
|
||||||
|
<div class="mb-1"><a href="/TODO/comm/{{ comm.Alias }}/unjoin">Unjoin</a></div>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
+10
-6
@@ -63,9 +63,9 @@ func EditProfileForm(ctxt ui.AmContext) (string, any, error) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
dlg.Field("remind").Value = u.PassReminder
|
dlg.Field("remind").Value = u.PassReminder
|
||||||
dlg.Field("prefix").SetVal(ci.Prefix)
|
dlg.Field("prefix").SetVal(ci.Prefix)
|
||||||
dlg.Field("first").Value = ci.GivenName
|
dlg.Field("first").SetVal(ci.GivenName)
|
||||||
dlg.Field("mid").SetVal(ci.MiddleInit)
|
dlg.Field("mid").SetVal(ci.MiddleInit)
|
||||||
dlg.Field("last").Value = ci.FamilyName
|
dlg.Field("last").SetVal(ci.FamilyName)
|
||||||
dlg.Field("suffix").SetVal(ci.Suffix)
|
dlg.Field("suffix").SetVal(ci.Suffix)
|
||||||
dlg.Field("company").SetVal(ci.Company)
|
dlg.Field("company").SetVal(ci.Company)
|
||||||
dlg.Field("addr1").SetVal(ci.Addr1)
|
dlg.Field("addr1").SetVal(ci.Addr1)
|
||||||
@@ -141,9 +141,9 @@ func EditProfile(ctxt ui.AmContext) (string, any, error) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
nci := ci.Clone()
|
nci := ci.Clone()
|
||||||
nci.Prefix = dlg.Field("prefix").ValPtr()
|
nci.Prefix = dlg.Field("prefix").ValPtr()
|
||||||
nci.GivenName = dlg.Field("first").Value
|
nci.GivenName = dlg.Field("first").ValPtr()
|
||||||
nci.MiddleInit = dlg.Field("mid").ValPtr()
|
nci.MiddleInit = dlg.Field("mid").ValPtr()
|
||||||
nci.FamilyName = dlg.Field("last").Value
|
nci.FamilyName = dlg.Field("last").ValPtr()
|
||||||
nci.Suffix = dlg.Field("suffix").ValPtr()
|
nci.Suffix = dlg.Field("suffix").ValPtr()
|
||||||
nci.Company = dlg.Field("company").ValPtr()
|
nci.Company = dlg.Field("company").ValPtr()
|
||||||
nci.Addr1 = dlg.Field("addr1").ValPtr()
|
nci.Addr1 = dlg.Field("addr1").ValPtr()
|
||||||
@@ -369,11 +369,15 @@ func ShowProfile(ctxt ui.AmContext) (string, any, error) {
|
|||||||
if ci.Prefix != nil && *ci.Prefix != "" {
|
if ci.Prefix != nil && *ci.Prefix != "" {
|
||||||
b.WriteString(*ci.Prefix + " ")
|
b.WriteString(*ci.Prefix + " ")
|
||||||
}
|
}
|
||||||
b.WriteString(ci.GivenName)
|
if ci.GivenName != nil {
|
||||||
|
b.WriteString(*ci.GivenName)
|
||||||
|
}
|
||||||
if ci.MiddleInit != nil && *ci.MiddleInit != "" && *ci.MiddleInit != " " {
|
if ci.MiddleInit != nil && *ci.MiddleInit != "" && *ci.MiddleInit != " " {
|
||||||
b.WriteString(" " + *ci.MiddleInit + ".")
|
b.WriteString(" " + *ci.MiddleInit + ".")
|
||||||
}
|
}
|
||||||
b.WriteString(" " + ci.FamilyName)
|
if ci.FamilyName != nil {
|
||||||
|
b.WriteString(" " + *ci.FamilyName)
|
||||||
|
}
|
||||||
if ci.Suffix != nil && *ci.Suffix != "" {
|
if ci.Suffix != nil && *ci.Suffix != "" {
|
||||||
b.WriteString(" " + *ci.Suffix)
|
b.WriteString(" " + *ci.Suffix)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user