completed the Scan() error handling

This commit is contained in:
2025-12-30 23:19:39 -07:00
parent d87b4b9411
commit c958bc5826
10 changed files with 179 additions and 153 deletions
+12
View File
@@ -367,6 +367,7 @@ func breakRange(topic *database.Topic, into []int32, param string, sep string) e
return nil return nil
} }
// templateExtractUserName extracts the user name from the post.
func templateExtractUserName(args jet.Arguments) reflect.Value { func templateExtractUserName(args jet.Arguments) reflect.Value {
rc := "<<ERROR>>" rc := "<<ERROR>>"
post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader) post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader)
@@ -380,6 +381,7 @@ func templateExtractUserName(args jet.Arguments) reflect.Value {
return reflect.ValueOf(rc) return reflect.ValueOf(rc)
} }
// templatePostText gets the text of a post.
func templatePostText(args jet.Arguments) reflect.Value { func templatePostText(args jet.Arguments) reflect.Value {
post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader) post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader)
ctxt := args.Get(1).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext) ctxt := args.Get(1).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext)
@@ -391,6 +393,7 @@ func templatePostText(args jet.Arguments) reflect.Value {
return reflect.ValueOf(rc) return reflect.ValueOf(rc)
} }
// templateOverrideLine creates the "override line" for a post, that is, what gets displayed in place of the post text.
func templateOverrideLine(args jet.Arguments) reflect.Value { func templateOverrideLine(args jet.Arguments) reflect.Value {
post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader) post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader)
ctxt := args.Get(1).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext) ctxt := args.Get(1).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext)
@@ -416,6 +419,7 @@ func templateOverrideLine(args jet.Arguments) reflect.Value {
return reflect.ValueOf(rc) return reflect.ValueOf(rc)
} }
// templateOverrideLink creates the "override link" for a post, which can make the override line a hyperlink.
func templateOverrideLink(args jet.Arguments) reflect.Value { func templateOverrideLink(args jet.Arguments) reflect.Value {
post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader) post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader)
root := args.Get(1).Convert(reflect.TypeFor[string]()).String() root := args.Get(1).Convert(reflect.TypeFor[string]()).String()
@@ -426,6 +430,14 @@ func templateOverrideLink(args jet.Arguments) reflect.Value {
return reflect.ValueOf(rc) return reflect.ValueOf(rc)
} }
/* ReadPosts displays posts in a topic.
* 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 ReadPosts(ctxt ui.AmContext) (string, any, error) { func ReadPosts(ctxt ui.AmContext) (string, any, error) {
// If we need to reset a topic's last read count (as with "Next & Keep New"), spin the task off. // If we need to reset a topic's last read count (as with "Next & Keep New"), spin the task off.
if ctxt.HasParameter("rst") { if ctxt.HasParameter("rst") {
+7 -14
View File
@@ -72,13 +72,11 @@ func loadCategories(ctx context.Context) error {
return errors.New("internal error loading categories") return errors.New("internal error loading categories")
} }
var ncats int32 var ncats int32
err = rs.Scan(&ncats) if err = rs.Scan(&ncats); err != nil {
if err != nil {
return err return err
} }
allCategories = make([]Category, 0, ncats) allCategories = make([]Category, 0, ncats)
err = amdb.SelectContext(ctx, &allCategories, "SELECT * FROM refcategory ORDER BY parent, name") if err = amdb.SelectContext(ctx, &allCategories, "SELECT * FROM refcategory ORDER BY parent, name"); err != nil {
if err != nil {
return err return err
} }
for i, c := range allCategories { for i, c := range allCategories {
@@ -104,8 +102,7 @@ func AmGetCategory(ctx context.Context, catid int32) (*Category, error) {
if !ok { if !ok {
return nil, errors.New("category feature not supported") return nil, errors.New("category feature not supported")
} }
err = loadCategories(ctx) if err = loadCategories(ctx); err != nil {
if err != nil {
return nil, err return nil, err
} }
c := categoryIdMap[catid] c := categoryIdMap[catid]
@@ -136,8 +133,7 @@ func AmGetCategoryHierarchy(ctx context.Context, catid int32) ([]*Category, erro
if !ok { if !ok {
return nil, errors.New("category feature not supported") return nil, errors.New("category feature not supported")
} }
err = loadCategories(ctx) if err = loadCategories(ctx); err != nil {
if err != nil {
return nil, err return nil, err
} }
// walk all the way to the "root" (parent = -1) // walk all the way to the "root" (parent = -1)
@@ -175,8 +171,7 @@ func AmGetSubCategories(ctx context.Context, catid int32) ([]*Category, error) {
if !ok { if !ok {
return nil, errors.New("category feature not supported") return nil, errors.New("category feature not supported")
} }
err = loadCategories(ctx) if err = loadCategories(ctx); err != nil {
if err != nil {
return nil, err return nil, err
} }
rc := make([]*Category, 0) rc := make([]*Category, 0)
@@ -247,8 +242,7 @@ func AmSearchCategories(ctx context.Context, oper int, term string, offset int,
return nil, -1, errors.New("internal error getting category total") return nil, -1, errors.New("internal error getting category total")
} }
var total int var total int
err = rs.Scan(&total) if err = rs.Scan(&total); err != nil {
if err != nil {
return nil, total, err return nil, total, err
} }
if total == 0 { if total == 0 {
@@ -265,8 +259,7 @@ func AmSearchCategories(ctx context.Context, oper int, term string, offset int,
rc := make([]*Category, 0, min(max, 1000)) rc := make([]*Category, 0, min(max, 1000))
for rs.Next() { for rs.Next() {
var catid int32 var catid int32
err = rs.Scan(&catid) if err = rs.Scan(&catid); err == nil {
if err == nil {
c, err := AmGetCategory(ctx, catid) c, err := AmGetCategory(ctx, catid)
if err == nil { if err == nil {
rc = append(rc, c) rc = append(rc, c)
+34 -40
View File
@@ -220,8 +220,7 @@ func (c *Community) Membership(ctx context.Context, u *User) (bool, bool, uint16
if rs.Next() { if rs.Next() {
var locked bool var locked bool
var level uint16 var level uint16
err = rs.Scan(&locked, &level) if err = rs.Scan(&locked, &level); err == nil {
if err == nil {
memberCache.Add(key, &memberCacheData{isMember: true, locked: locked, level: level}) memberCache.Add(key, &memberCacheData{isMember: true, locked: locked, level: level})
return true, locked, level, nil return true, locked, level, nil
} }
@@ -247,8 +246,7 @@ func (c *Community) MemberCount(ctx context.Context, hidden bool) (int, error) {
} }
if rs.Next() { if rs.Next() {
var rc int var rc int
err = rs.Scan(&rc) if err = rs.Scan(&rc); err == nil {
if err == nil {
return rc, nil return rc, nil
} else { } else {
return -1, err return -1, err
@@ -325,8 +323,7 @@ func (c *Community) ListMembers(ctx context.Context, field int, oper int, term s
return nil, -1, errors.New("internal error getting member count") return nil, -1, errors.New("internal error getting member count")
} }
var total int var total int
err = rs.Scan(&total) if err = rs.Scan(&total); err == nil {
if err == nil {
if offset > 0 { if offset > 0 {
rs, err = amdb.QueryContext(ctx, `SELECT m.uid FROM commmember m, users u, contacts c WHERE m.commid = ? AND m.uid = u.uid rs, err = amdb.QueryContext(ctx, `SELECT m.uid FROM commmember m, users u, contacts c WHERE m.commid = ? AND m.uid = u.uid
AND u.contactid = c.contactid`+q+" ORDER BY u.username LIMIT ? OFFSET ?", c.Id, max, offset) AND u.contactid = c.contactid`+q+" ORDER BY u.username LIMIT ? OFFSET ?", c.Id, max, offset)
@@ -341,12 +338,13 @@ func (c *Community) ListMembers(ctx context.Context, field int, oper int, term s
rc := make([]*User, 0, min(max, 10000)) rc := make([]*User, 0, min(max, 10000))
for rs.Next() { for rs.Next() {
var uid int32 var uid int32
rs.Scan(&uid) if err = rs.Scan(&uid); err == nil {
u, err := AmGetUser(ctx, uid) u, err := AmGetUser(ctx, uid)
if err == nil { if err == nil {
rc = append(rc, u) rc = append(rc, u)
} }
} }
}
return rc, total, nil return rc, total, nil
} }
@@ -390,8 +388,7 @@ func (c *Community) SetMembership(ctx context.Context, u *User, level uint16, lo
if rs.Next() { if rs.Next() {
var oldLevel uint16 var oldLevel uint16
var lockStatus bool var lockStatus bool
err = rs.Scan(&oldLevel, &lockStatus) if err = rs.Scan(&oldLevel, &lockStatus); err != nil {
if err != nil {
return err return err
} }
if level != oldLevel || lockStatus != locked { if level != oldLevel || lockStatus != locked {
@@ -409,14 +406,12 @@ func (c *Community) SetMembership(ctx context.Context, u *User, level uint16, lo
return err return err
} }
stuffMembership(c.Id, u.Uid, true, locked, level) stuffMembership(c.Id, u.Uid, true, locked, level)
err = AmOnUserJoinCommunityServices(ctx, tx, c, u) if err = AmOnUserJoinCommunityServices(ctx, tx, c, u); err != nil {
if err != nil {
return err return err
} }
} }
} }
err := c.TouchUpdateTx(ctx, tx) if err := c.TouchUpdateTx(ctx, tx); err == nil {
if err == nil {
ar := AmNewAudit(AuditCommunitySetMembership, personUID, ipaddr, fmt.Sprintf("cid=%d", c.Id), ar := AmNewAudit(AuditCommunitySetMembership, personUID, ipaddr, fmt.Sprintf("cid=%d", c.Id),
fmt.Sprintf("uid=%d", u.Uid), fmt.Sprintf("level=%d", level)) fmt.Sprintf("uid=%d", u.Uid), fmt.Sprintf("level=%d", level))
AmStoreAudit(ar) AmStoreAudit(ar)
@@ -556,10 +551,11 @@ func (c *Community) Touch(ctx context.Context) error {
if err == nil { if err == nil {
rs.Next() rs.Next()
var na time.Time var na time.Time
rs.Scan(&na) if err = rs.Scan(&na); err == nil {
c.LastAccess = &na c.LastAccess = &na
} }
} }
}
return err return err
} }
@@ -573,11 +569,12 @@ func (c *Community) TouchUpdateTx(ctx context.Context, tx *sqlx.Tx) error {
if err != nil { if err != nil {
rs.Next() rs.Next()
var na, nu time.Time var na, nu time.Time
rs.Scan(&na, &nu) if err = rs.Scan(&na, &nu); err == nil {
c.LastAccess = &na c.LastAccess = &na
c.LastUpdate = &nu c.LastUpdate = &nu
} }
} }
}
return err return err
} }
@@ -608,8 +605,7 @@ func AmGetCommunity(ctx context.Context, id int32) (*Community, error) {
rc, ok := communityCache.Get(id) rc, ok := communityCache.Get(id)
if !ok { if !ok {
var dbdata []Community var dbdata []Community
err := amdb.SelectContext(ctx, &dbdata, "SELECT * from communities WHERE commid = ?", id) if err := amdb.SelectContext(ctx, &dbdata, "SELECT * from communities WHERE commid = ?", id); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(dbdata) == 0 { if len(dbdata) == 0 {
@@ -638,8 +634,7 @@ func AmGetCommunityTx(ctx context.Context, tx *sqlx.Tx, id int32) (*Community, e
rc, ok := communityCache.Get(id) rc, ok := communityCache.Get(id)
if !ok { if !ok {
var dbdata []Community var dbdata []Community
err := tx.SelectContext(ctx, &dbdata, "SELECT * from communities WHERE commid = ?", id) if err := tx.SelectContext(ctx, &dbdata, "SELECT * from communities WHERE commid = ?", id); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(dbdata) == 0 { if len(dbdata) == 0 {
@@ -666,8 +661,9 @@ func AmGetCommunityByAlias(ctx context.Context, alias string) (*Community, error
if err == nil { if err == nil {
if rs.Next() { if rs.Next() {
var cid int32 var cid int32
rs.Scan(&cid) if err = rs.Scan(&cid); err == nil {
return AmGetCommunity(ctx, cid) return AmGetCommunity(ctx, cid)
}
} else { } else {
return nil, nil return nil, nil
} }
@@ -689,8 +685,9 @@ func AmGetCommunityByAliasTx(ctx context.Context, tx *sqlx.Tx, alias string) (*C
if err == nil { if err == nil {
if rs.Next() { if rs.Next() {
var cid int32 var cid int32
rs.Scan(&cid) if err = rs.Scan(&cid); err == nil {
return AmGetCommunityTx(ctx, tx, cid) return AmGetCommunityTx(ctx, tx, cid)
}
} else { } else {
return nil, nil return nil, nil
} }
@@ -768,7 +765,7 @@ func AmGetCommunityAccessLevel(ctx context.Context, uid int32, commid int32) (ui
if err == nil { if err == nil {
defer rows.Close() defer rows.Close()
if rows.Next() { if rows.Next() {
rows.Scan(&rc) err = rows.Scan(&rc)
} }
} }
return rc, err return rc, err
@@ -785,8 +782,7 @@ func AmGetCommunityAccessLevel(ctx context.Context, uid int32, commid int32) (ui
func AmAutoJoinCommunities(ctx context.Context, tx *sqlx.Tx, user *User) error { func AmAutoJoinCommunities(ctx context.Context, tx *sqlx.Tx, user *User) error {
// get list of current communities // get list of current communities
var current []int32 = make([]int32, 0) var current []int32 = make([]int32, 0)
err := tx.SelectContext(ctx, &current, "SELECT commid FROM commmember WHERE uid = ?", user.Uid) if err := tx.SelectContext(ctx, &current, "SELECT commid FROM commmember WHERE uid = ?", user.Uid); err != nil {
if err != nil {
return err return err
} }
@@ -799,8 +795,7 @@ func AmAutoJoinCommunities(ctx context.Context, tx *sqlx.Tx, user *User) error {
for rows.Next() { for rows.Next() {
var cid int32 var cid int32
var lock bool var lock bool
err = rows.Scan(&cid, &lock) if err = rows.Scan(&cid, &lock); err != nil {
if err != nil {
break break
} }
if !slices.Contains(current, cid) { if !slices.Contains(current, cid) {
@@ -825,8 +820,7 @@ func internalGetCommProp(ctx context.Context, cid int32, ndx int32) (*CommunityP
rc, ok := communityPropCache.Get(key) rc, ok := communityPropCache.Get(key)
if !ok { if !ok {
var dbdata []CommunityProperties var dbdata []CommunityProperties
err = amdb.SelectContext(ctx, &dbdata, "SELECT * from propcomm WHERE cid = ? AND ndx = ?", cid, ndx) if err = amdb.SelectContext(ctx, &dbdata, "SELECT * from propcomm WHERE cid = ? AND ndx = ?", cid, ndx); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(dbdata) == 0 { if len(dbdata) == 0 {
@@ -961,13 +955,11 @@ func AmCreateCommunity(ctx context.Context, name string, alias string, hostUid i
stuffMembership(comm.Id, hostUid, true, true, AmDefaultRole("Community.Creator").Level()) stuffMembership(comm.Id, hostUid, true, true, AmDefaultRole("Community.Creator").Level())
// Establish the community services. // Establish the community services.
err = AmEstablishCommunityServices(ctx, tx, comm) if err = AmEstablishCommunityServices(ctx, tx, comm); err != nil {
if err != nil {
return nil, err return nil, err
} }
err = tx.Commit() if err = tx.Commit(); err != nil {
if err != nil {
return nil, err return nil, err
} }
success = true success = true
@@ -1005,9 +997,9 @@ func AmGetCommunitiesForCategory(ctx context.Context, catid int32, offset int, m
return nil, -1, errors.New("internal error getting total match count") return nil, -1, errors.New("internal error getting total match count")
} }
var total int var total int
rs.Scan(&total) err = rs.Scan(&total)
if total == 0 { if err != nil || total == 0 {
return make([]*Community, 0), 0, nil // short-circuit return return make([]*Community, 0), 0, err // short-circuit return
} }
if showAll { if showAll {
if offset > 0 { if offset > 0 {
@@ -1030,12 +1022,13 @@ func AmGetCommunitiesForCategory(ctx context.Context, catid int32, offset int, m
rc := make([]*Community, 0, min(max, 10000)) rc := make([]*Community, 0, min(max, 10000))
for rs.Next() { for rs.Next() {
var commid int32 var commid int32
rs.Scan(&commid) if err = rs.Scan(&commid); err == nil {
c, err := AmGetCommunity(ctx, commid) c, err := AmGetCommunity(ctx, commid)
if err == nil { if err == nil {
rc = append(rc, c) rc = append(rc, c)
} }
} }
}
return rc, total, nil return rc, total, nil
} }
@@ -1097,9 +1090,9 @@ func AmSearchCommunities(ctx context.Context, field int, oper int, term string,
return nil, -1, errors.New("internal error getting count") return nil, -1, errors.New("internal error getting count")
} }
var total int var total int
rs.Scan(&total) err = rs.Scan(&total)
if total == 0 { if err != nil || total == 0 {
return make([]*Community, 0), 0, nil // short-circuit return return make([]*Community, 0), 0, err // short-circuit return
} }
if offset > 0 { if offset > 0 {
rs, err = amdb.QueryContext(ctx, "SELECT commid FROM communities "+q+" ORDER BY commname LIMIT ? OFFSET ?", max, offset) rs, err = amdb.QueryContext(ctx, "SELECT commid FROM communities "+q+" ORDER BY commname LIMIT ? OFFSET ?", max, offset)
@@ -1112,11 +1105,12 @@ func AmSearchCommunities(ctx context.Context, field int, oper int, term string,
rc := make([]*Community, 0, min(max, 10000)) rc := make([]*Community, 0, min(max, 10000))
for rs.Next() { for rs.Next() {
var commid int32 var commid int32
rs.Scan(&commid) if err = rs.Scan(&commid); err == nil {
c, err := AmGetCommunity(ctx, commid) c, err := AmGetCommunity(ctx, commid)
if err == nil { if err == nil {
rc = append(rc, c) rc = append(rc, c)
} }
} }
}
return rc, total, nil return rc, total, nil
} }
+22 -11
View File
@@ -101,12 +101,13 @@ func (c *Conference) Hosts(ctx context.Context) ([]*User, error) {
rc := make([]*User, 0, 5) rc := make([]*User, 0, 5)
for rs.Next() { for rs.Next() {
var uid int32 var uid int32
rs.Scan(&uid) if err = rs.Scan(&uid); err == nil {
u, err := AmGetUser(ctx, uid) u, err := AmGetUser(ctx, uid)
if err == nil { if err == nil {
rc = append(rc, u) rc = append(rc, u)
} }
} }
}
return rc, nil return rc, nil
} }
@@ -122,12 +123,15 @@ func (c *Conference) Membership(ctx context.Context, u *User) (bool, uint16, err
if err != nil { if err != nil {
return false, 0, err return false, 0, err
} }
rc := false
if rs.Next() { if rs.Next() {
rc = true
var level uint16 var level uint16
rs.Scan(&level) if err = rs.Scan(&level); err == nil {
return true, level, nil return rc, level, nil
} }
return false, 0, nil }
return rc, 0, err
} }
/* TestPermission is shorthand that tests if a user has a permission with respect to the conference. /* TestPermission is shorthand that tests if a user has a permission with respect to the conference.
@@ -162,8 +166,7 @@ func (c *Conference) TestPermission(perm string, level uint16) bool {
// Settings returns the settings for a user. // Settings returns the settings for a user.
func (c *Conference) Settings(ctx context.Context, u *User) (*ConferenceSettings, error) { func (c *Conference) Settings(ctx context.Context, u *User) (*ConferenceSettings, error) {
var dbdata []ConferenceSettings var dbdata []ConferenceSettings
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM confsettings WHERE confid = ? AND uid = ?", c.ConfId, u.Uid) if err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM confsettings WHERE confid = ? AND uid = ?", c.ConfId, u.Uid); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(dbdata) == 0 { if len(dbdata) == 0 {
@@ -250,8 +253,7 @@ func AmGetConference(ctx context.Context, id int32) (*Conference, error) {
rc, ok := conferenceCache.Get(id) rc, ok := conferenceCache.Get(id)
if !ok { if !ok {
var dbdata []Conference var dbdata []Conference
err = amdb.SelectContext(ctx, &dbdata, "SELECT * from confs where confid = ?", id) if err = amdb.SelectContext(ctx, &dbdata, "SELECT * from confs where confid = ?", id); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(dbdata) == 0 { if len(dbdata) == 0 {
@@ -286,7 +288,9 @@ func AmGetConferenceByAlias(ctx context.Context, alias string) (*Conference, err
if !rs.Next() { if !rs.Next() {
return nil, fmt.Errorf("alias not found: %s", alias) return nil, fmt.Errorf("alias not found: %s", alias)
} }
rs.Scan(&confid) if err = rs.Scan(&confid); err != nil {
return nil, err
}
conferenceAliasMap.Store(alias, confid) conferenceAliasMap.Store(alias, confid)
} }
return AmGetConference(ctx, confid) return AmGetConference(ctx, confid)
@@ -311,7 +315,9 @@ func AmGetConferenceByAliasInCommunity(ctx context.Context, cid int32, alias str
return nil, errors.New("conference not found") return nil, errors.New("conference not found")
} }
var confid int32 var confid int32
rs.Scan(&confid) if err = rs.Scan(&confid); err != nil {
return nil, err
}
return AmGetConference(ctx, confid) return AmGetConference(ctx, confid)
} }
@@ -337,10 +343,15 @@ func AmGetCommunityConferences(ctx context.Context, cid int32, showHidden bool)
rc := make([]*Conference, 0, 6) rc := make([]*Conference, 0, 6)
for rs.Next() { for rs.Next() {
var confid int32 var confid int32
rs.Scan(&confid) if err = rs.Scan(&confid); err == nil {
conf, err := AmGetConference(ctx, confid) conf, err := AmGetConference(ctx, confid)
if err == nil { if err == nil {
rc = append(rc, conf) rc = append(rc, conf)
} else {
log.Errorf("AmGetCommunityConferences conference error: %v", err)
}
} else {
log.Errorf("AmGetCommunityConferences scan error: %v", err)
} }
} }
return rc, nil return rc, nil
+4 -4
View File
@@ -57,7 +57,7 @@ func lookupCommunityContact(ctx context.Context, id int32) (int32, error) {
rs, err := amdb.QueryContext(ctx, "SELECT contactid FROM contacts WHERE owner_commid = ?", id) rs, err := amdb.QueryContext(ctx, "SELECT contactid FROM contacts WHERE owner_commid = ?", id)
if err == nil { if err == nil {
if rs.Next() { if rs.Next() {
rs.Scan(&rc) err = rs.Scan(&rc)
} }
} }
return rc, err return rc, err
@@ -69,7 +69,7 @@ func lookupUserContact(ctx context.Context, uid int32) (int32, error) {
rs, err := amdb.QueryContext(ctx, "SELECT contactid FROM contacts WHERE owner_uid = ? AND owner_commid = -1", uid) rs, err := amdb.QueryContext(ctx, "SELECT contactid FROM contacts WHERE owner_uid = ? AND owner_commid = -1", uid)
if err == nil { if err == nil {
if rs.Next() { if rs.Next() {
rs.Scan(&rc) err = rs.Scan(&rc)
} }
} }
return rc, err return rc, err
@@ -191,8 +191,8 @@ func (ci *ContactInfo) Save(ctx context.Context) (bool, error) {
if !rs.Next() { if !rs.Next() {
return false, errors.New("internal error rereading update timestamp") return false, errors.New("internal error rereading update timestamp")
} }
rs.Scan(&ci.LastUpdate) err = rs.Scan(&ci.LastUpdate)
return emailChange, nil return emailChange, err
} }
// Clone makes a copy of the ContactInfo. // Clone makes a copy of the ContactInfo.
+23 -6
View File
@@ -16,6 +16,7 @@ import (
"time" "time"
) )
// PostHeader represents the "header" of a post, everything except for its text and attachment.
type PostHeader struct { type PostHeader struct {
PostId int64 `db:"postid"` // ID of the post PostId int64 `db:"postid"` // ID of the post
Parent int64 `db:"parent"` // ID of parent (unused?) Parent int64 `db:"parent"` // ID of parent (unused?)
@@ -35,6 +36,7 @@ type PostData struct {
Data *string `db:"data"` // actual post data Data *string `db:"data"` // actual post data
} }
// ErrNoPostData is returned if post data is missing.
var ErrNoPostData = errors.New("no post data") var ErrNoPostData = errors.New("no post data")
// IsScribbled returns true if the post has been scribbled, false if not. // IsScribbled returns true if the post has been scribbled, false if not.
@@ -61,8 +63,7 @@ func (p *PostHeader) SetAttachment(ctx context.Context, fileName string, mimeTyp
// Text returns the text associated with a post. // Text returns the text associated with a post.
func (p *PostHeader) Text(ctx context.Context) (string, error) { func (p *PostHeader) Text(ctx context.Context) (string, error) {
var dbdata []PostData var dbdata []PostData
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM postdata WHERE postid = ?", p.PostId) if err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM postdata WHERE postid = ?", p.PostId); err != nil {
if err != nil {
return "", err return "", err
} }
if len(dbdata) > 1 { if len(dbdata) > 1 {
@@ -74,10 +75,17 @@ func (p *PostHeader) Text(ctx context.Context) (string, error) {
return *dbdata[0].Data, nil return *dbdata[0].Data, nil
} }
/* AmGetPost gets a single post from the database by ID.
* Parameters:
* ctx - Standard Go context value.
* postId - ID of the post to retrieve.
* Returns:
* Pointer to PostHeader for the post, or nil.
* Standard Go error status.
*/
func AmGetPost(ctx context.Context, postId int64) (*PostHeader, error) { func AmGetPost(ctx context.Context, postId int64) (*PostHeader, error) {
var dbdata []PostHeader var dbdata []PostHeader
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM posts WHERE postid = ?", postId) if err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM posts WHERE postid = ?", postId); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(dbdata) == 0 { if len(dbdata) == 0 {
@@ -89,10 +97,19 @@ func AmGetPost(ctx context.Context, postId int64) (*PostHeader, error) {
return &(dbdata[0]), nil return &(dbdata[0]), nil
} }
/* AmGetPostRage gets a range of posts from a topic by post numbers.
* Parameters:
* ctx - Standard Go context value.
* topic - Topic pointer to retrieve posts from.
* first - Number of first post to retrieve.
* last - Number of last post to retrieve.
* Returns:
* Array of pointers to PostHeader objects, or nil.
* Standard Go error status.
*/
func AmGetPostRange(ctx context.Context, topic *Topic, first, last int32) ([]*PostHeader, error) { func AmGetPostRange(ctx context.Context, topic *Topic, first, last int32) ([]*PostHeader, error) {
var posts []PostHeader var posts []PostHeader
err := amdb.SelectContext(ctx, &posts, "SELECT * FROM posts WHERE topicid = ? AND num >= ? AND num <= ? ORDER BY num", topic.TopicId, first, last) if err := amdb.SelectContext(ctx, &posts, "SELECT * FROM posts WHERE topicid = ? AND num >= ? AND num <= ? ORDER BY num", topic.TopicId, first, last); err != nil {
if err != nil {
return nil, err return nil, err
} }
rc := make([]*PostHeader, len(posts)) rc := make([]*PostHeader, len(posts))
+8 -10
View File
@@ -144,9 +144,10 @@ func AmGetCommunityServices(ctx context.Context, cid int32) ([]*ServiceDef, erro
a := make([]*ServiceDef, 0, len(dom.Services)) a := make([]*ServiceDef, 0, len(dom.Services))
for rs.Next() { for rs.Next() {
var ndx int16 var ndx int16
rs.Scan(&ndx) if err = rs.Scan(&ndx); err == nil {
a = append(a, dom.byIndex[ndx]) a = append(a, dom.byIndex[ndx])
} }
}
servicesCache.Add(cid, a) servicesCache.Add(cid, a)
rc = a rc = a
} }
@@ -175,9 +176,10 @@ func AmGetCommunityServicesTx(ctx context.Context, tx *sqlx.Tx, cid int32) ([]*S
a := make([]*ServiceDef, 0, len(dom.Services)) a := make([]*ServiceDef, 0, len(dom.Services))
for rs.Next() { for rs.Next() {
var ndx int16 var ndx int16
rs.Scan(&ndx) if err = rs.Scan(&ndx); err == nil {
a = append(a, dom.byIndex[ndx]) a = append(a, dom.byIndex[ndx])
} }
}
servicesCache.Add(cid, a) servicesCache.Add(cid, a)
rc = a rc = a
} }
@@ -209,8 +211,7 @@ func AmEstablishCommunityServices(ctx context.Context, tx *sqlx.Tx, c *Community
servicesCache.Add(c.Id, a) servicesCache.Add(c.Id, a)
servicesCacheMutex.Unlock() servicesCacheMutex.Unlock()
for _, svc := range a { for _, svc := range a {
err := svc.vtable.OnNewCommunity(ctx, tx, c) if err := svc.vtable.OnNewCommunity(ctx, tx, c); err != nil {
if err != nil {
return err return err
} }
} }
@@ -230,8 +231,7 @@ func AmDeleteCommunityServices(ctx context.Context, tx *sqlx.Tx, cid int32) erro
arr, err := AmGetCommunityServices(ctx, cid) arr, err := AmGetCommunityServices(ctx, cid)
if err == nil { if err == nil {
for _, svc := range arr { for _, svc := range arr {
err = svc.vtable.OnDeleteCommunity(ctx, tx, cid) if err = svc.vtable.OnDeleteCommunity(ctx, tx, cid); err != nil {
if err != nil {
break break
} }
} }
@@ -258,8 +258,7 @@ func AmOnUserJoinCommunityServices(ctx context.Context, tx *sqlx.Tx, c *Communit
arr, err := AmGetCommunityServicesTx(ctx, tx, c.Id) arr, err := AmGetCommunityServicesTx(ctx, tx, c.Id)
if err == nil { if err == nil {
for _, svc := range arr { for _, svc := range arr {
err = svc.vtable.OnUserJoinCommunity(ctx, tx, c, u) if err = svc.vtable.OnUserJoinCommunity(ctx, tx, c, u); err != nil {
if err != nil {
break break
} }
} }
@@ -280,8 +279,7 @@ func AmOnUserLeaveCommunityServices(ctx context.Context, tx *sqlx.Tx, c *Communi
arr, err := AmGetCommunityServicesTx(ctx, tx, c.Id) arr, err := AmGetCommunityServicesTx(ctx, tx, c.Id)
if err == nil { if err == nil {
for _, svc := range arr { for _, svc := range arr {
err = svc.vtable.OnUserLeaveCommunity(ctx, tx, c, u) if err = svc.vtable.OnUserLeaveCommunity(ctx, tx, c, u); err != nil {
if err != nil {
break break
} }
} }
+19 -9
View File
@@ -118,8 +118,7 @@ type TopicSummary struct {
*/ */
func AmGetTopic(ctx context.Context, topicId int32) (*Topic, error) { func AmGetTopic(ctx context.Context, topicId int32) (*Topic, error) {
var dbdata []Topic var dbdata []Topic
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM topics WHERE topicid = ?", topicId) if err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM topics WHERE topicid = ?", topicId); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(dbdata) == 0 { if len(dbdata) == 0 {
@@ -142,8 +141,7 @@ func AmGetTopic(ctx context.Context, topicId int32) (*Topic, error) {
*/ */
func AmGetTopicTx(ctx context.Context, tx *sqlx.Tx, topicId int32) (*Topic, error) { func AmGetTopicTx(ctx context.Context, tx *sqlx.Tx, topicId int32) (*Topic, error) {
var dbdata []Topic var dbdata []Topic
err := tx.SelectContext(ctx, &dbdata, "SELECT * FROM topics WHERE topicid = ?", topicId) if err := tx.SelectContext(ctx, &dbdata, "SELECT * FROM topics WHERE topicid = ?", topicId); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(dbdata) == 0 { if len(dbdata) == 0 {
@@ -313,9 +311,8 @@ func AmListTopics(ctx context.Context, confid int32, uid int32, viewOption int,
rc := make([]*TopicSummary, 0) rc := make([]*TopicSummary, 0)
for rs.Next() { for rs.Next() {
var rec TopicSummary var rec TopicSummary
err = rs.Scan(&rec.TopicID, &rec.Number, &rec.Name, &rec.Unread, &rec.Total, &rec.LastUpdate, &rec.Frozen, if err = rs.Scan(&rec.TopicID, &rec.Number, &rec.Name, &rec.Unread, &rec.Total, &rec.LastUpdate, &rec.Frozen,
&rec.Archived, &rec.Subscribed, &rec.Hidden, &rec.Sticky, &rec.NewFlag) &rec.Archived, &rec.Subscribed, &rec.Hidden, &rec.Sticky, &rec.NewFlag); err != nil {
if err != nil {
log.Errorf("AmListTopics scan error: %v", err) log.Errorf("AmListTopics scan error: %v", err)
} else { } else {
rc = append(rc, &rec) rc = append(rc, &rec)
@@ -324,6 +321,20 @@ func AmListTopics(ctx context.Context, confid int32, uid int32, viewOption int,
return rc, nil return rc, nil
} }
/* AmNewTopic creates a new topic.
* Parameters:
* ctx - Standard Go context value.
* conf - Conference to add the new post.
* user - User creating the new topic.
* title - The new topic's title.
* zeroPostPseud - Pseud for the topic's "zero post" (first post).
* zeroPost - Textual data for the zero post.
* zeroPostLines - Number of lines of text in zeroPost.
* ipaddr - IP address of the user making the topic, for audit purposes.
* Returns:
* Pointer to the new Topic data structure.
* Standard Go error status.
*/
func AmNewTopic(ctx context.Context, conf *Conference, user *User, title string, zeroPostPseud string, zeroPost string, func AmNewTopic(ctx context.Context, conf *Conference, user *User, title string, zeroPostPseud string, zeroPost string,
zeroPostLines int32, ipaddr string) (*Topic, error) { zeroPostLines int32, ipaddr string) (*Topic, error) {
var ar *AuditRecord = nil var ar *AuditRecord = nil
@@ -409,8 +420,7 @@ func AmNewTopic(ctx context.Context, conf *Conference, user *User, title string,
return nil, err return nil, err
} }
err = tx.Commit() if err = tx.Commit(); err != nil {
if err != nil {
return nil, err return nil, err
} }
success = true success = true
+14 -28
View File
@@ -267,10 +267,8 @@ func (u *User) ConfirmEMailAddress(ctx context.Context, confnum int32, remoteIP
if err == nil { if err == nil {
u.VerifyEMail = true u.VerifyEMail = true
u.BaseLevel = AmDefaultRole("Global.AfterVerify").Level() u.BaseLevel = AmDefaultRole("Global.AfterVerify").Level()
err = AmAutoJoinCommunities(ctx, tx, u) if err = AmAutoJoinCommunities(ctx, tx, u); err == nil {
if err == nil { if err = tx.Commit(); err == nil {
err = tx.Commit()
if err == nil {
success = true success = true
ar = AmNewAudit(AuditVerifyEmailOK, u.Uid, remoteIP) ar = AmNewAudit(AuditVerifyEmailOK, u.Uid, remoteIP)
} }
@@ -354,8 +352,7 @@ func (u *User) Prefs(ctx context.Context) (*UserPrefs, error) {
defer u.Mutex.Unlock() defer u.Mutex.Unlock()
if u.prefs == nil { if u.prefs == nil {
var dbdata []UserPrefs var dbdata []UserPrefs
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM userprefs WHERE uid = ?", u.Uid) if err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM userprefs WHERE uid = ?", u.Uid); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(dbdata) != 1 { if len(dbdata) != 1 {
@@ -402,8 +399,7 @@ func AmGetUser(ctx context.Context, uid int32) (*User, error) {
rc, ok := userCache.Get(uid) rc, ok := userCache.Get(uid)
if !ok { if !ok {
var dbdata []User var dbdata []User
err = amdb.SelectContext(ctx, &dbdata, "SELECT * from users WHERE uid = ?", uid) if err = amdb.SelectContext(ctx, &dbdata, "SELECT * from users WHERE uid = ?", uid); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(dbdata) > 1 { if len(dbdata) > 1 {
@@ -431,8 +427,7 @@ func AmGetUserTx(ctx context.Context, tx *sqlx.Tx, uid int32) (*User, error) {
rc, ok := userCache.Get(uid) rc, ok := userCache.Get(uid)
if !ok { if !ok {
var dbdata []User var dbdata []User
err = tx.SelectContext(ctx, &dbdata, "SELECT * from users WHERE uid = ?", uid) if err = tx.SelectContext(ctx, &dbdata, "SELECT * from users WHERE uid = ?", uid); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(dbdata) > 1 { if len(dbdata) > 1 {
@@ -596,8 +591,7 @@ func AmAuthenticateUser(ctx context.Context, name string, password string, remot
} }
log.Debug("...authenticated") log.Debug("...authenticated")
touchUser(ctx, tx, user) touchUser(ctx, tx, user)
err = tx.Commit() if err = tx.Commit(); err != nil {
if err != nil {
return nil, err return nil, err
} }
success = true success = true
@@ -680,8 +674,7 @@ func AmAuthenticateUserByToken(ctx context.Context, authString string, remoteIP
} }
log.Debug("...authenticated") log.Debug("...authenticated")
touchUser(ctx, tx, user) touchUser(ctx, tx, user)
err = tx.Commit() if err = tx.Commit(); err != nil {
if err != nil {
return nil, err return nil, err
} }
success = true success = true
@@ -754,8 +747,7 @@ func AmCreateNewUser(ctx context.Context, username string, password string, remi
// add user properties // add user properties
props := make([]UserProperties, 0) props := make([]UserProperties, 0)
err = tx.SelectContext(ctx, &props, "SELECT * FROM propuser WHERE uid = ?", anon) if err = tx.SelectContext(ctx, &props, "SELECT * FROM propuser WHERE uid = ?", anon); err != nil {
if err != nil {
return nil, err return nil, err
} }
for _, p := range props { for _, p := range props {
@@ -766,8 +758,7 @@ func AmCreateNewUser(ctx context.Context, username string, password string, remi
} }
// add user sideboxes // add user sideboxes
err = copySideboxes(ctx, tx, user.Uid, anon) if err = copySideboxes(ctx, tx, user.Uid, anon); err != nil {
if err != nil {
return nil, err return nil, err
} }
@@ -775,15 +766,13 @@ func AmCreateNewUser(ctx context.Context, username string, password string, remi
unlock = false unlock = false
// auto-join communities // auto-join communities
err = AmAutoJoinCommunities(ctx, tx, user) if err = AmAutoJoinCommunities(ctx, tx, user); err != nil {
if err != nil {
return nil, err return nil, err
} }
// TODO: copy conference hotlists // TODO: copy conference hotlists
err = tx.Commit() if err = tx.Commit(); err != nil {
if err != nil {
return nil, err return nil, err
} }
success = true success = true
@@ -802,8 +791,7 @@ func internalGetProp(ctx context.Context, uid int32, ndx int32) (*UserProperties
rc, ok := userPropCache.Get(key) rc, ok := userPropCache.Get(key)
if !ok { if !ok {
var dbdata []UserProperties var dbdata []UserProperties
err = amdb.SelectContext(ctx, &dbdata, "SELECT * from propuser WHERE uid = ? AND ndx = ?", uid, ndx) if err = amdb.SelectContext(ctx, &dbdata, "SELECT * from propuser WHERE uid = ? AND ndx = ?", uid, ndx); err != nil {
if err != nil {
return nil, err return nil, err
} }
if len(dbdata) == 0 { if len(dbdata) == 0 {
@@ -925,8 +913,7 @@ func AmSearchUsers(ctx context.Context, field int, oper int, term string, offset
return nil, -1, errors.New("internal error getting count") return nil, -1, errors.New("internal error getting count")
} }
var total int var total int
err = rs.Scan(&total) if err = rs.Scan(&total); err != nil {
if err != nil {
return nil, -1, err return nil, -1, err
} }
if total == 0 { if total == 0 {
@@ -945,8 +932,7 @@ func AmSearchUsers(ctx context.Context, field int, oper int, term string, offset
rc := make([]*User, 0, min(max, 10000)) rc := make([]*User, 0, min(max, 10000))
for rs.Next() { for rs.Next() {
var uid int32 var uid int32
err = rs.Scan(&uid) if err = rs.Scan(&uid); err == nil {
if err == nil {
var u *User var u *User
u, err = AmGetUser(ctx, uid) u, err = AmGetUser(ctx, uid)
if err == nil { if err == nil {
+14 -9
View File
@@ -14,6 +14,7 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"maps"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"strconv" "strconv"
@@ -304,8 +305,8 @@ func (c *amContext) SaveSession() error {
* Parameters: * Parameters:
* name = The name of the template to be rendered. * name = The name of the template to be rendered.
* Returns: * Returns:
* Byte array with the rendered data to be output * Byte array with the rendered data to be output.
* Standard Go error status * Standard Go error status.
*/ */
func (c *amContext) SubRender(name string) ([]byte, error) { func (c *amContext) SubRender(name string) ([]byte, error) {
view, err := views.GetTemplate(name) view, err := views.GetTemplate(name)
@@ -314,13 +315,20 @@ func (c *amContext) SubRender(name string) ([]byte, error) {
return nil, err return nil, err
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
err = view.Execute(buf, c.VarMap(), c) if err = view.Execute(buf, c.VarMap(), c); err != nil {
if err != nil {
log.Errorf("template \"%s\" failed subrender exec: %v", name, err) log.Errorf("template \"%s\" failed subrender exec: %v", name, err)
} }
return buf.Bytes(), err return buf.Bytes(), err
} }
/* SubRender2 renders a subtemplate to the output, with extra variables to be set.
* Parameters:
* name = The name of the template to be rendered.
* vals = Additional variable values to be set.
* Returns:
* Byte array with the rendered data to be output.
* Standard Go error status.
*/
func (c *amContext) SubRender2(name string, vals map[string]any) ([]byte, error) { func (c *amContext) SubRender2(name string, vals map[string]any) ([]byte, error) {
view, err := views.GetTemplate(name) view, err := views.GetTemplate(name)
if err != nil { if err != nil {
@@ -328,15 +336,12 @@ func (c *amContext) SubRender2(name string, vals map[string]any) ([]byte, error)
return nil, err return nil, err
} }
newmap := make(jet.VarMap) newmap := make(jet.VarMap)
for k, v := range c.VarMap() { maps.Copy(newmap, c.VarMap())
newmap.Set(k, v)
}
for k, v := range vals { for k, v := range vals {
newmap.Set(k, v) newmap.Set(k, v)
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
err = view.Execute(buf, newmap, c) if err = view.Execute(buf, newmap, c); err != nil {
if err != nil {
log.Errorf("template \"%s\" failed subrender exec: %v", name, err) log.Errorf("template \"%s\" failed subrender exec: %v", name, err)
} }
return buf.Bytes(), err return buf.Bytes(), err