all database operations now take a context.Context, which is propagated through from sources

This commit is contained in:
2025-12-20 22:29:26 -07:00
parent 9e6bf2feda
commit 5c8bb8dd5e
39 changed files with 605 additions and 504 deletions
+13 -13
View File
@@ -32,22 +32,22 @@ import (
*/
func ShowCommunity(ctxt ui.AmContext) (string, any, error) {
me := ctxt.CurrentUser()
prefs, err := me.Prefs()
prefs, err := me.Prefs(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
comm := ctxt.CurrentCommunity() // set by middleware
ci, err := comm.ContactInfo()
ci, err := comm.ContactInfo(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
host, err := comm.Host()
host, err := comm.Host(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
var cats []*database.Category
if !ctxt.GlobalFlags().Get(database.GlobalFlagNoCategories) {
cats, err = database.AmGetCategoryHierarchy(comm.CategoryId)
cats, err = database.AmGetCategoryHierarchy(ctxt.Ctx(), comm.CategoryId)
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -142,7 +142,7 @@ func ShowCommunity(ctxt ui.AmContext) (string, any, error) {
func JoinCommunity(ctxt ui.AmContext) (string, any, error) {
me := ctxt.CurrentUser()
comm := ctxt.CurrentCommunity() // set by middleware
mbr, _, _, err := comm.Membership(me)
mbr, _, _, err := comm.Membership(ctxt.Ctx(), me)
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -161,7 +161,7 @@ func JoinCommunity(ctxt ui.AmContext) (string, any, error) {
return dlg.Render(ctxt)
}
// if get here, this is a public community, and we can join
err = comm.SetMembership(me, database.AmDefaultRole("Community.NewUser").Level(), false, me.Uid, ctxt.RemoteIP())
err = comm.SetMembership(ctxt.Ctx(), me, database.AmDefaultRole("Community.NewUser").Level(), false, me.Uid, ctxt.RemoteIP())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -182,7 +182,7 @@ func JoinCommunity(ctxt ui.AmContext) (string, any, error) {
func JoinCommunityWithKey(ctxt ui.AmContext) (string, any, error) {
me := ctxt.CurrentUser()
comm := ctxt.CurrentCommunity() // set by middleware
mbr, _, _, err := comm.Membership(me)
mbr, _, _, err := comm.Membership(ctxt.Ctx(), me)
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -210,7 +210,7 @@ func JoinCommunityWithKey(ctxt ui.AmContext) (string, any, error) {
if comm.JoinKey != nil && key != *comm.JoinKey {
return dlg.RenderError(ctxt, "The join key does not match the community. Please try again.")
}
err = comm.SetMembership(me, database.AmDefaultRole("Community.NewUser").Level(), false, me.Uid, ctxt.RemoteIP())
err = comm.SetMembership(ctxt.Ctx(), me, database.AmDefaultRole("Community.NewUser").Level(), false, me.Uid, ctxt.RemoteIP())
if err != nil {
return dlg.RenderError(ctxt, fmt.Sprintf("Error joining: %v", err))
}
@@ -232,7 +232,7 @@ func JoinCommunityWithKey(ctxt ui.AmContext) (string, any, error) {
func UnjoinCommunity(ctxt ui.AmContext) (string, any, error) {
me := ctxt.CurrentUser()
comm := ctxt.CurrentCommunity() // set by middleware
mbr, lock, _, err := comm.Membership(me)
mbr, lock, _, err := comm.Membership(ctxt.Ctx(), me)
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -260,7 +260,7 @@ func UnjoinCommunity(ctxt ui.AmContext) (string, any, error) {
func UnjoinCommunityConfirm(ctxt ui.AmContext) (string, any, error) {
me := ctxt.CurrentUser()
comm := ctxt.CurrentCommunity() // set by middleware
mbr, lock, _, err := comm.Membership(me)
mbr, lock, _, err := comm.Membership(ctxt.Ctx(), me)
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -276,7 +276,7 @@ func UnjoinCommunityConfirm(ctxt ui.AmContext) (string, any, error) {
return "redirect", fmt.Sprintf("/comm/%s/profile", comm.Alias), nil
}
if ctxt.FormFieldIsSet("unjoin") {
err = comm.SetMembership(me, 0, false, me.Uid, ctxt.RemoteIP())
err = comm.SetMembership(ctxt.Ctx(), me, 0, false, me.Uid, ctxt.RemoteIP())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -314,7 +314,7 @@ func MemberList(ctxt ui.AmContext) (string, any, error) {
ctxt.VarMap().Set("ofs", ofs)
ctxt.VarMap().Set("amsterdam_pageTitle", "List Members")
listMax := int(ctxt.Globals().MaxCommunityMemberPage)
results, total, err := comm.ListMembers(database.ListMembersFieldNone, database.ListMembersOperNone, "", ofs*listMax, listMax, showHidden)
results, total, err := comm.ListMembers(ctxt.Ctx(), database.ListMembersFieldNone, database.ListMembersOperNone, "", ofs*listMax, listMax, showHidden)
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -385,7 +385,7 @@ func MemberSearch(ctxt ui.AmContext) (string, any, error) {
return "framed_template", "memberlist.jet", nil
}
listMax := int(ctxt.Globals().MaxCommunityMemberPage)
results, total, err := comm.ListMembers(iField, iOper, term, ofs*listMax, listMax, showHidden)
results, total, err := comm.ListMembers(ctxt.Ctx(), iField, iOper, term, ofs*listMax, listMax, showHidden)
if err != nil {
return ui.ErrorPage(ctxt, err)
}
+20 -20
View File
@@ -88,11 +88,11 @@ func CommunityProfileForm(ctxt ui.AmContext) (string, any, error) {
return ui.ErrorPage(ctxt, errors.New("you are not permitted to access this page"))
}
var ci *database.ContactInfo
ci, err := comm.ContactInfo()
ci, err := comm.ContactInfo(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
flags, err := comm.Flags()
flags, err := comm.Flags(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -184,12 +184,12 @@ func EditCommunityProfile(ctxt ui.AmContext) (string, any, error) {
return dlg.RenderError(ctxt, err.Error())
}
var ci *database.ContactInfo
ci, err = comm.ContactInfo()
ci, err = comm.ContactInfo(ctxt.Ctx())
if err != nil {
return dlg.RenderError(ctxt, err.Error())
}
var flags *util.OptionSet
flags, err = comm.Flags()
flags, err = comm.Flags(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -202,7 +202,7 @@ func EditCommunityProfile(ctxt ui.AmContext) (string, any, error) {
nci.Region = dlg.Field("reg").ValPtr()
nci.PostalCode = dlg.Field("pcode").ValPtr()
nci.Country = dlg.Field("country").ValPtr()
_, err = nci.Save()
_, err = nci.Save(ctxt.Ctx())
ci = nci
if err == nil {
var joinkey *string = nil
@@ -221,17 +221,17 @@ func EditCommunityProfile(ctxt ui.AmContext) (string, any, error) {
hidedir = true
hidesearch = true
}
err = comm.SetProfileData(dlg.Field("name").Value, dlg.Field("alias").Value, dlg.Field("synopsis").ValPtr(),
err = comm.SetProfileData(ctxt.Ctx(), dlg.Field("name").Value, dlg.Field("alias").Value, dlg.Field("synopsis").ValPtr(),
dlg.Field("rules").ValPtr(), dlg.Field("language").ValPtr(), joinkey, dlg.Field("membersonly").IsChecked(),
hidedir, hidesearch, dlg.Field("read_lvl").GetLevel(), dlg.Field("write_lvl").GetLevel(),
dlg.Field("create_lvl").GetLevel(), dlg.Field("delete_lvl").GetLevel(), dlg.Field("join_lvl").GetLevel())
}
if err == nil {
flags.Set(database.CommunityFlagPicturesInPosts, dlg.Field("pic_in_post").IsChecked())
err = comm.SaveFlags(flags)
err = comm.SaveFlags(ctxt.Ctx(), flags)
}
if err == nil {
err = comm.TouchUpdate()
err = comm.TouchUpdate(ctxt.Ctx())
}
if err != nil {
ctxt.ClearCommunityContext()
@@ -259,7 +259,7 @@ func CommunityLogoForm(ctxt ui.AmContext) (string, any, error) {
ctxt.SetRC(http.StatusForbidden)
return ui.ErrorPage(ctxt, errors.New("you are not permitted to access this page"))
}
ci, err := comm.ContactInfo()
ci, err := comm.ContactInfo(ctxt.Ctx())
if err == nil {
ctxt.VarMap().Set("commName", comm.Name)
ctxt.VarMap().Set("commAlias", comm.Alias)
@@ -284,7 +284,7 @@ func EditCommunityLogo(ctxt ui.AmContext) (string, any, error) {
ctxt.SetRC(http.StatusForbidden)
return ui.ErrorPage(ctxt, errors.New("you are not permitted to access this page"))
}
ci, err := comm.ContactInfo()
ci, err := comm.ContactInfo(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -300,13 +300,13 @@ func EditCommunityLogo(ctxt ui.AmContext) (string, any, error) {
ui.CommunityLogoMaxBytes)
if err == nil {
var img *database.ImageStore
img, err = database.AmStoreImage(database.ImageTypeCommunityLogo, comm.Id, mimeType, imageData)
img, err = database.AmStoreImage(ctxt.Ctx(), database.ImageTypeCommunityLogo, comm.Id, mimeType, imageData)
if err == nil {
photourl := fmt.Sprintf("/img/store/%d", img.ImgId)
ci.PhotoURL = &photourl
_, err = ci.Save()
_, err = ci.Save(ctxt.Ctx())
if err == nil {
err = comm.TouchUpdate()
err = comm.TouchUpdate(ctxt.Ctx())
}
if err == nil {
return "redirect", "/comm/" + comm.Alias + "/admin/profile", nil
@@ -336,7 +336,7 @@ func EditCommunityLogo(ctxt ui.AmContext) (string, any, error) {
defer func() {
if happy {
ampool.Submit(func(context.Context) {
err := database.AmDeleteImage(int32(id))
err := database.AmDeleteImage(ctxt.Ctx(), int32(id))
if err != nil {
log.Errorf("unable to delete image ID %d: %v", id, err)
}
@@ -345,7 +345,7 @@ func EditCommunityLogo(ctxt ui.AmContext) (string, any, error) {
}()
}
ci.PhotoURL = nil
_, err := ci.Save()
_, err := ci.Save(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -409,7 +409,7 @@ func CreateCommunity(ctxt ui.AmContext) (string, any, error) {
return dlg.RenderError(ctxt, err.Error())
}
var testcomm *database.Community
testcomm, err = database.AmGetCommunityByAlias(dlg.Field("alias").Value)
testcomm, err = database.AmGetCommunityByAlias(ctxt.Ctx(), dlg.Field("alias").Value)
if err != nil {
return dlg.RenderError(ctxt, err.Error())
}
@@ -429,7 +429,7 @@ func CreateCommunity(ctxt ui.AmContext) (string, any, error) {
hideSearch = true
}
var comm *database.Community
comm, err = database.AmCreateCommunity(dlg.Field("name").Value, dlg.Field("alias").Value, user.Uid,
comm, err = database.AmCreateCommunity(ctxt.Ctx(), dlg.Field("name").Value, dlg.Field("alias").Value, user.Uid,
dlg.Field("language").ValPtr(), dlg.Field("synopsis").ValPtr(), dlg.Field("rules").ValPtr(),
dlg.Field("joinkey").ValPtr(), hideDir, hideSearch, ctxt.RemoteIP())
if err != nil {
@@ -440,12 +440,12 @@ func CreateCommunity(ctxt ui.AmContext) (string, any, error) {
ci.Region = dlg.Field("reg").ValPtr()
ci.PostalCode = dlg.Field("pcode").ValPtr()
ci.Country = dlg.Field("country").ValPtr()
_, err = ci.Save()
_, err = ci.Save(ctxt.Ctx())
if err == nil {
err = comm.SetContactID(ci.ContactId)
err = comm.SetContactID(ctxt.Ctx(), ci.ContactId)
}
if err == nil {
err = comm.TouchUpdate()
err = comm.TouchUpdate(ctxt.Ctx())
}
if err != nil {
return dlg.RenderError(ctxt, err.Error())
+28 -26
View File
@@ -41,7 +41,7 @@ func Conferences(ctxt ui.AmContext) (string, any, error) {
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,
clist, err := database.AmGetCommunityConferences(ctxt.Ctx(), comm.Id,
comm.TestPermission("Community.ShowHiddenObjects", ctxt.EffectiveLevel()))
if err != nil {
return ui.ErrorPage(ctxt, err)
@@ -59,7 +59,7 @@ func Conferences(ctxt ui.AmContext) (string, any, error) {
* Standard Go error status.
*/
func Topics(ctxt ui.AmContext) (string, any, error) {
prefs, err := ctxt.CurrentUser().Prefs()
prefs, err := ctxt.CurrentUser().Prefs(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -94,7 +94,7 @@ func Topics(ctxt ui.AmContext) (string, any, error) {
sort = ctxt.QueryParamInt("sort", sort)
ctxt.SetSession("topic.sort", sort)
topics, err := database.AmListTopics(conf.ConfId, ctxt.CurrentUserId(), view, sort, false)
topics, err := database.AmListTopics(ctxt.Ctx(), conf.ConfId, ctxt.CurrentUserId(), view, sort, false)
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -137,12 +137,12 @@ func NewTopicForm(ctxt ui.AmContext) (string, any, error) {
ctxt.VarMap().Set("conferenceName", conf.Name)
ctxt.VarMap().Set("urlStem", fmt.Sprintf("/comm/%s/conf/%s", comm.Alias, ctxt.URLParam("confid")))
ctxt.VarMap().Set("topicName", "")
cs, err := conf.Settings(ctxt.CurrentUser())
cs, err := conf.Settings(ctxt.Ctx(), ctxt.CurrentUser())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
if cs == nil || cs.DefaultPseud == nil {
ci, err := ctxt.CurrentUser().ContactInfo()
ci, err := ctxt.CurrentUser().ContactInfo(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -178,7 +178,7 @@ func NewTopic(ctxt ui.AmContext) (string, any, error) {
}
if ctxt.FormFieldIsSet("preview") {
// start by escaping the title
checker, err := htmlcheck.AmNewHTMLChecker("escaper")
checker, err := htmlcheck.AmNewHTMLChecker(ctxt.Ctx(), "escaper")
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -203,7 +203,7 @@ func NewTopic(ctxt ui.AmContext) (string, any, error) {
ctxt.VarMap().Set("pb", v)
// run the preview
checker, err = htmlcheck.AmNewHTMLChecker("preview")
checker, err = htmlcheck.AmNewHTMLChecker(ctxt.Ctx(), "preview")
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -225,7 +225,7 @@ func NewTopic(ctxt ui.AmContext) (string, any, error) {
}
if ctxt.FormFieldIsSet("post1") {
// start by checking the title and pseud
checker, err := htmlcheck.AmNewHTMLChecker("post-pseud")
checker, err := htmlcheck.AmNewHTMLChecker(ctxt.Ctx(), "post-pseud")
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -238,7 +238,7 @@ func NewTopic(ctxt ui.AmContext) (string, any, error) {
zeroPostPseud, _ := checker.Value()
// now check the post data itself
checker, err = htmlcheck.AmNewHTMLChecker("post-body")
checker, err = htmlcheck.AmNewHTMLChecker(ctxt.Ctx(), "post-body")
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -249,7 +249,7 @@ func NewTopic(ctxt ui.AmContext) (string, any, error) {
lines, _ := checker.Lines()
// Add the topic!
topic, err := database.AmNewTopic(conf, ctxt.CurrentUser(), topicName, zeroPostPseud, zeroPost, int32(lines), ctxt.RemoteIP())
topic, err := database.AmNewTopic(ctxt.Ctx(), conf, ctxt.CurrentUser(), topicName, zeroPostPseud, zeroPost, int32(lines), ctxt.RemoteIP())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -258,7 +258,7 @@ func NewTopic(ctxt ui.AmContext) (string, any, error) {
return "redirect", urlStem, nil // no attachment - just redisplay topic list
}
post, err := topic.GetPost(0) // get the initial post in the new topic
post, err := topic.GetPost(ctxt.Ctx(), 0) // get the initial post in the new topic
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -303,12 +303,12 @@ func AttachmentUpload(ctxt ui.AmContext) (string, any, error) {
if err == nil {
if file.Size <= (1024 * 1024) { // 1 Mb
var post *database.PostHeader
post, err = database.AmGetPost(postId)
post, err = database.AmGetPost(ctxt.Ctx(), postId)
if err == nil {
var data []byte
data, err = slurpFile(file)
if err == nil {
err = post.SetAttachment(file.Filename, file.Header.Get("Content-Type"), int32(file.Size), data)
err = post.SetAttachment(ctxt.Ctx(), file.Filename, file.Header.Get("Content-Type"), int32(file.Size), data)
if err == nil {
return "redirect", target, nil
}
@@ -370,7 +370,8 @@ func breakRange(topic *database.Topic, into []int32, param string, sep string) e
func templateExtractUserName(args jet.Arguments) reflect.Value {
rc := "<<ERROR>>"
post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader)
user, err := database.AmGetUser(post.CreatorUid)
ctxt := args.Get(1).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext)
user, err := database.AmGetUser(ctxt.Ctx(), post.CreatorUid)
if err == nil {
rc = user.Username
} else {
@@ -381,7 +382,8 @@ func templateExtractUserName(args jet.Arguments) reflect.Value {
func templatePostText(args jet.Arguments) reflect.Value {
post := args.Get(0).Convert(reflect.TypeFor[*database.PostHeader]()).Interface().(*database.PostHeader)
rc, err := post.Text()
ctxt := args.Get(1).Convert(reflect.TypeFor[ui.AmContext]()).Interface().(ui.AmContext)
rc, err := post.Text(ctxt.Ctx())
if err != nil {
log.Errorf("templatePostText could not get post text from post #%d: %v", post.PostId, err)
rc = ""
@@ -395,10 +397,10 @@ func templateOverrideLine(args jet.Arguments) reflect.Value {
rc := ""
if post.IsScribbled() {
scr_date := ""
scr_user, err := database.AmGetUser(*post.ScribbleUid)
scr_user, err := database.AmGetUser(ctxt.Ctx(), *post.ScribbleUid)
if err == nil {
var p *database.UserPrefs
p, err = ctxt.CurrentUser().Prefs()
p, err = ctxt.CurrentUser().Prefs(ctxt.Ctx())
if err == nil {
scr_date = p.Localizer().Strftime("%b %e, %Y %r", *post.ScribbleDate)
}
@@ -430,20 +432,20 @@ func ReadPosts(ctxt ui.AmContext) (string, any, error) {
rst := strings.Split(ctxt.Parameter("rst"), ",")
if len(rst) >= 2 {
user := ctxt.CurrentUser()
ampool.Submit(func(context.Context) {
ampool.Submit(func(ctx context.Context) {
topicId, e1 := strconv.ParseInt(rst[0], 10, 32)
lastRead, e2 := strconv.ParseInt(rst[1], 10, 32)
if e1 == nil && e2 == nil {
topic, _ := database.AmGetTopic(int32(topicId))
topic, _ := database.AmGetTopic(ctx, int32(topicId))
if topic != nil {
topic.SetLastRead(user, int32(lastRead))
topic.SetLastRead(ctx, user, int32(lastRead))
}
}
})
}
}
// Get user prefs.
prefs, err := ctxt.CurrentUser().Prefs()
prefs, err := ctxt.CurrentUser().Prefs(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -452,7 +454,7 @@ func ReadPosts(ctxt ui.AmContext) (string, any, error) {
conf := ctxt.GetScratch("currentConference").(*database.Conference)
var topic *database.Topic = nil
if rawTopic, err := strconv.ParseInt(ctxt.URLParam("topic"), 10, 16); err == nil {
topic, err = database.AmGetTopicByNumber(conf, int16(rawTopic))
topic, err = database.AmGetTopicByNumber(ctxt.Ctx(), conf, int16(rawTopic))
}
if topic == nil {
ctxt.SetRC(http.StatusNotFound)
@@ -460,7 +462,7 @@ func ReadPosts(ctxt ui.AmContext) (string, any, error) {
}
// Determine the range of posts to display. The "pin" is the post number after which we display the horizontal line separating old and new posts.
lastRead, err := topic.GetLastRead(ctxt.CurrentUser())
lastRead, err := topic.GetLastRead(ctxt.Ctx(), ctxt.CurrentUser())
if err != nil {
ctxt.SetRC(http.StatusNotFound)
return ui.ErrorPage(ctxt, fmt.Errorf("posts not found in topic %d - %v", topic.Number, err))
@@ -495,7 +497,7 @@ func ReadPosts(ctxt ui.AmContext) (string, any, error) {
}
// Load the actual posts.
posts, err := database.AmGetPostRange(topic, postRange[0], postRange[1])
posts, err := database.AmGetPostRange(ctxt.Ctx(), topic, postRange[0], postRange[1])
if err != nil {
return ui.ErrorPage(ctxt, fmt.Errorf("internal error getting posts <%d:%d-%d> - %v", topic.Number, postRange[0], postRange[1], err))
}
@@ -533,8 +535,8 @@ func ReadPosts(ctxt ui.AmContext) (string, any, error) {
ctxt.VarMap().Set("amsterdam_pageTitle", fmt.Sprintf("%s: %s", topic.Name, summaryLine))
if resetLastRead {
user := ctxt.CurrentUser()
ampool.Submit(func(context.Context) {
topic.SetLastRead(user, topic.TopMessage)
ampool.Submit(func(ctx context.Context) {
topic.SetLastRead(ctx, user, topic.TopMessage)
})
}
return "framed_template", "posts.jet", nil
+4 -3
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
"fmt"
"time"
@@ -113,12 +114,12 @@ func AmNewAudit(rectype int32, uid int32, ip string, data ...string) *AuditRecor
}
// Store stores the audit record in the database.
func (ar *AuditRecord) Store() error {
func (ar *AuditRecord) Store(ctx context.Context) error {
if ar.Record > 0 {
return fmt.Errorf("audit record %d already stored", ar.Record)
}
moment := time.Now().UTC()
rs, err := amdb.Exec(`INSERT INTO audit (on_date, event, uid, commid, ip, data1, data2, data3, data4)
rs, err := amdb.ExecContext(ctx, `INSERT INTO audit (on_date, event, uid, commid, ip, data1, data2, data3, data4)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);`, moment, ar.Event, ar.Uid, ar.CommId, ar.IP,
ar.Data1, ar.Data2, ar.Data3, ar.Data4)
if err != nil {
@@ -132,7 +133,7 @@ func (ar *AuditRecord) Store() error {
// auditWriter is the routine that stores audit records in trhe background.
func auditWriter(workChan chan *AuditRecord, doneChan chan bool) {
for ar := range workChan {
err := ar.Store()
err := ar.Store(context.Background())
if err != nil {
log.Errorf("dropped audit record (%+v) on the floor: %v", *ar, err)
}
+26 -21
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
"errors"
"slices"
"strings"
@@ -45,12 +46,12 @@ var categoryIdMap map[int32]*Category = make(map[int32]*Category)
var categoryMutex sync.Mutex
// isCatEnabled determines if category features are enabled.
func isCatEnabled() (bool, error) {
g, err := AmGlobals()
func isCatEnabled(ctx context.Context) (bool, error) {
g, err := AmGlobals(ctx)
if err != nil {
return false, err
}
set, err := g.Flags()
set, err := g.Flags(ctx)
if err != nil {
return false, err
}
@@ -58,11 +59,11 @@ func isCatEnabled() (bool, error) {
}
// loadCategories loads the categories list from the database.
func loadCategories() error {
func loadCategories(ctx context.Context) error {
categoryMutex.Lock()
defer categoryMutex.Unlock()
if allCategories == nil {
rs, err := amdb.Query("SELECT COUNT(*) FROM refcategory")
rs, err := amdb.QueryContext(ctx, "SELECT COUNT(*) FROM refcategory")
if err != nil {
return err
}
@@ -72,7 +73,7 @@ func loadCategories() error {
var ncats int32
rs.Scan(&ncats)
allCategories = make([]Category, 0, ncats)
err = amdb.Select(&allCategories, "SELECT * FROM refcategory ORDER BY parent, name")
err = amdb.SelectContext(ctx, &allCategories, "SELECT * FROM refcategory ORDER BY parent, name")
if err != nil {
return err
}
@@ -85,20 +86,21 @@ func loadCategories() error {
/* AmGetCategory returns the category for the given name.
* Parameters:
* ctx - Standard Go context value.
* catid - The ID of the category to get.
* Returns:
* Pointer to the appropriate Category, or nil.
* Standard Go error status.
*/
func AmGetCategory(catid int32) (*Category, error) {
ok, err := isCatEnabled()
func AmGetCategory(ctx context.Context, catid int32) (*Category, error) {
ok, err := isCatEnabled(ctx)
if err != nil {
return nil, err
}
if !ok {
return nil, errors.New("category feature not supported")
}
err = loadCategories()
err = loadCategories(ctx)
if err != nil {
return nil, err
}
@@ -116,20 +118,21 @@ func AmGetCategory(catid int32) (*Category, error) {
/* AmGetCategoryHierarchy returns the category hierarchy for the given ID.
* Parameters:
* ctx - Standard Go context value.
* catid - The ID of the category to get.
* Returns:
* Array of pointers to the categories in hierarchical order, or nil.
* Standard Go error status.
*/
func AmGetCategoryHierarchy(catid int32) ([]*Category, error) {
ok, err := isCatEnabled()
func AmGetCategoryHierarchy(ctx context.Context, catid int32) ([]*Category, error) {
ok, err := isCatEnabled(ctx)
if err != nil {
return nil, err
}
if !ok {
return nil, errors.New("category feature not supported")
}
err = loadCategories()
err = loadCategories(ctx)
if err != nil {
return nil, err
}
@@ -154,20 +157,21 @@ func AmGetCategoryHierarchy(catid int32) ([]*Category, error) {
/* AmGetSubCategories returns a list of all subcategories of the given category ID.
* Parameters:
* ctx - Standard Go context value.
* catid - The parent category ID to use. May be -1 to return all "top level" categories.
* Returns:
* List of subcategories of this category.
* Standard Go error status.
*/
func AmGetSubCategories(catid int32) ([]*Category, error) {
ok, err := isCatEnabled()
func AmGetSubCategories(ctx context.Context, catid int32) ([]*Category, error) {
ok, err := isCatEnabled(ctx)
if err != nil {
return nil, err
}
if !ok {
return nil, errors.New("category feature not supported")
}
err = loadCategories()
err = loadCategories(ctx)
if err != nil {
return nil, err
}
@@ -185,6 +189,7 @@ func AmGetSubCategories(catid int32) ([]*Category, error) {
/* AmSearchCategories searches for categories matching certain criteria.
* Parameters:
* ctx - Standard Go context value.
* oper - The operation to perform on the category name:
* SearchCatOperPrefix - The category name has the string "term" as a prefix.
* SearchCatOperSubstring - The category name contains the string "term".
@@ -197,8 +202,8 @@ func AmGetSubCategories(catid int32) ([]*Category, error) {
* The total number of categories matching this query (could be greater than max)
* Standard Go error status.
*/
func AmSearchCategories(oper int, term string, offset int, max int, showAll bool, searchAll bool) ([]*Category, int, error) {
ok, err := isCatEnabled()
func AmSearchCategories(ctx context.Context, oper int, term string, offset int, max int, showAll bool, searchAll bool) ([]*Category, int, error) {
ok, err := isCatEnabled(ctx)
if err != nil {
return nil, -1, err
}
@@ -230,7 +235,7 @@ func AmSearchCategories(oper int, term string, offset int, max int, showAll bool
queryString.WriteString(" AND hide_search = 0")
}
q := queryString.String()
rs, err := amdb.Query("SELECT COUNT(*) FROM refcategory WHERE " + q)
rs, err := amdb.QueryContext(ctx, "SELECT COUNT(*) FROM refcategory WHERE "+q)
if err != nil {
return nil, -1, err
}
@@ -243,9 +248,9 @@ func AmSearchCategories(oper int, term string, offset int, max int, showAll bool
return make([]*Category, 0), 0, nil
}
if offset > 0 {
rs, err = amdb.Query("SELECT catid FROM refcategory WHERE "+q+" ORDER BY parent, name LIMIT ? OFFSET ?", max, offset)
rs, err = amdb.QueryContext(ctx, "SELECT catid FROM refcategory WHERE "+q+" ORDER BY parent, name LIMIT ? OFFSET ?", max, offset)
} else {
rs, err = amdb.Query("SELECT catid FROM refcategory WHERE "+q+" ORDER BY parent, name LIMIT ?", max)
rs, err = amdb.QueryContext(ctx, "SELECT catid FROM refcategory WHERE "+q+" ORDER BY parent, name LIMIT ?", max)
}
if err != nil {
return nil, total, err
@@ -254,7 +259,7 @@ func AmSearchCategories(oper int, term string, offset int, max int, showAll bool
for rs.Next() {
var catid int32
rs.Scan(&catid)
c, err := AmGetCategory(catid)
c, err := AmGetCategory(ctx, catid)
if err == nil {
rc = append(rc, c)
}
+107 -90
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
"database/sql"
"errors"
"fmt"
@@ -151,27 +152,27 @@ func (c *Community) Public() bool {
}
// ContactInfo returns the contact info structure for the community.
func (c *Community) ContactInfo() (*ContactInfo, error) {
func (c *Community) ContactInfo(ctx context.Context) (*ContactInfo, error) {
if c.ContactId < 0 {
return nil, nil
}
return AmGetContactInfo(c.ContactId)
return AmGetContactInfo(ctx, c.ContactId)
}
// Host returns the reference to the host of the community.
func (c *Community) Host() (*User, error) {
func (c *Community) Host(ctx context.Context) (*User, error) {
if c.HostUid == nil {
return nil, nil
}
return AmGetUser(*c.HostUid)
return AmGetUser(ctx, *c.HostUid)
}
// HostQ returns the reference to the community's host, quietly.
func (c *Community) HostQ() *User {
func (c *Community) HostQ(ctx context.Context) *User {
if c.HostUid == nil {
return nil
}
u, err := AmGetUser(*c.HostUid)
u, err := AmGetUser(ctx, *c.HostUid)
if err != nil {
return nil
}
@@ -192,6 +193,7 @@ func (c *Community) LanguageTag() (*language.Tag, error) {
/* Membership returns the details of the specified user's membership in the community.
* Parameters:
* ctxt - Standard Go context value.
* u - The user to check the membership of.
* Returns:
* true if the user is a member, false if not.
@@ -199,7 +201,7 @@ func (c *Community) LanguageTag() (*language.Tag, error) {
* User's access level in the community, or 0 if the user is not a member.
* Standard Go error status.
*/
func (c *Community) Membership(u *User) (bool, bool, uint16, error) {
func (c *Community) Membership(ctx context.Context, u *User) (bool, bool, uint16, error) {
key := fmt.Sprintf("%d:%d", c.Id, u.Uid)
memberMutex.Lock()
defer memberMutex.Unlock()
@@ -212,7 +214,7 @@ func (c *Community) Membership(u *User) (bool, bool, uint16, error) {
// "no join required" - they are effectively a member, but don't cache that
return true, false, u.BaseLevel, nil
}
rs, err := amdb.Query("SELECT locked, granted_lvl FROM commmember WHERE commid = ? AND uid = ?", c.Id, u.Uid)
rs, err := amdb.QueryContext(ctx, "SELECT locked, granted_lvl FROM commmember WHERE commid = ? AND uid = ?", c.Id, u.Uid)
if err == nil {
if rs.Next() {
var locked bool
@@ -227,13 +229,13 @@ func (c *Community) Membership(u *User) (bool, bool, uint16, error) {
}
// MemberCount returns the number of members in the community.
func (c *Community) MemberCount(hidden bool) (int, error) {
func (c *Community) MemberCount(ctx context.Context, hidden bool) (int, error) {
var rs *sql.Rows
var err error
if hidden {
rs, err = amdb.Query("SELECT COUNT(*) FROM commmember WHERE commid = ?", c.Id)
rs, err = amdb.QueryContext(ctx, "SELECT COUNT(*) FROM commmember WHERE commid = ?", c.Id)
} else {
rs, err = amdb.Query("SELECT COUNT(*) FROM commmember WHERE commid = ? AND hidden = 0", c.Id)
rs, err = amdb.QueryContext(ctx, "SELECT COUNT(*) FROM commmember WHERE commid = ? AND hidden = 0", c.Id)
}
if err != nil {
return -1, err
@@ -248,6 +250,7 @@ func (c *Community) MemberCount(hidden bool) (int, error) {
/* ListMembers lists or searches for community members matching certain criteria.
* Parameters:
* ctx - Standard Go context value.
* field - A value indicating which field to search:
* ListMembersFieldNone - Do not search, just return all community members.
* ListMembersFieldName - The user name.
@@ -267,7 +270,7 @@ func (c *Community) MemberCount(hidden bool) (int, error) {
* The total number of members matching this query (could be greater than max)
* Standard Go error status.
*/
func (c *Community) ListMembers(field int, oper int, term string, offset int, max int, showHidden bool) ([]*User, int, error) {
func (c *Community) ListMembers(ctx context.Context, field int, oper int, term string, offset int, max int, showHidden bool) ([]*User, int, error) {
var query strings.Builder
if field != ListMembersFieldNone && oper != ListMembersOperNone {
query.WriteString(" AND ")
@@ -304,7 +307,7 @@ func (c *Community) ListMembers(field int, oper int, term string, offset int, ma
query.WriteString(" AND m.hidden = 0")
}
q := query.String()
rs, err := amdb.Query(`SELECT COUNT(*) FROM commmember m, users u, contacts c WHERE m.commid = ? AND m.uid = u.uid
rs, err := amdb.QueryContext(ctx, `SELECT COUNT(*) FROM commmember m, users u, contacts c WHERE m.commid = ? AND m.uid = u.uid
AND u.contactid = c.contactid`+q, c.Id)
if err != nil {
return nil, -1, err
@@ -315,10 +318,10 @@ func (c *Community) ListMembers(field int, oper int, term string, offset int, ma
var total int
rs.Scan(&total)
if offset > 0 {
rs, err = amdb.Query(`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)
} else {
rs, err = amdb.Query(`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 ?", c.Id, max)
}
if err != nil {
@@ -328,7 +331,7 @@ func (c *Community) ListMembers(field int, oper int, term string, offset int, ma
for rs.Next() {
var uid int32
rs.Scan(&uid)
u, err := AmGetUser(uid)
u, err := AmGetUser(ctx, uid)
if err == nil {
rc = append(rc, u)
}
@@ -338,6 +341,7 @@ func (c *Community) ListMembers(field int, oper int, term string, offset int, ma
/* SetMembership sets a user's membership status within the community.
* Parameters:
* ctx - Standard Go context value.
* u - The user to change the membership status of.
* level - Their membership level. If this is 0, they are removed from membership.
* locked - Whether they can unjoin the community themselves. Ignored if removing them.
@@ -346,7 +350,7 @@ func (c *Community) ListMembers(field int, oper int, term string, offset int, ma
* Returns:
* Standard Go error status.
*/
func (c *Community) SetMembership(u *User, level uint16, locked bool, personUID int32, ipaddr string) error {
func (c *Community) SetMembership(ctx context.Context, u *User, level uint16, locked bool, personUID int32, ipaddr string) error {
success := false
tx := amdb.MustBegin()
defer func() {
@@ -355,20 +359,20 @@ func (c *Community) SetMembership(u *User, level uint16, locked bool, personUID
}
}()
if level == 0 {
res, err := tx.Exec("DELETE FROM commmember WHERE commid = ? AND uid = ?", c.Id, u.Uid)
res, err := tx.ExecContext(ctx, "DELETE FROM commmember WHERE commid = ? AND uid = ?", c.Id, u.Uid)
if err != nil {
return err
}
stuffMembership(c.Id, u.Uid, false, false, 0)
ra, err := res.RowsAffected()
if err == nil && ra > 0 {
err = AmOnUserLeaveCommunityServices(tx, c, u)
err = AmOnUserLeaveCommunityServices(ctx, tx, c, u)
if err != nil {
return err
}
}
} else {
rs, err := tx.Query("SELECT granted_lvl, locked FROM commmember WHERE commid = ? AND uid = ?", c.Id, u.Uid)
rs, err := tx.QueryContext(ctx, "SELECT granted_lvl, locked FROM commmember WHERE commid = ? AND uid = ?", c.Id, u.Uid)
if err != nil {
return err
}
@@ -377,7 +381,7 @@ func (c *Community) SetMembership(u *User, level uint16, locked bool, personUID
var lockStatus bool
rs.Scan(&oldLevel, &lockStatus)
if level != oldLevel || lockStatus != locked {
_, err := tx.Exec("UPDATE commmember SET granted_lvl = ?, locked = ? WHERE commid = ? AND uid = ?",
_, err := tx.ExecContext(ctx, "UPDATE commmember SET granted_lvl = ?, locked = ? WHERE commid = ? AND uid = ?",
level, locked, c.Id, u.Uid)
if err != nil {
return err
@@ -385,19 +389,19 @@ func (c *Community) SetMembership(u *User, level uint16, locked bool, personUID
stuffMembership(c.Id, u.Uid, true, locked, level)
}
} else {
_, err := tx.Exec("INSERT INTO commmember (commid, uid, granted_lvl, locked) VALUES (?, ?, ?, ?)",
_, err := tx.ExecContext(ctx, "INSERT INTO commmember (commid, uid, granted_lvl, locked) VALUES (?, ?, ?, ?)",
c.Id, u.Uid, level, locked)
if err != nil {
return err
}
stuffMembership(c.Id, u.Uid, true, locked, level)
err = AmOnUserJoinCommunityServices(tx, c, u)
err = AmOnUserJoinCommunityServices(ctx, tx, c, u)
if err != nil {
return err
}
}
}
err := c.TouchUpdateTx(tx)
err := c.TouchUpdateTx(ctx, tx)
if err == nil {
ar := AmNewAudit(AuditCommunitySetMembership, personUID, ipaddr, fmt.Sprintf("cid=%d", c.Id),
fmt.Sprintf("uid=%d", u.Uid), fmt.Sprintf("level=%d", level))
@@ -450,11 +454,11 @@ func (c *Community) PermissionLevel(perm string) uint16 {
}
// GetFlags retrieves the flags from the properties.
func (c *Community) Flags() (*util.OptionSet, error) {
func (c *Community) Flags(ctx context.Context) (*util.OptionSet, error) {
c.Mutex.Lock()
defer c.Mutex.Unlock()
if c.flags == nil {
s, err := AmGetCommunityProperty(c.Id, CommunityPropFlags)
s, err := AmGetCommunityProperty(ctx, c.Id, CommunityPropFlags)
if err != nil {
return nil, err
}
@@ -468,11 +472,11 @@ func (c *Community) Flags() (*util.OptionSet, error) {
}
// SaveFlags writes the flags to the database and stores them.
func (c *Community) SaveFlags(f *util.OptionSet) error {
func (c *Community) SaveFlags(ctx context.Context, f *util.OptionSet) error {
s := f.AsString()
c.Mutex.Lock()
defer c.Mutex.Unlock()
err := AmSetCommunityProperty(c.Id, CommunityPropFlags, &s)
err := AmSetCommunityProperty(ctx, c.Id, CommunityPropFlags, &s)
if err == nil {
c.flags = f
}
@@ -480,12 +484,12 @@ func (c *Community) SaveFlags(f *util.OptionSet) error {
}
// SetProfileData sets all the "settable" profile data
func (c *Community) SetProfileData(name string, alias string, synopsis *string, rules *string, language *string,
func (c *Community) SetProfileData(ctx context.Context, name string, alias string, synopsis *string, rules *string, language *string,
joinkey *string, membersonly bool, hideDirectory bool, hideSearch bool, read_lvl uint16, write_lvl uint16,
create_lvl uint16, delete_lvl uint16, join_lvl uint16) error {
c.Mutex.Lock()
defer c.Mutex.Unlock()
_, err := amdb.Exec(`UPDATE communities SET commname = ?, alias = ?, synopsis = ?, rules = ?, language = ?,
_, err := amdb.ExecContext(ctx, `UPDATE communities SET commname = ?, alias = ?, synopsis = ?, rules = ?, language = ?,
joinkey = ?, membersonly = ?, hide_dir = ?, hide_search = ?, read_lvl = ?, write_lvl = ?, create_lvl = ?,
delete_lvl = ?, join_lvl = ?, lastupdate = NOW() WHERE commid = ?`,
name, alias, synopsis, rules, language, joinkey, membersonly, hideDirectory, hideSearch, read_lvl, write_lvl,
@@ -505,7 +509,7 @@ func (c *Community) SetProfileData(name string, alias string, synopsis *string,
c.CreateLevel = create_lvl
c.DeleteLevel = delete_lvl
c.JoinLevel = join_lvl
rs, err2 := amdb.Query("SELECT lastupdate FROM communities WHERE commid = ?", c.Id)
rs, err2 := amdb.QueryContext(ctx, "SELECT lastupdate FROM communities WHERE commid = ?", c.Id)
if err2 != nil {
rs.Next()
rs.Scan(&c.LastUpdate)
@@ -515,10 +519,10 @@ func (c *Community) SetProfileData(name string, alias string, synopsis *string,
}
// SetContactID sets the contact ID for the community.
func (c *Community) SetContactID(cid int32) error {
func (c *Community) SetContactID(ctx context.Context, cid int32) error {
c.Mutex.Lock()
defer c.Mutex.Unlock()
if _, err := amdb.Exec("UPDATE communities SET contactid = ? WHERE commid = ?", cid, c.Id); err != nil {
if _, err := amdb.ExecContext(ctx, "UPDATE communities SET contactid = ? WHERE commid = ?", cid, c.Id); err != nil {
return err
}
c.ContactId = cid
@@ -526,12 +530,12 @@ func (c *Community) SetContactID(cid int32) error {
}
// Touch updates the last access time of the community.
func (c *Community) Touch() error {
func (c *Community) Touch(ctx context.Context) error {
c.Mutex.Lock()
defer c.Mutex.Unlock()
_, err := amdb.Exec("UPDATE communities SET lastaccess = NOW() WHERE commid = ?", c.Id)
_, err := amdb.ExecContext(ctx, "UPDATE communities SET lastaccess = NOW() WHERE commid = ?", c.Id)
if err == nil {
rs, err := amdb.Query("SELECT lastaccess FROM communities WHERE commid = ?", c.Id)
rs, err := amdb.QueryContext(ctx, "SELECT lastaccess FROM communities WHERE commid = ?", c.Id)
if err == nil {
rs.Next()
var na time.Time
@@ -543,12 +547,12 @@ func (c *Community) Touch() error {
}
// TouchUpdateTx updates the last access and last update times of the community.
func (c *Community) TouchUpdateTx(tx *sqlx.Tx) error {
func (c *Community) TouchUpdateTx(ctx context.Context, tx *sqlx.Tx) error {
c.Mutex.Lock()
defer c.Mutex.Unlock()
_, err := tx.Exec("UPDATE communities SET lastaccess = NOW(), lastupdate = NOW() WHERE commid = ?", c.Id)
_, err := tx.ExecContext(ctx, "UPDATE communities SET lastaccess = NOW(), lastupdate = NOW() WHERE commid = ?", c.Id)
if err == nil {
rs, err := tx.Query("SELECT lastaccess, lastupdate FROM communities WHERE commid = ?", c.Id)
rs, err := tx.QueryContext(ctx, "SELECT lastaccess, lastupdate FROM communities WHERE commid = ?", c.Id)
if err != nil {
rs.Next()
var na, nu time.Time
@@ -561,9 +565,9 @@ func (c *Community) TouchUpdateTx(tx *sqlx.Tx) error {
}
// TouchUpdateTx updates the last access and last update times of the community.
func (c *Community) TouchUpdate() error {
func (c *Community) TouchUpdate(ctx context.Context) error {
tx := amdb.MustBegin()
err := c.TouchUpdateTx(tx)
err := c.TouchUpdateTx(ctx, tx)
if err != nil {
err = tx.Commit()
}
@@ -575,18 +579,19 @@ func (c *Community) TouchUpdate() error {
/* AmGetCommunity returns a reference to the specified community.
* Parameters:
* ctx - Standard Go context value.
* id - The ID of the community.
* Returns:
* Pointer to Community containing community data, or nil
* Standard Go error status
*/
func AmGetCommunity(id int32) (*Community, error) {
func AmGetCommunity(ctx context.Context, id int32) (*Community, error) {
getCommunityMutex.Lock()
defer getCommunityMutex.Unlock()
rc, ok := communityCache.Get(id)
if !ok {
var dbdata []Community
err := amdb.Select(&dbdata, "SELECT * from communities WHERE commid = ?", id)
err := amdb.SelectContext(ctx, &dbdata, "SELECT * from communities WHERE commid = ?", id)
if err != nil {
return nil, err
}
@@ -603,19 +608,20 @@ func AmGetCommunity(id int32) (*Community, error) {
/* AmGetCommunityTx returns a reference to the specified community, in a transaction.
* Parameters:
* ctx - Standard Go context value.
* tx - The transaction to use.
* id - The ID of the community.
* Returns:
* Pointer to Community containing community data, or nil
* Standard Go error status
*/
func AmGetCommunityTx(tx *sqlx.Tx, id int32) (*Community, error) {
func AmGetCommunityTx(ctx context.Context, tx *sqlx.Tx, id int32) (*Community, error) {
getCommunityMutex.Lock()
defer getCommunityMutex.Unlock()
rc, ok := communityCache.Get(id)
if !ok {
var dbdata []Community
err := tx.Select(&dbdata, "SELECT * from communities WHERE commid = ?", id)
err := tx.SelectContext(ctx, &dbdata, "SELECT * from communities WHERE commid = ?", id)
if err != nil {
return nil, err
}
@@ -632,18 +638,19 @@ func AmGetCommunityTx(tx *sqlx.Tx, id int32) (*Community, error) {
/* AmGetCommunityByAlias returns a reference to the specified community.
* Parameters:
* ctx - Standard Go context value.
* alias - The alias for the community.
* Returns:
* Pointer to Community containing community data, or nil
* Standard Go error status (nil if community not found)
*/
func AmGetCommunityByAlias(alias string) (*Community, error) {
rs, err := amdb.Query("SELECT commid FROM communities WHERE alias = ?", alias)
func AmGetCommunityByAlias(ctx context.Context, alias string) (*Community, error) {
rs, err := amdb.QueryContext(ctx, "SELECT commid FROM communities WHERE alias = ?", alias)
if err == nil {
if rs.Next() {
var cid int32
rs.Scan(&cid)
return AmGetCommunity(cid)
return AmGetCommunity(ctx, cid)
} else {
return nil, nil
}
@@ -653,19 +660,20 @@ func AmGetCommunityByAlias(alias string) (*Community, error) {
/* AmGetCommunityByAliasTx returns a reference to the specified community, within a transaction.
* Parameters:
* ctx - Standard Go context value.
* tx - The transaction to use.
* alias - The alias for the community.
* Returns:
* Pointer to Community containing community data, or nil
* Standard Go error status (nil if community not found)
*/
func AmGetCommunityByAliasTx(tx *sqlx.Tx, alias string) (*Community, error) {
rs, err := tx.Query("SELECT commid FROM communities WHERE alias = ?", alias)
func AmGetCommunityByAliasTx(ctx context.Context, tx *sqlx.Tx, alias string) (*Community, error) {
rs, err := tx.QueryContext(ctx, "SELECT commid FROM communities WHERE alias = ?", alias)
if err == nil {
if rs.Next() {
var cid int32
rs.Scan(&cid)
return AmGetCommunityTx(tx, cid)
return AmGetCommunityTx(ctx, tx, cid)
} else {
return nil, nil
}
@@ -677,21 +685,22 @@ func AmGetCommunityByAliasTx(tx *sqlx.Tx, alias string) (*Community, error) {
* If the parameter is numeric, it's interpreted as a community ID. Otherwise, it's interpreted
* as a community alias.
* Parameters:
* ctx - Standard Go context value.
* id - The ID of the community.
* Returns:
* Pointer to Community containing community data, or nil
* Standard Go error status
*/
func AmGetCommunityFromParam(param string) (*Community, error) {
func AmGetCommunityFromParam(ctx context.Context, param string) (*Community, error) {
if util.IsNumeric(param) {
v, _ := strconv.Atoi(param)
c, err := AmGetCommunity(int32(v))
c, err := AmGetCommunity(ctx, int32(v))
if err == nil {
return c, nil
}
// else fall through to trying as alias
}
rc, err := AmGetCommunityByAlias(param)
rc, err := AmGetCommunityByAlias(ctx, param)
if err == nil {
if rc == nil {
return nil, fmt.Errorf("community with alias \"%s\" not found", param)
@@ -702,18 +711,19 @@ func AmGetCommunityFromParam(param string) (*Community, error) {
/* AmGetCommunitiesForUser returns a list of communities the user is a member of.
* Parameters:
* ctx - Standard Go context value.
* uid - The ID of the user.
* Returns:
* Array of pointers to communities for the user
* Standard Go error status
*/
func AmGetCommunitiesForUser(uid int32) ([]*Community, error) {
func AmGetCommunitiesForUser(ctx context.Context, uid int32) ([]*Community, error) {
var rc []*Community = make([]*Community, 0)
var ids []int32 = make([]int32, 0)
err := amdb.Select(&ids, "SELECT commid FROM commmember WHERE uid = ?", uid)
err := amdb.SelectContext(ctx, &ids, "SELECT commid FROM commmember WHERE uid = ?", uid)
if err == nil {
for _, id := range ids {
c, err := AmGetCommunity(id)
c, err := AmGetCommunity(ctx, id)
if err == nil {
rc = append(rc, c)
} else {
@@ -727,15 +737,16 @@ func AmGetCommunitiesForUser(uid int32) ([]*Community, error) {
/* AmGetCommunityAccessLevel returns the access level of the specified user with respect to the community.
* This may reflect the user's admin status as well as their status within the community.
* Parameters:
* ctx - Standard Go context value.
* uid - The UID of the user.
* commid - The ID of the community.
* Returns:
* Access level within the community, or 0 if the user is not a member.
* Standard Go error status.
*/
func AmGetCommunityAccessLevel(uid int32, commid int32) (uint16, error) {
func AmGetCommunityAccessLevel(ctx context.Context, uid int32, commid int32) (uint16, error) {
var rc uint16 = 0
rows, err := amdb.Queryx(`SELECT GREATEST(m.granted_lvl, u.base_lvl) AS level FROM users u, commmember m
rows, err := amdb.QueryxContext(ctx, `SELECT GREATEST(m.granted_lvl, u.base_lvl) AS level FROM users u, commmember m
WHERE u.uid = m.uid AND m.uid = ? AND m.commid = ?`, uid, commid)
if err == nil {
defer rows.Close()
@@ -748,21 +759,22 @@ func AmGetCommunityAccessLevel(uid int32, commid int32) (uint16, error) {
/* AmAutoJoinCommunities joins the specified user to any communities they're not yet a part of.
* Parameters:
* ctx - Standard Go context value.
* tx - The current transaction to be used for database access.
* user - The user to be auto-joined to communities.
* Returns:
* Standard Go error status.
*/
func AmAutoJoinCommunities(tx *sqlx.Tx, user *User) error {
func AmAutoJoinCommunities(ctx context.Context, tx *sqlx.Tx, user *User) error {
// get list of current communities
var current []int32 = make([]int32, 0)
err := tx.Select(&current, "SELECT commid FROM commmember WHERE uid = ?", user.Uid)
err := tx.SelectContext(ctx, &current, "SELECT commid FROM commmember WHERE uid = ?", user.Uid)
if err != nil {
return err
}
// look for candidate communities
rows, err := tx.Queryx(`SELECT m.commid, m.locked FROM users u, communities c, commmember m
rows, err := tx.QueryxContext(ctx, `SELECT m.commid, m.locked FROM users u, communities c, commmember m
WHERE m.uid = u.uid AND m.commid = c.commid AND u.is_anon = 1 AND c.join_lvl <= ?`, user.BaseLevel)
if err == nil {
defer rows.Close()
@@ -772,7 +784,7 @@ func AmAutoJoinCommunities(tx *sqlx.Tx, user *User) error {
var lock bool
rows.Scan(&cid, &lock)
if !slices.Contains(current, cid) {
_, err = tx.Exec("INSERT INTO commmember (commid, uid, granted_lvl, locked) VALUES (?, ?, ?, ?)",
_, err = tx.ExecContext(ctx, "INSERT INTO commmember (commid, uid, granted_lvl, locked) VALUES (?, ?, ?, ?)",
cid, user.Uid, grantLevel, lock)
if err != nil {
break
@@ -785,7 +797,7 @@ func AmAutoJoinCommunities(tx *sqlx.Tx, user *User) error {
}
// internalGetProp is a helper used by the property functions.
func internalGetCommProp(cid int32, ndx int32) (*CommunityProperties, error) {
func internalGetCommProp(ctx context.Context, cid int32, ndx int32) (*CommunityProperties, error) {
var err error = nil
key := fmt.Sprintf("%d:%d", cid, ndx)
getCommunityPropMutex.Lock()
@@ -793,7 +805,7 @@ func internalGetCommProp(cid int32, ndx int32) (*CommunityProperties, error) {
rc, ok := communityPropCache.Get(key)
if !ok {
var dbdata []CommunityProperties
err = amdb.Select(&dbdata, "SELECT * from propcomm WHERE cid = ? AND ndx = ?", cid, ndx)
err = amdb.SelectContext(ctx, &dbdata, "SELECT * from propcomm WHERE cid = ? AND ndx = ?", cid, ndx)
if err != nil {
return nil, err
}
@@ -811,14 +823,15 @@ func internalGetCommProp(cid int32, ndx int32) (*CommunityProperties, error) {
/* AmGetCommunityProperty retrieves the value of a user property.
* Parameters:
* ctx - Standard Go context value.
* cid - The ID of the community to get the property for.
* ndx - The index of the property to retrieve.
* Returns:
* Value of the property string.
* Standard Go error status.
*/
func AmGetCommunityProperty(cid int32, ndx int32) (*string, error) {
p, err := internalGetCommProp(cid, ndx)
func AmGetCommunityProperty(ctx context.Context, cid int32, ndx int32) (*string, error) {
p, err := internalGetCommProp(ctx, cid, ndx)
if err != nil {
return nil, err
} else if p == nil {
@@ -829,27 +842,28 @@ func AmGetCommunityProperty(cid int32, ndx int32) (*string, error) {
/* AmSetCommunityProperty sets the value of a community property.
* Parameters:
* ctx - Standard Go context value.
* cid - The ID of the community to set the property for.
* ndx - The index of the property to set.
* val - The new value of the property.
* Returns:
* Standard Go error status.
*/
func AmSetCommunityProperty(cid int32, ndx int32, val *string) error {
p, err := internalGetCommProp(cid, ndx)
func AmSetCommunityProperty(ctx context.Context, cid int32, ndx int32, val *string) error {
p, err := internalGetCommProp(ctx, cid, ndx)
if err != nil {
return err
}
getCommunityPropMutex.Lock()
defer getCommunityPropMutex.Unlock()
if p != nil {
_, err = amdb.Exec("UPDATE propcomm SET data = ? WHERE cid = ? AND ndx = ?", val, cid, ndx)
_, err = amdb.ExecContext(ctx, "UPDATE propcomm SET data = ? WHERE cid = ? AND ndx = ?", val, cid, ndx)
if err == nil {
p.Data = val
}
} else {
prop := CommunityProperties{Cid: cid, Index: ndx, Data: val}
_, err := amdb.NamedExec("INSERT INTO propcomm (cid, ndx, data) VALUES(:cid, :ndx, :data)", prop)
_, err := amdb.NamedExecContext(ctx, "INSERT INTO propcomm (cid, ndx, data) VALUES(:cid, :ndx, :data)", prop)
if err == nil {
communityPropCache.Add(fmt.Sprintf("%d:%d", cid, ndx), prop)
}
@@ -859,6 +873,7 @@ func AmSetCommunityProperty(cid int32, ndx int32, val *string) error {
/* AmCreateCommunity creates a new community.
* Parameters:
* ctx - Standard Go context value.
* name - The name for the new community.
* alias - The alias for the new community. Must be unique.
* hostUid - The UID of the creator and new host of the community.
@@ -873,7 +888,7 @@ func AmSetCommunityProperty(cid int32, ndx int32, val *string) error {
* Pointer to new Community record, or nil.
* Standard Go error status.
*/
func AmCreateCommunity(name string, alias string, hostUid int32, language *string, synopsis *string,
func AmCreateCommunity(ctx context.Context, name string, alias string, hostUid int32, language *string, synopsis *string,
rules *string, joinkey *string, hideDirectory bool, hideSearch bool, remoteIP string) (*Community, error) {
var ar *AuditRecord = nil
defer func() {
@@ -888,7 +903,7 @@ func AmCreateCommunity(name string, alias string, hostUid int32, language *strin
}()
// validate alias does not already exist
rs, err := tx.Query("SELECT commid FROM communities WHERE alias = ?", alias)
rs, err := tx.QueryContext(ctx, "SELECT commid FROM communities WHERE alias = ?", alias)
if err != nil {
return nil, err
}
@@ -897,7 +912,7 @@ func AmCreateCommunity(name string, alias string, hostUid int32, language *strin
}
// establish the community record
_, err = tx.Exec(`INSERT INTO communities (createdate, lastaccess, lastupdate, read_lvl, write_lvl,
_, err = tx.ExecContext(ctx, `INSERT INTO communities (createdate, lastaccess, lastupdate, read_lvl, write_lvl,
create_lvl, delete_lvl, join_lvl, host_uid, hide_dir, hide_search, commname, language,
synopsis, rules, joinkey, alias) VALUES (NOW(), NOW(), NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
AmRoleList("Community.Read").Default().Level(), AmRoleList("Community.Write").Default().Level(),
@@ -909,7 +924,7 @@ func AmCreateCommunity(name string, alias string, hostUid int32, language *strin
}
// Read back the community, which also puts it in the cache.
comm, err := AmGetCommunityByAliasTx(tx, alias)
comm, err := AmGetCommunityByAliasTx(ctx, tx, alias)
if err != nil {
return nil, err
} else if comm == nil {
@@ -918,7 +933,7 @@ func AmCreateCommunity(name string, alias string, hostUid int32, language *strin
// Ensure the new host has host privileges in the community. The host's membership is "locked" so they
// can't unjoin and leave the community hostless.
_, err = tx.Exec("INSERT INTO commmember (commid, uid, granted_lvl, locked) VALUES (?, ?, ?, 1)", comm.Id, hostUid,
_, err = tx.ExecContext(ctx, "INSERT INTO commmember (commid, uid, granted_lvl, locked) VALUES (?, ?, ?, 1)", comm.Id, hostUid,
AmDefaultRole("Community.Creator").Level())
if err != nil {
return nil, err
@@ -926,7 +941,7 @@ func AmCreateCommunity(name string, alias string, hostUid int32, language *strin
stuffMembership(comm.Id, hostUid, true, true, AmDefaultRole("Community.Creator").Level())
// Establish the community services.
err = AmEstablishCommunityServices(tx, comm)
err = AmEstablishCommunityServices(ctx, tx, comm)
if err != nil {
return nil, err
}
@@ -945,6 +960,7 @@ func AmCreateCommunity(name string, alias string, hostUid int32, language *strin
/* AmGetCommunitiesForCategory returns a list of communities for the specified category.
* Parameters:
* ctx - Standard Go context value.
* catid - Category ID to search for.
* offset - Number of communities to skip at beginning of list.
* max - Maximum number of communities to return.
@@ -954,13 +970,13 @@ func AmCreateCommunity(name string, alias string, hostUid int32, language *strin
* The total number of communities matching this query (could be greater than max)
* Standard Go error status.
*/
func AmGetCommunitiesForCategory(catid int32, offset int, max int, showAll bool) ([]*Community, int, error) {
func AmGetCommunitiesForCategory(ctx context.Context, catid int32, offset int, max int, showAll bool) ([]*Community, int, error) {
var rs *sql.Rows
var err error
if showAll {
rs, err = amdb.Query("SELECT COUNT(*) FROM communities WHERE catid = ?", catid)
rs, err = amdb.QueryContext(ctx, "SELECT COUNT(*) FROM communities WHERE catid = ?", catid)
} else {
rs, err = amdb.Query("SELECT COUNT(*) FROM communities WHERE catid = ? AND hide_dir = 0", catid)
rs, err = amdb.QueryContext(ctx, "SELECT COUNT(*) FROM communities WHERE catid = ? AND hide_dir = 0", catid)
}
if err != nil {
return nil, -1, err
@@ -975,17 +991,17 @@ func AmGetCommunitiesForCategory(catid int32, offset int, max int, showAll bool)
}
if showAll {
if offset > 0 {
rs, err = amdb.Query("SELECT commid FROM communities WHERE catid = ? ORDER BY commname LIMIT ? OFFSET ?",
rs, err = amdb.QueryContext(ctx, "SELECT commid FROM communities WHERE catid = ? ORDER BY commname LIMIT ? OFFSET ?",
catid, max, offset)
} else {
rs, err = amdb.Query("SELECT commid FROM communities WHERE catid = ? ORDER BY commname LIMIT ?", catid, max)
rs, err = amdb.QueryContext(ctx, "SELECT commid FROM communities WHERE catid = ? ORDER BY commname LIMIT ?", catid, max)
}
} else {
if offset > 0 {
rs, err = amdb.Query("SELECT commid FROM communities WHERE catid = ? AND hide_dir = 0 ORDER BY commname LIMIT ? OFFSET ?",
rs, err = amdb.QueryContext(ctx, "SELECT commid FROM communities WHERE catid = ? AND hide_dir = 0 ORDER BY commname LIMIT ? OFFSET ?",
catid, max, offset)
} else {
rs, err = amdb.Query("SELECT commid FROM communities WHERE catid = ? AND hide_dir = 0 ORDER BY commname LIMIT ?", catid, max)
rs, err = amdb.QueryContext(ctx, "SELECT commid FROM communities WHERE catid = ? AND hide_dir = 0 ORDER BY commname LIMIT ?", catid, max)
}
}
if err != nil {
@@ -995,7 +1011,7 @@ func AmGetCommunitiesForCategory(catid int32, offset int, max int, showAll bool)
for rs.Next() {
var commid int32
rs.Scan(&commid)
c, err := AmGetCommunity(commid)
c, err := AmGetCommunity(ctx, commid)
if err == nil {
rc = append(rc, c)
}
@@ -1005,6 +1021,7 @@ func AmGetCommunitiesForCategory(catid int32, offset int, max int, showAll bool)
/* AmSearchCommunities searches for communities matching certain criteria.
* Parameters:
* ctx - Standard Go context value.
* field - A value indicating which field to search:
* SearchCommFieldName - The community name.
* SearchCommFieldSynopsis - The communty synopsis.
@@ -1021,7 +1038,7 @@ func AmGetCommunitiesForCategory(catid int32, offset int, max int, showAll bool)
* The total number of communities matching this query (could be greater than max)
* Standard Go error status.
*/
func AmSearchCommunities(field int, oper int, term string, offset int, max int, showAll bool) ([]*Community, int, error) {
func AmSearchCommunities(ctx context.Context, field int, oper int, term string, offset int, max int, showAll bool) ([]*Community, int, error) {
var queryPortion strings.Builder
queryPortion.WriteString("WHERE ")
switch field {
@@ -1052,7 +1069,7 @@ func AmSearchCommunities(field int, oper int, term string, offset int, max int,
queryPortion.WriteString(" AND hide_search = 0")
}
q := queryPortion.String()
rs, err := amdb.Query("SELECT COUNT(*) FROM communities " + q)
rs, err := amdb.QueryContext(ctx, "SELECT COUNT(*) FROM communities "+q)
if err != nil {
return nil, -1, err
}
@@ -1065,9 +1082,9 @@ func AmSearchCommunities(field int, oper int, term string, offset int, max int,
return make([]*Community, 0), 0, nil // short-circuit return
}
if offset > 0 {
rs, err = amdb.Query("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)
} else {
rs, err = amdb.Query("SELECT commid FROM communities "+q+" ORDER BY commname LIMIT ?", max)
rs, err = amdb.QueryContext(ctx, "SELECT commid FROM communities "+q+" ORDER BY commname LIMIT ?", max)
}
if err != nil {
return nil, total, err
@@ -1076,7 +1093,7 @@ func AmSearchCommunities(field int, oper int, term string, offset int, max int,
for rs.Next() {
var commid int32
rs.Scan(&commid)
c, err := AmGetCommunity(commid)
c, err := AmGetCommunity(ctx, commid)
if err == nil {
rc = append(rc, c)
}
+40 -35
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
"errors"
"fmt"
"sync"
@@ -66,8 +67,8 @@ 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)
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)
if err != nil {
return nil, err
}
@@ -81,14 +82,14 @@ func (c *Conference) Aliases() ([]string, error) {
}
// AliasesQ returns the list of aliases for this conference, quietly.
func (c *Conference) AliasesQ() []string {
rc, _ := c.Aliases()
func (c *Conference) AliasesQ(ctx context.Context) []string {
rc, _ := c.Aliases(ctx)
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 = ?",
func (c *Conference) Hosts(ctx context.Context) ([]*User, error) {
rs, err := amdb.QueryContext(ctx, "SELECT uid FROM confmember WHERE confid = ? AND granted_lvl = ?",
c.ConfId, AmRole("Conference.Host").Level())
if err != nil {
return nil, err
@@ -97,7 +98,7 @@ func (c *Conference) Hosts() ([]*User, error) {
for rs.Next() {
var uid int32
rs.Scan(&uid)
u, err := AmGetUser(uid)
u, err := AmGetUser(ctx, uid)
if err == nil {
rc = append(rc, u)
}
@@ -106,14 +107,14 @@ func (c *Conference) Hosts() ([]*User, error) {
}
// Hosts returns the list of users that host this conference, quietly.
func (c *Conference) HostsQ() []*User {
rc, _ := c.Hosts()
func (c *Conference) HostsQ(ctx context.Context) []*User {
rc, _ := c.Hosts(ctx)
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)
func (c *Conference) Membership(ctx context.Context, u *User) (bool, uint16, error) {
rs, err := amdb.QueryContext(ctx, "SELECT granted_lvl FROM confmember WHERE confid = ? AND uid = ?", c.ConfId, u.Uid)
if err != nil {
return false, 0, err
}
@@ -155,9 +156,9 @@ func (c *Conference) TestPermission(perm string, level uint16) bool {
}
// Settings returns the settings for a user.
func (c *Conference) Settings(u *User) (*ConferenceSettings, error) {
func (c *Conference) Settings(ctx context.Context, u *User) (*ConferenceSettings, error) {
var dbdata []ConferenceSettings
err := amdb.Select(&dbdata, "SELECT * FROM confsettings WHERE confid = ? AND uid = ?", c.ConfId, u.Uid)
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM confsettings WHERE confid = ? AND uid = ?", c.ConfId, u.Uid)
if err != nil {
return nil, err
}
@@ -171,24 +172,24 @@ func (c *Conference) Settings(u *User) (*ConferenceSettings, error) {
}
// TouchRead updates the "last posted" date/time in the conference for the user.
func (c *Conference) TouchRead(tx *sqlx.Tx, u *User) (*ConferenceSettings, error) {
cs, err := c.Settings(u)
func (c *Conference) TouchRead(ctx context.Context, tx *sqlx.Tx, u *User) (*ConferenceSettings, error) {
cs, err := c.Settings(ctx, u)
if err != nil {
return nil, err
}
if !u.IsAnon { // anon user can't update squat
if cs == nil {
ci, cerr := u.ContactInfo()
ci, cerr := u.ContactInfo(ctx)
if cerr != nil {
return nil, cerr
}
_, err = tx.Exec("INSERT INTO confsettings (confid, uid, default_pseud, last_read) VALUES (?, ?, ?, NOW())",
_, err = tx.ExecContext(ctx, "INSERT INTO confsettings (confid, uid, default_pseud, last_read) VALUES (?, ?, ?, NOW())",
c.ConfId, u.Uid, ci.FullName(false))
} else {
_, err = tx.Exec("UPDATE confsettings SET last_read = NOW() WHERE confid = ? AND uid = ?", c.ConfId, u.Uid)
_, err = tx.ExecContext(ctx, "UPDATE confsettings SET last_read = NOW() WHERE confid = ? AND uid = ?", c.ConfId, u.Uid)
}
if err == nil {
cs, err = c.Settings(u) // reread to get updated or inserted values
cs, err = c.Settings(ctx, u) // reread to get updated or inserted values
}
if err != nil {
return nil, err
@@ -198,14 +199,14 @@ func (c *Conference) TouchRead(tx *sqlx.Tx, u *User) (*ConferenceSettings, error
}
// TouchPost updates the "last posted" date/time in the conference for the user.
func (c *Conference) TouchPost(tx *sqlx.Tx, u *User, lastPost time.Time) (*ConferenceSettings, error) {
cs, err := c.Settings(u)
func (c *Conference) TouchPost(ctx context.Context, tx *sqlx.Tx, u *User, lastPost time.Time) (*ConferenceSettings, error) {
cs, err := c.Settings(ctx, u)
if err != nil {
return nil, err
}
if !u.IsAnon { // anon user can't update squat
if cs == nil {
ci, cerr := u.ContactInfo()
ci, cerr := u.ContactInfo(ctx)
if cerr != nil {
return nil, cerr
}
@@ -217,10 +218,10 @@ func (c *Conference) TouchPost(tx *sqlx.Tx, u *User, lastPost time.Time) (*Confe
LastRead: &lastPost,
LastPost: &lastPost,
}
_, err = tx.Exec("INSERT INTO confsettings (confid, uid, default_pseud, last_read, last_post) VALUES (?, ?, ?, ?, ?)",
_, err = tx.ExecContext(ctx, "INSERT INTO confsettings (confid, uid, default_pseud, last_read, last_post) VALUES (?, ?, ?, ?, ?)",
c.ConfId, u.Uid, defaultPseud, lastPost, lastPost)
} else {
_, err = tx.Exec("UPDATE confsettings SET last_post = ? WHERE confid = ? AND uid = ?", lastPost, c.ConfId, u.Uid)
_, err = tx.ExecContext(ctx, "UPDATE confsettings SET last_post = ? WHERE confid = ? AND uid = ?", lastPost, c.ConfId, u.Uid)
cs.LastPost = &lastPost
}
if err != nil {
@@ -232,19 +233,20 @@ func (c *Conference) TouchPost(tx *sqlx.Tx, u *User, lastPost time.Time) (*Confe
/* AmGetConference returns a conference given its ID.
* Parameters:
* ctx - Standard Go context value.
* id - The ID of the conference.
* Returns:
* Pointer to the conference, or nil.
* Standard Go error status.
*/
func AmGetConference(id int32) (*Conference, error) {
func AmGetConference(ctx context.Context, id int32) (*Conference, error) {
var err error = nil
getConferenceMutex.Lock()
defer getConferenceMutex.Unlock()
rc, ok := conferenceCache.Get(id)
if !ok {
var dbdata []Conference
err = amdb.Select(&dbdata, "SELECT * from confs where confid = ?", id)
err = amdb.SelectContext(ctx, &dbdata, "SELECT * from confs where confid = ?", id)
if err != nil {
return nil, err
}
@@ -261,18 +263,19 @@ func AmGetConference(id int32) (*Conference, error) {
/* AmGetConferenceByAlias returns a conference given its alias.
* Parameters:
* ctx - Standard Go context value.
* alias - The alias to look up.
* Returns:
* Pointer to the conference, or nil.
* Standard Go error status.
*/
func AmGetConferenceByAlias(alias string) (*Conference, error) {
func AmGetConferenceByAlias(ctx context.Context, alias string) (*Conference, error) {
var confid int32
xconf, ok := conferenceAliasMap.Load(alias)
if ok {
confid = xconf.(int32)
} else {
rs, err := amdb.Query("SELECT confid FROM confalias WHERE alias = ?", alias)
rs, err := amdb.QueryContext(ctx, "SELECT confid FROM confalias WHERE alias = ?", alias)
if err != nil {
return nil, err
}
@@ -282,19 +285,20 @@ func AmGetConferenceByAlias(alias string) (*Conference, error) {
rs.Scan(&confid)
conferenceAliasMap.Store(alias, confid)
}
return AmGetConference(confid)
return AmGetConference(ctx, confid)
}
/* AmGetConferenceByAliasInCommunity returns a conference in a community given its alias.
* Parameters:
* ctx - Standard Go context value.
* cid - The community to look inside.
* alias - The alias to look up.
* Returns:
* Pointer to the conference, or nil.
* Standard Go error status.
*/
func AmGetConferenceByAliasInCommunity(cid int32, alias string) (*Conference, error) {
rs, err := amdb.Query(`SELECT c.confid FROM commtoconf c, confalias a WHERE c.confid = a.confid
func AmGetConferenceByAliasInCommunity(ctx context.Context, cid int32, alias string) (*Conference, error) {
rs, err := amdb.QueryContext(ctx, `SELECT c.confid FROM commtoconf c, confalias a WHERE c.confid = a.confid
AND c.commid = ? AND a.alias = ?`, cid, alias)
if err != nil {
return nil, err
@@ -304,23 +308,24 @@ func AmGetConferenceByAliasInCommunity(cid int32, alias string) (*Conference, er
}
var confid int32
rs.Scan(&confid)
return AmGetConference(confid)
return AmGetConference(ctx, confid)
}
/* AmGetCommunityConferences 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.
* Stanbard Go error status.
*/
func AmGetCommunityConferences(cid int32, showHidden bool) ([]*Conference, error) {
func AmGetCommunityConferences(ctx context.Context, cid int32, showHidden bool) ([]*Conference, error) {
q := ""
if !showHidden {
q = " AND x.hide_list = 0"
}
rs, err := amdb.Query(`SELECT x.confid FROM commtoconf x, confs c WHERE x.confid = c.confid
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)
if err != nil {
return nil, err
@@ -329,7 +334,7 @@ func AmGetCommunityConferences(cid int32, showHidden bool) ([]*Conference, error
for rs.Next() {
var confid int32
rs.Scan(&confid)
conf, err := AmGetConference(confid)
conf, err := AmGetConference(ctx, confid)
if err == nil {
rc = append(rc, conf)
}
+19 -15
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
"errors"
"fmt"
"strings"
@@ -51,9 +52,9 @@ type ContactInfo struct {
}
// lookupCommunityContact looks up the ID of a contact for a community.
func lookupCommunityContact(id int32) (int32, error) {
func lookupCommunityContact(ctx context.Context, id int32) (int32, error) {
var rc int32 = -1
rs, err := amdb.Query("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 rs.Next() {
rs.Scan(&rc)
@@ -63,9 +64,9 @@ func lookupCommunityContact(id int32) (int32, error) {
}
// lookupUserContact looks up the ID of a contact for a user.
func lookupUserContact(uid int32) (int32, error) {
func lookupUserContact(ctx context.Context, uid int32) (int32, error) {
var rc int32 = -1
rs, err := amdb.Query("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 rs.Next() {
rs.Scan(&rc)
@@ -114,11 +115,13 @@ func (ci *ContactInfo) FullName(ps bool) string {
}
/* Save saves the contact info to the database.
* Parameters:
* ctx - Standard Go context value.
* Returns:
* true if the E-mail address on this account has been changed, false if not.
* Standard Go error status.
*/
func (ci *ContactInfo) Save() (bool, error) {
func (ci *ContactInfo) Save(ctx context.Context) (bool, error) {
ci.Mutex.Lock()
defer ci.Mutex.Unlock()
@@ -129,9 +132,9 @@ func (ci *ContactInfo) Save() (bool, error) {
var nx int32
var err error
if ci.OwnerCommId > 0 {
nx, err = lookupCommunityContact(ci.OwnerCommId)
nx, err = lookupCommunityContact(ctx, ci.OwnerCommId)
} else {
nx, err = lookupUserContact(ci.OwnerUid)
nx, err = lookupUserContact(ctx, ci.OwnerUid)
}
if err != nil {
return false, err
@@ -147,7 +150,7 @@ func (ci *ContactInfo) Save() (bool, error) {
}
if !emailChange {
// we don't THINK the E-mail address is changing, but we could be wrong...
rs, err := amdb.Query("SELECT contactid FROM contacts WHERE contactid = ? AND email = ?", ci.ContactId, ci.Email)
rs, err := amdb.QueryContext(ctx, "SELECT contactid FROM contacts WHERE contactid = ? AND email = ?", ci.ContactId, ci.Email)
if err != nil {
return false, err
}
@@ -157,7 +160,7 @@ func (ci *ContactInfo) Save() (bool, error) {
}
// Handle the database heavy lifting.
if updateMode {
_, err := amdb.NamedExec(`UPDATE contacts SET given_name = :given_name, family_name = :family_name, middle_init = :middle_init,
_, err := amdb.NamedExecContext(ctx, `UPDATE contacts SET given_name = :given_name, family_name = :family_name, middle_init = :middle_init,
prefix = :prefix, suffix = :suffix, company = :company, addr1 = :addr1, addr2 = :addr2, locality = :locality, region = :region,
pcode = :pcode, country = :country, phone = :phone, fax = :fax, mobile = :mobile, email = :email, pvt_addr = :pvt_addr,
pvt_phone = :pvt_phone, pvt_fax = :pvt_fax, pvt_email = :pvt_email, photo_url = :photo_url, url = :url, lastupdate = NOW()
@@ -167,7 +170,7 @@ func (ci *ContactInfo) Save() (bool, error) {
}
contactCache.Add(ci.ContactId, ci)
} else {
res, err := amdb.NamedExec(`INSERT INTO contacts (given_name, family_name, middle_init, prefix, suffix, company, addr1,
res, err := amdb.NamedExecContext(ctx, `INSERT INTO contacts (given_name, family_name, middle_init, prefix, suffix, company, addr1,
addr2, locality, region, pcode, country, phone, fax, mobile, email, pvt_addr, pvt_phone, pvt_fax,
pvt_email, owner_uid, owner_commid, photo_url, url, lastupdate)
VALUES (:given_name, :family_name, :middle_init, :prefix, :suffix, :company, :addr1, :addr2, :locality,
@@ -181,7 +184,7 @@ func (ci *ContactInfo) Save() (bool, error) {
contactCache.Add(ci.ContactId, ci)
}
// Refresh the last update date.
rs, err := amdb.Query("SELECT lastupdate FROM contacts WHERE contactid = ?", ci.ContactId)
rs, err := amdb.QueryContext(ctx, "SELECT lastupdate FROM contacts WHERE contactid = ?", ci.ContactId)
if err != nil {
return false, err
}
@@ -241,9 +244,9 @@ func init() {
}
// internalContactInfo retrieves the contact info from the database.
func internalContactInfo(id int32) (*ContactInfo, error) {
func internalContactInfo(ctx context.Context, id int32) (*ContactInfo, error) {
var dbdata []ContactInfo
err := amdb.Select(&dbdata, "SELECT * from contacts WHERE contactid = ?", id)
err := amdb.SelectContext(ctx, &dbdata, "SELECT * from contacts WHERE contactid = ?", id)
if err == nil {
if len(dbdata) > 1 {
err = fmt.Errorf("internalContactInfo(%d): Too many responses (%d)", id, len(dbdata))
@@ -258,19 +261,20 @@ func internalContactInfo(id int32) (*ContactInfo, error) {
/* AmGetContactInfo retrieves the contact info for a given identifier.
* Parameters:
* ctx - Standard Go context value.
* id - The contact info ID top retrieve.
* Returns:
* ContactInfo retrieved, or nil.
* Standard Go error status.
*/
func AmGetContactInfo(id int32) (*ContactInfo, error) {
func AmGetContactInfo(ctx context.Context, id int32) (*ContactInfo, error) {
getContactMutex.Lock()
defer getContactMutex.Unlock()
rc, ok := contactCache.Get(id)
if ok {
return rc.(*ContactInfo), nil
}
rc2, err := internalContactInfo(id)
rc2, err := internalContactInfo(ctx, id)
if err == nil {
if rc2 != nil {
contactCache.Add(id, rc2)
+5 -2
View File
@@ -9,15 +9,18 @@
// The database package contains database management and storage logic.
package database
import "context"
/* AmIsEmailAddressBanned returns true if the given E-mail address is on the "banned" list.
* Parameters:
* ctx - Standard Go context value.
* address - The E-mail address to be checked.
* Returns:
* true if the address is banned, false if not.
* Standard Go error status.
*/
func AmIsEmailAddressBanned(address string) (bool, error) {
rs, err := amdb.Query("SELECT by_uid FROM emailban WHERE address = ?", address)
func AmIsEmailAddressBanned(ctx context.Context, address string) (bool, error) {
rs, err := amdb.QueryContext(ctx, "SELECT by_uid FROM emailban WHERE address = ?", address)
if err != nil {
return false, err
}
+15 -12
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
"errors"
"sync"
@@ -60,11 +61,11 @@ var globalProps map[int32]string = make(map[int32]string)
var globalPropMutex sync.Mutex
// Flags returns the global flags.
func (g *Globals) Flags() (*util.OptionSet, error) {
func (g *Globals) Flags(ctx context.Context) (*util.OptionSet, error) {
g.Mutex.Lock()
defer g.Mutex.Unlock()
if g.flags == nil {
s, err := AmGetGlobalProperty(GlobalPropFlags)
s, err := AmGetGlobalProperty(ctx, GlobalPropFlags)
if err != nil {
return nil, err
}
@@ -74,11 +75,11 @@ func (g *Globals) Flags() (*util.OptionSet, error) {
}
// SaveFlags saves off the global flags.
func (g *Globals) SaveFlags(f *util.OptionSet) error {
func (g *Globals) SaveFlags(ctx context.Context, f *util.OptionSet) error {
s := f.AsString()
g.Mutex.Lock()
defer g.Mutex.Unlock()
err := AmSetGlobalProperty(GlobalPropFlags, s)
err := AmSetGlobalProperty(ctx, GlobalPropFlags, s)
if err == nil {
g.flags = f
}
@@ -86,12 +87,12 @@ func (g *Globals) SaveFlags(f *util.OptionSet) error {
}
// AmGlobals returns trhe pointer to the singleton Globals instance.
func AmGlobals() (*Globals, error) {
func AmGlobals(ctx context.Context) (*Globals, error) {
globalsMutex.Lock()
defer globalsMutex.Unlock()
if theGlobals == nil {
var dbdata []Globals
err := amdb.Select(&dbdata, "SELECT * FROM globals")
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM globals")
if err != nil {
return nil, err
}
@@ -105,17 +106,18 @@ func AmGlobals() (*Globals, error) {
/* AmGetGlobalProperty returns the value of a global property.
* Parameters:
* ctx - Standard Go context value.
* index - The index of the property to retrieve.
* Returns:
* Value of the property, or empty string.
* Standard Go error status.
*/
func AmGetGlobalProperty(index int32) (string, error) {
func AmGetGlobalProperty(ctx context.Context, index int32) (string, error) {
globalPropMutex.Lock()
defer globalPropMutex.Unlock()
rc, ok := globalProps[index]
if !ok {
rs, err := amdb.Query("SELECT data FROM propglobal WHERE ndx = ?", index)
rs, err := amdb.QueryContext(ctx, "SELECT data FROM propglobal WHERE ndx = ?", index)
if err != nil {
return "", err
}
@@ -131,17 +133,18 @@ func AmGetGlobalProperty(index int32) (string, error) {
/* AmSetGlobalProperty sets the value of a global property.
* Parameters:
* ctx - Standard Go context value.
* index - The index of the property to set.
* value - The value of the property to set.
* Returns:
* Standard Go error status.
*/
func AmSetGlobalProperty(index int32, value string) error {
func AmSetGlobalProperty(ctx context.Context, index int32, value string) error {
globalPropMutex.Lock()
defer globalPropMutex.Unlock()
_, updateMode := globalProps[index]
if !updateMode {
rs, err := amdb.Query("SELECT data FROM propglobal WHERE ndx = ?", index)
rs, err := amdb.QueryContext(ctx, "SELECT data FROM propglobal WHERE ndx = ?", index)
if err != nil {
return err
}
@@ -149,9 +152,9 @@ func AmSetGlobalProperty(index int32, value string) error {
}
var err error = nil
if updateMode {
_, err = amdb.Exec("UPDATE propglobal SET data = ? WHERE ndx = ?", value, index)
_, err = amdb.ExecContext(ctx, "UPDATE propglobal SET data = ? WHERE ndx = ?", value, index)
} else {
_, err = amdb.Exec("INSERT INTO propglobal (ndx, data) VALUES (?, ?)", index, value)
_, err = amdb.ExecContext(ctx, "INSERT INTO propglobal (ndx, data) VALUES (?, ?)", index, value)
}
if err == nil {
globalProps[index] = value
+15 -11
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
"database/sql"
"fmt"
)
@@ -31,14 +32,14 @@ const (
)
// Save persists the ImageStore record to the database.
func (img *ImageStore) Save() error {
func (img *ImageStore) Save(ctx context.Context) error {
var err error
if img.ImgId > 0 {
_, err = amdb.NamedExec(`UPDATE imagestore SET typecode = :typecode, ownerid = :ownerid, mimetype = :mimetype,
_, err = amdb.NamedExecContext(ctx, `UPDATE imagestore SET typecode = :typecode, ownerid = :ownerid, mimetype = :mimetype,
length = :length, data = :data WHERE imgid = :imgid`, img)
} else {
var rs sql.Result
rs, err = amdb.NamedExec(`INSERT INTO imagestore (typecode, ownerid, mimetype, length, data)
rs, err = amdb.NamedExecContext(ctx, `INSERT INTO imagestore (typecode, ownerid, mimetype, length, data)
VALUES (:typecode, :ownerid, :mimetype, :length, :data)`, img)
if err == nil {
var lii int64
@@ -53,14 +54,15 @@ func (img *ImageStore) Save() error {
/* AmLoadImage loads an image from the database.
* Parameters:
* ctx - Standard Go context value.
* id - The ID of the image to be loaded.
* Returns:
* Pointer to ImageStore, or nil.
* Standard Go error status.
*/
func AmLoadImage(id int32) (*ImageStore, error) {
func AmLoadImage(ctx context.Context, id int32) (*ImageStore, error) {
var dbdata []ImageStore
err := amdb.Select(&dbdata, "SELECT * FROM imagestore WHERE imgid = ?", id)
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM imagestore WHERE imgid = ?", id)
if err != nil {
return nil, err
}
@@ -74,6 +76,7 @@ func AmLoadImage(id int32) (*ImageStore, error) {
/* AmStoreImage stores an image in the database, overwriting one with the same type code and owner if it exists.
* Parameters:
* ctx - Standard Go context value.
* typecode - Type code for the image.
* owner - Owner Id for the image (UID or community ID)
* mimetype - MIME type of the image.
@@ -82,8 +85,8 @@ func AmLoadImage(id int32) (*ImageStore, error) {
* Pointer to ImageStore, or nil.
* Standard Go error status.
*/
func AmStoreImage(typecode int16, owner int32, mimetype string, data []byte) (*ImageStore, error) {
rs, err := amdb.Query("SELECT imgid FROM imagestore WHERE typecode = ? AND ownerid = ?", typecode, owner)
func AmStoreImage(ctx context.Context, typecode int16, owner int32, mimetype string, data []byte) (*ImageStore, error) {
rs, err := amdb.QueryContext(ctx, "SELECT imgid FROM imagestore WHERE typecode = ? AND ownerid = ?", typecode, owner)
if err != nil {
return nil, err
}
@@ -91,7 +94,7 @@ func AmStoreImage(typecode int16, owner int32, mimetype string, data []byte) (*I
if rs.Next() {
var id int32
rs.Scan(&id)
img, err = AmLoadImage(id)
img, err = AmLoadImage(ctx, id)
if err != nil {
return nil, err
}
@@ -108,7 +111,7 @@ func AmStoreImage(typecode int16, owner int32, mimetype string, data []byte) (*I
Data: data,
}
}
err = img.Save()
err = img.Save(ctx)
if err != nil {
return nil, err
}
@@ -117,11 +120,12 @@ func AmStoreImage(typecode int16, owner int32, mimetype string, data []byte) (*I
/* AmDeleteImage erases an image from the database.
* Parameters:
* ctx - Standard Go context value.
* id - The ID of the image to be deleted.
* Returns:
* Standard Go error status.
*/
func AmDeleteImage(id int32) error {
_, err := amdb.Exec("DELETE FROM imagestore WHERE imgid = ?", id)
func AmDeleteImage(ctx context.Context, id int32) error {
_, err := amdb.ExecContext(ctx, "DELETE FROM imagestore WHERE imgid = ?", id)
return err
}
+4 -3
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
"fmt"
"math/big"
"net"
@@ -40,12 +41,13 @@ func init() {
/* AmTestIPBan tests an IP address to see if it's on the banned list.
* Parameters:
* ctx - Standard Go context parameter.
* ip_address - The IP address to be tested.
* Returns:
* Ban message if the address is banned, or empty string if it isn't.
* Standard Go error status.
*/
func AmTestIPBan(ip_address string) (string, error) {
func AmTestIPBan(ctx context.Context, ip_address string) (string, error) {
banMutex.Lock()
defer banMutex.Unlock()
rc := knownBans[ip_address]
@@ -63,8 +65,7 @@ func AmTestIPBan(ip_address string) (string, error) {
iv.SetBytes(addr)
iv_lo := big.NewInt(0).And(iv, low64mask).Uint64()
iv_hi := big.NewInt(0).Rsh(iv, 64).Uint64()
rows, err := amdb.Query(`
SELECT message FROM ipban WHERE (address_lo & mask_lo) = (? & mask_lo)
rows, err := amdb.QueryContext(ctx, `SELECT message FROM ipban WHERE (address_lo & mask_lo) = (? & mask_lo)
AND (address_hi & mask_hi) = (? & mask_hi) AND (expire IS NULL OR expire >= ?)
AND enable <> 0 ORDER BY mask_hi DESC, mask_lo DESC`, iv_lo, iv_hi, time.Now().UTC())
if err != nil {
+10 -8
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
"errors"
"fmt"
"time"
@@ -43,6 +44,7 @@ func (p *PostHeader) IsScribbled() bool {
/* SetAttachment sets the attachment data for a post.
* Parameters:
* ctx - Standard Go context value.
* fileName - Name of the original attachment file.
* mimeType - MIME type of the attachment data.
* length - Length of the attachment data in bytes.
@@ -50,16 +52,16 @@ func (p *PostHeader) IsScribbled() bool {
* Returns:
* Standard Go error status.
*/
func (p *PostHeader) SetAttachment(fileName string, mimeType string, length int32, data []byte) error {
_, err := amdb.Exec("INSERT INTO postattach (postid, datalen, filename, mimetype, data) VALUES (?, ?, ?, ?, ?)",
func (p *PostHeader) SetAttachment(ctx context.Context, fileName string, mimeType string, length int32, data []byte) error {
_, err := amdb.ExecContext(ctx, "INSERT INTO postattach (postid, datalen, filename, mimetype, data) VALUES (?, ?, ?, ?, ?)",
p.PostId, length, fileName, mimeType, data)
return err
}
// Text returns the text associated with a post.
func (p *PostHeader) Text() (string, error) {
func (p *PostHeader) Text(ctx context.Context) (string, error) {
var dbdata []PostData
err := amdb.Select(&dbdata, "SELECT * FROM postdata WHERE postid = ?", p.PostId)
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM postdata WHERE postid = ?", p.PostId)
if err != nil {
return "", err
}
@@ -72,9 +74,9 @@ func (p *PostHeader) Text() (string, error) {
return *dbdata[0].Data, nil
}
func AmGetPost(postId int64) (*PostHeader, error) {
func AmGetPost(ctx context.Context, postId int64) (*PostHeader, error) {
var dbdata []PostHeader
err := amdb.Select(&dbdata, "SELECT * FROM posts WHERE postid = ?", postId)
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM posts WHERE postid = ?", postId)
if err != nil {
return nil, err
}
@@ -87,9 +89,9 @@ func AmGetPost(postId int64) (*PostHeader, error) {
return &(dbdata[0]), nil
}
func AmGetPostRange(topic *Topic, first, last int32) ([]PostHeader, error) {
func AmGetPostRange(ctx context.Context, topic *Topic, first, last int32) ([]PostHeader, error) {
var rc []PostHeader
err := amdb.Select(&rc, "SELECT * FROM posts WHERE topicid = ? AND num >= ? AND num <= ? ORDER BY num", topic.TopicId, first, last)
err := amdb.SelectContext(ctx, &rc, "SELECT * FROM posts WHERE topicid = ? AND num >= ? AND num <= ? ORDER BY num", topic.TopicId, first, last)
if err != nil {
return nil, err
}
+4 -3
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
"errors"
"fmt"
"math"
@@ -32,9 +33,9 @@ func (d *PostLinkData) NeedsDBVerification() bool {
}
// VerifyNames verifies the post link data against the database.
func (d *PostLinkData) VerifyNames() error {
func (d *PostLinkData) VerifyNames(ctx context.Context) error {
if d.Community != "" {
comm, err := AmGetCommunityByAlias(d.Community)
comm, err := AmGetCommunityByAlias(ctx, d.Community)
if err != nil {
return err
}
@@ -43,7 +44,7 @@ func (d *PostLinkData) VerifyNames() error {
}
}
if d.Conference != "" {
conf, err := AmGetConferenceByAlias(d.Conference)
conf, err := AmGetConferenceByAlias(ctx, d.Conference)
if err != nil {
return err
}
+34 -27
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
_ "embed"
"slices"
"sync"
@@ -21,28 +22,28 @@ import (
// ServiceVTable is a serioes of functions called for services on specific events.
type ServiceVTable interface {
OnNewCommunity(*sqlx.Tx, *Community) error
OnDeleteCommunity(*sqlx.Tx, int32) error
OnUserJoinCommunity(*sqlx.Tx, *Community, *User) error
OnUserLeaveCommunity(*sqlx.Tx, *Community, *User) error
OnNewCommunity(context.Context, *sqlx.Tx, *Community) error
OnDeleteCommunity(context.Context, *sqlx.Tx, int32) error
OnUserJoinCommunity(context.Context, *sqlx.Tx, *Community, *User) error
OnUserLeaveCommunity(context.Context, *sqlx.Tx, *Community, *User) error
}
// emptyServiceVTable is a default ServiceVTable that does nothing.
type emptyServiceVTable struct{}
func (*emptyServiceVTable) OnNewCommunity(*sqlx.Tx, *Community) error {
func (*emptyServiceVTable) OnNewCommunity(context.Context, *sqlx.Tx, *Community) error {
return nil
}
func (*emptyServiceVTable) OnDeleteCommunity(*sqlx.Tx, int32) error {
func (*emptyServiceVTable) OnDeleteCommunity(context.Context, *sqlx.Tx, int32) error {
return nil
}
func (*emptyServiceVTable) OnUserJoinCommunity(*sqlx.Tx, *Community, *User) error {
func (*emptyServiceVTable) OnUserJoinCommunity(context.Context, *sqlx.Tx, *Community, *User) error {
return nil
}
func (*emptyServiceVTable) OnUserLeaveCommunity(*sqlx.Tx, *Community, *User) error {
func (*emptyServiceVTable) OnUserLeaveCommunity(context.Context, *sqlx.Tx, *Community, *User) error {
return nil
}
@@ -124,17 +125,18 @@ func init() {
/* AmGetCommunityServices returns all the community service definitions for a community.
* Parameters:
* ctx - Standard Go context value.
* cid - Community ID to get services for.
* Returns:
* Array of ServiceDef pointers for the community's services.
* Standard Go error status.
*/
func AmGetCommunityServices(cid int32) ([]*ServiceDef, error) {
func AmGetCommunityServices(ctx context.Context, cid int32) ([]*ServiceDef, error) {
servicesCacheMutex.Lock()
defer servicesCacheMutex.Unlock()
rc, ok := servicesCache.Get(cid)
if !ok {
rs, err := amdb.Query("SELECT ftr_code FROM commftrs WHERE commid = ?", cid)
rs, err := amdb.QueryContext(ctx, "SELECT ftr_code FROM commftrs WHERE commid = ?", cid)
if err != nil {
return nil, err
}
@@ -153,18 +155,19 @@ func AmGetCommunityServices(cid int32) ([]*ServiceDef, error) {
/* AmGetCommunityServices returns all the community service definitions for a community, using a transaction.
* Parameters:
* ctx - Standard Go context value.
* tx - Transaction to be used.
* cid - Community ID to get services for.
* Returns:
* Array of ServiceDef pointers for the community's services.
* Standard Go error status.
*/
func AmGetCommunityServicesTx(tx *sqlx.Tx, cid int32) ([]*ServiceDef, error) {
func AmGetCommunityServicesTx(ctx context.Context, tx *sqlx.Tx, cid int32) ([]*ServiceDef, error) {
servicesCacheMutex.Lock()
defer servicesCacheMutex.Unlock()
rc, ok := servicesCache.Get(cid)
if !ok {
rs, err := tx.Query("SELECT ftr_code FROM commftrs WHERE commid = ?", cid)
rs, err := tx.QueryContext(ctx, "SELECT ftr_code FROM commftrs WHERE commid = ?", cid)
if err != nil {
return nil, err
}
@@ -184,17 +187,18 @@ func AmGetCommunityServicesTx(tx *sqlx.Tx, cid int32) ([]*ServiceDef, error) {
/* AmEstablishCommunityServices establishes the service (feature) records for a new community,
* and allows the services to establish themselves.
* Parameters:
* ctx - Standard Go context value.
* tx - The transaction to use.
* c - The new community.
* Returns:
* Standard Go error status.
*/
func AmEstablishCommunityServices(tx *sqlx.Tx, c *Community) error {
func AmEstablishCommunityServices(ctx context.Context, tx *sqlx.Tx, c *Community) error {
dom := serviceRoot.byName["community"]
a := make([]*ServiceDef, 0, len(dom.Services))
for i, svc := range dom.Services {
if svc.Default {
_, err := tx.Exec("INSERT INTO commftrs (commid, ftr_code) VALUES (?, ?)", c.Id, svc.Index)
_, err := tx.ExecContext(ctx, "INSERT INTO commftrs (commid, ftr_code) VALUES (?, ?)", c.Id, svc.Index)
if err != nil {
return err
}
@@ -205,7 +209,7 @@ func AmEstablishCommunityServices(tx *sqlx.Tx, c *Community) error {
servicesCache.Add(c.Id, a)
servicesCacheMutex.Unlock()
for _, svc := range a {
err := svc.vtable.OnNewCommunity(tx, c)
err := svc.vtable.OnNewCommunity(ctx, tx, c)
if err != nil {
return err
}
@@ -216,23 +220,24 @@ func AmEstablishCommunityServices(tx *sqlx.Tx, c *Community) error {
/* AmDeleteCommunityServices cleans up all services associated with a community that has gone away,
* and then cleans up the service records.
* Parameters:
* ctx - Standard Go context value.
* tx - The transaction to use.
* cid - The ID of the departing community.
* Returns:
* Standard Go error status.
*/
func AmDeleteCommunityServices(tx *sqlx.Tx, cid int32) error {
arr, err := AmGetCommunityServices(cid)
func AmDeleteCommunityServices(ctx context.Context, tx *sqlx.Tx, cid int32) error {
arr, err := AmGetCommunityServices(ctx, cid)
if err == nil {
for _, svc := range arr {
err = svc.vtable.OnDeleteCommunity(tx, cid)
err = svc.vtable.OnDeleteCommunity(ctx, tx, cid)
if err != nil {
break
}
}
}
if err == nil {
_, err = tx.Exec("DELETE FROM commftrs WHERE commid = ?", cid)
_, err = tx.ExecContext(ctx, "DELETE FROM commftrs WHERE commid = ?", cid)
servicesCacheMutex.Lock()
servicesCache.Remove(cid)
servicesCacheMutex.Unlock()
@@ -242,17 +247,18 @@ func AmDeleteCommunityServices(tx *sqlx.Tx, cid int32) error {
/* AmOnUserJoinCommunityServices gives services a chance to update themselves when a user joins a community.
* Parameters:
* ctx - Standard Go context value.
* tx - The current database transaction.
* c - The community that is being joined.
* u - The user leaving that community.
* Returns:
* Standard Go error status.
*/
func AmOnUserJoinCommunityServices(tx *sqlx.Tx, c *Community, u *User) error {
arr, err := AmGetCommunityServicesTx(tx, c.Id)
func AmOnUserJoinCommunityServices(ctx context.Context, tx *sqlx.Tx, c *Community, u *User) error {
arr, err := AmGetCommunityServicesTx(ctx, tx, c.Id)
if err == nil {
for _, svc := range arr {
err = svc.vtable.OnUserJoinCommunity(tx, c, u)
err = svc.vtable.OnUserJoinCommunity(ctx, tx, c, u)
if err != nil {
break
}
@@ -263,17 +269,18 @@ func AmOnUserJoinCommunityServices(tx *sqlx.Tx, c *Community, u *User) error {
/* AmOnUserLeaveCommunityServices gives services a chance to update themselves when a user leaves a community.
* Parameters:
* ctx - Standard Go context value.
* tx - The current database transaction.
* c - The community that is being left.
* u - The user leaving that community.
* Returns:
* Standard Go error status.
*/
func AmOnUserLeaveCommunityServices(tx *sqlx.Tx, c *Community, u *User) error {
arr, err := AmGetCommunityServicesTx(tx, c.Id)
func AmOnUserLeaveCommunityServices(ctx context.Context, tx *sqlx.Tx, c *Community, u *User) error {
arr, err := AmGetCommunityServicesTx(ctx, tx, c.Id)
if err == nil {
for _, svc := range arr {
err = svc.vtable.OnUserLeaveCommunity(tx, c, u)
err = svc.vtable.OnUserLeaveCommunity(ctx, tx, c, u)
if err != nil {
break
}
@@ -282,8 +289,8 @@ func AmOnUserLeaveCommunityServices(tx *sqlx.Tx, c *Community, u *User) error {
return err
}
func AmTestService(c *Community, serviceId string) (bool, error) {
arr, err := AmGetCommunityServices(c.Id)
func AmTestService(ctx context.Context, c *Community, serviceId string) (bool, error) {
arr, err := AmGetCommunityServices(ctx, c.Id)
if err == nil {
for _, svc := range arr {
if svc.Id == serviceId {
+11 -6
View File
@@ -9,7 +9,11 @@
// The database package contains database management and storage logic.
package database
import "github.com/jmoiron/sqlx"
import (
"context"
"github.com/jmoiron/sqlx"
)
type Sidebox struct {
Uid int32 `db:"uid"`
@@ -19,12 +23,12 @@ type Sidebox struct {
}
// copySideboxes copies sideboxes from one user to another.
func copySideboxes(tx *sqlx.Tx, toUid int32, fromUid int32) error {
func copySideboxes(ctx context.Context, tx *sqlx.Tx, toUid int32, fromUid int32) error {
sbox := make([]Sidebox, 0, 3)
err := tx.Select(&sbox, "SELECT * from sideboxes WHERE uid = ?", fromUid)
err := tx.SelectContext(ctx, &sbox, "SELECT * from sideboxes WHERE uid = ?", fromUid)
if err == nil {
for _, sb := range sbox {
_, err := tx.Exec("INSERT INTO sideboxes (uid, boxid, sequence, param) VALUES (?, ?, ?, ?)", toUid, sb.Boxid, sb.Sequence, sb.Param)
_, err := tx.ExecContext(ctx, "INSERT INTO sideboxes (uid, boxid, sequence, param) VALUES (?, ?, ?, ?)", toUid, sb.Boxid, sb.Sequence, sb.Param)
if err != nil {
break
}
@@ -35,13 +39,14 @@ func copySideboxes(tx *sqlx.Tx, toUid int32, fromUid int32) error {
/* AmGetSideboxes returns all the configured sideboxes for a user.
* Parameters:
* ctx = Standard Go context value.
* uid = The ID of the user to retrieve sideboxes for.
* Returns:
* Array of Sidebox structures for the user, or nil
* Standard Go error status
*/
func AmGetSideboxes(uid int32) ([]*Sidebox, error) {
func AmGetSideboxes(ctx context.Context, uid int32) ([]*Sidebox, error) {
sboxes := make([]*Sidebox, 0, 3)
err := amdb.Select(&sboxes, "SELECT * FROM sideboxes WHERE uid = ? ORDER BY SEQUENCE", uid)
err := amdb.SelectContext(ctx, &sboxes, "SELECT * FROM sideboxes WHERE uid = ? ORDER BY SEQUENCE", uid)
return sboxes, err
}
+33 -26
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
"errors"
"fmt"
"strings"
@@ -34,12 +35,12 @@ type Topic struct {
}
// GetPost returns a post in the topic by number.
func (t *Topic) GetPost(num int32) (*PostHeader, error) {
func (t *Topic) GetPost(ctx context.Context, num int32) (*PostHeader, error) {
if num > t.TopMessage {
return nil, fmt.Errorf("no post %d in topic %d", num, t.TopicId)
}
var dbdata []PostHeader
err := amdb.Select(&dbdata, "SELECT * FROM posts WHERE topicid = ? AND num = ?", t.TopicId, num)
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM posts WHERE topicid = ? AND num = ?", t.TopicId, num)
if err == nil {
if len(dbdata) == 0 {
err = fmt.Errorf("no post %d in topic %d", num, t.TopicId)
@@ -53,8 +54,8 @@ func (t *Topic) GetPost(num int32) (*PostHeader, error) {
}
// GetLastRead returns the "last read" message for a user on a topic.
func (t *Topic) GetLastRead(u *User) (int32, error) {
rs, err := amdb.Query("SELECT last_message FROM topicsettings WHERE topicid = ? AND uid = ?", t.TopicId, u.Uid)
func (t *Topic) GetLastRead(ctx context.Context, u *User) (int32, error) {
rs, err := amdb.QueryContext(ctx, "SELECT last_message FROM topicsettings WHERE topicid = ? AND uid = ?", t.TopicId, u.Uid)
if err != nil {
return -1, err
}
@@ -66,12 +67,14 @@ func (t *Topic) GetLastRead(u *User) (int32, error) {
}
// SetLastRead sets the "last read" message for a user on a topic.
func (t *Topic) SetLastRead(u *User, postNum int32) error {
rs, err := amdb.Exec("UPDATE topicsettings SET last_message = ?, last_read = NOW() WHERE topicid = ? AND uid = ?", postNum, t.TopicId, u.Uid)
func (t *Topic) SetLastRead(ctx context.Context, u *User, postNum int32) error {
rs, err := amdb.ExecContext(ctx, "UPDATE topicsettings SET last_message = ?, last_read = NOW() WHERE topicid = ? AND uid = ?",
postNum, t.TopicId, u.Uid)
if err == nil {
nrow, _ := rs.RowsAffected()
if nrow == 0 {
_, err = amdb.Exec("INSERT INTO topicsettings (topicid, uid, last_message, last_read, last_post) VALUES (?, ?, ?, NOW(), NULL)", t.TopicId, u.Uid, postNum)
_, err = amdb.ExecContext(ctx, "INSERT INTO topicsettings (topicid, uid, last_message, last_read, last_post) VALUES (?, ?, ?, NOW(), NULL)",
t.TopicId, u.Uid, postNum)
}
}
return err
@@ -104,14 +107,15 @@ type TopicSummary struct {
/* AmGetTopic retrieves a topic by ID.
* Parameters:
* ctx - Standard Go context value.
* topicId - ID of the topic to retrieve.
* Returns:
* The topic pointer, or nil.
* Standard Go error status.
*/
func AmGetTopic(topicId int32) (*Topic, error) {
func AmGetTopic(ctx context.Context, topicId int32) (*Topic, error) {
var dbdata []Topic
err := amdb.Select(&dbdata, "SELECT * FROM topics WHERE topicid = ?", topicId)
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM topics WHERE topicid = ?", topicId)
if err != nil {
return nil, err
}
@@ -126,15 +130,16 @@ func AmGetTopic(topicId int32) (*Topic, error) {
/* AmGetTopicTx retrieves a topic by ID, in a transaction.
* Parameters:
* ctx - Standard Go context value.
* tx - The transaction to use.
* topicId - ID of the topic to retrieve.
* Returns:
* The topic pointer, or nil.
* Standard Go error status.
*/
func AmGetTopicTx(tx *sqlx.Tx, topicId int32) (*Topic, error) {
func AmGetTopicTx(ctx context.Context, tx *sqlx.Tx, topicId int32) (*Topic, error) {
var dbdata []Topic
err := tx.Select(&dbdata, "SELECT * FROM topics WHERE topicid = ?", topicId)
err := tx.SelectContext(ctx, &dbdata, "SELECT * FROM topics WHERE topicid = ?", topicId)
if err != nil {
return nil, err
}
@@ -149,15 +154,16 @@ func AmGetTopicTx(tx *sqlx.Tx, topicId int32) (*Topic, error) {
/* AmGetTopicByNumber retrieves a topic by conference and sequence number.
* Parameters:
* ctx - Standard Go context value.
* conf - The conference to look in.
* topicNum - The topic number within that conference.
* Returns:
* Pointer to the Topic, or nil.
* Standard Go error status.
*/
func AmGetTopicByNumber(conf *Conference, topicNum int16) (*Topic, error) {
func AmGetTopicByNumber(ctx context.Context, conf *Conference, topicNum int16) (*Topic, error) {
var dbdata []Topic
err := amdb.Select(&dbdata, "SELECT * FROM topics WHERE confid = ? AND num = ?", conf.ConfId, topicNum)
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM topics WHERE confid = ? AND num = ?", conf.ConfId, topicNum)
if err == nil {
if len(dbdata) == 0 {
err = fmt.Errorf("no topic numbered %d in conference %s (#%d)", topicNum, conf.Name, conf.ConfId)
@@ -189,6 +195,7 @@ const (
/* AmListTopics produces a list of topic summary information according to specific options.
* Parameters:
* ctx - Standard Go context value.
* confid - The ID of the conference to list topics in.
* uid - The UID of the user to consider the settings of.
* viewOption - One of the following constants:
@@ -210,7 +217,7 @@ const (
* List of TopicSummary pointers.
* Standard Go error status.
*/
func AmListTopics(confid int32, uid int32, viewOption int, sortOption int, ignoreSticky bool) ([]*TopicSummary, error) {
func AmListTopics(ctx context.Context, confid int32, uid int32, viewOption int, sortOption int, ignoreSticky bool) ([]*TopicSummary, error) {
// Decode the viewOption into a WHERE clause.
var whereClause string
switch viewOption {
@@ -296,7 +303,7 @@ func AmListTopics(confid int32, uid int32, viewOption int, sortOption int, ignor
fullStatement.WriteString(orderByClause)
// Execute and capture results
rs, err := amdb.Query(fullStatement.String(), uid, confid)
rs, err := amdb.QueryContext(ctx, fullStatement.String(), uid, confid)
if err != nil {
return nil, err
}
@@ -310,7 +317,7 @@ func AmListTopics(confid int32, uid int32, viewOption int, sortOption int, ignor
return rc, nil
}
func AmNewTopic(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) {
var ar *AuditRecord = nil
defer func() {
@@ -325,16 +332,16 @@ func AmNewTopic(conf *Conference, user *User, title string, zeroPostPseud string
}()
unlock := true
tx.Exec("LOCK TABLES confs WRITE, topics WRITE, topicsettings WRITE, posts WRITE, postdata WRITE;")
tx.ExecContext(ctx, "LOCK TABLES confs WRITE, topics WRITE, topicsettings WRITE, posts WRITE, postdata WRITE;")
defer func() {
if unlock {
tx.Exec("UNLOCK TABLES;")
tx.ExecContext(ctx, "UNLOCK TABLES;")
}
}()
// Insert the new topic into the database.
conf.Mutex.Lock()
rs, err := tx.Exec("INSERT INTO topics (confid, num, creator_uid, createdate, lastupdate, name) VALUES (?, ?, ?, NOW(), NOW(), ?)",
rs, err := tx.ExecContext(ctx, "INSERT INTO topics (confid, num, creator_uid, createdate, lastupdate, name) VALUES (?, ?, ?, NOW(), NOW(), ?)",
conf.ConfId, conf.TopTopic+1, user.Uid, title)
if err != nil {
conf.Mutex.Unlock()
@@ -347,14 +354,14 @@ func AmNewTopic(conf *Conference, user *User, title string, zeroPostPseud string
return nil, err
}
// Get the topic.
topic, err := AmGetTopicTx(tx, int32(xid))
topic, err := AmGetTopicTx(ctx, tx, int32(xid))
if err != nil {
conf.Mutex.Unlock()
return nil, err
}
// Update the conference to set the last update and top topic.
_, err = tx.Exec("UPDATE confs SET lastupdate = ?, top_topic = ? WHERE confid = ?", topic.CreateDate, conf.TopTopic+1, conf.ConfId)
_, err = tx.ExecContext(ctx, "UPDATE confs SET lastupdate = ?, top_topic = ? WHERE confid = ?", topic.CreateDate, conf.TopTopic+1, conf.ConfId)
if err != nil {
conf.Mutex.Unlock()
return nil, err
@@ -364,7 +371,7 @@ func AmNewTopic(conf *Conference, user *User, title string, zeroPostPseud string
conf.Mutex.Unlock()
// Add the "header record" for the first post.
rs, err = tx.Exec("INSERT INTO posts (topicid, num, linecount, creator_uid, posted, pseud) VALUES (?, 0, ?, ?, ?, ?)",
rs, err = tx.ExecContext(ctx, "INSERT INTO posts (topicid, num, linecount, creator_uid, posted, pseud) VALUES (?, 0, ?, ?, ?, ?)",
topic.TopicId, zeroPostLines, user.Uid, topic.CreateDate, zeroPostPseud)
if err != nil {
return nil, err
@@ -374,23 +381,23 @@ func AmNewTopic(conf *Conference, user *User, title string, zeroPostPseud string
return nil, err
}
// Add the post data.
_, err = tx.Exec("INSERT INTO postdata (postid, data) VALUES (?, ?)", int32(xid), zeroPost)
_, err = tx.ExecContext(ctx, "INSERT INTO postdata (postid, data) VALUES (?, ?)", int32(xid), zeroPost)
if err != nil {
return nil, err
}
// Add a new topic settings record for the user, too.
_, err = tx.Exec("INSERT INTO topicsettings (topicid, uid, last_post) VALUES (?, ?, ?)",
_, err = tx.ExecContext(ctx, "INSERT INTO topicsettings (topicid, uid, last_post) VALUES (?, ?, ?)",
topic.TopicId, user.Uid, topic.CreateDate)
if err != nil {
return nil, err
}
tx.Exec("UNLOCK TABLES;")
tx.ExecContext(ctx, "UNLOCK TABLES;")
unlock = false
// update the "last posted" date in the conference settings
_, err = conf.TouchPost(tx, user, topic.CreateDate)
_, err = conf.TouchPost(ctx, tx, user, topic.CreateDate)
if err != nil {
return nil, err
}
+90 -75
View File
@@ -10,6 +10,7 @@
package database
import (
"context"
"crypto/sha1"
"encoding/hex"
"errors"
@@ -53,11 +54,11 @@ func (p *UserPrefs) Clone() *UserPrefs {
}
// Save saves off the user preferences, replacing the prefs on the user if necessary.
func (p *UserPrefs) Save(u *User) error {
func (p *UserPrefs) Save(ctx context.Context, u *User) error {
if u != nil && u.Uid != p.Uid {
return errors.New("internal mismatch of IDs")
}
_, err := amdb.NamedExec("UPDATE userprefs SET localeid = :localeid, tzid = :tzid WHERE uid = :uid", p)
_, err := amdb.NamedExecContext(ctx, "UPDATE userprefs SET localeid = :localeid, tzid = :tzid WHERE uid = :uid", p)
if err == nil && u != nil {
u.prefs = p
}
@@ -181,27 +182,27 @@ func init() {
}
// ContactInfo returns the contact info structure for the user.
func (u *User) ContactInfo() (*ContactInfo, error) {
func (u *User) ContactInfo(ctx context.Context) (*ContactInfo, error) {
if u.ContactID < 0 {
return nil, nil
}
return AmGetContactInfo(u.ContactID)
return AmGetContactInfo(ctx, u.ContactID)
}
// ContactInfo returns the contact info structure for the user, quietly.
func (u *User) ContactInfoQ() *ContactInfo {
func (u *User) ContactInfoQ(ctx context.Context) *ContactInfo {
if u.ContactID < 0 {
return nil
}
ci, _ := AmGetContactInfo(u.ContactID)
ci, _ := AmGetContactInfo(ctx, u.ContactID)
return ci
}
// SetContactID sets the contact ID of a user.
func (u *User) SetContactID(cid int32) error {
func (u *User) SetContactID(ctx context.Context, cid int32) error {
u.Mutex.Lock()
defer u.Mutex.Unlock()
if _, err := amdb.Exec("UPDATE users SET contactid = ? WHERE uid = ?", cid, u.Uid); err != nil {
if _, err := amdb.ExecContext(ctx, "UPDATE users SET contactid = ? WHERE uid = ?", cid, u.Uid); err != nil {
return err
}
u.ContactID = cid
@@ -213,14 +214,14 @@ func (u *User) SetContactID(cid int32) error {
* Authentication token value
* Standard Go error status.
*/
func (u *User) NewAuthToken() (string, error) {
func (u *User) NewAuthToken(ctx context.Context) (string, error) {
if u.IsAnon {
return "", errors.New("cannot generate token for anonymous user")
}
u.Mutex.Lock()
defer u.Mutex.Unlock()
newToken := util.GenerateRandomAuthString()
if _, err := amdb.Exec("UPDATE users SET tokenauth = ? WHERE uid = ?", newToken, u.Uid); err != nil {
if _, err := amdb.ExecContext(ctx, "UPDATE users SET tokenauth = ? WHERE uid = ?", newToken, u.Uid); err != nil {
return "", err
}
u.Tokenauth = &newToken
@@ -230,12 +231,13 @@ func (u *User) NewAuthToken() (string, error) {
/* ConfirmEMailAddress checks the E-mail confirmation number and sets "verified" status if it's OK.
* Parameters:
* ctx - Standard Go context value.
* confnum - The entered confirmation number.
* remoteIP - The remote IP address for audit messages.
* Returns:
* Standard Go error status.
*/
func (u *User) ConfirmEMailAddress(confnum int32, remoteIP string) error {
func (u *User) ConfirmEMailAddress(ctx context.Context, confnum int32, remoteIP string) error {
var ar *AuditRecord = nil
defer func() {
AmStoreAudit(ar)
@@ -260,12 +262,12 @@ func (u *User) ConfirmEMailAddress(confnum int32, remoteIP string) error {
ar = AmNewAudit(AuditVerifyEmailFail, u.Uid, remoteIP, "Invalid confirmation number")
return errors.New("confirmation number is incorrect. Please try again")
}
_, err := tx.Exec("UPDATE users SET verify_email = 1, base_lvl = ? WHERE uid = ?",
_, err := tx.ExecContext(ctx, "UPDATE users SET verify_email = 1, base_lvl = ? WHERE uid = ?",
AmDefaultRole("Global.AfterVerify").Level(), u.Uid)
if err == nil {
u.VerifyEMail = true
u.BaseLevel = AmDefaultRole("Global.AfterVerify").Level()
err = AmAutoJoinCommunities(tx, u)
err = AmAutoJoinCommunities(ctx, tx, u)
if err == nil {
err = tx.Commit()
if err == nil {
@@ -278,11 +280,11 @@ func (u *User) ConfirmEMailAddress(confnum int32, remoteIP string) error {
}
// NewEmailConfirmationNumber creates a new confirmation number for a user and saves it off.
func (u *User) NewEmailConfirmationNumber() error {
func (u *User) NewEmailConfirmationNumber(ctx context.Context) error {
u.Mutex.Lock()
defer u.Mutex.Unlock()
newnum := util.GenerateRandomConfirmationNumber()
_, err := amdb.Exec("UPDATE users SET email_confnum = ? WHERE uid = ?", newnum, u.Uid)
_, err := amdb.ExecContext(ctx, "UPDATE users SET email_confnum = ? WHERE uid = ?", newnum, u.Uid)
if err != nil {
u.EmailConfNum = newnum
}
@@ -290,7 +292,7 @@ func (u *User) NewEmailConfirmationNumber() error {
}
// ChangePassword resets a user's password.
func (u *User) ChangePassword(password string, remoteIP string) error {
func (u *User) ChangePassword(ctx context.Context, password string, remoteIP string) error {
var ar *AuditRecord = nil
defer func() {
AmStoreAudit(ar)
@@ -299,7 +301,7 @@ func (u *User) ChangePassword(password string, remoteIP string) error {
u.Mutex.Lock()
defer u.Mutex.Unlock()
pval := hashPassword(password)
_, err := amdb.Exec("UPDATE users SET passhash = ? WHERE uid = ?", pval, u.Uid)
_, err := amdb.ExecContext(ctx, "UPDATE users SET passhash = ? WHERE uid = ?", pval, u.Uid)
if err == nil {
u.Passhash = pval
ar = AmNewAudit(AuditChangePassword, u.Uid, remoteIP, "via password change request")
@@ -308,11 +310,11 @@ func (u *User) ChangePassword(password string, remoteIP string) error {
}
// GetFlags retrieves the flags from the properties.
func (u *User) Flags() (*util.OptionSet, error) {
func (u *User) Flags(ctx context.Context) (*util.OptionSet, error) {
u.Mutex.Lock()
defer u.Mutex.Unlock()
if u.flags == nil {
s, err := AmGetUserProperty(u.Uid, UserPropFlags)
s, err := AmGetUserProperty(ctx, u.Uid, UserPropFlags)
if err != nil {
return nil, err
}
@@ -325,11 +327,11 @@ func (u *User) Flags() (*util.OptionSet, error) {
}
// SaveFlags writes the flags to the database and stores them.
func (u *User) SaveFlags(f *util.OptionSet) error {
func (u *User) SaveFlags(ctx context.Context, f *util.OptionSet) error {
s := f.AsString()
u.Mutex.Lock()
defer u.Mutex.Unlock()
err := AmSetUserProperty(u.Uid, UserPropFlags, &s)
err := AmSetUserProperty(ctx, u.Uid, UserPropFlags, &s)
if err == nil {
u.flags = f
}
@@ -337,8 +339,8 @@ func (u *User) SaveFlags(f *util.OptionSet) error {
}
// FlagValue returns the boolean value of one of the user flags.
func (u *User) FlagValue(ndx uint) bool {
f, err := u.Flags()
func (u *User) FlagValue(ctx context.Context, ndx uint) bool {
f, err := u.Flags(ctx)
if err != nil {
log.Errorf("flag retrieval error for user %d: %v", u.Uid, err)
return false
@@ -347,12 +349,12 @@ func (u *User) FlagValue(ndx uint) bool {
}
// Prefs returns the user's preferences record.
func (u *User) Prefs() (*UserPrefs, error) {
func (u *User) Prefs(ctx context.Context) (*UserPrefs, error) {
u.Mutex.Lock()
defer u.Mutex.Unlock()
if u.prefs == nil {
var dbdata []UserPrefs
err := amdb.Select(&dbdata, "SELECT * FROM userprefs WHERE uid = ?", u.Uid)
err := amdb.SelectContext(ctx, &dbdata, "SELECT * FROM userprefs WHERE uid = ?", u.Uid)
if err != nil {
return nil, err
}
@@ -366,13 +368,14 @@ func (u *User) Prefs() (*UserPrefs, error) {
/* SetProfileData sets the "profile" variables for this user.
* Parameters:
* ctx - Standard Go context value.
* reminder - Password reminder string.
* dob - Date of birth field.
* descr - Description string.
* Returns:
* Standard Go error status.
*/
func (u *User) SetProfileData(reminder string, dob *time.Time, descr *string) error {
func (u *User) SetProfileData(ctx context.Context, reminder string, dob *time.Time, descr *string) error {
u.Mutex.Lock()
defer u.Mutex.Unlock()
_, err := amdb.Exec("UPDATE users SET passreminder = ?, dob = ?, description = ? WHERE uid = ?", reminder, dob, descr, u.Uid)
@@ -386,19 +389,20 @@ func (u *User) SetProfileData(reminder string, dob *time.Time, descr *string) er
/* AmGetUser returns a reference to the specified user.
* Parameters:
* ctx - Standard Go context value.
* uid - The UID of the user.
* Returns:
* Pointer to User containing user data, or nil
* Standard Go error status
*/
func AmGetUser(uid int32) (*User, error) {
func AmGetUser(ctx context.Context, uid int32) (*User, error) {
var err error = nil
getUserMutex.Lock()
defer getUserMutex.Unlock()
rc, ok := userCache.Get(uid)
if !ok {
var dbdata []User
err = amdb.Select(&dbdata, "SELECT * from users WHERE uid = ?", uid)
err = amdb.SelectContext(ctx, &dbdata, "SELECT * from users WHERE uid = ?", uid)
if err != nil {
return nil, err
}
@@ -413,20 +417,21 @@ func AmGetUser(uid int32) (*User, error) {
/* AmGetUserTx returns a reference to the specified user inside a transaction.
* Parameters:
* ctxt - Standard Go context value.
* tx - The transaction we're in.
* uid - The UID of the user.
* Returns:
* Pointer to User containing user data, or nil
* Standard Go error status
*/
func AmGetUserTx(tx *sqlx.Tx, uid int32) (*User, error) {
func AmGetUserTx(ctx context.Context, tx *sqlx.Tx, uid int32) (*User, error) {
var err error = nil
getUserMutex.Lock()
defer getUserMutex.Unlock()
rc, ok := userCache.Get(uid)
if !ok {
var dbdata []User
err = tx.Select(&dbdata, "SELECT * from users WHERE uid = ?", uid)
err = tx.SelectContext(ctx, &dbdata, "SELECT * from users WHERE uid = ?", uid)
if err != nil {
return nil, err
}
@@ -441,19 +446,20 @@ func AmGetUserTx(tx *sqlx.Tx, uid int32) (*User, error) {
/* AmGetUserByName returns a reference to the specified user.
* Parameters:
* ctx - Standard Go context value.
* name - The username of the user.
* tx - If this is not nil, use this transaction.
* Returns:
* Pointer to User containing user data, or nil
* Standard Go error status
*/
func AmGetUserByName(name string, tx *sqlx.Tx) (*User, error) {
func AmGetUserByName(ctx context.Context, name string, tx *sqlx.Tx) (*User, error) {
var dbdata []User
var err error
if tx != nil {
err = tx.Select(&dbdata, "SELECT * FROM users WHERE username = ?", name)
err = tx.SelectContext(ctx, &dbdata, "SELECT * FROM users WHERE username = ?", name)
} else {
err = amdb.Select(&dbdata, "SELECT * FROM users WHERE username = ?", name)
err = amdb.SelectContext(ctx, &dbdata, "SELECT * FROM users WHERE username = ?", name)
}
if err != nil {
return nil, err
@@ -472,9 +478,9 @@ func AmGetUserByName(name string, tx *sqlx.Tx) (*User, error) {
}
// getAnonUserID retrieves the UID of the "anonymous" user from the database.
func getAnonUserID() (int32, error) {
func getAnonUserID(ctx context.Context) (int32, error) {
if anonUid < 0 {
rows, err := amdb.Query("SELECT uid FROM users WHERE is_anon = 1")
rows, err := amdb.QueryContext(ctx, "SELECT uid FROM users WHERE is_anon = 1")
if err == nil {
defer rows.Close()
if rows.Next() {
@@ -495,26 +501,29 @@ func getAnonUserID() (int32, error) {
/* AmIsUserAnon returns true if the specified user ID is the anonymous one.
* Parameters:
* ctx = Standard Go context value.
* uid = The user ID to test.
* Returns:
* true if the user is anonymous, false if not
* Standard Go error status
*/
func AmIsUserAnon(uid int32) (bool, error) {
auid, err := getAnonUserID()
func AmIsUserAnon(ctx context.Context, uid int32) (bool, error) {
auid, err := getAnonUserID(ctx)
return (uid == auid), err
}
/* AmGetAnonUser returns a reference to the anonymous user.
* Parameters:
* ctx = Standard Go context value.
* Returns:
* Pointer to User containing anonymous user data, or nil
* Standard Go error status
*/
func AmGetAnonUser() (*User, error) {
func AmGetAnonUser(ctx context.Context) (*User, error) {
var rc *User = nil
auid, err := getAnonUserID()
auid, err := getAnonUserID(ctx)
if err == nil {
rc, err = AmGetUser(auid)
rc, err = AmGetUser(ctx, auid)
}
return rc, err
}
@@ -531,16 +540,17 @@ func hashPassword(password string) string {
}
// touchUser updates the last access time for the user.
func touchUser(tx *sqlx.Tx, user *User) {
func touchUser(ctx context.Context, tx *sqlx.Tx, user *User) {
user.Mutex.Lock()
defer user.Mutex.Unlock()
moment := time.Now().UTC()
tx.Exec("UPDATE user SET lastaccess = ? WHERE uid = ?", moment, user.Uid)
tx.ExecContext(ctx, "UPDATE user SET lastaccess = ? WHERE uid = ?", moment, user.Uid)
user.LastAccess = &moment
}
/* AmAuthenticateUser authenticates a user by name and password.
* Parameters:
* ctx - Standard Go context parameter.
* name - The user name to try.
* password - The password to try.
* remote_ip - The remote IP address, for audit records.
@@ -548,8 +558,8 @@ func touchUser(tx *sqlx.Tx, user *User) {
* The User pointer if authenticated, or nil if not.
* Standard Go error status.
*/
func AmAuthenticateUser(name string, password string, remoteIP string) (*User, error) {
log.Debugf("AmAuthenicate() authenticating user %s...", name)
func AmAuthenticateUser(ctx context.Context, name string, password string, remoteIP string) (*User, error) {
log.Debugf("AmAuthenticateUser() authenticating user %s...", name)
var ar *AuditRecord = nil
defer func() {
AmStoreAudit(ar)
@@ -562,7 +572,7 @@ func AmAuthenticateUser(name string, password string, remoteIP string) (*User, e
}
}()
user, err := AmGetUserByName(name, tx)
user, err := AmGetUserByName(ctx, name, tx)
if err != nil {
log.Error("...user not found")
ar = AmNewAudit(AuditLoginFail, 0, remoteIP, fmt.Sprintf("Bad username: %s", name))
@@ -585,7 +595,7 @@ func AmAuthenticateUser(name string, password string, remoteIP string) (*User, e
return nil, errors.New("the password you have specified is incorrect; please try again")
}
log.Debug("...authenticated")
touchUser(tx, user)
touchUser(ctx, tx, user)
err = tx.Commit()
if err != nil {
return nil, err
@@ -621,13 +631,14 @@ func crackAuthString(authString string) (int32, string, error) {
/* AmAuthenticateUserByToken authenticates a user via the stored cookie authentication string.
* Parameters:
* ctx - Standard Go context value.
* authString - The stored cookie authentication string.
* remoteIP - The remote IP address wheter trhe user is logging in from.
* Returns:
* Pointer to the authenticated User, or nil.
* Standard Go error status.
*/
func AmAuthenticateUserByToken(authString string, remoteIP string) (*User, error) {
func AmAuthenticateUserByToken(ctx context.Context, authString string, remoteIP string) (*User, error) {
var ar *AuditRecord = nil
defer func() {
AmStoreAudit(ar)
@@ -645,7 +656,7 @@ func AmAuthenticateUserByToken(authString string, remoteIP string) (*User, error
return nil, fmt.Errorf("authString not valid, ignored: %v", err)
}
var user *User
user, err = AmGetUserTx(tx, uid)
user, err = AmGetUserTx(ctx, tx, uid)
if err != nil {
log.Error("...user not found")
ar = AmNewAudit(AuditLoginFail, 0, remoteIP, fmt.Sprintf("Bad uid: %d", uid))
@@ -668,7 +679,7 @@ func AmAuthenticateUserByToken(authString string, remoteIP string) (*User, error
return nil, errors.New("token mismatch")
}
log.Debug("...authenticated")
touchUser(tx, user)
touchUser(ctx, tx, user)
err = tx.Commit()
if err != nil {
return nil, err
@@ -680,6 +691,7 @@ func AmAuthenticateUserByToken(authString string, remoteIP string) (*User, error
/* AmCreateNewUser creates a new user record in the database.
* Parameters:
* ctx - Standard Go context value.
* username - New user name.
* password - New password.
* reminder - Password reminder string.
@@ -689,12 +701,12 @@ func AmAuthenticateUserByToken(authString string, remoteIP string) (*User, error
* Pointer to new user record.
* Standard Go error status.
*/
func AmCreateNewUser(username string, password string, reminder string, dob *time.Time, remoteIP string) (*User, error) {
func AmCreateNewUser(ctx context.Context, username string, password string, reminder string, dob *time.Time, remoteIP string) (*User, error) {
var ar *AuditRecord = nil
defer func() {
AmStoreAudit(ar)
}()
anon, _ := getAnonUserID()
anon, _ := getAnonUserID(ctx)
success := false
tx := amdb.MustBegin()
defer func() {
@@ -703,15 +715,15 @@ func AmCreateNewUser(username string, password string, reminder string, dob *tim
}
}()
unlock := true
tx.Exec("LOCK TABLES users WRITE, userprefs WRITE, propuser WRITE, commmember WRITE, sideboxes WRITE, confhotlist WRITE;")
tx.ExecContext(ctx, "LOCK TABLES users WRITE, userprefs WRITE, propuser WRITE, commmember WRITE, sideboxes WRITE, confhotlist WRITE;")
defer func() {
if unlock {
tx.Exec("UNLOCK TABLES;")
tx.ExecContext(ctx, "UNLOCK TABLES;")
}
}()
// Test if the user name is already taken.
rs, err := tx.Query("SELECT uid FROM users WHERE username = ?", username)
rs, err := tx.QueryContext(ctx, "SELECT uid FROM users WHERE username = ?", username)
if err != nil {
return nil, err
} else if rs.Next() {
@@ -720,7 +732,7 @@ func AmCreateNewUser(username string, password string, reminder string, dob *tim
}
// Insert the user record.
_, err2 := tx.Exec(`INSERT INTO users (username, passhash, verify_email, lockout, email_confnum,
_, err2 := tx.ExecContext(ctx, `INSERT INTO users (username, passhash, verify_email, lockout, email_confnum,
base_lvl, created, lastaccess, passreminder, description, dob) VALUES (?, ?, 0, 0, ?, ?, NOW(), NOW(), ?, '', ?)`,
username, hashPassword(password), util.GenerateRandomConfirmationNumber(), AmDefaultRole("Global.NewUser").Level(),
reminder, dob)
@@ -728,42 +740,42 @@ func AmCreateNewUser(username string, password string, reminder string, dob *tim
return nil, err2
}
// Read back the user, which also puts it in the cache.
user, err3 := AmGetUserByName(username, tx)
user, err3 := AmGetUserByName(ctx, username, tx)
if err3 != nil {
return nil, err3
}
log.Debugf("...created new user \"%s\" with UID %d", username, user.Uid)
// add user preferences
_, err = tx.Exec("INSERT INTO userprefs (uid) VALUES (?)", user.Uid)
_, err = tx.ExecContext(ctx, "INSERT INTO userprefs (uid) VALUES (?)", user.Uid)
if err != nil {
return nil, err
}
// add user properties
props := make([]UserProperties, 0)
err = tx.Select(&props, "SELECT * FROM propuser WHERE uid = ?", anon)
err = tx.SelectContext(ctx, &props, "SELECT * FROM propuser WHERE uid = ?", anon)
if err != nil {
return nil, err
}
for _, p := range props {
_, err := tx.Exec("INSERT INTO propuser (uid, ndx, data) VALUES (?, ?, ?)", user.Uid, p.Index, p.Data)
_, err := tx.ExecContext(ctx, "INSERT INTO propuser (uid, ndx, data) VALUES (?, ?, ?)", user.Uid, p.Index, p.Data)
if err != nil {
return nil, err
}
}
// add user sideboxes
err = copySideboxes(tx, user.Uid, anon)
err = copySideboxes(ctx, tx, user.Uid, anon)
if err != nil {
return nil, err
}
tx.Exec("UNLOCK TABLES;")
tx.ExecContext(ctx, "UNLOCK TABLES;")
unlock = false
// auto-join communities
err = AmAutoJoinCommunities(tx, user)
err = AmAutoJoinCommunities(ctx, tx, user)
if err != nil {
return nil, err
}
@@ -782,7 +794,7 @@ func AmCreateNewUser(username string, password string, reminder string, dob *tim
}
// internalGetProp is a helper used by the property functions.
func internalGetProp(uid int32, ndx int32) (*UserProperties, error) {
func internalGetProp(ctx context.Context, uid int32, ndx int32) (*UserProperties, error) {
var err error = nil
key := fmt.Sprintf("%d:%d", uid, ndx)
getUserPropMutex.Lock()
@@ -790,7 +802,7 @@ func internalGetProp(uid int32, ndx int32) (*UserProperties, error) {
rc, ok := userPropCache.Get(key)
if !ok {
var dbdata []UserProperties
err = amdb.Select(&dbdata, "SELECT * from propuser WHERE uid = ? AND ndx = ?", uid, ndx)
err = amdb.SelectContext(ctx, &dbdata, "SELECT * from propuser WHERE uid = ? AND ndx = ?", uid, ndx)
if err != nil {
return nil, err
}
@@ -808,14 +820,15 @@ func internalGetProp(uid int32, ndx int32) (*UserProperties, error) {
/* AmGetUserProperty retrieves the value of a user property.
* Parameters:
* ctx - Standard Go context value.
* uid - The UID of the user to get the property for.
* ndx - The index of the property to retrieve.
* Returns:
* Value of the property string.
* Standard Go error status.
*/
func AmGetUserProperty(uid int32, ndx int32) (*string, error) {
p, err := internalGetProp(uid, ndx)
func AmGetUserProperty(ctx context.Context, uid int32, ndx int32) (*string, error) {
p, err := internalGetProp(ctx, uid, ndx)
if err != nil {
return nil, err
}
@@ -824,27 +837,28 @@ func AmGetUserProperty(uid int32, ndx int32) (*string, error) {
/* AmSetUserProperty sets the value of a user property.
* Parameters:
* ctx - Standard Go context value.
* uid - The UID of the user to set the property for.
* ndx - The index of the property to set.
* val - The new value of the property.
* Returns:
* Standard Go error status.
*/
func AmSetUserProperty(uid int32, ndx int32, val *string) error {
p, err := internalGetProp(uid, ndx)
func AmSetUserProperty(ctx context.Context, uid int32, ndx int32, val *string) error {
p, err := internalGetProp(ctx, uid, ndx)
if err != nil {
return err
}
getUserPropMutex.Lock()
defer getUserPropMutex.Unlock()
if p != nil {
_, err = amdb.Exec("UPDATE propuser SET data = ? WHERE uid = ? AND ndx = ?", val, uid, ndx)
_, err = amdb.ExecContext(ctx, "UPDATE propuser SET data = ? WHERE uid = ? AND ndx = ?", val, uid, ndx)
if err == nil {
p.Data = val
}
} else {
prop := UserProperties{Uid: uid, Index: ndx, Data: val}
_, err := amdb.NamedExec("INSERT INTO propuser (uid, ndx, data) VALUES(:uid, :ndx, :data)", prop)
_, err := amdb.NamedExecContext(ctx, "INSERT INTO propuser (uid, ndx, data) VALUES(:uid, :ndx, :data)", prop)
if err == nil {
userPropCache.Add(fmt.Sprintf("%d:%d", uid, ndx), prop)
}
@@ -854,6 +868,7 @@ func AmSetUserProperty(uid int32, ndx int32, val *string) error {
/* AmSearchUsers searches for users matching certain criteria.
* Parameters:
* ctx - Standard Go context value.
* field - A value indicating which field to search:
* SearchUserFieldName - The user name.
* SearchUserFieldDescription - The user description.
@@ -871,7 +886,7 @@ func AmSetUserProperty(uid int32, ndx int32, val *string) error {
* The total number of users matching this query (could be greater than max)
* Standard Go error status.
*/
func AmSearchUsers(field int, oper int, term string, offset int, max int) ([]*User, int, error) {
func AmSearchUsers(ctx context.Context, field int, oper int, term string, offset int, max int) ([]*User, int, error) {
var queryPortion strings.Builder
switch field {
case SearchUserFieldName:
@@ -902,7 +917,7 @@ func AmSearchUsers(field int, oper int, term string, offset int, max int) ([]*Us
return nil, -1, errors.New("invalid operator selector")
}
q := queryPortion.String()
rs, err := amdb.Query("SELECT COUNT(*) FROM users u, contacts c WHERE u.contactid = c.contactid AND u.is_anon = 0 AND " + q)
rs, err := amdb.QueryContext(ctx, "SELECT COUNT(*) FROM users u, contacts c WHERE u.contactid = c.contactid AND u.is_anon = 0 AND "+q)
if err != nil {
return nil, -1, err
}
@@ -915,10 +930,10 @@ func AmSearchUsers(field int, oper int, term string, offset int, max int) ([]*Us
return make([]*User, 0), 0, nil
}
if offset > 0 {
rs, err = amdb.Query("SELECT u.uid FROM users u, contacts c WHERE u.contactid = c.contactid AND u.is_anon = 0 AND "+q+
rs, err = amdb.QueryContext(ctx, "SELECT u.uid FROM users u, contacts c WHERE u.contactid = c.contactid AND u.is_anon = 0 AND "+q+
" ORDER BY u.username LIMIT ? OFFSET ?", max, offset)
} else {
rs, err = amdb.Query("SELECT u.uid FROM users u, contacts c WHERE u.contactid = c.contactid AND u.is_anon = 0 AND "+q+
rs, err = amdb.QueryContext(ctx, "SELECT u.uid FROM users u, contacts c WHERE u.contactid = c.contactid AND u.is_anon = 0 AND "+q+
" ORDER BY u.username LIMIT ?", max)
}
if err != nil {
@@ -928,7 +943,7 @@ func AmSearchUsers(field int, oper int, term string, offset int, max int) ([]*Us
for rs.Next() {
var uid int32
rs.Scan(&uid)
u, err := AmGetUser(uid)
u, err := AmGetUser(ctx, uid)
if err == nil {
rc = append(rc, u)
}
+4 -3
View File
@@ -12,6 +12,7 @@ package email
import (
"bytes"
"context"
"embed"
"fmt"
"io"
@@ -47,7 +48,7 @@ var mailHost string
var auth smtp.Auth
// formatMessage takes a message and turns it into serialized bytes for sending.
func formatMessage(m *amMessage) ([]byte, error) {
func formatMessage(ctx context.Context, m *amMessage) ([]byte, error) {
if m.template != "" {
// Render the template for the message, which may reset Subject.
templ, err := emailRenderer.GetTemplate(m.template)
@@ -62,7 +63,7 @@ func formatMessage(m *amMessage) ([]byte, error) {
return make([]byte, 0), err
}
}
user, err := database.AmGetUser(m.uid)
user, err := database.AmGetUser(ctx, m.uid)
if err == nil {
// Build the final headers.
hdrs := make(map[string]string)
@@ -177,7 +178,7 @@ func transmitMessage(m *amMessage, body []byte) {
// senderLoop collects E-mail messages from the channel and pushes them out.
func senderLoop(sent chan *amMessage, done chan bool) {
for m := range sent {
body, err := formatMessage(m)
body, err := formatMessage(context.Background(), m)
if err == nil {
transmitMessage(m, body)
} else {
+7 -7
View File
@@ -38,7 +38,7 @@ func loadCategoryInformation(ctxt ui.AmContext, offset int) error {
}
}
if catid > -1 {
cat, err := database.AmGetCategory(catid) // this step also resolves symlinks
cat, err := database.AmGetCategory(ctxt.Ctx(), catid) // this step also resolves symlinks
if err != nil {
return err
}
@@ -48,12 +48,12 @@ func loadCategoryInformation(ctxt ui.AmContext, offset int) error {
ctxt.VarMap().Set("catid", catid)
showHidden := database.AmTestPermission("Global.ShowHiddenCategories", u.BaseLevel)
ctxt.VarMap().Set("showHiddenCat", showHidden)
hier, err := database.AmGetCategoryHierarchy(catid)
hier, err := database.AmGetCategoryHierarchy(ctxt.Ctx(), catid)
if err != nil {
return err
}
ctxt.VarMap().Set("catHierarchy", hier)
subs, err := database.AmGetSubCategories(catid)
subs, err := database.AmGetSubCategories(ctxt.Ctx(), catid)
if err != nil {
return err
}
@@ -63,7 +63,7 @@ func loadCategoryInformation(ctxt ui.AmContext, offset int) error {
if catid > -1 {
// search for communities in this category
listMax := int(ctxt.Globals().MaxSearchPage)
commList, numComm, err := database.AmGetCommunitiesForCategory(catid, offset*listMax, listMax, showHidden)
commList, numComm, err := database.AmGetCommunitiesForCategory(ctxt.Ctx(), catid, offset*listMax, listMax, showHidden)
if err != nil {
return err
}
@@ -207,7 +207,7 @@ func Find(ctxt ui.AmContext) (string, any, error) {
return "framed_template", "find.jet", nil
}
var clist []*database.Community
clist, total, err = database.AmSearchCommunities(iField, iOper, term, ofs*listMax, listMax,
clist, total, err = database.AmSearchCommunities(ctxt.Ctx(), iField, iOper, term, ofs*listMax, listMax,
ctxt.TestPermission("Global.SearchHiddenCommunities"))
if err == nil {
if clist == nil {
@@ -244,7 +244,7 @@ func Find(ctxt ui.AmContext) (string, any, error) {
return "framed_template", "find.jet", nil
}
var ulist []*database.User
ulist, total, err = database.AmSearchUsers(iField, iOper, term, ofs*listMax, listMax)
ulist, total, err = database.AmSearchUsers(ctxt.Ctx(), iField, iOper, term, ofs*listMax, listMax)
if err == nil {
if ulist == nil {
numResults = 0
@@ -268,7 +268,7 @@ func Find(ctxt ui.AmContext) (string, any, error) {
return "framed_template", "find.jet", nil
}
var catlist []*database.Category
catlist, total, err = database.AmSearchCategories(iOper, term, ofs*listMax, listMax,
catlist, total, err = database.AmSearchCategories(ctxt.Ctx(), iOper, term, ofs*listMax, listMax,
ctxt.TestPermission("Global.ShowHiddenCategories"), ctxt.TestPermission("Global.SearchHiddenCategories"))
if err == nil {
if catlist == nil {
+7 -3
View File
@@ -10,6 +10,7 @@
package htmlcheck
import (
"context"
"errors"
"fmt"
"net/url"
@@ -81,6 +82,7 @@ const hyphApos = "-'"
// htmlCheckerImpl is the implementation of the HTML checker.
type htmlCheckerImpl struct {
ctx context.Context // request context, as instances are generally per-request
config *HTMLCheckerConfig // pointer to configuration
started bool // has checker been started?
finished bool // has checker been finished?
@@ -147,13 +149,14 @@ func (ht *htmlCheckerImpl) copyOutputFilters(dest []outputFilter, source []strin
*/
/* AmNewHTMLChecker creates a new HTML Checker object.
* Parametrers:
* Parameters:
* ctx - Standard Go context value.
* configName - Name of the configuration to use.
* Returns:
* New HTML checker reference.
* Standard Go error status.
*/
func AmNewHTMLChecker(configName string) (HTMLChecker, error) {
func AmNewHTMLChecker(ctx context.Context, configName string) (HTMLChecker, error) {
config, ok := configsRegistry[configName]
if !ok {
return nil, fmt.Errorf("configuration %s not found", configName)
@@ -166,6 +169,7 @@ func AmNewHTMLChecker(configName string) (HTMLChecker, error) {
}
}
rc := htmlCheckerImpl{
ctx: ctx,
config: config,
started: false,
finished: false,
@@ -486,7 +490,7 @@ func (ht *htmlCheckerImpl) emitFromStartOfTempBuffer(nrunes int) {
// attemptRewrite attempts to apply a list of rewriters on the text, returning the first one that matches.
func (ht *htmlCheckerImpl) attemptRewrite(rewriters []rewriter, data string) *markupData {
for _, r := range rewriters {
rc := r.Rewrite(data, ht)
rc := r.Rewrite(ht.ctx, data, ht)
if rc != nil {
return rc
}
+3 -1
View File
@@ -10,6 +10,7 @@
package htmlcheck
import (
"context"
_ "embed"
"os"
@@ -76,12 +77,13 @@ func (rw *spellingRewriter) Name() string {
/* Rewrite rewrites the given string data and adds markup before and after if needed.
* Parameters:
* ctx - Standard Go error status.
* data - The data to be rewritten.
* svc - Services interface we can use.
* Returns:
* Pointer to markup data, or nil.
*/
func (rw *spellingRewriter) Rewrite(data string, svc rewriterServices) *markupData {
func (rw *spellingRewriter) Rewrite(ctx context.Context, data string, svc rewriterServices) *markupData {
if rw.dict.CheckWord(data) {
return nil
}
+5 -2
View File
@@ -10,6 +10,7 @@
package htmlcheck
import (
"context"
_ "embed"
"math"
"regexp"
@@ -90,12 +91,13 @@ func (rw *emoticonRewriter) Name() string {
/* Rewrite rewrites the given string data and adds markup before and after if needed.
* Parameters:
* ctx - Standard Go context value.
* data - The data to be rewritten.
* svc - Services interface we can use.
* Returns:
* Pointer to markup data, or nil.
*/
func (rw *emoticonRewriter) Rewrite(data string, svc rewriterServices) *markupData {
func (rw *emoticonRewriter) Rewrite(ctx context.Context, data string, svc rewriterServices) *markupData {
pos := math.MaxInt
for _, c := range rw.prefixChars {
foo := strings.IndexByte(data, c)
@@ -163,12 +165,13 @@ func (rw *emoticonTagRewriter) Name() string {
/* Rewrite rewrites the given string data and adds markup before and after if needed.
* Parameters:
* ctx - Standard Go context value.
* data - The data to be rewritten.
* svc - Services interface we can use.
* Returns:
* Pointer to markup data, or nil.
*/
func (rw *emoticonTagRewriter) Rewrite(data string, svc rewriterServices) *markupData {
func (rw *emoticonTagRewriter) Rewrite(ctx context.Context, data string, svc rewriterServices) *markupData {
m := rw.re.FindStringSubmatch(data)
if m == nil {
return nil
+10 -8
View File
@@ -10,6 +10,7 @@
package htmlcheck
import (
"context"
"fmt"
"net/mail"
"net/url"
@@ -41,7 +42,7 @@ type rewriterServices interface {
// rewriter is the interface for components that rewrite source text and place markup around it.
type rewriter interface {
Name() string
Rewrite(string, rewriterServices) *markupData
Rewrite(context.Context, string, rewriterServices) *markupData
}
// rewriterRegistry contains a list of all rewriters.
@@ -72,7 +73,7 @@ func (rw *emailRewriter) Name() string {
* Returns:
* Pointer to markup data, or nil.
*/
func (rw *emailRewriter) Rewrite(data string, svc rewriterServices) *markupData {
func (rw *emailRewriter) Rewrite(ctx context.Context, data string, svc rewriterServices) *markupData {
_, err := mail.ParseAddress(data)
if err != nil {
return nil
@@ -154,7 +155,7 @@ func buildPostLink(decoded, context *database.PostLinkData) string {
* Returns:
* Pointer to markup data, or nil.
*/
func (rw *postLinkRewriter) Rewrite(data string, svc rewriterServices) *markupData {
func (rw *postLinkRewriter) Rewrite(ctx context.Context, data string, svc rewriterServices) *markupData {
q := svc.rewriterContextValue("PostLinkDecoderContext")
if q == nil {
return nil
@@ -165,7 +166,7 @@ func (rw *postLinkRewriter) Rewrite(data string, svc rewriterServices) *markupDa
if err != nil {
return nil
}
err = mydata.VerifyNames()
err = mydata.VerifyNames(ctx)
if err != nil {
return nil
}
@@ -205,17 +206,18 @@ func (rw *userLinkRewriter) Name() string {
/* Rewrite rewrites the given string data and adds markup before and after if needed.
* Parameters:
* ctx - Standard Go context value.
* data - The data to be rewritten.
* svc - Services interface we can use.
* Returns:
* Pointer to markup data, or nil.
*/
func (rw *userLinkRewriter) Rewrite(data string, svc rewriterServices) *markupData {
func (rw *userLinkRewriter) Rewrite(ctx context.Context, data string, svc rewriterServices) *markupData {
if data == "" || len(data) > 64 || !database.AmIsValidAmsterdamID(data) {
return nil
}
user, err := database.AmGetUserByName(data, nil)
user, err := database.AmGetUserByName(ctx, data, nil)
if err != nil || user == nil {
return nil
}
@@ -258,8 +260,8 @@ func (rw *countingRewriter) Name() string {
* Returns:
* Pointer to markup data, or nil.
*/
func (rw *countingRewriter) Rewrite(data string, svc rewriterServices) *markupData {
rc := rw.inner.Rewrite(data, svc)
func (rw *countingRewriter) Rewrite(ctx context.Context, data string, svc rewriterServices) *markupData {
rc := rw.inner.Rewrite(ctx, data, svc)
if rc != nil && !rc.rescan {
rw.count++
}
+3 -1
View File
@@ -10,6 +10,7 @@
package htmlcheck
import (
"context"
"net/url"
"regexp"
"strings"
@@ -72,12 +73,13 @@ func (rw *urlRewriter) Name() string {
/* Rewrite rewrites the given string data and adds markup before and after if needed.
* Parameters:
* ctx - Standard Go context value.
* data - The data to be rewritten.
* svc - Services interface we can use.
* Returns:
* Pointer to markup data, or nil.
*/
func (rw *urlRewriter) Rewrite(data string, svc rewriterServices) *markupData {
func (rw *urlRewriter) Rewrite(ctx context.Context, data string, svc rewriterServices) *markupData {
for _, ue := range urlElements {
s := ue.eval(data)
if s != "" {
+13 -13
View File
@@ -80,10 +80,10 @@ func Login(ctxt ui.AmContext) (string, any, error) {
return dlg.RenderError(ctxt, "User name not specified.")
}
if action == "remind" { // Password Reminder button pressed
user, uerr := database.AmGetUserByName(username, nil)
user, uerr := database.AmGetUserByName(ctxt.Ctx(), username, nil)
if uerr == nil {
var ci *database.ContactInfo
ci, uerr = user.ContactInfo()
ci, uerr = user.ContactInfo(ctxt.Ctx())
if uerr == nil {
if ci != nil && ci.Email != nil && *ci.Email != "" {
pchange := database.AmNewPasswordChangeRequest(user.Uid, user.Username, *ci.Email)
@@ -109,14 +109,14 @@ func Login(ctxt ui.AmContext) (string, any, error) {
}
if action == "login" { // Login button pressed
// authenticate the user
user, uerr := database.AmAuthenticateUser(username, dlg.Field("pass").Value, ctxt.RemoteIP())
user, uerr := database.AmAuthenticateUser(ctxt.Ctx(), username, dlg.Field("pass").Value, ctxt.RemoteIP())
if uerr != nil {
return dlg.RenderError(ctxt, uerr.Error())
}
ctxt.ReplaceUser(user)
if dlg.Field("saveme").IsChecked() {
// create and save an authentication token
authString, cerr := user.NewAuthToken()
authString, cerr := user.NewAuthToken(ctxt.Ctx())
if cerr == nil {
ctxt.SetLoginCookie(authString)
@@ -240,9 +240,9 @@ func VerifyEMail(ctxt ui.AmContext) (string, any, error) {
}
if action == "sendagain" {
var ci *database.ContactInfo
ci, err = user.ContactInfo()
ci, err = user.ContactInfo(ctxt.Ctx())
if err == nil {
err = user.NewEmailConfirmationNumber()
err = user.NewEmailConfirmationNumber(ctxt.Ctx())
if err == nil {
err = sendEmailConfirmationEmail(user, ci, ctxt.RemoteIP())
}
@@ -257,7 +257,7 @@ func VerifyEMail(ctxt ui.AmContext) (string, any, error) {
err = dlg.Validate()
if err == nil {
cn, _ := dlg.Field("num").ValueInt()
err = user.ConfirmEMailAddress(int32(cn), ctxt.RemoteIP())
err = user.ConfirmEMailAddress(ctxt.Ctx(), int32(cn), ctxt.RemoteIP())
if err == nil {
return "redirect", target, nil
}
@@ -358,14 +358,14 @@ func NewAccount(ctxt ui.AmContext) (string, any, error) {
return dlg.RenderError(ctxt, "The typed passwords do not match.")
}
var banned bool
banned, err = database.AmIsEmailAddressBanned(dlg.Field("email").Value)
banned, err = database.AmIsEmailAddressBanned(ctxt.Ctx(), dlg.Field("email").Value)
if err == nil {
if banned {
return dlg.RenderError(ctxt, "This E-mail address may not register a new account.")
}
// Create new user account
var user *database.User
user, err = database.AmCreateNewUser(dlg.Field("user").Value, dlg.Field("pass1").Value,
user, err = database.AmCreateNewUser(ctxt.Ctx(), dlg.Field("user").Value, dlg.Field("pass1").Value,
dlg.Field("remind").Value, dlg.Field("dob").AsDate(), ctxt.RemoteIP())
if err == nil {
// create and save contact info
@@ -384,9 +384,9 @@ func NewAccount(ctxt ui.AmContext) (string, any, error) {
ci.PostalCode = dlg.Field("pcode").ValPtr()
ci.Country = dlg.Field("country").ValPtr()
ci.Email = dlg.Field("email").ValPtr()
_, err = ci.Save()
_, err = ci.Save(ctxt.Ctx())
if err == nil {
err = user.SetContactID(ci.ContactId)
err = user.SetContactID(ctxt.Ctx(), ci.ContactId)
}
if err == nil {
err = sendEmailConfirmationEmail(user, ci, ctxt.RemoteIP())
@@ -435,10 +435,10 @@ func PasswordRecovery(ctxt ui.AmContext) (string, any, error) {
}
if err == nil {
user, err := database.AmGetUser(int32(uid))
user, err := database.AmGetUser(ctxt.Ctx(), int32(uid))
if err == nil {
newpass := util.GenerateRandomPassword()
err = user.ChangePassword(newpass, ctxt.RemoteIP())
err = user.ChangePassword(ctxt.Ctx(), newpass, ctxt.RemoteIP())
if err == nil {
// send the password change message
msg := email.AmNewEmailMessage(user.Uid, ctxt.RemoteIP())
+3 -3
View File
@@ -124,9 +124,6 @@ func main() {
closer = ui.SetupAmContext()
defer closer()
// Set up Echo.
e := setupEcho()
// Set up to trap SIGINT and shut down gracefully
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
@@ -138,6 +135,9 @@ func main() {
ampool.Shutdown()
}()
// Set up Echo.
e := setupEcho()
// Start server
go func() {
if err := e.Start(":1323"); err != nil && err != http.ErrServerClosed {
+9 -9
View File
@@ -48,11 +48,11 @@ type RenderedSidebox struct {
* Returns:
* Standard Go error status.
*/
func buildCommunitiesSidebox(uid int32, out *RenderedSidebox, in *database.Sidebox) error {
user, err := database.AmGetUser(uid)
func buildCommunitiesSidebox(ctxt ui.AmContext, uid int32, out *RenderedSidebox, in *database.Sidebox) error {
user, err := database.AmGetUser(ctxt.Ctx(), uid)
if err == nil {
var g *database.Globals
g, err = database.AmGlobals()
g, err = database.AmGlobals(ctxt.Ctx())
if err == nil {
if user.IsAnon {
out.Title = "Featured Communities"
@@ -60,7 +60,7 @@ func buildCommunitiesSidebox(uid int32, out *RenderedSidebox, in *database.Sideb
out.Title = "Your Communities"
}
var l []*database.Community
l, err = database.AmGetCommunitiesForUser(uid)
l, err = database.AmGetCommunitiesForUser(ctxt.Ctx(), uid)
if err == nil {
out.Items = make([]RenderedSideboxItem, len(l))
for i, c := range l {
@@ -69,7 +69,7 @@ func buildCommunitiesSidebox(uid int32, out *RenderedSidebox, in *database.Sideb
out.Items[i].Link = &lk
out.Items[i].Flags = make(map[string]bool)
var level uint16
level, err = database.AmGetCommunityAccessLevel(uid, c.Id)
level, err = database.AmGetCommunityAccessLevel(ctxt.Ctx(), uid, c.Id)
if err == nil && database.AmTestPermission("Community.ShowAdmin", level) {
out.Items[i].Flags["admin"] = true
}
@@ -150,10 +150,10 @@ func buildUsersOnline(uid int32, out *RenderedSidebox, in *database.Sidebox) err
* Returns:
* Standard Go error status.
*/
func buildRenderedSidebox(uid int32, out *RenderedSidebox, in *database.Sidebox) error {
func buildRenderedSidebox(ctxt ui.AmContext, uid int32, out *RenderedSidebox, in *database.Sidebox) error {
switch in.Boxid {
case 1:
return buildCommunitiesSidebox(uid, out, in)
return buildCommunitiesSidebox(ctxt, uid, out, in)
case 2:
return buildFeaturedConferences(uid, out, in)
case 3:
@@ -177,14 +177,14 @@ func TopPage(ctxt ui.AmContext) (string, any, error) {
// Retrieve the sideboxes and create the data to be presented.
uid := ctxt.CurrentUserId()
sboxes, err := database.AmGetSideboxes(uid)
sboxes, err := database.AmGetSideboxes(ctxt.Ctx(), uid)
if err != nil {
return "string", "Unable to retrieve sideboxes", err
}
rc := make([]RenderedSidebox, len(sboxes))
for i, sb := range sboxes {
err = buildRenderedSidebox(uid, &(rc[i]), sb)
err = buildRenderedSidebox(ctxt, uid, &(rc[i]), sb)
if err != nil {
return "string", "Unable to render sideboxes", err
}
+8 -8
View File
@@ -120,7 +120,7 @@ func (c *amContext) ClearLoginCookie() {
// ClearSession clears the current session.
func (c *amContext) ClearSession() {
AmResetSession(c.session)
AmResetSession(c.echoContext.Request().Context(), c.session)
c.user = nil
c.effectiveLevel = 0
}
@@ -144,7 +144,7 @@ func (c *amContext) CurrentCommunity() *database.Community {
// CurrentUser returns the current user from the session.
func (c *amContext) CurrentUser() *database.User {
if c.user == nil {
u, err := database.AmGetUser(AmSessionUid(c.session))
u, err := database.AmGetUser(c.echoContext.Request().Context(), AmSessionUid(c.session))
if err != nil {
log.Errorf("unable to retrieve current user")
}
@@ -327,12 +327,12 @@ func (c *amContext) SubRender(name string) ([]byte, error) {
* Standard Go error status.
*/
func (c *amContext) SetCommunityContext(param string) error {
comm, err := database.AmGetCommunityFromParam(param)
comm, err := database.AmGetCommunityFromParam(c.echoContext.Request().Context(), param)
if err != nil {
return err
}
if c.community == nil || c.community.Id != comm.Id {
mbr, lock, level, err := comm.Membership(c.CurrentUser())
mbr, lock, level, err := comm.Membership(c.echoContext.Request().Context(), c.CurrentUser())
if err != nil {
return err
}
@@ -459,11 +459,11 @@ func newContext(ctxt echo.Context) (*amContext, error) {
}
var err error
if rc.globals, err = database.AmGlobals(); err != nil {
if rc.globals, err = database.AmGlobals(ctxt.Request().Context()); err != nil {
amContextRecycleBin <- rc
return nil, err
}
if rc.globalFlags, err = rc.globals.Flags(); err != nil {
if rc.globalFlags, err = rc.globals.Flags(ctxt.Request().Context()); err != nil {
amContextRecycleBin <- rc
return nil, err
}
@@ -475,12 +475,12 @@ func newContext(ctxt echo.Context) (*amContext, error) {
rc.session = sess
sess.Options = defoptions
if sess.IsNew {
AmSessionFirstTime(sess)
AmSessionFirstTime(ctxt.Request().Context(), sess)
} else {
AmHitSession(sess)
}
}
rc.user, err = database.AmGetUser(AmSessionUid(sess))
rc.user, err = database.AmGetUser(ctxt.Request().Context(), AmSessionUid(sess))
if err == nil {
rc.effectiveLevel = rc.user.BaseLevel
} else {
+1 -1
View File
@@ -78,7 +78,7 @@ func AmServeImage(ctxt AmContext) (string, any, error) {
id, err = strconv.Atoi(components[3])
if err == nil {
var img *database.ImageStore
img, err = database.AmLoadImage(int32(id))
img, err = database.AmLoadImage(ctxt.Ctx(), int32(id))
if err == nil {
ctxt.SetOutputType(img.MimeType)
return "bytes", img.Data, nil
+4 -2
View File
@@ -10,6 +10,7 @@
package ui
import (
"context"
_ "embed"
"fmt"
"slices"
@@ -141,19 +142,20 @@ func AmMenu(name string) *MenuDefinition {
/* AmBuildCommunityMenu buids a community menu for the specified community.
* Parameters:
* ctx - Standard Go context value.
* comm - The community to build the menu for.
* Returns:
* The new menu definition.
* Standard Go error status.
*/
func AmBuildCommunityMenu(comm *database.Community) (*MenuDefinition, error) {
func AmBuildCommunityMenu(ctx context.Context, comm *database.Community) (*MenuDefinition, error) {
menuCacheMutex.Lock()
defer menuCacheMutex.Unlock()
m, ok := menuCache.Get(comm.Id)
if ok {
return m.(*MenuDefinition), nil
}
sdef, err := database.AmGetCommunityServices(comm.Id)
sdef, err := database.AmGetCommunityServices(ctx, comm.Id)
if err != nil {
return nil, err
}
+6 -6
View File
@@ -31,7 +31,7 @@ func middlewareErrorPage(c echo.Context, ctxt AmContext, err error) error {
func IPBanTest(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Check IP banning.
banmsg, banerr := database.AmTestIPBan(c.RealIP())
banmsg, banerr := database.AmTestIPBan(c.Request().Context(), c.RealIP())
if banerr != nil {
c.Logger().Warnf("address %s could not be tested: %v", c.RealIP(), banerr)
// but let the request pass anyway
@@ -55,12 +55,12 @@ func CookieLoginTest(next echo.HandlerFunc) echo.HandlerFunc {
cookie, err := c.Cookie(config.GlobalConfig.Site.LoginCookieName)
if err == nil {
var user *database.User
user, err = database.AmAuthenticateUserByToken(cookie.Value, c.RealIP())
user, err = database.AmAuthenticateUserByToken(c.Request().Context(), cookie.Value, c.RealIP())
if err == nil {
// log the user in and rotate login cookie
amctxt.ReplaceUser(user)
var newToken string
if newToken, err = user.NewAuthToken(); err == nil {
if newToken, err = user.NewAuthToken(c.Request().Context()); err == nil {
amctxt.SetLoginCookie(newToken)
} else {
log.Warnf("unable to rotate login cookie: %v", err)
@@ -99,7 +99,7 @@ func ValidateConference(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
ctxt := AmContextFromEchoContext(c)
comm := ctxt.CurrentCommunity() // set by middleware
b, err := database.AmTestService(comm, "Conference")
b, err := database.AmTestService(c.Request().Context(), comm, "Conference")
if err != nil {
return middlewareErrorPage(c, ctxt, err)
}
@@ -122,11 +122,11 @@ func ValidateConference(next echo.HandlerFunc) echo.HandlerFunc {
func SetConference(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
ctxt := AmContextFromEchoContext(c)
conf, err := database.AmGetConferenceByAliasInCommunity(ctxt.CurrentCommunity().Id, ctxt.URLParam("confid"))
conf, err := database.AmGetConferenceByAliasInCommunity(ctxt.Ctx(), ctxt.CurrentCommunity().Id, ctxt.URLParam("confid"))
if err != nil {
return middlewareErrorPage(c, ctxt, err)
}
m, lvl, err := conf.Membership(ctxt.CurrentUser())
m, lvl, err := conf.Membership(ctxt.Ctx(), ctxt.CurrentUser())
if err != nil {
return middlewareErrorPage(c, ctxt, err)
}
+1 -1
View File
@@ -51,7 +51,7 @@ func AmSendPageData(ctxt echo.Context, amctxt AmContext, command string, data an
case "top":
menus[0] = AmMenu("top")
case "community":
md, err := AmBuildCommunityMenu(amctxt.CurrentCommunity())
md, err := AmBuildCommunityMenu(ctxt.Request().Context(), amctxt.CurrentCommunity())
if err != nil {
return err
}
+7 -6
View File
@@ -11,6 +11,7 @@
package ui
import (
"context"
"crypto/rand"
"encoding/gob"
"encoding/hex"
@@ -220,8 +221,8 @@ func AmSetSessionUser(session *sessions.Session, user *database.User) {
}
// setSessionAnon sets the user for the session to the anonymous user.
func setSessionAnon(session *sessions.Session) {
u, err := database.AmGetAnonUser()
func setSessionAnon(ctx context.Context, session *sessions.Session) {
u, err := database.AmGetAnonUser(ctx)
if err == nil {
AmSetSessionUser(session, u)
} else {
@@ -232,20 +233,20 @@ func setSessionAnon(session *sessions.Session) {
var lastHitMutex sync.Mutex
// AmSessionFirstTime initializes the session after it's first created.
func AmSessionFirstTime(session *sessions.Session) {
func AmSessionFirstTime(ctx context.Context, session *sessions.Session) {
lastHitMutex.Lock()
setSessionAnon(session)
setSessionAnon(ctx, session)
session.Values["lasthit"] = time.Now()
lastHitMutex.Unlock()
}
// AmResetSession clears the specified session.
func AmResetSession(session *sessions.Session) {
func AmResetSession(ctx context.Context, session *sessions.Session) {
lastHitMutex.Lock()
for k := range session.Values {
delete(session.Values, k)
}
setSessionAnon(session)
setSessionAnon(ctx, session)
session.Values["lasthit"] = time.Now()
lastHitMutex.Unlock()
}
+8 -6
View File
@@ -112,7 +112,8 @@ func immediateIf(a jet.Arguments) reflect.Value {
func extractCommunityLogo(a jet.Arguments) reflect.Value {
rc := "/img/builtin/default-community.jpg"
comm := a.Get(0).Convert(reflect.TypeFor[*database.Community]()).Interface().(*database.Community)
ci, err := comm.ContactInfo()
ctxt := a.Get(1).Convert(reflect.TypeFor[AmContext]()).Interface().(AmContext)
ci, err := comm.ContactInfo(ctxt.Ctx())
if err == nil {
if ci.PhotoURL != nil && *ci.PhotoURL != "" {
rc = *ci.PhotoURL
@@ -125,7 +126,7 @@ func extractCommunityLogo(a jet.Arguments) reflect.Value {
func displayDateTime(a jet.Arguments) reflect.Value {
timeval := a.Get(0).Convert(reflect.TypeFor[time.Time]()).Interface().(time.Time)
ctxt := a.Get(1).Convert(reflect.TypeFor[AmContext]()).Interface().(AmContext)
prefs, err := ctxt.CurrentUser().Prefs()
prefs, err := ctxt.CurrentUser().Prefs(ctxt.Ctx())
if err == nil {
loc := prefs.Localizer()
return reflect.ValueOf(loc.Strftime("%b %e, %Y %r", timeval))
@@ -137,7 +138,7 @@ func displayDateTime(a jet.Arguments) reflect.Value {
func displayActivity(a jet.Arguments) reflect.Value {
timeval := a.Get(0).Convert(reflect.TypeFor[*time.Time]()).Interface().(*time.Time)
ctxt := a.Get(1).Convert(reflect.TypeFor[AmContext]()).Interface().(AmContext)
prefs, err := ctxt.CurrentUser().Prefs()
prefs, err := ctxt.CurrentUser().Prefs(ctxt.Ctx())
if err == nil {
return reflect.ValueOf(util.AmActivityString(timeval, prefs.Localizer()))
}
@@ -150,14 +151,14 @@ func displayMemberCount(a jet.Arguments) reflect.Value {
comm := a.Get(0).Convert(reflect.TypeFor[*database.Community]()).Interface().(*database.Community)
ctxt := a.Get(1).Convert(reflect.TypeFor[AmContext]()).Interface().(AmContext)
level := ctxt.CurrentUser().BaseLevel
mbr, _, clevel, err := comm.Membership(ctxt.CurrentUser())
mbr, _, clevel, err := comm.Membership(ctxt.Ctx(), ctxt.CurrentUser())
if err == nil {
if mbr && clevel > level {
level = clevel
}
showHidden = comm.TestPermission("Community.ShowHiddenMembers", level)
}
count, err := comm.MemberCount(showHidden)
count, err := comm.MemberCount(ctxt.Ctx(), showHidden)
if err != nil {
return reflect.ValueOf(-1)
}
@@ -194,7 +195,8 @@ func displayFullName(a jet.Arguments) reflect.Value {
// displayExpandCat displays a category expanded into a hierarchy.
func displayExpandCat(a jet.Arguments) reflect.Value {
cat := a.Get(0).Convert(reflect.TypeFor[*database.Category]()).Interface().(*database.Category)
hier, _ := database.AmGetCategoryHierarchy(cat.CatId)
ctxt := a.Get(1).Convert(reflect.TypeFor[AmContext]()).Interface().(AmContext)
hier, _ := database.AmGetCategoryHierarchy(ctxt.Ctx(), cat.CatId)
var rc strings.Builder
for i, c := range hier {
if i > 0 {
+1 -1
View File
@@ -257,7 +257,7 @@
<span class="text-sm pt-0.5 flex-shrink-0">🟣</span>
<div class="flex-1 mb-2">
<a href="/find?mode=COM&catid={{ rx.CatId }}"
class="text-blue-700 hover:text-blue-900 font-bold text-base">{{ DisplayExpandCat(rx) }}</a>
class="text-blue-700 hover:text-blue-900 font-bold text-base">{{ DisplayExpandCat(rx, .) }}</a>
</div>
</div>
{{ else if mode == "PST" }}
+1 -1
View File
@@ -10,7 +10,7 @@
{{ comm := .CurrentCommunity() }}
<div class="mb-2 mt-2">
<div class="mb-1">
<img src="{{ ExtractCommunityLogo(comm) }}" alt="{{ comm.Name }}" class="w-28 h-16 rounded">
<img src="{{ ExtractCommunityLogo(comm, .) }}" alt="{{ comm.Name }}" class="w-28 h-16 rounded">
</div>
<div class="font-bold mb-1">{{ menu.Title }}</div>
{{ ctxt := . }}
+2 -2
View File
@@ -79,8 +79,8 @@
{{ post_overrideLine := "" }}
{{ post_overrideLink := "" }}
{{ range i, post_cur := posts }}
{{ post_userName = post_getUserName(post_cur) }}
{{ post_text = post_getText(post_cur) }}
{{ post_userName = post_getUserName(post_cur, .) }}
{{ post_text = post_getText(post_cur, .) }}
{{ post_overrideLine = post_getOverrideLine(post_cur, .) }}
{{ post_overrideLink = post_getOverrideLink(post_cur, post_topicPermalink) }}
{{ .SubRender("singlepost.jet") | raw }}
+24 -24
View File
@@ -57,10 +57,10 @@ func EditProfileForm(ctxt ui.AmContext) (string, any, error) {
dlg.Field("tgt").Value = target
ctxt.VarMap().Set("target", target)
var ci *database.ContactInfo
ci, err = u.ContactInfo()
ci, err = u.ContactInfo(ctxt.Ctx())
if err == nil {
var prefs *database.UserPrefs
prefs, err = u.Prefs()
prefs, err = u.Prefs(ctxt.Ctx())
if err == nil {
dlg.Field("remind").Value = u.PassReminder
dlg.Field("prefix").SetVal(ci.Prefix)
@@ -87,8 +87,8 @@ func EditProfileForm(ctxt ui.AmContext) (string, any, error) {
dlg.Field("dob").SetDate(u.DOB)
dlg.Field("descr").SetVal(u.Description)
dlg.Field("photo").Value = userPhotoURL(ci)
dlg.Field("pic_in_post").SetChecked(u.FlagValue(database.UserFlagPicturesInPosts))
dlg.Field("no_mass_mail").SetChecked(u.FlagValue(database.UserFlagMassMailOptOut))
dlg.Field("pic_in_post").SetChecked(u.FlagValue(ctxt.Ctx(), database.UserFlagPicturesInPosts))
dlg.Field("no_mass_mail").SetChecked(u.FlagValue(ctxt.Ctx(), database.UserFlagMassMailOptOut))
dlg.Field("locale").Value = prefs.ReadLocale()
dlg.Field("tz").Value = prefs.TimeZoneID
return dlg.Render(ctxt)
@@ -130,15 +130,15 @@ func EditProfile(ctxt ui.AmContext) (string, any, error) {
return dlg.RenderError(ctxt, err.Error())
}
var ci *database.ContactInfo
ci, err = u.ContactInfo()
ci, err = u.ContactInfo(ctxt.Ctx())
if err == nil {
var prefs *database.UserPrefs
emailChange := false
prefs, err = u.Prefs()
prefs, err = u.Prefs(ctxt.Ctx())
if err == nil && !(dlg.Field("pass1").IsEmpty() && dlg.Field("pass2").IsEmpty()) {
p1 := dlg.Field("pass1").Value
if p1 == dlg.Field("pass2").Value {
err = u.ChangePassword(p1, ctxt.RemoteIP())
err = u.ChangePassword(ctxt.Ctx(), p1, ctxt.RemoteIP())
} else {
err = errors.New("passwords do not match")
}
@@ -166,27 +166,27 @@ func EditProfile(ctxt ui.AmContext) (string, any, error) {
nci.Email = dlg.Field("email").ValPtr()
nci.PrivateEmail = dlg.Field("pvt_email").IsChecked()
nci.URL = dlg.Field("url").ValPtr()
emailChange, err = nci.Save()
emailChange, err = nci.Save(ctxt.Ctx())
ci = nci
}
if err == nil {
nprefs := prefs.Clone()
nprefs.WriteLocale(dlg.Field("locale").Value)
nprefs.TimeZoneID = dlg.Field("tz").Value
err = nprefs.Save(u)
err = nprefs.Save(ctxt.Ctx(), u)
}
if err == nil {
var f *util.OptionSet
f, err = u.Flags()
f, err = u.Flags(ctxt.Ctx())
if err == nil {
nf := f.Clone()
nf.Set(database.UserFlagPicturesInPosts, dlg.Field("pic_in_post").IsChecked())
nf.Set(database.UserFlagMassMailOptOut, dlg.Field("no_mass_mail").IsChecked())
err = u.SaveFlags(nf)
err = u.SaveFlags(ctxt.Ctx(), nf)
}
}
if err == nil {
err = u.SetProfileData(dlg.Field("remind").Value, dlg.Field("dob").AsDate(), dlg.Field("descr").ValPtr())
err = u.SetProfileData(ctxt.Ctx(), dlg.Field("remind").Value, dlg.Field("dob").AsDate(), dlg.Field("descr").ValPtr())
}
if err == nil {
if emailChange {
@@ -223,7 +223,7 @@ func ProfilePhotoForm(ctxt ui.AmContext) (string, any, error) {
if u.IsAnon {
return ui.ErrorPage(ctxt, errors.New("you are not logged in"))
}
ci, err := u.ContactInfo()
ci, err := u.ContactInfo(ctxt.Ctx())
if err == nil {
ctxt.VarMap().Set("target", target)
ctxt.VarMap().Set("photo_url", userPhotoURL(ci))
@@ -247,7 +247,7 @@ func ProfilePhoto(ctxt ui.AmContext) (string, any, error) {
if u.IsAnon {
return ui.ErrorPage(ctxt, errors.New("you are not logged in"))
}
ci, err := u.ContactInfo()
ci, err := u.ContactInfo(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -267,11 +267,11 @@ func ProfilePhoto(ctxt ui.AmContext) (string, any, error) {
ui.UserPhotoMaxBytes)
if err == nil {
var img *database.ImageStore
img, err = database.AmStoreImage(database.ImageTypeUserPhoto, u.Uid, mimeType, imageData)
img, err = database.AmStoreImage(ctxt.Ctx(), database.ImageTypeUserPhoto, u.Uid, mimeType, imageData)
if err == nil {
photourl := fmt.Sprintf("/img/store/%d", img.ImgId)
ci.PhotoURL = &photourl
_, err = ci.Save()
_, err = ci.Save(ctxt.Ctx())
if err == nil {
return "redirect", "/profile?tgt=" + url.QueryEscape(target), nil
}
@@ -300,7 +300,7 @@ func ProfilePhoto(ctxt ui.AmContext) (string, any, error) {
defer func() {
if happy {
ampool.Submit(func(context.Context) {
err := database.AmDeleteImage(int32(id))
err := database.AmDeleteImage(ctxt.Ctx(), int32(id))
if err != nil {
log.Errorf("unable to delete image ID %d: %v", id, err)
}
@@ -309,7 +309,7 @@ func ProfilePhoto(ctxt ui.AmContext) (string, any, error) {
}()
}
ci.PhotoURL = nil
_, err := ci.Save()
_, err := ci.Save(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -329,18 +329,18 @@ func ProfilePhoto(ctxt ui.AmContext) (string, any, error) {
*/
func ShowProfile(ctxt ui.AmContext) (string, any, error) {
me := ctxt.CurrentUser()
prefs, err := me.Prefs()
prefs, err := me.Prefs(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
// Gather the info on the current user.
user, err := database.AmGetUserByName(ctxt.URLParam("uname"), nil)
user, err := database.AmGetUserByName(ctxt.Ctx(), ctxt.URLParam("uname"), nil)
if err != nil {
ctxt.SetRC(http.StatusNotFound)
return ui.ErrorPage(ctxt, err)
}
ci, err := user.ContactInfo()
ci, err := user.ContactInfo(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -449,7 +449,7 @@ func QuickEMail(ctxt ui.AmContext) (string, any, error) {
if me.IsAnon {
return ui.ErrorPage(ctxt, errors.New("you are not logged in"))
}
myCI, err := me.ContactInfo()
myCI, err := me.ContactInfo(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}
@@ -457,14 +457,14 @@ func QuickEMail(ctxt ui.AmContext) (string, any, error) {
if err != nil {
return ui.ErrorPage(ctxt, err)
}
user, err := database.AmGetUser(int32(toUid))
user, err := database.AmGetUser(ctxt.Ctx(), int32(toUid))
if err != nil {
return ui.ErrorPage(ctxt, err)
}
if user.IsAnon {
return ui.ErrorPage(ctxt, errors.New("cannot send quick E-mail to anonymous user"))
}
ci, err := user.ContactInfo()
ci, err := user.ContactInfo(ctxt.Ctx())
if err != nil {
return ui.ErrorPage(ctxt, err)
}