From 83bd817630a29eaf4afafe13acf682a30315566a Mon Sep 17 00:00:00 2001 From: Amy Gale Ruth Bowersox Date: Fri, 17 Oct 2025 23:21:31 -0600 Subject: [PATCH] added community profile setting from dialog (untested) --- communityadmin.go | 100 ++++++++++++++++++++++++++++++++++++++++++ database/community.go | 35 +++++++++++++++ main.go | 1 + ui/amcontext.go | 9 ++++ userdata.go | 4 ++ 5 files changed, 149 insertions(+) diff --git a/communityadmin.go b/communityadmin.go index 21b27c6..10a6b66 100644 --- a/communityadmin.go +++ b/communityadmin.go @@ -14,9 +14,11 @@ import ( "errors" "fmt" "net/http" + "strconv" "git.erbosoft.com/amy/amsterdam/database" "git.erbosoft.com/amy/amsterdam/ui" + "git.erbosoft.com/amy/amsterdam/util" ) /* CommunityAdminMenu renders the community administration menu. @@ -122,3 +124,101 @@ func CommunityProfileForm(ctxt ui.AmContext) (string, any, error) { } return ui.ErrorPage(ctxt, err) } + +// levelFld is a quick routine to extract a level value from a drop-down. +func levelFld(d *ui.Dialog, name string) uint16 { + v, _ := strconv.Atoi(d.Field(name).Value) + return uint16(v) +} + +/* EditCommunityProfile updates the community's profile from the dialog. + * Parameters: + * ctxt - The AmContext for the request. + * Returns: + * Command string dictating what to be rendered. + * Data as a parameter for the command string. + * Standard Go error status. + */ +func EditCommunityProfile(ctxt ui.AmContext) (string, any, error) { + err := ctxt.SetCommunityContext(ctxt.URLParam("cid")) + if err != nil { + ctxt.SetRC(http.StatusNotFound) + return ui.ErrorPage(ctxt, err) + } + comm := ctxt.CurrentCommunity() + if !comm.TestPermission("Community.Write", ctxt.EffectiveLevel()) { + ctxt.SetRC(http.StatusForbidden) + return ui.ErrorPage(ctxt, errors.New("you are not permitted to access this page")) + } + dlg, err := ui.AmLoadDialog("commprofile") + if err == nil { + setupCommunityProfileDialog(dlg, comm) + dlg.LoadFromForm(ctxt) + + action := dlg.WhichButton(ctxt) + if action == "cancel" { + return "redirect", fmt.Sprintf("/comm/%s/admin", comm.Alias), nil + } + if action == "update" { + err = dlg.Validate() + if err != nil { + return dlg.RenderError(ctxt, err.Error()) + } + var ci *database.ContactInfo + ci, err = comm.ContactInfo() + if err != nil { + return dlg.RenderError(ctxt, err.Error()) + } + var flags *util.OptionSet + flags, err = comm.Flags() + if err != nil { + return ui.ErrorPage(ctxt, err) + } + nci := ci.Clone() + nci.URL = dlg.Field("url").ValPtr() + nci.Company = dlg.Field("company").ValPtr() + nci.Addr1 = dlg.Field("addr1").ValPtr() + nci.Addr2 = dlg.Field("addr2").ValPtr() + nci.Locality = dlg.Field("loc").ValPtr() + nci.Region = dlg.Field("reg").ValPtr() + nci.PostalCode = dlg.Field("pcode").ValPtr() + nci.Country = dlg.Field("country").ValPtr() + _, err = nci.Save() + ci = nci + if err == nil { + var joinkey *string = nil + if dlg.Field("comtype").Value == "1" { + joinkey = dlg.Field("joinkey").ValPtr() + } + var hidedir, hidesearch bool + switch dlg.Field("hidemode").Value { + case "NONE": + hidedir = false + hidesearch = false + case "DIRECTORY": + hidedir = false + hidesearch = true + case "BOTH": + hidedir = true + hidesearch = true + } + err = comm.SetProfileData(dlg.Field("name").Value, dlg.Field("alias").Value, dlg.Field("synopsis").ValPtr(), + dlg.Field("rules").ValPtr(), dlg.Field("language").ValPtr(), joinkey, dlg.Field("membersonly").IsChecked(), + hidedir, hidesearch, levelFld(dlg, "read_lvl"), levelFld(dlg, "write_lvl"), levelFld(dlg, "create_lvl"), + levelFld(dlg, "delete_lvl"), levelFld(dlg, "join_lvl")) + } + if err == nil { + flags.Set(database.CommunityFlagPicturesInPosts, dlg.Field("pic_in_post").IsChecked()) + err = comm.SaveFlags(flags) + } + if err != nil { + ctxt.ClearCommunityContext() + return dlg.RenderError(ctxt, err.Error()) + } else { + return "redirect", fmt.Sprintf("/comm/%s/admin", comm.Alias), nil + } + } + return dlg.RenderError(ctxt, "No known button click on POST to community profile.") + } + return ui.ErrorPage(ctxt, err) +} diff --git a/database/community.go b/database/community.go index a9f6881..d450fc4 100644 --- a/database/community.go +++ b/database/community.go @@ -267,6 +267,41 @@ func (c *Community) SaveFlags(f *util.OptionSet) error { return err } +// SetProfileData sets all the "settable" profile data +func (c *Community) SetProfileData(name string, alias string, synopsis *string, rules *string, language *string, + joinkey *string, membersonly bool, hideDirectory bool, hideSearch bool, read_lvl uint16, write_lvl uint16, + create_lvl uint16, delete_lvl uint16, join_lvl uint16) error { + c.Mutex.Lock() + defer c.Mutex.Unlock() + _, err := amdb.Exec(`UPDATE communities SET commname = ?, alias = ?, synopsis = ? rules = ?, language = ?, + joinkey = ?, membersonly = ?, hide_dir = ?, hide_search = ?, read_lvl = ?, write_lvl = ?, create_lvl = ?, + delete_lvl = ?, join_lvl = ?, lastupdate = NOW() WHERE commid = ?`, + name, alias, synopsis, rules, joinkey, membersonly, hideDirectory, hideSearch, read_lvl, write_lvl, + create_lvl, delete_lvl, join_lvl, c.Id) + if err == nil { + c.Name = name + c.Alias = alias + c.Synopsis = synopsis + c.Rules = rules + c.Language = language + c.JoinKey = joinkey + c.MembersOnly = membersonly + c.HideFromDirectory = hideDirectory + c.HideFromSearch = hideSearch + c.ReadLevel = read_lvl + c.WriteLevel = write_lvl + c.CreateLevel = create_lvl + c.DeleteLevel = delete_lvl + c.JoinLevel = join_lvl + rs, err2 := amdb.Query("SELECT lastupdate FROM communities WHERE commid = ?", c.Id) + if err2 != nil { + rs.Next() + rs.Scan(&c.LastUpdate) + } + } + return err +} + /* AmGetCommunity returns a reference to the specified community. * Parameters: * id - The ID of the community. diff --git a/main.go b/main.go index df59211..84440f1 100644 --- a/main.go +++ b/main.go @@ -67,6 +67,7 @@ func setupEcho() *echo.Echo { e.GET("/comm/:cid/profile", ui.AmWrap(ShowCommunity)) e.GET("/comm/:cid/admin", ui.AmWrap(CommunityAdminMenu)) e.GET("/comm/:cid/admin/profile", ui.AmWrap(CommunityProfileForm)) + e.POST("/comm/:cid/admin/profile", ui.AmWrap(EditCommunityProfile)) return e } diff --git a/ui/amcontext.go b/ui/amcontext.go index 3340a07..6b2c688 100644 --- a/ui/amcontext.go +++ b/ui/amcontext.go @@ -30,6 +30,7 @@ import ( // AmContext is the interface for Amsterdam's wrapper context that exposes the required functionality. type AmContext interface { + ClearCommunityContext() ClearLoginCookie() ClearSession() CurrentCommunity() *database.Community @@ -84,6 +85,14 @@ type amContext struct { isMemberLocked bool } +// ClearCommunityContext clears the community context so changes will be reflected. +func (c *amContext) ClearCommunityContext() { + c.community = nil + c.isMember = false + c.isMemberLocked = false + c.effectiveLevel = c.user.BaseLevel +} + // ClearLoginCookie overwrites and removes the login cookie. func (c *amContext) ClearLoginCookie() { cookie := new(http.Cookie) diff --git a/userdata.go b/userdata.go index bbd4ce3..4a4caa9 100644 --- a/userdata.go +++ b/userdata.go @@ -124,6 +124,10 @@ func EditProfile(ctxt ui.AmContext) (string, any, error) { return "redirect", target, nil } if action == "update" { + err = dlg.Validate() + if err != nil { + return dlg.RenderError(ctxt, err.Error()) + } var ci *database.ContactInfo ci, err = u.ContactInfo() if err == nil {