[WIP] first draft of conference members functionality
This commit is contained in:
@@ -1056,3 +1056,93 @@ func AmSearchCommunities(ctx context.Context, field int, oper int, term string,
|
||||
}
|
||||
return rc, total, nil
|
||||
}
|
||||
|
||||
/* AmSearchCommunityMembers searches for members of a community matching certain criteria.
|
||||
* Parameters:
|
||||
* ctx - Standard Go context value.
|
||||
* c - The community within which to search.
|
||||
* field - A value indicating which field to search:
|
||||
* SearchUserFieldName - The user name.
|
||||
* SearchUserFieldDescription - The user description.
|
||||
* SearchUserFieldFirstName - The user's first name.
|
||||
* SearchUserFieldLastName - The user's last name.
|
||||
* oper - The operation to perform on the search field:
|
||||
* SearchUserOperPrefix - The specified field has the string "term" as a prefix.
|
||||
* SearchUserOperSubstring - The specified field contains the string "term".
|
||||
* SearchUserOperRegex - The specified field matches the regular expression in "term".
|
||||
* term - The search term, as specified above.
|
||||
* offset - Number of users to skip at beginning of list.
|
||||
* max - Maximum number of users to return.
|
||||
* Returns:
|
||||
* Array of User pointers representing the return elements.
|
||||
* The total number of users matching this query (could be greater than max)
|
||||
* Standard Go error status.
|
||||
*/
|
||||
func AmSearchCommunityMembers(ctx context.Context, c *Community, field int, oper int, term string, offset int, max int) ([]*User, int, error) {
|
||||
var queryPortion strings.Builder
|
||||
switch field {
|
||||
case SearchUserFieldName:
|
||||
queryPortion.WriteString("u.username ")
|
||||
case SearchUserFieldDescription:
|
||||
queryPortion.WriteString("u.description ")
|
||||
case SearchUserFieldFirstName:
|
||||
queryPortion.WriteString("c.given_name ")
|
||||
case SearchUserFieldLastName:
|
||||
queryPortion.WriteString("c.family_name ")
|
||||
default:
|
||||
return nil, -1, errors.New("invalid field selector")
|
||||
}
|
||||
switch oper {
|
||||
case SearchUserOperPrefix:
|
||||
queryPortion.WriteString("LIKE '")
|
||||
queryPortion.WriteString(util.SqlEscape(term, true))
|
||||
queryPortion.WriteString("%'")
|
||||
case SearchUserOperSubstring:
|
||||
queryPortion.WriteString("LIKE '%")
|
||||
queryPortion.WriteString(util.SqlEscape(term, true))
|
||||
queryPortion.WriteString("%'")
|
||||
case SearchUserOperRegex:
|
||||
queryPortion.WriteString("REGEXP '")
|
||||
queryPortion.WriteString(util.SqlEscape(term, false))
|
||||
queryPortion.WriteString("'")
|
||||
default:
|
||||
return nil, -1, errors.New("invalid operator selector")
|
||||
}
|
||||
q := queryPortion.String()
|
||||
row := amdb.QueryRowContext(ctx, `SELECT COUNT(*) FROM users u, contacts c, commmember m WHERE u.contactid = c.contactid AND u.uid = m.uid
|
||||
AND m.commid = ? AND u.is_anon = 0 AND `+q, c.Id)
|
||||
var total int
|
||||
err := row.Scan(&total)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
if total == 0 {
|
||||
return make([]*User, 0), 0, nil
|
||||
}
|
||||
var rs *sql.Rows
|
||||
if offset > 0 {
|
||||
rs, err = amdb.QueryContext(ctx, `SELECT u.uid FROM users u, contacts c, commmember m WHERE u.contactid = c.contactid AND u.uid = m.uid
|
||||
AND m.commid = ? AND u.is_anon = 0 AND `+q+" ORDER BY u.username LIMIT ? OFFSET ?", c.Id, max, offset)
|
||||
} else {
|
||||
rs, err = amdb.QueryContext(ctx, `SELECT u.uid FROM users u, contacts c, commmember m WHERE u.contactid = c.contactid AND u.uid = m.uid
|
||||
AND m.commid = ? AND u.is_anon = 0 AND `+q+" ORDER BY u.username LIMIT ?", c.Id, max)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, total, err
|
||||
}
|
||||
rc := make([]*User, 0, min(max, 10000))
|
||||
for rs.Next() {
|
||||
var uid int32
|
||||
if err = rs.Scan(&uid); err == nil {
|
||||
var u *User
|
||||
u, err = AmGetUser(ctx, uid)
|
||||
if err == nil {
|
||||
rc = append(rc, u)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("AmSearchCommunityMembers scan error: %v", err)
|
||||
}
|
||||
}
|
||||
return rc, total, nil
|
||||
}
|
||||
|
||||
+57
-14
@@ -14,6 +14,8 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -44,6 +46,7 @@ type Conference struct {
|
||||
flags *util.OptionSet
|
||||
}
|
||||
|
||||
// ConferenceSettings represents a user's settings within the conference.
|
||||
type ConferenceSettings struct {
|
||||
ConfId int32 `db:"confid"` // conference ID
|
||||
Uid int32 `db:"uid"` // user ID
|
||||
@@ -53,6 +56,13 @@ type ConferenceSettings struct {
|
||||
newflag bool
|
||||
}
|
||||
|
||||
// ConferenceMember represents the membership entries in a conference.
|
||||
type ConferenceMember struct {
|
||||
ConfId int32 `db:"confid"` // conference ID
|
||||
Uid int32 `db:"uid"` // user ID
|
||||
Level uint16 `db:"granted_lvl"` // level granted within the conference
|
||||
}
|
||||
|
||||
// ConferenceProperties represents a property entry for a conference.
|
||||
type ConferenceProperties struct {
|
||||
ConfId int32 `db:"confid"` // conference ID
|
||||
@@ -162,6 +172,7 @@ func (c *Conference) AddAlias(ctx context.Context, alias string, u *User, ipaddr
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveAlias removes an alias from the conference.
|
||||
func (c *Conference) RemoveAlias(ctx context.Context, alias string, u *User, ipaddr string) error {
|
||||
row := amdb.QueryRowContext(ctx, "SELECT COUNT(*) FROM confalias WHERE confid = ?", c.ConfId)
|
||||
aliasCount := 0
|
||||
@@ -200,24 +211,30 @@ func (c *Conference) RemoveAlias(ctx context.Context, alias string, u *User, ipa
|
||||
|
||||
// Hosts returns the list of users that host this conference.
|
||||
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())
|
||||
var uids []int32
|
||||
err := amdb.SelectContext(ctx, &uids, "SELECT uid FROM confmember WHERE confid = ? AND granted_lvl = ?", c.ConfId, AmRole("Conference.Host").Level())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rc := make([]*User, 0, 5)
|
||||
for rs.Next() {
|
||||
var uid int32
|
||||
if err = rs.Scan(&uid); err == nil {
|
||||
u, err := AmGetUser(ctx, uid)
|
||||
if err == nil {
|
||||
rc = append(rc, u)
|
||||
}
|
||||
rc := make([]*User, 0, len(uids))
|
||||
for _, uid := range uids {
|
||||
u, err := AmGetUser(ctx, uid)
|
||||
if err == nil {
|
||||
rc = append(rc, u)
|
||||
}
|
||||
}
|
||||
slices.SortFunc(rc, func(a, b *User) int {
|
||||
return strings.Compare(strings.ToLower(a.Username), strings.ToLower(b.Username))
|
||||
})
|
||||
return rc, nil
|
||||
}
|
||||
|
||||
// Hosts returns the list of users that host this conference, quietly.
|
||||
func (c *Conference) HostsQ(ctx context.Context) []*User {
|
||||
rc, _ := c.Hosts(ctx)
|
||||
return rc
|
||||
}
|
||||
|
||||
// InCommunity returns true if the specified conference is in the community.
|
||||
func (c *Conference) InCommunity(ctx context.Context, comm *Community) (bool, error) {
|
||||
row := amdb.QueryRowContext(ctx, "SELECT commid FROM commtoconf WHERE commid = ? AND confid = ?", comm.Id, c.ConfId)
|
||||
@@ -274,10 +291,11 @@ func (c *Conference) ContainedBy(ctx context.Context) ([]*Community, error) {
|
||||
return rc, nil
|
||||
}
|
||||
|
||||
// Hosts returns the list of users that host this conference, quietly.
|
||||
func (c *Conference) HostsQ(ctx context.Context) []*User {
|
||||
rc, _ := c.Hosts(ctx)
|
||||
return rc
|
||||
// Members returns all the members of this conference, with their granted user levels.
|
||||
func (c *Conference) Members(ctx context.Context) ([]ConferenceMember, error) {
|
||||
var rc []ConferenceMember
|
||||
err := amdb.SelectContext(ctx, &rc, "SELECT * FROM confmember WHERE confid = ?", c.ConfId)
|
||||
return rc, err
|
||||
}
|
||||
|
||||
// Membership returns a membership flag and granted level for the user in this conference.
|
||||
@@ -294,6 +312,31 @@ func (c *Conference) Membership(ctx context.Context, u *User) (bool, uint16, err
|
||||
return false, 0, err
|
||||
}
|
||||
|
||||
// SetMembership sets the membership level for the given user in this conference.
|
||||
func (c *Conference) SetMembership(ctx context.Context, u *User, level uint16, by *User, ipaddr string) error {
|
||||
if level == 0 {
|
||||
_, err := amdb.ExecContext(ctx, "DELETE FROM confmember WHERE confid = ? AND uid = ?", c.ConfId, u.Uid)
|
||||
return err
|
||||
}
|
||||
row := amdb.QueryRowContext(ctx, "SELECT granted_lvl FROM confmember WHERE confid = ? AND uid = ?", c.ConfId, u.Uid)
|
||||
var oldLevel uint16
|
||||
err := row.Scan(&oldLevel)
|
||||
switch err {
|
||||
case nil:
|
||||
if oldLevel == level {
|
||||
return nil
|
||||
}
|
||||
_, err = amdb.ExecContext(ctx, "UPDATE confmember SET granted_lvl = ? WHERE confid = ? AND uid = ?", level, c.ConfId, u.Uid)
|
||||
case sql.ErrNoRows:
|
||||
_, err = amdb.ExecContext(ctx, "INSERT INTO confmember (confid, uid, granted_lvl) VALUES (?, ?, ?)", c.ConfId, u.Uid, level)
|
||||
}
|
||||
if err != nil {
|
||||
ar := AmNewAudit(AuditConferenceMembership, by.Uid, ipaddr, fmt.Sprintf("conf=%d", c.ConfId), fmt.Sprintf("uid=%d", u.Uid), fmt.Sprintf("level=%d", level))
|
||||
AmStoreAudit(ar)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
/* TestPermission is shorthand that tests if a user has a permission with respect to the conference.
|
||||
* Parameters:
|
||||
* user - The user to be checked.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# Amsterdam Web Communities System
|
||||
# Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved
|
||||
# Copyright (c) 2025-2026 Erbosoft Metaverse Design Solutions, All Rights Reserved
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@@ -15,7 +15,7 @@ scopes:
|
||||
index: 6
|
||||
roles:
|
||||
- name: "NotInList"
|
||||
display: "Not In List"
|
||||
display: "(not in list)"
|
||||
scope: Global
|
||||
value: "0"
|
||||
- name: "UnrestrictedUser"
|
||||
|
||||
Reference in New Issue
Block a user