correct architectural goof where conference aliases had global scope instead of community scope (untested)

This commit is contained in:
2026-04-18 21:13:00 -05:00
parent 70dcf82234
commit 1348d0225f
11 changed files with 103 additions and 67 deletions
+28 -30
View File
@@ -152,8 +152,8 @@ func (cs *ConferenceSettings) Save(ctx context.Context) error {
}
// Aliases returns the list of aliases for this conference.
func (c *Conference) Aliases(ctx context.Context) ([]string, error) {
rs, err := amdb.QueryContext(ctx, "SELECT alias FROM confalias WHERE confid = ? ORDER BY alias", c.ConfId)
func (c *Conference) Aliases(ctx context.Context, commid int32) ([]string, error) {
rs, err := amdb.QueryContext(ctx, "SELECT alias FROM confalias WHERE commid = ? AND confid = ? ORDER BY alias", commid, c.ConfId)
if err != nil {
return nil, err
}
@@ -169,22 +169,18 @@ func (c *Conference) Aliases(ctx context.Context) ([]string, error) {
return rc, nil
}
// AliasesQ returns the list of aliases for this conference, quietly.
func (c *Conference) AliasesQ(ctx context.Context) []string {
rc, _ := c.Aliases(ctx)
return rc
}
// AddAlias adds an alias to the conference.
func (c *Conference) AddAlias(ctx context.Context, alias string, u *User, comm *Community, ipaddr string) error {
tmp := ""
if err := amdb.GetContext(ctx, &tmp, "SELECT alias FROM confalias WHERE confid = ? AND alias = ?", c.ConfId, alias); err != sql.ErrNoRows {
if err := amdb.GetContext(ctx, &tmp, "SELECT alias FROM confalias WHERE commid = ? AND confid = ? AND alias = ?",
comm.Id, c.ConfId, alias); err != sql.ErrNoRows {
if err == nil {
return fmt.Errorf("the alias '%s' is already in use by another conference", alias)
}
return err
}
if _, err := amdb.ExecContext(ctx, "INSERT INTO confalias (confid, alias) VALUES (?, ?)", c.ConfId, alias); err != nil {
if _, err := amdb.ExecContext(ctx, "INSERT INTO confalias (commid, confid, alias) VALUES (?, ?)",
comm.Id, c.ConfId, alias); err != nil {
return err
}
@@ -195,13 +191,15 @@ func (c *Conference) AddAlias(ctx context.Context, alias string, u *User, comm *
// RemoveAlias removes an alias from the conference.
func (c *Conference) RemoveAlias(ctx context.Context, alias string, u *User, comm *Community, ipaddr string) error {
aliasCount := 0
if err := amdb.GetContext(ctx, &aliasCount, "SELECT COUNT(*) FROM confalias WHERE confid = ?", c.ConfId); err != nil {
if err := amdb.GetContext(ctx, &aliasCount, "SELECT COUNT(*) FROM confalias WHERE commid = ? AND confid = ?",
comm.Id, c.ConfId); err != nil {
return err
}
if aliasCount == 1 {
tmp := ""
err := amdb.GetContext(ctx, &tmp, "SELECT alias FROM confalias WHERE confid = ? AND alias = ?", c.ConfId, alias)
err := amdb.GetContext(ctx, &tmp, "SELECT alias FROM confalias WHERE commid = ? AND confid = ? AND alias = ?",
comm.Id, c.ConfId, alias)
if err == nil {
return errors.New("the conference must have at least one alias")
} else if err != sql.ErrNoRows {
@@ -209,7 +207,7 @@ func (c *Conference) RemoveAlias(ctx context.Context, alias string, u *User, com
}
}
rs, err := amdb.ExecContext(ctx, "DELETE FROM confalias WHERE confid = ? AND alias = ?", c.ConfId, alias)
rs, err := amdb.ExecContext(ctx, "DELETE FROM confalias WHERE commid = ? confid = ? AND alias = ?", comm.Id, c.ConfId, alias)
if err != nil {
return err
}
@@ -431,8 +429,8 @@ func (c *Conference) Settings(ctx context.Context, u *User) (*ConferenceSettings
}
// Link returns a link string to this conference.
func (c *Conference) Link(ctx context.Context, scope string) (string, error) {
aliases, err := c.Aliases(ctx)
func (c *Conference) Link(ctx context.Context, commid int32, scope string) (string, error) {
aliases, err := c.Aliases(ctx, commid)
if err != nil {
return "", err
}
@@ -440,9 +438,9 @@ func (c *Conference) Link(ctx context.Context, scope string) (string, error) {
return fmt.Sprintf("%s.", aliases[0]), nil
}
if scope == "global" {
comms, err := c.ContainedBy(ctx)
comm, err := AmGetCommunity(ctx, commid)
if err == nil {
return fmt.Sprintf("%s!%s", comms[0].Alias, aliases[0]), nil
return fmt.Sprintf("%s!%s", comm.Alias, aliases[0]), nil
}
return "", err
}
@@ -872,22 +870,22 @@ func (c *Conference) Delete(ctx context.Context, comm *Community, u *User, ipadd
// any references to conference other than this community?
refCount := 0
if err := tx.GetContext(ctx, &refCount, "SELECT COUNT(*) FROM commtoconf WHERE confid = ? AND commid <> ?", c.ConfId, comm.Id); err != nil {
err := tx.GetContext(ctx, &refCount, "SELECT COUNT(*) FROM commtoconf WHERE confid = ? AND commid <> ?", c.ConfId, comm.Id)
if err != nil {
return err
}
// break the link with the community
if _, err := tx.ExecContext(ctx, "DELETE FROM commtoconf WHERE commid = ? AND confid = ?", comm.Id, c.ConfId); err != nil {
if _, err = tx.ExecContext(ctx, "DELETE FROM commtoconf WHERE commid = ? AND confid = ?", comm.Id, c.ConfId); err == nil {
_, err = tx.ExecContext(ctx, "DELETE FROM confalias WHERE commid = ? AND confid = ?", comm.Id, c.ConfId)
}
if err != nil {
return err
}
var err error
if refCount == 0 {
// We have to delete all the conference core data now.
_, err = tx.ExecContext(ctx, "DELETE FROM confs WHERE confid = ?", c.ConfId)
if err == nil {
_, err = tx.ExecContext(ctx, "DELETE FROM confalias WHERE confid = ?", c.ConfId)
}
}
if err != nil {
return err
@@ -1038,13 +1036,13 @@ func AmGetConference(ctx context.Context, id int32) (*Conference, error) {
* Pointer to the conference, or nil.
* Standard Go error status.
*/
func AmGetConferenceByAlias(ctx context.Context, alias string) (*Conference, error) {
func AmGetConferenceByAlias(ctx context.Context, commid int32, alias string) (*Conference, error) {
var confid int32
xconf, ok := conferenceAliasMap.Load(alias)
if ok {
confid = xconf.(int32)
} else {
err := amdb.GetContext(ctx, &confid, "SELECT confid FROM confalias WHERE alias = ?", alias)
err := amdb.GetContext(ctx, &confid, "SELECT confid FROM confalias WHERE commid = ? AND alias = ?", commid, alias)
if err == sql.ErrNoRows {
return nil, fmt.Errorf("alias not found: %s", alias)
} else if err != nil {
@@ -1085,8 +1083,7 @@ func AmGetConferenceContainingPost(ctx context.Context, postId int64) (*Conferen
*/
func AmGetConferenceByAliasInCommunity(ctx context.Context, cid int32, alias string) (*Conference, error) {
var confid int32
err := amdb.GetContext(ctx, &confid, `SELECT c.confid FROM commtoconf c, confalias a WHERE c.confid = a.confid
AND c.commid = ? AND a.alias = ?`, cid, alias)
err := amdb.GetContext(ctx, &confid, `SELECT confid FROM confalias WHERE commid = ? AND alias = ?`, cid, alias)
switch err {
case nil:
return AmGetConference(ctx, confid)
@@ -1125,7 +1122,7 @@ func AmListConferences(ctx context.Context, cid int32, showHidden bool) ([]*Conf
}
}
for i := range rc {
err := amdb.GetContext(ctx, &(rc[i].Alias), "SELECT alias FROM confalias WHERE confid = ?", rc[i].ConfId)
err := amdb.GetContext(ctx, &(rc[i].Alias), "SELECT alias FROM confalias WHERE commid = ? AND confid = ?", cid, rc[i].ConfId)
if err != nil {
return nil, err
}
@@ -1271,7 +1268,7 @@ func AmCreateConference(ctx context.Context, comm *Community, name, alias, descr
// Ensure the alias is not in use.
var tmp int32
err := tx.GetContext(ctx, &tmp, "SELECT confid FROM confalias WHERE alias = ?", alias)
err := tx.GetContext(ctx, &tmp, "SELECT confid FROM confalias WHERE commid = ? AND alias = ?", comm.Id, alias)
if err == nil {
return nil, fmt.Errorf("the alias '%s' is already in use by a different conference", alias)
} else if err != sql.ErrNoRows {
@@ -1295,7 +1292,8 @@ func AmCreateConference(ctx context.Context, comm *Community, name, alias, descr
}
// Attach the alias to the conference.
if _, err = tx.ExecContext(ctx, "INSERT INTO confalias (confid, alias) VALUES (?, ?)", rc.ConfId, alias); err != nil {
if _, err = tx.ExecContext(ctx, "INSERT INTO confalias (commid, confid, alias) VALUES (?, ?, ?)",
comm.Id, rc.ConfId, alias); err != nil {
return nil, err
}
+23
View File
@@ -0,0 +1,23 @@
# 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/.
#
# SPDX-License-Identifier: MPL-2.0
#
CREATE TABLE newconfalias (
commid INT NOT NULL,
confid INT NOT NULL,
alias VARCHAR(64) NOT NULL,
PRIMARY KEY (commid, alias),
INDEX confid_x (commid, confid)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
INSERT INTO newconfalias (commid, confid, alias)
SELECT c.commid, c.confid, a.alias FROM commtoconf c, confalias a
WHERE c.confid = a.confid;
DROP TABLE confalias;
ALTER TABLE newconfalias RENAME TO confalias;
+24 -16
View File
@@ -253,7 +253,7 @@ func (p *PostHeader) Text(ctx context.Context) (string, error) {
}
// Link returns a link string to this post.
func (p *PostHeader) Link(ctx context.Context, scope string) (string, error) {
func (p *PostHeader) Link(ctx context.Context, commid int32, scope string) (string, error) {
if scope == "topic" {
return fmt.Sprintf("%d", p.Num), nil
}
@@ -262,7 +262,7 @@ func (p *PostHeader) Link(ctx context.Context, scope string) (string, error) {
if err != nil {
return "", err
}
parent, err := topic.Link(ctx, scope)
parent, err := topic.Link(ctx, commid, scope)
if err != nil {
return "", err
}
@@ -628,47 +628,55 @@ func AmNewPost(ctx context.Context, conf *Conference, topic *Topic, user *User,
* Array of post headers, or nil.
* Standard Go error status.
*/
func AmGetPublishedPosts(ctx context.Context) ([]*PostHeader, error) {
func AmGetPublishedPosts(ctx context.Context) ([]*PostHeader, []*Community, error) {
// Read the globals.
gv, err := AmGlobals(ctx)
if err != nil {
return nil, err
return nil, nil, err
}
// Read the published posts.
rs, err := amdb.QueryContext(ctx, "SELECT postid FROM postpublish ORDER BY on_date DESC")
rs, err := amdb.QueryContext(ctx, "SELECT commid, postid FROM postpublish ORDER BY on_date DESC")
if err != nil {
return nil, err
return nil, nil, err
}
// Extract post IDs to an array.
cids := make([]int32, gv.FrontPagePosts)
pids := make([]int64, gv.FrontPagePosts)
i := 0
for i < int(gv.FrontPagePosts) && rs.Next() {
if err = rs.Scan(&(pids[i])); err != nil {
return nil, err
if err = rs.Scan(&(cids[i]), &(pids[i])); err != nil {
return nil, nil, err
}
i++
}
if i == 0 { // no published posts, short-circuit response
return make([]*PostHeader, 0), nil
return make([]*PostHeader, 0), make([]*Community, 0), nil
}
if i < int(gv.FrontPagePosts) {
cids = cids[:i]
pids = pids[:i] // truncate if we have fewer posts than spaces
}
// Build the communities return array.
comms := make([]*Community, len(cids))
for i, cid := range cids {
comms[i], _ = AmGetCommunity(ctx, cid)
}
// Use the post IDs to build a SQL statement.
query, args, err := sqlx.In("SELECT * FROM posts WHERE postid IN (?)", pids)
if err != nil {
return nil, err
return nil, nil, err
}
query = amdb.Rebind(query)
// Use the SQL to read in all the post headers using a single database query.
var data []PostHeader
if err = amdb.SelectContext(ctx, &data, query, args...); err != nil {
return nil, err
return nil, nil, err
}
if len(data) < len(pids) {
return nil, errors.New("internal error reading post headers")
return nil, nil, errors.New("internal error reading post headers")
}
// Build the return array by making sure we point to the post headers in the same order the post IDs were returned.
@@ -683,10 +691,10 @@ func AmGetPublishedPosts(ctx context.Context) ([]*PostHeader, error) {
}
}
if q < len(pids) {
return nil, errors.New("internal error generating output")
return nil, nil, errors.New("internal error generating output")
}
return rc, nil
return rc, comms, nil
}
type PostSearchResult struct {
@@ -898,13 +906,13 @@ func AmSearchPosts(ctx context.Context, searchTerms string, u *User, offset, max
if err != nil {
return nil, count, err
}
alias, err := conf.Aliases(ctx)
alias, err := conf.Aliases(ctx, commid)
if err != nil {
return nil, count, err
}
// Build the post link.
plink := AmCreatePostLinkContext(commAlias, alias[0], topicNum)
plink := AmCreatePostLinkContext(commAlias, commid, alias[0], topicNum)
plink.FirstPost = postnum
plink.LastPost = postnum
rc[i].PostLink = plink.AsString()
+6 -2
View File
@@ -23,6 +23,7 @@ import (
// PostLinkData is the structure holding the decoded parts of the post link.
type PostLinkData struct {
Community string
CommId int32
Conference string
Topic int16
FirstPost int32
@@ -36,6 +37,7 @@ func (d *PostLinkData) NeedsDBVerification() bool {
// VerifyNames verifies the post link data against the database.
func (d *PostLinkData) VerifyNames(ctx context.Context) error {
commid := d.CommId
if d.Community != "" {
comm, err := AmGetCommunityByAlias(ctx, d.Community)
if err != nil {
@@ -44,9 +46,10 @@ func (d *PostLinkData) VerifyNames(ctx context.Context) error {
if comm == nil {
return errors.New("community alias not found")
}
commid = comm.Id
}
if d.Conference != "" {
conf, err := AmGetConferenceByAlias(ctx, d.Conference)
conf, err := AmGetConferenceByAlias(ctx, commid, d.Conference)
if err != nil {
return err
}
@@ -392,9 +395,10 @@ func AmDecodePostLink(data string) (*PostLinkData, error) {
return &rc, nil
}
func AmCreatePostLinkContext(community string, conference string, topic int16) *PostLinkData {
func AmCreatePostLinkContext(community string, commid int32, conference string, topic int16) *PostLinkData {
return &PostLinkData{
Community: community,
CommId: commid,
Conference: conference,
Topic: topic,
FirstPost: -1,
+2 -2
View File
@@ -43,7 +43,7 @@ type Topic struct {
}
// Link returns a link string to this topic.
func (t *Topic) Link(ctx context.Context, scope string) (string, error) {
func (t *Topic) Link(ctx context.Context, commid int32, scope string) (string, error) {
if scope == "conference" {
return fmt.Sprintf("%d.", t.Number), nil
}
@@ -51,7 +51,7 @@ func (t *Topic) Link(ctx context.Context, scope string) (string, error) {
conf, err := AmGetConference(ctx, t.ConfId)
if err == nil {
var plink string
plink, err = conf.Link(ctx, scope)
plink, err = conf.Link(ctx, commid, scope)
if err == nil {
if strings.HasSuffix(plink, ".") {
return fmt.Sprintf("%s%d", plink, t.Number), nil