groundwork for services by introducing service vtables, also SetMembership as groundwork for join/unjoin
This commit is contained in:
@@ -7,6 +7,7 @@ After the point where it reaches feature parity with Venice circa 2006.
|
|||||||
* A better way to set up the database than `setup/database.sql`. Bring the table setup into the application somehow.
|
* A better way to set up the database than `setup/database.sql`. Bring the table setup into the application somehow.
|
||||||
The [migrate](https://github.com/golang-migrate/migrate) library might be of use here.
|
The [migrate](https://github.com/golang-migrate/migrate) library might be of use here.
|
||||||
* Either implement the Calendar and Chat, or take those menu entries out.
|
* Either implement the Calendar and Chat, or take those menu entries out.
|
||||||
|
* Should those be community "services" instead?
|
||||||
* For Chat, if it's implemented, it should use XMPP.
|
* For Chat, if it's implemented, it should use XMPP.
|
||||||
* Implement proper help and online documentation.
|
* Implement proper help and online documentation.
|
||||||
|
|
||||||
|
|||||||
+58
-6
@@ -231,6 +231,58 @@ func (c *Community) MemberCount(hidden bool) (int, error) {
|
|||||||
return -1, errors.New("internal error reading member count")
|
return -1, errors.New("internal error reading member count")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SetMembership sets a user's membership status within the community.
|
||||||
|
* Parameters:
|
||||||
|
* 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.
|
||||||
|
* Returns:
|
||||||
|
* Standard Go error status.
|
||||||
|
*/
|
||||||
|
func (c *Community) SetMembership(u *User, level uint16, locked bool) error {
|
||||||
|
if level == 0 {
|
||||||
|
_, err := amdb.Exec("DELETE FROM commmember WHERE commid = ? AND uid = ?", c.Id, u.Uid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stuffMembership(c.Id, u.Uid, false, false, 0)
|
||||||
|
err = AmOnUserLeaveCommunityServices(c, u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rs, err := amdb.Query("SELECT granted_lvl, locked FROM commmember WHERE commid = ? AND uid = ?", c.Id, u.Uid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if rs.Next() {
|
||||||
|
var oldLevel uint16
|
||||||
|
var lockStatus bool
|
||||||
|
rs.Scan(&oldLevel, &lockStatus)
|
||||||
|
if level != oldLevel || lockStatus != locked {
|
||||||
|
_, err := amdb.Exec("UPDATE commmember SET granted_lvl = ?, locked = ? WHERE commid = ? AND uid = ?",
|
||||||
|
level, locked, c.Id, u.Uid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stuffMembership(c.Id, u.Uid, true, locked, level)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, err := amdb.Exec("INSERT INTO commmember (comm_id, 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(c, u)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
/* TestPermission is shorthand that tests if a user has a permission with respect to the community.
|
/* TestPermission is shorthand that tests if a user has a permission with respect to the community.
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* user - The user to be checked.
|
* user - The user to be checked.
|
||||||
@@ -670,12 +722,6 @@ func AmCreateCommunity(name string, alias string, hostUid int32, language *strin
|
|||||||
return nil, errors.New("unable to find newly-generated community")
|
return nil, errors.New("unable to find newly-generated community")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Establish the community services.
|
|
||||||
err = AmEstablishCommunityServices(comm.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the new host has host privileges in the community. The host's membership is "locked" so they
|
// 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.
|
// can't unjoin and leave the community hostless.
|
||||||
_, err = amdb.Exec("INSERT INTO commmember (commid, uid, granted_lvl, locked) VALUES (?, ?, ?, 1)", comm.Id, hostUid,
|
_, err = amdb.Exec("INSERT INTO commmember (commid, uid, granted_lvl, locked) VALUES (?, ?, ?, 1)", comm.Id, hostUid,
|
||||||
@@ -685,6 +731,12 @@ func AmCreateCommunity(name string, alias string, hostUid int32, language *strin
|
|||||||
}
|
}
|
||||||
stuffMembership(comm.Id, hostUid, true, true, AmDefaultRole("Community.Creator").Level())
|
stuffMembership(comm.Id, hostUid, true, true, AmDefaultRole("Community.Creator").Level())
|
||||||
|
|
||||||
|
// Establish the community services.
|
||||||
|
err = AmEstablishCommunityServices(comm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// operation was a success - add an audit record
|
// operation was a success - add an audit record
|
||||||
ar = AmNewAudit(AuditCommunityCreate, hostUid, remoteIP, fmt.Sprintf("id=%d", comm.Id),
|
ar = AmNewAudit(AuditCommunityCreate, hostUid, remoteIP, fmt.Sprintf("id=%d", comm.Id),
|
||||||
fmt.Sprintf("name=%s", comm.Name), fmt.Sprintf("alias=%s", comm.Alias))
|
fmt.Sprintf("name=%s", comm.Name), fmt.Sprintf("alias=%s", comm.Alias))
|
||||||
|
|||||||
+113
-5
@@ -18,6 +18,33 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServiceVTable is a serioes of functions called for services on specific events.
|
||||||
|
type ServiceVTable interface {
|
||||||
|
OnNewCommunity(*Community) error
|
||||||
|
OnDeleteCommunity(int32) error
|
||||||
|
OnUserJoinCommunity(*Community, *User) error
|
||||||
|
OnUserLeaveCommunity(*Community, *User) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// emptyServiceVTable is a default ServiceVTable that does nothing.
|
||||||
|
type emptyServiceVTable struct{}
|
||||||
|
|
||||||
|
func (*emptyServiceVTable) OnNewCommunity(*Community) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*emptyServiceVTable) OnDeleteCommunity(int32) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*emptyServiceVTable) OnUserJoinCommunity(*Community, *User) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*emptyServiceVTable) OnUserLeaveCommunity(*Community, *User) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceDef holds the definition for an individual service.
|
// ServiceDef holds the definition for an individual service.
|
||||||
type ServiceDef struct {
|
type ServiceDef struct {
|
||||||
Id string `yaml:"id"`
|
Id string `yaml:"id"`
|
||||||
@@ -29,6 +56,7 @@ type ServiceDef struct {
|
|||||||
LinkSequence int `yaml:"linkSequence"`
|
LinkSequence int `yaml:"linkSequence"`
|
||||||
Link string `yaml:"link"`
|
Link string `yaml:"link"`
|
||||||
Title string `yaml:"title"`
|
Title string `yaml:"title"`
|
||||||
|
vtable ServiceVTable
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceDomain holds each individual configured service domain.
|
// ServiceDomain holds each individual configured service domain.
|
||||||
@@ -80,6 +108,13 @@ func init() {
|
|||||||
serviceRoot.Domains[i].seqOrder = sqo
|
serviceRoot.Domains[i].seqOrder = sqo
|
||||||
serviceRoot.byName[dom.DomainName] = &(serviceRoot.Domains[i])
|
serviceRoot.byName[dom.DomainName] = &(serviceRoot.Domains[i])
|
||||||
}
|
}
|
||||||
|
dom := serviceRoot.byName["community"]
|
||||||
|
empty := emptyServiceVTable{}
|
||||||
|
dom.byId["Profile"].vtable = &empty
|
||||||
|
dom.byId["Admin"].vtable = &empty
|
||||||
|
dom.byId["SysAdmin"].vtable = &empty
|
||||||
|
dom.byId["Conference"].vtable = &empty // TODO
|
||||||
|
dom.byId["Members"].vtable = &empty
|
||||||
servicesCache, err = lru.New2Q(50)
|
servicesCache, err = lru.New2Q(50)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -115,18 +150,19 @@ func AmGetCommunityServices(cid int32) ([]*ServiceDef, error) {
|
|||||||
return rc.([]*ServiceDef), nil
|
return rc.([]*ServiceDef), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AmEstablishCommunityServices extablishes the service (feature) records for a new community.
|
/* AmEstablishCommunityServices establishes the service (feature) records for a new community,
|
||||||
|
* and allows the services to establish themselves.
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* cid - ID of the new community.
|
* c - The new community.
|
||||||
* Returns:
|
* Returns:
|
||||||
* Standard Go error status.
|
* Standard Go error status.
|
||||||
*/
|
*/
|
||||||
func AmEstablishCommunityServices(cid int32) error {
|
func AmEstablishCommunityServices(c *Community) error {
|
||||||
dom := serviceRoot.byName["community"]
|
dom := serviceRoot.byName["community"]
|
||||||
a := make([]*ServiceDef, 0, len(dom.Services))
|
a := make([]*ServiceDef, 0, len(dom.Services))
|
||||||
for i, svc := range dom.Services {
|
for i, svc := range dom.Services {
|
||||||
if svc.Default {
|
if svc.Default {
|
||||||
_, err := amdb.Exec("INSERT INTO commftrs (commid, ftr_code) VALUES (?, ?)", cid, svc.Index)
|
_, err := amdb.Exec("INSERT INTO commftrs (commid, ftr_code) VALUES (?, ?)", c.Id, svc.Index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -134,7 +170,79 @@ func AmEstablishCommunityServices(cid int32) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
servicesCacheMutex.Lock()
|
servicesCacheMutex.Lock()
|
||||||
servicesCache.Add(cid, a)
|
servicesCache.Add(c.Id, a)
|
||||||
servicesCacheMutex.Unlock()
|
servicesCacheMutex.Unlock()
|
||||||
|
for _, svc := range a {
|
||||||
|
err := svc.vtable.OnNewCommunity(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* AmDeleteCommunityServices cleans up all services associated with a community that has gone away,
|
||||||
|
* and then cleans up the service records.
|
||||||
|
* Parameters:
|
||||||
|
* cid - The ID of the departing community.
|
||||||
|
* Returns:
|
||||||
|
* Standard Go error status.
|
||||||
|
*/
|
||||||
|
func AmDeleteCommunityServices(cid int32) error {
|
||||||
|
arr, err := AmGetCommunityServices(cid)
|
||||||
|
if err == nil {
|
||||||
|
for _, svc := range arr {
|
||||||
|
err = svc.vtable.OnDeleteCommunity(cid)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
_, err = amdb.Exec("DELETE FROM commftrs WHERE commid = ?", cid)
|
||||||
|
servicesCacheMutex.Lock()
|
||||||
|
servicesCache.Remove(cid)
|
||||||
|
servicesCacheMutex.Unlock()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AmOnUserJoinCommunityServices gives services a chance to update themselves when a user joins a community.
|
||||||
|
* Parameters:
|
||||||
|
* c - The community that is being joined.
|
||||||
|
* u - The user leaving that community.
|
||||||
|
* Returns:
|
||||||
|
* Standard Go error status.
|
||||||
|
*/
|
||||||
|
func AmOnUserJoinCommunityServices(c *Community, u *User) error {
|
||||||
|
arr, err := AmGetCommunityServices(c.Id)
|
||||||
|
if err == nil {
|
||||||
|
for _, svc := range arr {
|
||||||
|
err = svc.vtable.OnUserJoinCommunity(c, u)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AmOnUserLeaveCommunityServices gives services a chance to update themselves when a user leaves a community.
|
||||||
|
* Parameters:
|
||||||
|
* c - The community that is being left.
|
||||||
|
* u - The user leaving that community.
|
||||||
|
* Returns:
|
||||||
|
* Standard Go error status.
|
||||||
|
*/
|
||||||
|
func AmOnUserLeaveCommunityServices(c *Community, u *User) error {
|
||||||
|
arr, err := AmGetCommunityServices(c.Id)
|
||||||
|
if err == nil {
|
||||||
|
for _, svc := range arr {
|
||||||
|
err = svc.vtable.OnUserLeaveCommunity(c, u)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user