Refactored the GroupStore to make it more thread-safe.
Refactored the store initialisers to better propogate errors upwards. Moved the user initialisation logic to a method on *User. Added the Reload and GlobalCount methods to the GroupStore. Added the CacheSet method to the GroupCache. Renamed plugin_socialgroups to plugin_guilds 3/3
This commit is contained in:
parent
3c13f4da7f
commit
d0363f3eb1
30
database.go
30
database.go
|
@ -21,7 +21,10 @@ func initDatabase() (err error) {
|
|||
}
|
||||
|
||||
log.Print("Loading the usergroups.")
|
||||
gstore = NewMemoryGroupStore()
|
||||
gstore, err = NewMemoryGroupStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = gstore.LoadGroups()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -30,15 +33,30 @@ func initDatabase() (err error) {
|
|||
// We have to put this here, otherwise LoadForums() won't be able to get the last poster data when building it's forums
|
||||
log.Print("Initialising the user and topic stores")
|
||||
if config.CacheTopicUser == CACHE_STATIC {
|
||||
users = NewMemoryUserStore(config.UserCacheCapacity)
|
||||
topics = NewMemoryTopicStore(config.TopicCacheCapacity)
|
||||
users, err = NewMemoryUserStore(config.UserCacheCapacity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
topics, err = NewMemoryTopicStore(config.TopicCacheCapacity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
users = NewSQLUserStore()
|
||||
topics = NewSQLTopicStore()
|
||||
users, err = NewSQLUserStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
topics, err = NewSQLTopicStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Print("Loading the forums.")
|
||||
fstore = NewMemoryForumStore()
|
||||
fstore, err = NewMemoryForumStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = fstore.LoadForums()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
21
errors.go
21
errors.go
|
@ -37,9 +37,8 @@ func (err *RouteErrorImpl) Type() string {
|
|||
// System errors may contain sensitive information we don't want the user to see
|
||||
if err.system {
|
||||
return "system"
|
||||
} else {
|
||||
return "user"
|
||||
}
|
||||
return "user"
|
||||
}
|
||||
|
||||
func (err *RouteErrorImpl) Error() string {
|
||||
|
@ -101,9 +100,8 @@ func InternalError(err error, w http.ResponseWriter, r *http.Request) RouteError
|
|||
func InternalErrorJSQ(err error, w http.ResponseWriter, r *http.Request, isJs bool) RouteError {
|
||||
if !isJs {
|
||||
return InternalError(err, w, r)
|
||||
} else {
|
||||
return InternalErrorJS(err, w, r)
|
||||
}
|
||||
return InternalErrorJS(err, w, r)
|
||||
}
|
||||
|
||||
// InternalErrorJS is the JSON version of InternalError on routes we know will only be requested via JSON. E.g. An API.
|
||||
|
@ -160,9 +158,8 @@ func PreErrorJS(errmsg string, w http.ResponseWriter, r *http.Request) RouteErro
|
|||
func PreErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, isJs bool) RouteError {
|
||||
if !isJs {
|
||||
return PreError(errmsg, w, r)
|
||||
} else {
|
||||
return PreErrorJS(errmsg, w, r)
|
||||
}
|
||||
return PreErrorJS(errmsg, w, r)
|
||||
}
|
||||
|
||||
// LocalError is an error shown to the end-user when something goes wrong and it's not the software's fault
|
||||
|
@ -184,9 +181,8 @@ func LocalError(errmsg string, w http.ResponseWriter, r *http.Request, user User
|
|||
func LocalErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, user User, isJs bool) RouteError {
|
||||
if !isJs {
|
||||
return LocalError(errmsg, w, r, user)
|
||||
} else {
|
||||
return LocalErrorJS(errmsg, w, r)
|
||||
}
|
||||
return LocalErrorJS(errmsg, w, r)
|
||||
}
|
||||
|
||||
func LocalErrorJS(errmsg string, w http.ResponseWriter, r *http.Request) RouteError {
|
||||
|
@ -216,9 +212,8 @@ func NoPermissions(w http.ResponseWriter, r *http.Request, user User) RouteError
|
|||
func NoPermissionsJSQ(w http.ResponseWriter, r *http.Request, user User, isJs bool) RouteError {
|
||||
if !isJs {
|
||||
return NoPermissions(w, r, user)
|
||||
} else {
|
||||
return NoPermissionsJS(w, r, user)
|
||||
}
|
||||
return NoPermissionsJS(w, r, user)
|
||||
}
|
||||
|
||||
func NoPermissionsJS(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
|
@ -248,9 +243,8 @@ func Banned(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
|||
func BannedJSQ(w http.ResponseWriter, r *http.Request, user User, isJs bool) RouteError {
|
||||
if !isJs {
|
||||
return Banned(w, r, user)
|
||||
} else {
|
||||
return BannedJS(w, r, user)
|
||||
}
|
||||
return BannedJS(w, r, user)
|
||||
}
|
||||
|
||||
func BannedJS(w http.ResponseWriter, r *http.Request, user User) RouteError {
|
||||
|
@ -331,9 +325,8 @@ func CustomError(errmsg string, errcode int, errtitle string, w http.ResponseWri
|
|||
func CustomErrorJSQ(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, user User, isJs bool) RouteError {
|
||||
if !isJs {
|
||||
return CustomError(errmsg, errcode, errtitle, w, r, user)
|
||||
} else {
|
||||
return CustomErrorJS(errmsg, errcode, errtitle, w, r, user)
|
||||
}
|
||||
return CustomErrorJS(errmsg, errcode, errtitle, w, r, user)
|
||||
}
|
||||
|
||||
// CustomErrorJS is the pure JSON version of CustomError
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"UName":"guilds",
|
||||
"Name":"Guilds",
|
||||
"Author":"Azareal",
|
||||
"URL":"https://github.com/Azareal/Gosora",
|
||||
"Skip":true
|
||||
}
|
|
@ -66,30 +66,30 @@ type MemoryForumStore struct {
|
|||
}
|
||||
|
||||
// NewMemoryForumStore gives you a new instance of MemoryForumStore
|
||||
func NewMemoryForumStore() *MemoryForumStore {
|
||||
func NewMemoryForumStore() (*MemoryForumStore, error) {
|
||||
getStmt, err := qgen.Builder.SimpleSelect("forums", "name, desc, active, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID", "fid = ?", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
getAllStmt, err := qgen.Builder.SimpleSelect("forums", "fid, name, desc, active, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID", "", "fid ASC", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
// TODO: Do a proper delete
|
||||
deleteStmt, err := qgen.Builder.SimpleUpdate("forums", "name= '', active = 0", "fid = ?")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
forumCountStmt, err := qgen.Builder.SimpleCount("forums", "name != ''", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
return &MemoryForumStore{
|
||||
get: getStmt,
|
||||
getAll: getAllStmt,
|
||||
delete: deleteStmt,
|
||||
getForumCount: forumCountStmt,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TODO: Add support for subforums
|
||||
|
|
27
group.go
27
group.go
|
@ -11,7 +11,7 @@ type GroupAdmin struct {
|
|||
CanDelete bool
|
||||
}
|
||||
|
||||
// ! Fix the data races
|
||||
// ! Fix the data races in the fperms
|
||||
type Group struct {
|
||||
ID int
|
||||
Name string
|
||||
|
@ -27,26 +27,31 @@ type Group struct {
|
|||
CanSee []int // The IDs of the forums this group can see
|
||||
}
|
||||
|
||||
// TODO: Reload the group from the database rather than modifying it via it's pointer
|
||||
func (group *Group) ChangeRank(isAdmin bool, isMod bool, isBanned bool) (err error) {
|
||||
_, err = updateGroupRankStmt.Exec(isAdmin, isMod, isBanned, group.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
group.IsAdmin = isAdmin
|
||||
group.IsMod = isMod
|
||||
if isAdmin || isMod {
|
||||
group.IsBanned = false
|
||||
} else {
|
||||
group.IsBanned = isBanned
|
||||
}
|
||||
|
||||
gstore.Reload(group.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ! Ahem, don't listen to the comment below. It's not concurrency safe right now.
|
||||
// Copy gives you a non-pointer concurrency safe copy of the group
|
||||
func (group *Group) Copy() Group {
|
||||
return *group
|
||||
}
|
||||
|
||||
// TODO: Replace this sorting mechanism with something a lot more efficient
|
||||
// ? - Use sort.Slice instead?
|
||||
type SortGroup []*Group
|
||||
|
||||
func (sg SortGroup) Len() int {
|
||||
return len(sg)
|
||||
}
|
||||
func (sg SortGroup) Swap(i, j int) {
|
||||
sg[i], sg[j] = sg[j], sg[i]
|
||||
}
|
||||
func (sg SortGroup) Less(i, j int) bool {
|
||||
return sg[i].ID < sg[j].ID
|
||||
}
|
||||
|
|
222
group_store.go
222
group_store.go
|
@ -2,16 +2,16 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"./query_gen/lib"
|
||||
)
|
||||
|
||||
var groupCreateMutex sync.Mutex
|
||||
var groupUpdateMutex sync.Mutex
|
||||
var gstore GroupStore
|
||||
|
||||
// ? - We could fallback onto the database when an item can't be found in the cache?
|
||||
|
@ -24,23 +24,40 @@ type GroupStore interface {
|
|||
Create(name string, tag string, isAdmin bool, isMod bool, isBanned bool) (int, error)
|
||||
GetAll() ([]*Group, error)
|
||||
GetRange(lower int, higher int) ([]*Group, error)
|
||||
Reload(id int) error // ? - Should we move this to GroupCache? It might require us to do some unnecessary casting though
|
||||
GlobalCount() int
|
||||
}
|
||||
|
||||
type GroupCache interface {
|
||||
CacheSet(group *Group) error
|
||||
Length() int
|
||||
}
|
||||
|
||||
type MemoryGroupStore struct {
|
||||
groups []*Group // TODO: Use a sync.Map instead of a slice
|
||||
groupCapCount int
|
||||
groups map[int]*Group // TODO: Use a sync.Map instead of a map?
|
||||
groupCount int
|
||||
get *sql.Stmt
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func NewMemoryGroupStore() *MemoryGroupStore {
|
||||
return &MemoryGroupStore{}
|
||||
func NewMemoryGroupStore() (*MemoryGroupStore, error) {
|
||||
getGroupStmt, err := qgen.Builder.SimpleSelect("users_groups", "name, permissions, plugin_perms, is_mod, is_admin, is_banned, tag", "gid = ?", "", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MemoryGroupStore{
|
||||
groups: make(map[int]*Group),
|
||||
groupCount: 0,
|
||||
get: getGroupStmt,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (mgs *MemoryGroupStore) LoadGroups() error {
|
||||
mgs.groups = []*Group{&Group{ID: 0, Name: "Unknown"}}
|
||||
mgs.Lock()
|
||||
defer mgs.Unlock()
|
||||
mgs.groups[0] = &Group{ID: 0, Name: "Unknown"}
|
||||
|
||||
rows, err := getGroupsStmt.Query()
|
||||
if err != nil {
|
||||
|
@ -50,13 +67,95 @@ func (mgs *MemoryGroupStore) LoadGroups() error {
|
|||
|
||||
i := 1
|
||||
for ; rows.Next(); i++ {
|
||||
group := Group{ID: 0}
|
||||
group := &Group{ID: 0}
|
||||
err := rows.Scan(&group.ID, &group.Name, &group.PermissionsText, &group.PluginPermsText, &group.IsMod, &group.IsAdmin, &group.IsBanned, &group.Tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(group.PermissionsText, &group.Perms)
|
||||
err = mgs.initGroup(group)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mgs.groups[group.ID] = group
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mgs.groupCount = i
|
||||
|
||||
if dev.DebugMode {
|
||||
log.Print("Binding the Not Loggedin Group")
|
||||
}
|
||||
GuestPerms = mgs.dirtyGetUnsafe(6).Perms
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Hit the database when the item isn't in memory
|
||||
func (mgs *MemoryGroupStore) dirtyGetUnsafe(gid int) *Group {
|
||||
group, ok := mgs.groups[gid]
|
||||
if !ok {
|
||||
return &blankGroup
|
||||
}
|
||||
return group
|
||||
}
|
||||
|
||||
// TODO: Hit the database when the item isn't in memory
|
||||
func (mgs *MemoryGroupStore) DirtyGet(gid int) *Group {
|
||||
mgs.RLock()
|
||||
group, ok := mgs.groups[gid]
|
||||
mgs.RUnlock()
|
||||
if !ok {
|
||||
return &blankGroup
|
||||
}
|
||||
return group
|
||||
}
|
||||
|
||||
// TODO: Hit the database when the item isn't in memory
|
||||
func (mgs *MemoryGroupStore) Get(gid int) (*Group, error) {
|
||||
mgs.RLock()
|
||||
group, ok := mgs.groups[gid]
|
||||
mgs.RUnlock()
|
||||
if !ok {
|
||||
return nil, ErrNoRows
|
||||
}
|
||||
return group, nil
|
||||
}
|
||||
|
||||
// TODO: Hit the database when the item isn't in memory
|
||||
func (mgs *MemoryGroupStore) GetCopy(gid int) (Group, error) {
|
||||
mgs.RLock()
|
||||
group, ok := mgs.groups[gid]
|
||||
mgs.RUnlock()
|
||||
if !ok {
|
||||
return blankGroup, ErrNoRows
|
||||
}
|
||||
return *group, nil
|
||||
}
|
||||
|
||||
func (mgs *MemoryGroupStore) Reload(id int) error {
|
||||
var group = &Group{ID: id}
|
||||
err := mgs.get.QueryRow(id).Scan(&group.Name, &group.PermissionsText, &group.PluginPermsText, &group.IsMod, &group.IsAdmin, &group.IsBanned, &group.Tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = mgs.initGroup(group)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
mgs.CacheSet(group)
|
||||
|
||||
err = rebuildGroupPermissions(id)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgs *MemoryGroupStore) initGroup(group *Group) error {
|
||||
err := json.Unmarshal(group.PermissionsText, &group.Perms)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -78,51 +177,26 @@ func (mgs *MemoryGroupStore) LoadGroups() error {
|
|||
if group.IsAdmin || group.IsMod {
|
||||
group.IsBanned = false
|
||||
}
|
||||
mgs.groups = append(mgs.groups, &group)
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mgs.groupCapCount = i
|
||||
|
||||
if dev.DebugMode {
|
||||
log.Print("Binding the Not Loggedin Group")
|
||||
}
|
||||
GuestPerms = mgs.groups[6].Perms
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mgs *MemoryGroupStore) DirtyGet(gid int) *Group {
|
||||
if !mgs.Exists(gid) {
|
||||
return &blankGroup
|
||||
}
|
||||
return mgs.groups[gid]
|
||||
}
|
||||
|
||||
func (mgs *MemoryGroupStore) Get(gid int) (*Group, error) {
|
||||
if !mgs.Exists(gid) {
|
||||
return nil, ErrNoRows
|
||||
}
|
||||
return mgs.groups[gid], nil
|
||||
}
|
||||
|
||||
func (mgs *MemoryGroupStore) GetCopy(gid int) (Group, error) {
|
||||
if !mgs.Exists(gid) {
|
||||
return blankGroup, ErrNoRows
|
||||
}
|
||||
return *mgs.groups[gid], nil
|
||||
func (mgs *MemoryGroupStore) CacheSet(group *Group) error {
|
||||
mgs.Lock()
|
||||
mgs.groups[group.ID] = group
|
||||
mgs.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Hit the database when the item isn't in memory
|
||||
func (mgs *MemoryGroupStore) Exists(gid int) bool {
|
||||
return (gid <= mgs.groupCapCount) && (gid >= 0) && mgs.groups[gid].Name != ""
|
||||
mgs.RLock()
|
||||
group, ok := mgs.groups[gid]
|
||||
mgs.RUnlock()
|
||||
return ok && group.Name != ""
|
||||
}
|
||||
|
||||
// ? Allow two groups with the same name?
|
||||
func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod bool, isBanned bool) (int, error) {
|
||||
groupCreateMutex.Lock()
|
||||
defer groupCreateMutex.Unlock()
|
||||
|
||||
func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod bool, isBanned bool) (gid int, err error) {
|
||||
var permstr = "{}"
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
|
@ -143,7 +217,7 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var gid = int(gid64)
|
||||
gid = int(gid64)
|
||||
|
||||
var perms = BlankPerms
|
||||
var blankForums []ForumPerms
|
||||
|
@ -199,8 +273,10 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
|
|||
isBanned = false
|
||||
}
|
||||
|
||||
mgs.groups = append(mgs.groups, &Group{gid, name, isMod, isAdmin, isBanned, tag, perms, []byte(permstr), pluginPerms, pluginPermsBytes, blankForums, blankIntList})
|
||||
mgs.groupCapCount++
|
||||
mgs.Lock()
|
||||
mgs.groups[gid] = &Group{gid, name, isMod, isAdmin, isBanned, tag, perms, []byte(permstr), pluginPerms, pluginPermsBytes, blankForums, blankIntList}
|
||||
mgs.groupCount++
|
||||
mgs.Unlock()
|
||||
|
||||
for _, forum := range fdata {
|
||||
err = rebuildForumPermissions(forum.ID)
|
||||
|
@ -212,34 +288,62 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
|
|||
return gid, nil
|
||||
}
|
||||
|
||||
// ! NOT CONCURRENT
|
||||
func (mgs *MemoryGroupStore) GetAll() ([]*Group, error) {
|
||||
func (mgs *MemoryGroupStore) GetAll() (results []*Group, err error) {
|
||||
var i int
|
||||
mgs.RLock()
|
||||
results = make([]*Group, len(mgs.groups))
|
||||
for _, group := range mgs.groups {
|
||||
results[i] = group
|
||||
i++
|
||||
}
|
||||
mgs.RUnlock()
|
||||
sort.Sort(SortGroup(results))
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (mgs *MemoryGroupStore) GetAllMap() (map[int]*Group, error) {
|
||||
mgs.RLock()
|
||||
defer mgs.RUnlock()
|
||||
return mgs.groups, nil
|
||||
}
|
||||
|
||||
// ? - It's currently faster to use GetAll(), but we'll be dropping the guarantee that the slices are ordered soon
|
||||
// ? - Set the lower and higher numbers to 0 to remove the bounds
|
||||
// ? - Currently uses slicing for efficiency, so it might behave a little weirdly
|
||||
// TODO: Might be a little slow right now, maybe we can cache the groups in a slice or break the map up into chunks
|
||||
func (mgs *MemoryGroupStore) GetRange(lower int, higher int) (groups []*Group, err error) {
|
||||
if lower == 0 && higher == 0 {
|
||||
return mgs.GetAll()
|
||||
} else if lower == 0 {
|
||||
}
|
||||
|
||||
if lower == 0 {
|
||||
if higher < 0 {
|
||||
return nil, errors.New("higher may not be lower than 0")
|
||||
}
|
||||
if higher > len(mgs.groups) {
|
||||
higher = len(mgs.groups)
|
||||
}
|
||||
groups = mgs.groups[:higher]
|
||||
} else if higher == 0 {
|
||||
if lower < 0 {
|
||||
return nil, errors.New("lower may not be lower than 0")
|
||||
}
|
||||
groups = mgs.groups[lower:]
|
||||
}
|
||||
|
||||
mgs.RLock()
|
||||
for gid, group := range mgs.groups {
|
||||
if gid >= lower && (gid <= higher || higher == 0) {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
}
|
||||
mgs.RUnlock()
|
||||
sort.Sort(SortGroup(groups))
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func (mgs *MemoryGroupStore) Length() int {
|
||||
return len(mgs.groups)
|
||||
mgs.RLock()
|
||||
defer mgs.RUnlock()
|
||||
return mgs.groupCount
|
||||
}
|
||||
|
||||
func (mgs *MemoryGroupStore) GlobalCount() int {
|
||||
mgs.RLock()
|
||||
defer mgs.RUnlock()
|
||||
return mgs.groupCount
|
||||
}
|
||||
|
|
14
misc_test.go
14
misc_test.go
|
@ -37,10 +37,13 @@ func TestUserStore(t *testing.T) {
|
|||
initPlugins()
|
||||
}
|
||||
|
||||
users = NewMemoryUserStore(config.UserCacheCapacity)
|
||||
var err error
|
||||
users, err = NewMemoryUserStore(config.UserCacheCapacity)
|
||||
expectNilErr(t, err)
|
||||
users.(UserCache).Flush()
|
||||
userStoreTest(t, 2)
|
||||
users = NewSQLUserStore()
|
||||
users, err = NewSQLUserStore()
|
||||
expectNilErr(t, err)
|
||||
userStoreTest(t, 3)
|
||||
}
|
||||
func userStoreTest(t *testing.T, newUserID int) {
|
||||
|
@ -453,9 +456,12 @@ func TestTopicStore(t *testing.T) {
|
|||
initPlugins()
|
||||
}
|
||||
|
||||
topics = NewMemoryTopicStore(config.TopicCacheCapacity)
|
||||
var err error
|
||||
topics, err = NewMemoryTopicStore(config.TopicCacheCapacity)
|
||||
expectNilErr(t, err)
|
||||
topicStoreTest(t)
|
||||
topics = NewSQLTopicStore()
|
||||
topics, err = NewSQLTopicStore()
|
||||
expectNilErr(t, err)
|
||||
topicStoreTest(t)
|
||||
}
|
||||
func topicStoreTest(t *testing.T) {
|
||||
|
|
|
@ -1485,8 +1485,6 @@ func routePanelGroupsEditSubmit(w http.ResponseWriter, r *http.Request, user Use
|
|||
originalRank = "Member"
|
||||
}
|
||||
|
||||
groupUpdateMutex.Lock()
|
||||
defer groupUpdateMutex.Unlock()
|
||||
if rank != originalRank {
|
||||
if !user.Perms.EditGroupGlobalPerms {
|
||||
return LocalError("You need the EditGroupGlobalPerms permission to change the group type.", w, r, user)
|
||||
|
@ -1517,12 +1515,12 @@ func routePanelGroupsEditSubmit(w http.ResponseWriter, r *http.Request, user Use
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Move this to *Group
|
||||
_, err = updateGroupStmt.Exec(gname, gtag, gid)
|
||||
if err != nil {
|
||||
return InternalError(err, w, r)
|
||||
}
|
||||
group.Name = gname
|
||||
group.Tag = gtag
|
||||
gstore.Reload(gid)
|
||||
|
||||
http.Redirect(w, r, "/panel/groups/edit/"+strconv.Itoa(gid), http.StatusSeeOther)
|
||||
return nil
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"database/sql"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"./query_gen/lib"
|
||||
|
@ -434,47 +433,9 @@ func rebuildForumPermissions(fid int) error {
|
|||
forumPerms[gid][fid] = pperms
|
||||
}
|
||||
|
||||
groups, err := gstore.GetAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, group := range groups {
|
||||
if dev.DebugMode {
|
||||
log.Print("Updating the forum permissions for Group #" + strconv.Itoa(group.ID))
|
||||
}
|
||||
group.Forums = []ForumPerms{BlankForumPerms}
|
||||
group.CanSee = []int{}
|
||||
|
||||
for _, ffid := range fids {
|
||||
forumPerm, ok := forumPerms[group.ID][ffid]
|
||||
if ok {
|
||||
//log.Print("Overriding permissions for forum #" + strconv.Itoa(fid))
|
||||
group.Forums = append(group.Forums, forumPerm)
|
||||
} else {
|
||||
//log.Print("Inheriting from default for forum #" + strconv.Itoa(fid))
|
||||
forumPerm = BlankForumPerms
|
||||
group.Forums = append(group.Forums, forumPerm)
|
||||
}
|
||||
if forumPerm.Overrides {
|
||||
if forumPerm.ViewTopic {
|
||||
group.CanSee = append(group.CanSee, ffid)
|
||||
}
|
||||
} else if group.Perms.ViewTopic {
|
||||
group.CanSee = append(group.CanSee, ffid)
|
||||
}
|
||||
}
|
||||
if dev.SuperDebug {
|
||||
log.Printf("group.CanSee %+v\n", group.CanSee)
|
||||
log.Printf("group.Forums %+v\n", group.Forums)
|
||||
log.Print("len(group.CanSee)", len(group.CanSee))
|
||||
log.Print("len(group.Forums)", len(group.Forums)) // This counts blank aka 0
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return cascadePermSetToGroups(forumPerms, fids)
|
||||
}
|
||||
|
||||
// ? - We could have buildForumPermissions and rebuildForumPermissions call a third function containing common logic?
|
||||
func buildForumPermissions() error {
|
||||
fids, err := fstore.GetAllIDs()
|
||||
if err != nil {
|
||||
|
@ -496,6 +457,7 @@ func buildForumPermissions() error {
|
|||
log.Print("forumPerms[gid][fid]")
|
||||
}
|
||||
}
|
||||
|
||||
// Temporarily store the forum perms in a map before transferring it to a much faster and thread-safe slice
|
||||
forumPerms = make(map[int]map[int]ForumPerms)
|
||||
for rows.Next() {
|
||||
|
@ -529,6 +491,10 @@ func buildForumPermissions() error {
|
|||
forumPerms[gid][fid] = pperms
|
||||
}
|
||||
|
||||
return cascadePermSetToGroups(forumPerms, fids)
|
||||
}
|
||||
|
||||
func cascadePermSetToGroups(forumPerms map[int]map[int]ForumPerms, fids []int) error {
|
||||
groups, err := gstore.GetAll()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -536,10 +502,23 @@ func buildForumPermissions() error {
|
|||
|
||||
for _, group := range groups {
|
||||
if dev.DebugMode {
|
||||
log.Print("Adding the forum permissions for Group #" + strconv.Itoa(group.ID) + " - " + group.Name)
|
||||
log.Printf("Updating the forum permissions for Group #%d", group.ID)
|
||||
}
|
||||
group.Forums = []ForumPerms{BlankForumPerms}
|
||||
group.CanSee = []int{}
|
||||
cascadePermSetToGroup(forumPerms, group, fids)
|
||||
|
||||
if dev.SuperDebug {
|
||||
log.Printf("group.CanSee %+v\n", group.CanSee)
|
||||
log.Printf("group.Forums %+v\n", group.Forums)
|
||||
log.Print("len(group.CanSee): ", len(group.CanSee))
|
||||
log.Print("len(group.Forums): ", len(group.Forums)) // This counts blank aka 0
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cascadePermSetToGroup(forumPerms map[int]map[int]ForumPerms, group *Group, fids []int) {
|
||||
for _, fid := range fids {
|
||||
if dev.SuperDebug {
|
||||
log.Printf("Forum #%+v\n", fid)
|
||||
|
@ -569,14 +548,6 @@ func buildForumPermissions() error {
|
|||
log.Print("group.CanSee: ", group.CanSee)
|
||||
}
|
||||
}
|
||||
if dev.SuperDebug {
|
||||
log.Printf("group.CanSee %+v\n", group.CanSee)
|
||||
log.Printf("group.Forums %+v\n", group.Forums)
|
||||
log.Print("len(group.CanSee)", len(group.CanSee))
|
||||
log.Print("len(group.Forums)", len(group.Forums)) // This counts blank aka 0
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func forumPermsToGroupForumPreset(fperms ForumPerms) string {
|
||||
|
|
|
@ -67,7 +67,7 @@ type GuildListPage struct {
|
|||
Title string
|
||||
CurrentUser User
|
||||
Header *HeaderVars
|
||||
GroupList []*Guild
|
||||
GuildList []*Guild
|
||||
}
|
||||
|
||||
type GuildMemberListPage struct {
|
||||
|
|
|
@ -18,6 +18,7 @@ type PluginMeta struct {
|
|||
Settings string
|
||||
Tag string
|
||||
|
||||
Skip bool // Skip this folder?
|
||||
Main string // The main file
|
||||
Hooks map[string]string // Hooks mapped to functions
|
||||
}
|
||||
|
@ -62,6 +63,9 @@ func InitPluginLangs() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if plugin.Skip {
|
||||
continue
|
||||
}
|
||||
|
||||
if plugin.UName == "" {
|
||||
return errors.New("The UName field must not be blank on plugin '" + pluginItem + "'")
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
<main>
|
||||
|
||||
<div class="rowblock rowhead">
|
||||
<div class="rowitem"><h1>Create Group</h1></div>
|
||||
<div class="rowitem"><h1>Create Guild</h1></div>
|
||||
</div>
|
||||
<div class="rowblock">
|
||||
<form action="/group/create/submit/" method="post">
|
||||
<form action="/guild/create/submit/" method="post">
|
||||
<div class="formrow">
|
||||
<div class="formitem formlabel"><a>Group Name</a></div>
|
||||
<div class="formitem formlabel"><a>Guild Name</a></div>
|
||||
<div class="formitem"><input name="group_name" type="text" placeholder="Group Name" /></div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
|
@ -25,7 +25,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="formrow">
|
||||
<div class="formitem"><button name="group_button" class="formbutton">Create Group</button></div>
|
||||
<div class="formitem"><button name="group_button" class="formbutton">Create Guild</button></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{{template "header.html" . }}
|
||||
<main>
|
||||
<div class="rowblock opthead">
|
||||
<div class="rowitem"><a>Group List</a></div>
|
||||
<div class="rowitem"><a>Guild List</a></div>
|
||||
</div>
|
||||
<div class="rowblock">
|
||||
{{range .GroupList}}<div class="rowitem datarow">
|
||||
{{range .GuildList}}<div class="rowitem datarow">
|
||||
<span style="float: left;">
|
||||
<a href="{{.Link}}" style="">{{.Name}}</a>
|
||||
<br /><span class="rowsmall">{{.Desc}}</span>
|
||||
|
@ -15,7 +15,7 @@
|
|||
</span>
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
{{else}}<div class="rowitem passive">There aren't any visible groups.</div>{{end}}
|
||||
{{else}}<div class="rowitem passive">There aren't any visible guilds.</div>{{end}}
|
||||
</div>
|
||||
</main>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
{{template "header.html" . }}
|
||||
{{/** TODO: Move this into a CSS file **/}}
|
||||
{{template "socialgroups_css.html" . }}
|
||||
{{/** TODO: Move this into a per-theme CSS file **/}}
|
||||
{{template "guilds_css.html" . }}
|
||||
|
||||
{{/** TODO: Add <link> next / prev bits **/}}
|
||||
{{/** TODO: Port the page template functions to the template interpreter **/}}
|
||||
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" href="/group/members/{{.SocialGroup.ID}}?page={{subtract .Page 1}}"><</a></div>{{end}}
|
||||
{{if ne .LastPage .Page}}<link rel="prerender" href="/group/members/{{.SocialGroup.ID}}?page={{add .Page 1}}" />
|
||||
<div id="nextFloat" class="next_button"><a class="next_link" href="/group/members/{{.SocialGroup.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
||||
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" href="/guild/members/{{.Guild.ID}}?page={{subtract .Page 1}}"><</a></div>{{end}}
|
||||
{{if ne .LastPage .Page}}<link rel="prerender" href="/guild/members/{{.Guild.ID}}?page={{add .Page 1}}" />
|
||||
<div id="nextFloat" class="next_button"><a class="next_link" href="/guild/members/{{.Guild.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
||||
|
||||
<div class="sgBackdrop">
|
||||
<nav class="miniMenu">
|
||||
<div class="menuItem"><a href="/group/{{.SocialGroup.ID}}">{{.SocialGroup.Name}}</a></div>
|
||||
<div class="menuItem"><a href="/guild/{{.Guild.ID}}">{{.Guild.Name}}</a></div>
|
||||
<div class="menuItem"><a href="#">About</a></div>
|
||||
<div class="menuItem"><a href="/group/members/{{.SocialGroup.ID}}">Members</a></div>
|
||||
<div class="menuItem"><a href="/guild/members/{{.Guild.ID}}">Members</a></div>
|
||||
<div class="menuItem rightMenu"><a href="#">Edit</a></div>
|
||||
<div class="menuItem rightMenu"><a href="/group/join/{{.SocialGroup.ID}}">Join</a></div>
|
||||
<div class="menuItem rightMenu"><a href="/guild/join/{{.Guild.ID}}">Join</a></div>
|
||||
</nav>
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
|
@ -25,7 +26,7 @@
|
|||
</span>
|
||||
<span>
|
||||
<a class="rowtopic" href="{{.Link}}">{{.User.Name}}</a>
|
||||
{{/** Use this for badges instead of rank? Both? Group Titles? **/}}
|
||||
{{/** Use this for badges instead of rank? Both? Guild Titles? **/}}
|
||||
<br /><span class="rowsmall postCount">{{.PostCount}} posts</span>
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -3,18 +3,18 @@
|
|||
{{template "socialgroups_css.html" . }}
|
||||
|
||||
{{/** TODO: Port the page template functions to the template interpreter **/}}
|
||||
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" href="/group/{{.SocialGroup.ID}}?page={{subtract .Page 1}}"><</a></div>{{end}}
|
||||
{{if ne .LastPage .Page}}<link rel="prerender" href="/group/{{.SocialGroup.ID}}?page={{add .Page 1}}" />
|
||||
<div id="nextFloat" class="next_button"><a class="next_link" href="/group/{{.SocialGroup.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
||||
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" href="/guild/{{.Guild.ID}}?page={{subtract .Page 1}}"><</a></div>{{end}}
|
||||
{{if ne .LastPage .Page}}<link rel="prerender" href="/guild/{{.Guild.ID}}?page={{add .Page 1}}" />
|
||||
<div id="nextFloat" class="next_button"><a class="next_link" href="/guild/{{.Guild.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
||||
|
||||
<div class="sgBackdrop">
|
||||
<nav class="miniMenu">
|
||||
<div class="menuItem"><a href="/group/{{.SocialGroup.ID}}">{{.SocialGroup.Name}}</a></div>
|
||||
<div class="menuItem"><a href="/guild/{{.Guild.ID}}">{{.Guild.Name}}</a></div>
|
||||
<div class="menuItem"><a href="#">About</a></div>
|
||||
<div class="menuItem"><a href="/group/members/{{.SocialGroup.ID}}">Members</a></div>
|
||||
<div class="menuItem"><a href="/guild/members/{{.Guild.ID}}">Members</a></div>
|
||||
<div class="menuItem rightMenu"><a href="#">Edit</a></div>
|
||||
<div class="menuItem rightMenu"><a href="/topics/create/{{.Forum.ID}}">Reply</a></div>
|
||||
<div class="menuItem rightMenu"><a href="/group/join/{{.SocialGroup.ID}}">Join</a></div>
|
||||
<div class="menuItem rightMenu"><a href="/guild/join/{{.Guild.ID}}">Join</a></div>
|
||||
</nav>
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,6 @@ package main
|
|||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -63,18 +62,18 @@ type MemoryTopicStore struct {
|
|||
}
|
||||
|
||||
// NewMemoryTopicStore gives you a new instance of MemoryTopicStore
|
||||
func NewMemoryTopicStore(capacity int) *MemoryTopicStore {
|
||||
func NewMemoryTopicStore(capacity int) (*MemoryTopicStore, error) {
|
||||
getStmt, err := qgen.Builder.SimpleSelect("topics", "title, content, createdBy, createdAt, lastReplyAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data", "tid = ?", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
existsStmt, err := qgen.Builder.SimpleSelect("topics", "tid", "tid = ?", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
topicCountStmt, err := qgen.Builder.SimpleCount("topics", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
return &MemoryTopicStore{
|
||||
items: make(map[int]*Topic),
|
||||
|
@ -82,7 +81,7 @@ func NewMemoryTopicStore(capacity int) *MemoryTopicStore {
|
|||
get: getStmt,
|
||||
exists: existsStmt,
|
||||
topicCount: topicCountStmt,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (mts *MemoryTopicStore) CacheGet(id int) (*Topic, error) {
|
||||
|
@ -267,24 +266,24 @@ type SQLTopicStore struct {
|
|||
topicCount *sql.Stmt
|
||||
}
|
||||
|
||||
func NewSQLTopicStore() *SQLTopicStore {
|
||||
func NewSQLTopicStore() (*SQLTopicStore, error) {
|
||||
getStmt, err := qgen.Builder.SimpleSelect("topics", "title, content, createdBy, createdAt, lastReplyAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data", "tid = ?", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
existsStmt, err := qgen.Builder.SimpleSelect("topics", "tid", "tid = ?", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
topicCountStmt, err := qgen.Builder.SimpleCount("topics", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
return &SQLTopicStore{
|
||||
get: getStmt,
|
||||
exists: existsStmt,
|
||||
topicCount: topicCountStmt,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (sts *SQLTopicStore) Get(id int) (*Topic, error) {
|
||||
|
|
14
user.go
14
user.go
|
@ -12,6 +12,7 @@ import (
|
|||
"database/sql"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"./query_gen/lib"
|
||||
|
@ -65,6 +66,19 @@ type Email struct {
|
|||
Token string
|
||||
}
|
||||
|
||||
func (user *User) Init() {
|
||||
if user.Avatar != "" {
|
||||
if user.Avatar[0] == '.' {
|
||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||
}
|
||||
} else {
|
||||
user.Avatar = strings.Replace(config.Noavatar, "{id}", strconv.Itoa(user.ID), 1)
|
||||
}
|
||||
user.Link = buildProfileURL(nameToSlug(user.Name), user.ID)
|
||||
user.Tag = gstore.DirtyGet(user.Group).Tag
|
||||
user.initPerms()
|
||||
}
|
||||
|
||||
func (user *User) Ban(duration time.Duration, issuedBy int) error {
|
||||
return user.ScheduleGroupUpdate(banGroup, issuedBy, duration)
|
||||
}
|
||||
|
|
110
user_store.go
110
user_store.go
|
@ -5,7 +5,6 @@ import (
|
|||
"errors"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
|
@ -56,32 +55,32 @@ type MemoryUserStore struct {
|
|||
}
|
||||
|
||||
// NewMemoryUserStore gives you a new instance of MemoryUserStore
|
||||
func NewMemoryUserStore(capacity int) *MemoryUserStore {
|
||||
func NewMemoryUserStore(capacity int) (*MemoryUserStore, error) {
|
||||
getStmt, err := qgen.Builder.SimpleSelect("users", "name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group", "uid = ?", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
existsStmt, err := qgen.Builder.SimpleSelect("users", "uid", "uid = ?", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add an admin version of register_stmt with more flexibility?
|
||||
// create_account_stmt, err = db.Prepare("INSERT INTO
|
||||
registerStmt, err := qgen.Builder.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt", "?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
usernameExistsStmt, err := qgen.Builder.SimpleSelect("users", "name", "name = ?", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userCountStmt, err := qgen.Builder.SimpleCount("users", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &MemoryUserStore{
|
||||
|
@ -92,7 +91,7 @@ func NewMemoryUserStore(capacity int) *MemoryUserStore {
|
|||
register: registerStmt,
|
||||
usernameExists: usernameExistsStmt,
|
||||
userCount: userCountStmt,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (mus *MemoryUserStore) CacheGet(id int) (*User, error) {
|
||||
|
@ -124,17 +123,7 @@ func (mus *MemoryUserStore) Get(id int) (*User, error) {
|
|||
user = &User{ID: id, Loggedin: true}
|
||||
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
|
||||
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
||||
if user.Avatar != "" {
|
||||
if user.Avatar[0] == '.' {
|
||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||
}
|
||||
} else {
|
||||
user.Avatar = strings.Replace(config.Noavatar, "{id}", strconv.Itoa(user.ID), 1)
|
||||
}
|
||||
user.Link = buildProfileURL(nameToSlug(user.Name), id)
|
||||
user.Tag = gstore.DirtyGet(user.Group).Tag
|
||||
user.initPerms()
|
||||
user.Init()
|
||||
if err == nil {
|
||||
mus.CacheSet(user)
|
||||
}
|
||||
|
@ -203,18 +192,8 @@ func (mus *MemoryUserStore) BulkGetMap(ids []int) (list map[int]*User, err error
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
||||
// Initialise the user
|
||||
if user.Avatar != "" {
|
||||
if user.Avatar[0] == '.' {
|
||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||
}
|
||||
} else {
|
||||
user.Avatar = strings.Replace(config.Noavatar, "{id}", strconv.Itoa(user.ID), 1)
|
||||
}
|
||||
user.Link = buildProfileURL(nameToSlug(user.Name), user.ID)
|
||||
user.Tag = gstore.DirtyGet(user.Group).Tag
|
||||
user.initPerms()
|
||||
user.Init()
|
||||
|
||||
// Add it to the cache...
|
||||
_ = mus.CacheSet(user)
|
||||
|
@ -255,17 +234,7 @@ func (mus *MemoryUserStore) BypassGet(id int) (*User, error) {
|
|||
user := &User{ID: id, Loggedin: true}
|
||||
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
|
||||
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
||||
if user.Avatar != "" {
|
||||
if user.Avatar[0] == '.' {
|
||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||
}
|
||||
} else {
|
||||
user.Avatar = strings.Replace(config.Noavatar, "{id}", strconv.Itoa(user.ID), 1)
|
||||
}
|
||||
user.Link = buildProfileURL(nameToSlug(user.Name), id)
|
||||
user.Tag = gstore.DirtyGet(user.Group).Tag
|
||||
user.initPerms()
|
||||
user.Init()
|
||||
return user, err
|
||||
}
|
||||
|
||||
|
@ -277,17 +246,7 @@ func (mus *MemoryUserStore) Reload(id int) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
||||
if user.Avatar != "" {
|
||||
if user.Avatar[0] == '.' {
|
||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||
}
|
||||
} else {
|
||||
user.Avatar = strings.Replace(config.Noavatar, "{id}", strconv.Itoa(user.ID), 1)
|
||||
}
|
||||
user.Link = buildProfileURL(nameToSlug(user.Name), id)
|
||||
user.Tag = gstore.DirtyGet(user.Group).Tag
|
||||
user.initPerms()
|
||||
user.Init()
|
||||
_ = mus.CacheSet(user)
|
||||
return nil
|
||||
}
|
||||
|
@ -425,32 +384,32 @@ type SQLUserStore struct {
|
|||
userCount *sql.Stmt
|
||||
}
|
||||
|
||||
func NewSQLUserStore() *SQLUserStore {
|
||||
func NewSQLUserStore() (*SQLUserStore, error) {
|
||||
getStmt, err := qgen.Builder.SimpleSelect("users", "name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group", "uid = ?", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
existsStmt, err := qgen.Builder.SimpleSelect("users", "uid", "uid = ?", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add an admin version of register_stmt with more flexibility?
|
||||
// create_account_stmt, err = db.Prepare("INSERT INTO
|
||||
registerStmt, err := qgen.Builder.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt", "?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
usernameExistsStmt, err := qgen.Builder.SimpleSelect("users", "name", "name = ?", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userCountStmt, err := qgen.Builder.SimpleCount("users", "", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SQLUserStore{
|
||||
|
@ -459,23 +418,14 @@ func NewSQLUserStore() *SQLUserStore {
|
|||
register: registerStmt,
|
||||
usernameExists: usernameExistsStmt,
|
||||
userCount: userCountStmt,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (mus *SQLUserStore) Get(id int) (*User, error) {
|
||||
user := &User{ID: id, Loggedin: true}
|
||||
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
|
||||
if user.Avatar != "" {
|
||||
if user.Avatar[0] == '.' {
|
||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||
}
|
||||
} else {
|
||||
user.Avatar = strings.Replace(config.Noavatar, "{id}", strconv.Itoa(user.ID), 1)
|
||||
}
|
||||
user.Link = buildProfileURL(nameToSlug(user.Name), id)
|
||||
user.Tag = gstore.DirtyGet(user.Group).Tag
|
||||
user.initPerms()
|
||||
user.Init()
|
||||
return user, err
|
||||
}
|
||||
|
||||
|
@ -508,16 +458,7 @@ func (mus *SQLUserStore) BulkGetMap(ids []int) (list map[int]*User, err error) {
|
|||
}
|
||||
|
||||
// Initialise the user
|
||||
if user.Avatar != "" {
|
||||
if user.Avatar[0] == '.' {
|
||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||
}
|
||||
} else {
|
||||
user.Avatar = strings.Replace(config.Noavatar, "{id}", strconv.Itoa(user.ID), 1)
|
||||
}
|
||||
user.Link = buildProfileURL(nameToSlug(user.Name), user.ID)
|
||||
user.Tag = gstore.DirtyGet(user.Group).Tag
|
||||
user.initPerms()
|
||||
user.Init()
|
||||
|
||||
// Add it to the list to be returned
|
||||
list[user.ID] = user
|
||||
|
@ -530,16 +471,7 @@ func (mus *SQLUserStore) BypassGet(id int) (*User, error) {
|
|||
user := &User{ID: id, Loggedin: true}
|
||||
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
|
||||
if user.Avatar != "" {
|
||||
if user.Avatar[0] == '.' {
|
||||
user.Avatar = "/uploads/avatar_" + strconv.Itoa(user.ID) + user.Avatar
|
||||
}
|
||||
} else {
|
||||
user.Avatar = strings.Replace(config.Noavatar, "{id}", strconv.Itoa(user.ID), 1)
|
||||
}
|
||||
user.Link = buildProfileURL(nameToSlug(user.Name), id)
|
||||
user.Tag = gstore.DirtyGet(user.Group).Tag
|
||||
user.initPerms()
|
||||
user.Init()
|
||||
return user, err
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue