diff --git a/conference.go b/conference.go index 3c22619..ed75b62 100644 --- a/conference.go +++ b/conference.go @@ -40,15 +40,18 @@ func Conferences(ctxt ui.AmContext) (string, any) { ctxt.VarMap().Set("commName", comm.Name) ctxt.VarMap().Set("commAlias", comm.Alias) ctxt.SetFrameTitle("Conference Listing: " + comm.Name) - clist, err := database.AmGetCommunityConferences(ctxt.Ctx(), comm.Id, - comm.TestPermission("Community.ShowHiddenObjects", ctxt.EffectiveLevel())) + clist, err := database.AmListConferences(ctxt.Ctx(), comm.Id, comm.TestPermission("Community.ShowHiddenObjects", ctxt.EffectiveLevel())) if err != nil { return "error", err } ctxt.VarMap().Set("conferences", clist) if len(clist) > 0 { newflag := make([]bool, len(clist)) - for i, conf := range clist { + for i, c := range clist { + conf, err := c.Conf(ctxt.Ctx()) + if err != nil { + return "error", err + } msgCount, err := conf.UnreadMessages(ctxt.Ctx(), ctxt.CurrentUser()) if err != nil { return "error", err diff --git a/conferenceadmin.go b/conferenceadmin.go index cdf6e87..6910be6 100644 --- a/conferenceadmin.go +++ b/conferenceadmin.go @@ -789,3 +789,43 @@ func CreateConference(ctxt ui.AmContext) (string, any) { log.Infof("Created conference '%s'", conf.Name) return "redirect", fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, alias) } + +/* ManageConferenceList displays the list for managing conferences. + * Parameters: + * ctxt - The AmContext for the request. + * Returns: + * Command string dictating what to be rendered. + * Data as a parameter for the command string. + */ +func ManageConferenceList(ctxt ui.AmContext) (string, any) { + comm := ctxt.CurrentCommunity() + if !comm.TestPermission("Community.Create", ctxt.EffectiveLevel()) { + return "error", ENOPERM + } + + clist, err := database.AmListConferences(ctxt.Ctx(), comm.Id, true) + if err != nil { + return "error", err + } + + ntopics := make([]int, len(clist)) + nposts := make([]int, len(clist)) + for i, c := range clist { + conf, err := c.Conf(ctxt.Ctx()) + if err != nil { + return "error", err + } + ntopics[i], nposts[i], err = conf.Stats(ctxt.Ctx()) + if err != nil { + return "error", err + } + } + ctxt.VarMap().Set("confs", clist) + ctxt.VarMap().Set("ntopics", ntopics) + ctxt.VarMap().Set("nposts", nposts) + ctxt.VarMap().Set("commName", comm.Name) + ctxt.VarMap().Set("baseUrl", fmt.Sprintf("/comm/%s/manage_conf", comm.Alias)) + ctxt.VarMap().Set("returnUrl", fmt.Sprintf("/comm/%s/conf", comm.Alias)) + ctxt.SetFrameTitle("Manage Conference List") + return "framed", "manage_conflist.jet" +} diff --git a/database/conference.go b/database/conference.go index 15eeafa..be3ce60 100644 --- a/database/conference.go +++ b/database/conference.go @@ -70,6 +70,23 @@ type ConferenceProperties struct { Data *string `db:"data"` // property data } +// ConferenceSummary represents summary information about a conference. +type ConferenceSummary struct { + ConfId int32 // conference ID + Name string // conference name + Alias string // an alias for the conference + LastUpdate *time.Time // last update date/time + Hosts []string // usernames of the hosts + Description string // description string + Sequence int16 // sequence number in the list + Hidden bool // hidden in list? +} + +// Conf gets the conference from the summary. +func (cs *ConferenceSummary) Conf(ctx context.Context) (*Conference, error) { + return AmGetConference(ctx, cs.ConfId) +} + // Default spacing between sequence numbers in commtoconf table. const COMMTOCONF_SEQ_SPACING = 10 @@ -786,6 +803,15 @@ func (c *Conference) GetActiveUserEMailAddrs(ctx context.Context, userSelect, da return rc, nil } +// Stats retrieves the number of topics and posts in this conference. +func (c *Conference) Stats(ctx context.Context) (int, int, error) { + row := amdb.QueryRowContext(ctx, "SELECT COUNT(*), SUM(top_message + 1) FROM topics WHERE confid = ?", c.ConfId) + ntopic := 0 + npost := 0 + err := row.Scan(&ntopic, &npost) + return ntopic, npost, err +} + /* AmGetConference returns a conference given its ID. * Parameters: * ctx - Standard Go context value. @@ -864,37 +890,44 @@ func AmGetConferenceByAliasInCommunity(ctx context.Context, cid int32, alias str return nil, err } -/* AmGetCommunityConferences returns all conferences for a given community. +/* AmListConferences returns all conferences for a given community. * Parameters: * ctx - Standard Go context value. * cid - Community ID to get conferences for. * showHidden - true to show hidden conferences. * Returns: - * Array containing the COnference pointers, or nil. + * Array containing the ConferenceSummary pointers, or nil. * Stanbard Go error status. */ -func AmGetCommunityConferences(ctx context.Context, cid int32, showHidden bool) ([]*Conference, error) { +func AmListConferences(ctx context.Context, cid int32, showHidden bool) ([]*ConferenceSummary, error) { q := "" if !showHidden { q = " AND x.hide_list = 0" } - rs, err := amdb.QueryContext(ctx, `SELECT x.confid FROM commtoconf x, confs c WHERE x.confid = c.confid - AND x.commid = ?`+q+" ORDER BY x.sequence, c.name", cid) + rs, err := amdb.QueryContext(ctx, `SELECT x.confid, c.name, c.lastupdate, c.descr, x.sequence, x.hide_list FROM commtoconf x, confs c + WHERE x.confid = c.confid AND x.commid = ?`+q+" ORDER BY x.sequence, c.name", cid) if err != nil { return nil, err } - rc := make([]*Conference, 0, 6) + rc := make([]*ConferenceSummary, 0) for rs.Next() { - var confid int32 - if err = rs.Scan(&confid); err == nil { - conf, err := AmGetConference(ctx, confid) - if err == nil { - rc = append(rc, conf) - } else { - log.Errorf("AmGetCommunityConferences conference error: %v", err) - } + var cs ConferenceSummary + if err = rs.Scan(&(cs.ConfId), &(cs.Name), &(cs.LastUpdate), &(cs.Description), &(cs.Sequence), &(cs.Hidden)); err == nil { + rc = append(rc, &cs) } else { - log.Errorf("AmGetCommunityConferences scan error: %v", err) + return nil, err + } + } + for i := range rc { + row := amdb.QueryRowContext(ctx, "SELECT alias FROM confalias WHERE confid = ?", rc[i].ConfId) + err = row.Scan(&(rc[i].Alias)) + if err != nil { + return nil, err + } + err = amdb.SelectContext(ctx, &(rc[i].Hosts), `SELECT u.username FROM confmember m, users u WHERE u.uid = m.uid AND m.confid = ? + AND m.granted_lvl = ? ORDER BY u.username`, rc[i].ConfId, AmRole("Conference.Host").Level()) + if err != nil { + return nil, err } } return rc, nil diff --git a/main.go b/main.go index 4a71490..e503e8c 100644 --- a/main.go +++ b/main.go @@ -117,6 +117,7 @@ func setupEcho() *echo.Echo { // conference group commGroup.GET("/create_conf", ui.AmWrap(CreateConferenceForm)) commGroup.POST("/create_conf", ui.AmWrap(CreateConference)) + commGroup.GET("/manage_conf", ui.AmWrap(ManageConferenceList)) commGroup.GET("/conf", ui.AmWrap(Conferences), ui.ValidateConference) confGroup := commGroup.Group("/conf/:confid", ui.ValidateConference, ui.SetConference) confGroup.GET("", ui.AmWrap(Topics)) diff --git a/ui/views/conflist.jet b/ui/views/conflist.jet index 7f5669f..afe4042 100644 --- a/ui/views/conflist.jet +++ b/ui/views/conflist.jet @@ -23,7 +23,7 @@ 🟣
- {{ c.Name }} - Latest activity: {{ DisplayActivity(c.LastUpdate, .) }} {{ if newflag[i] }} @@ -31,16 +31,15 @@ {{ end }}
- {{ hl := c.HostsQ(.Ctx()) }} - {{ if len(hl) == 1 }} + {{ if len(c.Hosts) == 1 }} Host: {{ else }} Hosts: {{ end }} - {{ if len(hl) > 0 }} - {{ range j, u := hl }} + {{ if len(c.Hosts) > 0 }} + {{ range j, u := c.Hosts }} {{ if j > 0 }}, {{ end }} - {{ u.Username }} + {{ u }} {{ end }} {{ else }} None @@ -60,7 +59,7 @@ Find Posts {{ if canManage }} - Manage diff --git a/ui/views/manage_conflist.jet b/ui/views/manage_conflist.jet new file mode 100644 index 0000000..3aff616 --- /dev/null +++ b/ui/views/manage_conflist.jet @@ -0,0 +1,125 @@ +{* + * Amsterdam Web Communities System + * Copyright (c) 2025-2026 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/. + *} +
+ +
+
+

Manage Conference List

+

Community: {{ commName }}

+
+
+
+ + + + + +
+
+ {{ if len(confs) > 0 }} + + + + + + + + + + + {{ range i, c := confs }} + + + + + + + + + + {{ end }} + +
 Conference NameTopicsPosts
+ {{ if c.Hidden }} + + ⚫ + + {{ else }} + + 🟢 + + {{ end }} + + {{ if i < (len(confs) - 1) }} + ⬇️ + {{ else }} +   + {{ end }} + + {{ if i > 0 }} + ⬆️ + {{ else }} +   + {{ end }} + + + +
{{ c.Name }}
+
+
{{ ntopics[i] }}
+
+
{{ nposts[i] }}
+
+ {{ else }} + No conferences in this community. + {{ end }} +
+
+ + +
+
+

How to update the community's conference list:

+
+
+ 🟢 + This indicates that the conference is displayed in the community's conference list. Click the symbol to hide it. +
+
+ + This indicates that the conference is hidden in the community's conference list. Click the symbol to display it. +
+
+ ⬇️ + Click this symbol to move the specified conference down in the community's conference list. +
+
+ ⬆️ + Click this symbol to move the specified conference up in the community's conference list. +
+
+ + Click this symbol to delete the specified conference. You will be prompted to confirm this action. +
+
+
+
+ + +