Landed conference list page - still needs some work (doesn't crash, but some display info is incorrect)
This commit is contained in:
@@ -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
@@ -19,6 +19,7 @@ import (
|
|||||||
|
|
||||||
// Conference struct is the top-level structure for a conference.
|
// Conference struct is the top-level structure for a conference.
|
||||||
type Conference struct {
|
type Conference struct {
|
||||||
|
Mutex sync.Mutex
|
||||||
ConfId int32 `db:"confid"`
|
ConfId int32 `db:"confid"`
|
||||||
CreateDate time.Time `db:"createdate"`
|
CreateDate time.Time `db:"createdate"`
|
||||||
LastUpdate *time.Time `db:"lastupdate"`
|
LastUpdate *time.Time `db:"lastupdate"`
|
||||||
@@ -27,8 +28,8 @@ type Conference struct {
|
|||||||
CreateLevel uint16 `db:"create_lvl"`
|
CreateLevel uint16 `db:"create_lvl"`
|
||||||
HideLevel uint16 `db:"hide_lvl"`
|
HideLevel uint16 `db:"hide_lvl"`
|
||||||
NukeLevel uint16 `db:"nuke_lvl"`
|
NukeLevel uint16 `db:"nuke_lvl"`
|
||||||
ChangeLevel uint16 `db:"change_level"`
|
ChangeLevel uint16 `db:"change_lvl"`
|
||||||
DeleteLevel uint16 `db:"delete_level"`
|
DeleteLevel uint16 `db:"delete_lvl"`
|
||||||
TopTopic int16 `db:"top_topic"`
|
TopTopic int16 `db:"top_topic"`
|
||||||
Name string `db:"name"`
|
Name string `db:"name"`
|
||||||
Description *string `db:"descr"`
|
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.
|
/* AmGetConference returns a conference given its ID.
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* id - The ID of the conference.
|
* id - The ID of the conference.
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ domains:
|
|||||||
requirePermission: "Community.Read"
|
requirePermission: "Community.Read"
|
||||||
requireRole: ""
|
requireRole: ""
|
||||||
linkSequence: 500
|
linkSequence: 500
|
||||||
link: "/TODO/comm/[CID]/conf"
|
link: "/comm/[CID]/conf"
|
||||||
title: "Conferences"
|
title: "Conferences"
|
||||||
- id: "Members"
|
- id: "Members"
|
||||||
index: 4
|
index: 4
|
||||||
|
|||||||
@@ -246,3 +246,15 @@ func AmOnUserLeaveCommunityServices(c *Community, u *User) error {
|
|||||||
}
|
}
|
||||||
return err
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ func setupEcho() *echo.Echo {
|
|||||||
e.POST("/comm/:cid/admin/profile", ui.AmWrap(EditCommunityProfile))
|
e.POST("/comm/:cid/admin/profile", ui.AmWrap(EditCommunityProfile))
|
||||||
e.GET("/comm/:cid/admin/logo", ui.AmWrap(CommunityLogoForm))
|
e.GET("/comm/:cid/admin/logo", ui.AmWrap(CommunityLogoForm))
|
||||||
e.POST("/comm/:cid/admin/logo", ui.AmWrap(EditCommunityLogo))
|
e.POST("/comm/:cid/admin/logo", ui.AmWrap(EditCommunityLogo))
|
||||||
|
e.GET("/comm/:cid/conf", ui.AmWrap(Conferences))
|
||||||
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
Reference in New Issue
Block a user