diff --git a/conference.go b/conference.go new file mode 100644 index 0000000..2c8b75d --- /dev/null +++ b/conference.go @@ -0,0 +1,72 @@ +/* + * 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 main contains the high-level Amsterdam logic. +package main + +import ( + "errors" + "net/http" + + "git.erbosoft.com/amy/amsterdam/database" + "git.erbosoft.com/amy/amsterdam/ui" +) + +// conferencesPrequel consolidates some of the basic conference checks into one function. +func conferencesPrequel(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() + b, err := database.AmTestService(comm, "Conference") + if err != nil { + return ui.ErrorPage(ctxt, err) + } + if !b { + ctxt.SetRC(http.StatusNotFound) + return ui.ErrorPage(ctxt, errors.New("this community does not use conferencing services")) + } + if comm.MembersOnly && !ctxt.IsMember() { + ctxt.SetRC(http.StatusForbidden) + return ui.ErrorPage(ctxt, errors.New("you are not a member of this community")) + } + if !comm.TestPermission("Community.Read", ctxt.EffectiveLevel()) { + ctxt.SetRC(http.StatusForbidden) + return ui.ErrorPage(ctxt, errors.New("you are not authorized access to conferences")) + } + return "", nil, nil +} + +/* Conferences displayes the list of conferences in a community. + * 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 Conferences(ctxt ui.AmContext) (string, any, error) { + cmd, arg, err := conferencesPrequel(ctxt) + if cmd != "" { + return cmd, arg, err + } + comm := ctxt.CurrentCommunity() + ctxt.VarMap().Set("commName", comm.Name) + ctxt.VarMap().Set("commAlias", comm.Alias) + ctxt.VarMap().Set("amsterdam_pageTitle", "Conference Listing: "+comm.Name) + clist, err := database.AmGetCommunityConferences(comm.Id, + comm.TestPermission("Community.ShowHiddenObjects", ctxt.EffectiveLevel())) + if err != nil { + return ui.ErrorPage(ctxt, err) + } + ctxt.VarMap().Set("conferences", clist) + return "framed_template", "conflist.jet", err +} diff --git a/database/conference.go b/database/conference.go index c064843..baff68f 100644 --- a/database/conference.go +++ b/database/conference.go @@ -19,6 +19,7 @@ import ( // Conference struct is the top-level structure for a conference. type Conference struct { + Mutex sync.Mutex ConfId int32 `db:"confid"` CreateDate time.Time `db:"createdate"` LastUpdate *time.Time `db:"lastupdate"` @@ -27,8 +28,8 @@ type Conference struct { CreateLevel uint16 `db:"create_lvl"` HideLevel uint16 `db:"hide_lvl"` NukeLevel uint16 `db:"nuke_lvl"` - ChangeLevel uint16 `db:"change_level"` - DeleteLevel uint16 `db:"delete_level"` + ChangeLevel uint16 `db:"change_lvl"` + DeleteLevel uint16 `db:"delete_lvl"` TopTopic int16 `db:"top_topic"` Name string `db:"name"` Description *string `db:"descr"` @@ -54,6 +55,95 @@ func init() { } } +// Aliases returns the list of aliases for this conference. +func (c *Conference) Aliases() ([]string, error) { + rs, err := amdb.Query("SELECT alias FROM confalias WHERE confid = ? ORDER BY alias", c.ConfId) + if err != nil { + return nil, err + } + rc := make([]string, 0, 5) + for rs.Next() { + var a string + rs.Scan(&a) + rc = append(rc, a) + } + return rc, nil +} + +// AliasesQ returns the list of aliases for this conference, quietly. +func (c *Conference) AliasesQ() []string { + rc, _ := c.Aliases() + return rc +} + +// Hosts returns the list of users that host this conference. +func (c *Conference) Hosts() ([]*User, error) { + rs, err := amdb.Query("SELECT uid FROM confmember WHERE confid = ? AND granted_lvl = ?", + c.ConfId, AmRole("Conference.Host").Level()) + if err != nil { + return nil, err + } + rc := make([]*User, 0, 5) + for rs.Next() { + var uid int32 + rs.Scan(&uid) + u, err := AmGetUser(uid) + if err == nil { + rc = append(rc, u) + } + } + return rc, nil +} + +// Hosts returns the list of users that host this conference, quietly. +func (c *Conference) HostsQ() []*User { + rc, _ := c.Hosts() + return rc +} + +// Membership returns a membership flag and granted level for the user in this conference. +func (c *Conference) Membership(u *User) (bool, uint16, error) { + rs, err := amdb.Query("SELECT granted_lvl FROM confmember WHERE confid = ? AND uid = ?", c.ConfId, u.Uid) + if err != nil { + return false, 0, err + } + if rs.Next() { + var level uint16 + rs.Scan(&level) + return true, level, nil + } + return false, 0, nil +} + +/* TestPermission is shorthand that tests if a user has a permission with respect to the conference. + * Parameters: + * user - The user to be checked. + * perm - The permission to be tested. + * Returns: + * true if the user has the permission, false if not. + * Standard Go error status. + */ +func (c *Conference) TestPermission(perm string, level uint16) bool { + switch perm { + case "Conference.Read": + return level >= c.ReadLevel + case "Conference.Post": + return level >= c.PostLevel + case "Conference.Create": + return level >= c.CreateLevel + case "Conference.Hide": + return level >= c.HideLevel + case "Conference.Nuke": + return level >= c.NukeLevel + case "Conference.Change": + return level >= c.ChangeLevel + case "Conference.Delete": + return level >= c.DeleteLevel + default: + return AmTestPermission(perm, level) + } +} + /* AmGetConference returns a conference given its ID. * Parameters: * id - The ID of the conference. diff --git a/database/servicedefs.yaml b/database/servicedefs.yaml index 1acc79c..8edbf65 100644 --- a/database/servicedefs.yaml +++ b/database/servicedefs.yaml @@ -43,7 +43,7 @@ domains: requirePermission: "Community.Read" requireRole: "" linkSequence: 500 - link: "/TODO/comm/[CID]/conf" + link: "/comm/[CID]/conf" title: "Conferences" - id: "Members" index: 4 diff --git a/database/services.go b/database/services.go index add0fd4..301c06e 100644 --- a/database/services.go +++ b/database/services.go @@ -246,3 +246,15 @@ func AmOnUserLeaveCommunityServices(c *Community, u *User) error { } return err } + +func AmTestService(c *Community, serviceId string) (bool, error) { + arr, err := AmGetCommunityServices(c.Id) + if err == nil { + for _, svc := range arr { + if svc.Id == serviceId { + return true, nil + } + } + } + return false, err +} diff --git a/main.go b/main.go index a6f4d00..a7d80ce 100644 --- a/main.go +++ b/main.go @@ -83,6 +83,7 @@ func setupEcho() *echo.Echo { e.POST("/comm/:cid/admin/profile", ui.AmWrap(EditCommunityProfile)) e.GET("/comm/:cid/admin/logo", ui.AmWrap(CommunityLogoForm)) e.POST("/comm/:cid/admin/logo", ui.AmWrap(EditCommunityLogo)) + e.GET("/comm/:cid/conf", ui.AmWrap(Conferences)) return e } diff --git a/ui/views/conflist.jet b/ui/views/conflist.jet new file mode 100644 index 0000000..69710d0 --- /dev/null +++ b/ui/views/conflist.jet @@ -0,0 +1,69 @@ +{* + * 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/. + *} +
+ About Conferences: Conferences are discussion areas within this community. + Click on a conference name to view topics and participate in discussions. +
+