Landed conference list page - still needs some work (doesn't crash, but some display info is incorrect)

This commit is contained in:
2025-10-22 23:19:11 -06:00
parent 4f3677b3b4
commit b93bc93e00
6 changed files with 247 additions and 3 deletions
+72
View File
@@ -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
}
+92 -2
View File
@@ -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.
+1 -1
View File
@@ -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
+12
View File
@@ -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
}
+1
View File
@@ -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
}
+69
View File
@@ -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/.
*}
<div class="p-4">
<!-- Page Title -->
<div class="mb-6">
<h1 class="text-blue-800 text-4xl font-bold inline">Conference List:</h1>
<span class="text-blue-800 text-2xl font-bold ml-2">{{ commName }}</span>
<hr class="border-2 border-gray-400 w-4/5 mt-2 mb-6">
</div>
<!-- Conference Listing -->
<div class="max-w-4xl">
<div class="bg-gray-50 p-6 rounded-lg mb-6">
<div class="space-y-6">
{{ range _, c := conferences }}
<div class="flex items-start gap-3">
<span class="text-lg pt-0.5 flex-shrink-0">🟣</span>
<div class="flex-1">
<div class="mb-2">
<a href="/TODO/comm/{{ commAlias }}/conf/{{ c.AliasesQ()[0] }}"
class="text-blue-700 hover:text-blue-900 font-bold text-lg">{{ c.Name }}</a>
<span class="text-gray-600 text-sm ml-2">- Latest activity: {{ DisplayActivity(c.LastUpdate, .) }}</span>
</div>
<div class="text-sm text-gray-700 mb-1">
{{ hl := c.HostsQ() }}
{{ if len(hl) == 1 }}
<span class="font-medium">Host:</span>
{{ else }}
<span class="font-medium">Hosts:</span>
{{ end }}
{{ if len(hl) > 0 }}
{{ range i, u := hl }}
{{ if i > 0 }}, {{ end }}
<a href="/users/{{ u.Username }}" class="text-blue-700 hover:text-blue-900">{{ u.Username }}</a>
{{ end }}
{{ else }}
None
{{ end }}
</div>
<div class="italic text-gray-600 text-sm">{{ c.Description }}</div>
</div>
</div>
{{ end }}
</div>
</div>
<!-- Action Buttons -->
<div class="flex gap-4">
<a href="/find?mode=PST"
class="inline-block bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded font-medium transition-colors">
Find Posts
</a>
</div>
<!-- Information Note -->
<div class="mt-6 p-4 bg-blue-50 border-l-4 border-blue-400">
<p class="text-sm text-gray-700">
<strong>About Conferences:</strong> Conferences are discussion areas within this community.
Click on a conference name to view topics and participate in discussions.
</p>
</div>
</div>
</div>