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.")
|
log.Print("Loading the usergroups.")
|
||||||
gstore = NewMemoryGroupStore()
|
gstore, err = NewMemoryGroupStore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
err = gstore.LoadGroups()
|
err = gstore.LoadGroups()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// 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")
|
log.Print("Initialising the user and topic stores")
|
||||||
if config.CacheTopicUser == CACHE_STATIC {
|
if config.CacheTopicUser == CACHE_STATIC {
|
||||||
users = NewMemoryUserStore(config.UserCacheCapacity)
|
users, err = NewMemoryUserStore(config.UserCacheCapacity)
|
||||||
topics = NewMemoryTopicStore(config.TopicCacheCapacity)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
topics, err = NewMemoryTopicStore(config.TopicCacheCapacity)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
users = NewSQLUserStore()
|
users, err = NewSQLUserStore()
|
||||||
topics = NewSQLTopicStore()
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
topics, err = NewSQLTopicStore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Loading the forums.")
|
log.Print("Loading the forums.")
|
||||||
fstore = NewMemoryForumStore()
|
fstore, err = NewMemoryForumStore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
err = fstore.LoadForums()
|
err = fstore.LoadForums()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// System errors may contain sensitive information we don't want the user to see
|
||||||
if err.system {
|
if err.system {
|
||||||
return "system"
|
return "system"
|
||||||
} else {
|
|
||||||
return "user"
|
|
||||||
}
|
}
|
||||||
|
return "user"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *RouteErrorImpl) Error() string {
|
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 {
|
func InternalErrorJSQ(err error, w http.ResponseWriter, r *http.Request, isJs bool) RouteError {
|
||||||
if !isJs {
|
if !isJs {
|
||||||
return InternalError(err, w, r)
|
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.
|
// 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 {
|
func PreErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, isJs bool) RouteError {
|
||||||
if !isJs {
|
if !isJs {
|
||||||
return PreError(errmsg, w, r)
|
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
|
// 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 {
|
func LocalErrorJSQ(errmsg string, w http.ResponseWriter, r *http.Request, user User, isJs bool) RouteError {
|
||||||
if !isJs {
|
if !isJs {
|
||||||
return LocalError(errmsg, w, r, user)
|
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 {
|
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 {
|
func NoPermissionsJSQ(w http.ResponseWriter, r *http.Request, user User, isJs bool) RouteError {
|
||||||
if !isJs {
|
if !isJs {
|
||||||
return NoPermissions(w, r, user)
|
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 {
|
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 {
|
func BannedJSQ(w http.ResponseWriter, r *http.Request, user User, isJs bool) RouteError {
|
||||||
if !isJs {
|
if !isJs {
|
||||||
return Banned(w, r, user)
|
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 {
|
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 {
|
func CustomErrorJSQ(errmsg string, errcode int, errtitle string, w http.ResponseWriter, r *http.Request, user User, isJs bool) RouteError {
|
||||||
if !isJs {
|
if !isJs {
|
||||||
return CustomError(errmsg, errcode, errtitle, w, r, user)
|
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
|
// 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
|
// 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 = ?", "", "")
|
getStmt, err := qgen.Builder.SimpleSelect("forums", "name, desc, active, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID", "fid = ?", "", "")
|
||||||
if err != nil {
|
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", "")
|
getAllStmt, err := qgen.Builder.SimpleSelect("forums", "fid, name, desc, active, preset, parentID, parentType, topicCount, lastTopicID, lastReplyerID", "", "fid ASC", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
// TODO: Do a proper delete
|
// TODO: Do a proper delete
|
||||||
deleteStmt, err := qgen.Builder.SimpleUpdate("forums", "name= '', active = 0", "fid = ?")
|
deleteStmt, err := qgen.Builder.SimpleUpdate("forums", "name= '', active = 0", "fid = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
forumCountStmt, err := qgen.Builder.SimpleCount("forums", "name != ''", "")
|
forumCountStmt, err := qgen.Builder.SimpleCount("forums", "name != ''", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
return &MemoryForumStore{
|
return &MemoryForumStore{
|
||||||
get: getStmt,
|
get: getStmt,
|
||||||
getAll: getAllStmt,
|
getAll: getAllStmt,
|
||||||
delete: deleteStmt,
|
delete: deleteStmt,
|
||||||
getForumCount: forumCountStmt,
|
getForumCount: forumCountStmt,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add support for subforums
|
// TODO: Add support for subforums
|
||||||
|
|
27
group.go
27
group.go
|
@ -11,7 +11,7 @@ type GroupAdmin struct {
|
||||||
CanDelete bool
|
CanDelete bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! Fix the data races
|
// ! Fix the data races in the fperms
|
||||||
type Group struct {
|
type Group struct {
|
||||||
ID int
|
ID int
|
||||||
Name string
|
Name string
|
||||||
|
@ -27,26 +27,31 @@ type Group struct {
|
||||||
CanSee []int // The IDs of the forums this group can see
|
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) {
|
func (group *Group) ChangeRank(isAdmin bool, isMod bool, isBanned bool) (err error) {
|
||||||
_, err = updateGroupRankStmt.Exec(isAdmin, isMod, isBanned, group.ID)
|
_, err = updateGroupRankStmt.Exec(isAdmin, isMod, isBanned, group.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
group.IsAdmin = isAdmin
|
gstore.Reload(group.ID)
|
||||||
group.IsMod = isMod
|
|
||||||
if isAdmin || isMod {
|
|
||||||
group.IsBanned = false
|
|
||||||
} else {
|
|
||||||
group.IsBanned = isBanned
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
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
|
// Copy gives you a non-pointer concurrency safe copy of the group
|
||||||
func (group *Group) Copy() Group {
|
func (group *Group) Copy() Group {
|
||||||
return *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
|
||||||
|
}
|
||||||
|
|
216
group_store.go
216
group_store.go
|
@ -2,16 +2,16 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
|
"sort"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"./query_gen/lib"
|
"./query_gen/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
var groupCreateMutex sync.Mutex
|
|
||||||
var groupUpdateMutex sync.Mutex
|
|
||||||
var gstore GroupStore
|
var gstore GroupStore
|
||||||
|
|
||||||
// ? - We could fallback onto the database when an item can't be found in the cache?
|
// ? - 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)
|
Create(name string, tag string, isAdmin bool, isMod bool, isBanned bool) (int, error)
|
||||||
GetAll() ([]*Group, error)
|
GetAll() ([]*Group, error)
|
||||||
GetRange(lower int, higher int) ([]*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 {
|
type GroupCache interface {
|
||||||
|
CacheSet(group *Group) error
|
||||||
Length() int
|
Length() int
|
||||||
}
|
}
|
||||||
|
|
||||||
type MemoryGroupStore struct {
|
type MemoryGroupStore struct {
|
||||||
groups []*Group // TODO: Use a sync.Map instead of a slice
|
groups map[int]*Group // TODO: Use a sync.Map instead of a map?
|
||||||
groupCapCount int
|
groupCount int
|
||||||
|
get *sql.Stmt
|
||||||
|
|
||||||
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMemoryGroupStore() *MemoryGroupStore {
|
func NewMemoryGroupStore() (*MemoryGroupStore, error) {
|
||||||
return &MemoryGroupStore{}
|
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 {
|
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()
|
rows, err := getGroupsStmt.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -50,79 +67,136 @@ func (mgs *MemoryGroupStore) LoadGroups() error {
|
||||||
|
|
||||||
i := 1
|
i := 1
|
||||||
for ; rows.Next(); i++ {
|
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)
|
err := rows.Scan(&group.ID, &group.Name, &group.PermissionsText, &group.PluginPermsText, &group.IsMod, &group.IsAdmin, &group.IsBanned, &group.Tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(group.PermissionsText, &group.Perms)
|
err = mgs.initGroup(group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if dev.DebugMode {
|
mgs.groups[group.ID] = group
|
||||||
log.Print(group.Name + ": ")
|
|
||||||
log.Printf("%+v\n", group.Perms)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(group.PluginPermsText, &group.PluginPerms)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if dev.DebugMode {
|
|
||||||
log.Print(group.Name + ": ")
|
|
||||||
log.Printf("%+v\n", group.PluginPerms)
|
|
||||||
}
|
|
||||||
//group.Perms.ExtData = make(map[string]bool)
|
|
||||||
// TODO: Can we optimise the bit where this cascades down to the user now?
|
|
||||||
if group.IsAdmin || group.IsMod {
|
|
||||||
group.IsBanned = false
|
|
||||||
}
|
|
||||||
mgs.groups = append(mgs.groups, &group)
|
|
||||||
}
|
}
|
||||||
err = rows.Err()
|
err = rows.Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mgs.groupCapCount = i
|
mgs.groupCount = i
|
||||||
|
|
||||||
if dev.DebugMode {
|
if dev.DebugMode {
|
||||||
log.Print("Binding the Not Loggedin Group")
|
log.Print("Binding the Not Loggedin Group")
|
||||||
}
|
}
|
||||||
GuestPerms = mgs.groups[6].Perms
|
GuestPerms = mgs.dirtyGetUnsafe(6).Perms
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mgs *MemoryGroupStore) DirtyGet(gid int) *Group {
|
// TODO: Hit the database when the item isn't in memory
|
||||||
if !mgs.Exists(gid) {
|
func (mgs *MemoryGroupStore) dirtyGetUnsafe(gid int) *Group {
|
||||||
|
group, ok := mgs.groups[gid]
|
||||||
|
if !ok {
|
||||||
return &blankGroup
|
return &blankGroup
|
||||||
}
|
}
|
||||||
return mgs.groups[gid]
|
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) {
|
func (mgs *MemoryGroupStore) Get(gid int) (*Group, error) {
|
||||||
if !mgs.Exists(gid) {
|
mgs.RLock()
|
||||||
|
group, ok := mgs.groups[gid]
|
||||||
|
mgs.RUnlock()
|
||||||
|
if !ok {
|
||||||
return nil, ErrNoRows
|
return nil, ErrNoRows
|
||||||
}
|
}
|
||||||
return mgs.groups[gid], nil
|
return group, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Hit the database when the item isn't in memory
|
||||||
func (mgs *MemoryGroupStore) GetCopy(gid int) (Group, error) {
|
func (mgs *MemoryGroupStore) GetCopy(gid int) (Group, error) {
|
||||||
if !mgs.Exists(gid) {
|
mgs.RLock()
|
||||||
|
group, ok := mgs.groups[gid]
|
||||||
|
mgs.RUnlock()
|
||||||
|
if !ok {
|
||||||
return blankGroup, ErrNoRows
|
return blankGroup, ErrNoRows
|
||||||
}
|
}
|
||||||
return *mgs.groups[gid], nil
|
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
|
||||||
|
}
|
||||||
|
if dev.DebugMode {
|
||||||
|
log.Print(group.Name + ": ")
|
||||||
|
log.Printf("%+v\n", group.Perms)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(group.PluginPermsText, &group.PluginPerms)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dev.DebugMode {
|
||||||
|
log.Print(group.Name + ": ")
|
||||||
|
log.Printf("%+v\n", group.PluginPerms)
|
||||||
|
}
|
||||||
|
//group.Perms.ExtData = make(map[string]bool)
|
||||||
|
// TODO: Can we optimise the bit where this cascades down to the user now?
|
||||||
|
if group.IsAdmin || group.IsMod {
|
||||||
|
group.IsBanned = false
|
||||||
|
}
|
||||||
|
return 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 {
|
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?
|
// ? Allow two groups with the same name?
|
||||||
func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod bool, isBanned bool) (int, error) {
|
func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod bool, isBanned bool) (gid int, err error) {
|
||||||
groupCreateMutex.Lock()
|
|
||||||
defer groupCreateMutex.Unlock()
|
|
||||||
|
|
||||||
var permstr = "{}"
|
var permstr = "{}"
|
||||||
tx, err := db.Begin()
|
tx, err := db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -143,7 +217,7 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
var gid = int(gid64)
|
gid = int(gid64)
|
||||||
|
|
||||||
var perms = BlankPerms
|
var perms = BlankPerms
|
||||||
var blankForums []ForumPerms
|
var blankForums []ForumPerms
|
||||||
|
@ -199,8 +273,10 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
|
||||||
isBanned = false
|
isBanned = false
|
||||||
}
|
}
|
||||||
|
|
||||||
mgs.groups = append(mgs.groups, &Group{gid, name, isMod, isAdmin, isBanned, tag, perms, []byte(permstr), pluginPerms, pluginPermsBytes, blankForums, blankIntList})
|
mgs.Lock()
|
||||||
mgs.groupCapCount++
|
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 {
|
for _, forum := range fdata {
|
||||||
err = rebuildForumPermissions(forum.ID)
|
err = rebuildForumPermissions(forum.ID)
|
||||||
|
@ -212,34 +288,62 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
|
||||||
return gid, nil
|
return gid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! NOT CONCURRENT
|
func (mgs *MemoryGroupStore) GetAll() (results []*Group, err error) {
|
||||||
func (mgs *MemoryGroupStore) GetAll() ([]*Group, 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
|
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
|
// ? - 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) {
|
func (mgs *MemoryGroupStore) GetRange(lower int, higher int) (groups []*Group, err error) {
|
||||||
if lower == 0 && higher == 0 {
|
if lower == 0 && higher == 0 {
|
||||||
return mgs.GetAll()
|
return mgs.GetAll()
|
||||||
} else if lower == 0 {
|
}
|
||||||
|
|
||||||
|
if lower == 0 {
|
||||||
if higher < 0 {
|
if higher < 0 {
|
||||||
return nil, errors.New("higher may not be lower than 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 {
|
} else if higher == 0 {
|
||||||
if lower < 0 {
|
if lower < 0 {
|
||||||
return nil, errors.New("lower may not be lower than 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
|
return groups, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mgs *MemoryGroupStore) Length() int {
|
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()
|
initPlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
users = NewMemoryUserStore(config.UserCacheCapacity)
|
var err error
|
||||||
|
users, err = NewMemoryUserStore(config.UserCacheCapacity)
|
||||||
|
expectNilErr(t, err)
|
||||||
users.(UserCache).Flush()
|
users.(UserCache).Flush()
|
||||||
userStoreTest(t, 2)
|
userStoreTest(t, 2)
|
||||||
users = NewSQLUserStore()
|
users, err = NewSQLUserStore()
|
||||||
|
expectNilErr(t, err)
|
||||||
userStoreTest(t, 3)
|
userStoreTest(t, 3)
|
||||||
}
|
}
|
||||||
func userStoreTest(t *testing.T, newUserID int) {
|
func userStoreTest(t *testing.T, newUserID int) {
|
||||||
|
@ -453,9 +456,12 @@ func TestTopicStore(t *testing.T) {
|
||||||
initPlugins()
|
initPlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
topics = NewMemoryTopicStore(config.TopicCacheCapacity)
|
var err error
|
||||||
|
topics, err = NewMemoryTopicStore(config.TopicCacheCapacity)
|
||||||
|
expectNilErr(t, err)
|
||||||
topicStoreTest(t)
|
topicStoreTest(t)
|
||||||
topics = NewSQLTopicStore()
|
topics, err = NewSQLTopicStore()
|
||||||
|
expectNilErr(t, err)
|
||||||
topicStoreTest(t)
|
topicStoreTest(t)
|
||||||
}
|
}
|
||||||
func topicStoreTest(t *testing.T) {
|
func topicStoreTest(t *testing.T) {
|
||||||
|
|
|
@ -1485,8 +1485,6 @@ func routePanelGroupsEditSubmit(w http.ResponseWriter, r *http.Request, user Use
|
||||||
originalRank = "Member"
|
originalRank = "Member"
|
||||||
}
|
}
|
||||||
|
|
||||||
groupUpdateMutex.Lock()
|
|
||||||
defer groupUpdateMutex.Unlock()
|
|
||||||
if rank != originalRank {
|
if rank != originalRank {
|
||||||
if !user.Perms.EditGroupGlobalPerms {
|
if !user.Perms.EditGroupGlobalPerms {
|
||||||
return LocalError("You need the EditGroupGlobalPerms permission to change the group type.", w, r, user)
|
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)
|
_, err = updateGroupStmt.Exec(gname, gtag, gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return InternalError(err, w, r)
|
return InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
group.Name = gname
|
gstore.Reload(gid)
|
||||||
group.Tag = gtag
|
|
||||||
|
|
||||||
http.Redirect(w, r, "/panel/groups/edit/"+strconv.Itoa(gid), http.StatusSeeOther)
|
http.Redirect(w, r, "/panel/groups/edit/"+strconv.Itoa(gid), http.StatusSeeOther)
|
||||||
return nil
|
return nil
|
||||||
|
|
113
permissions.go
113
permissions.go
|
@ -4,7 +4,6 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"./query_gen/lib"
|
"./query_gen/lib"
|
||||||
|
@ -434,47 +433,9 @@ func rebuildForumPermissions(fid int) error {
|
||||||
forumPerms[gid][fid] = pperms
|
forumPerms[gid][fid] = pperms
|
||||||
}
|
}
|
||||||
|
|
||||||
groups, err := gstore.GetAll()
|
return cascadePermSetToGroups(forumPerms, fids)
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ? - We could have buildForumPermissions and rebuildForumPermissions call a third function containing common logic?
|
|
||||||
func buildForumPermissions() error {
|
func buildForumPermissions() error {
|
||||||
fids, err := fstore.GetAllIDs()
|
fids, err := fstore.GetAllIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -496,6 +457,7 @@ func buildForumPermissions() error {
|
||||||
log.Print("forumPerms[gid][fid]")
|
log.Print("forumPerms[gid][fid]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporarily store the forum perms in a map before transferring it to a much faster and thread-safe slice
|
// 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)
|
forumPerms = make(map[int]map[int]ForumPerms)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
|
@ -529,6 +491,10 @@ func buildForumPermissions() error {
|
||||||
forumPerms[gid][fid] = pperms
|
forumPerms[gid][fid] = pperms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return cascadePermSetToGroups(forumPerms, fids)
|
||||||
|
}
|
||||||
|
|
||||||
|
func cascadePermSetToGroups(forumPerms map[int]map[int]ForumPerms, fids []int) error {
|
||||||
groups, err := gstore.GetAll()
|
groups, err := gstore.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -536,49 +502,54 @@ func buildForumPermissions() error {
|
||||||
|
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
if dev.DebugMode {
|
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.Forums = []ForumPerms{BlankForumPerms}
|
||||||
group.CanSee = []int{}
|
group.CanSee = []int{}
|
||||||
for _, fid := range fids {
|
cascadePermSetToGroup(forumPerms, group, fids)
|
||||||
if dev.SuperDebug {
|
|
||||||
log.Printf("Forum #%+v\n", fid)
|
|
||||||
}
|
|
||||||
forumPerm, ok := forumPerms[group.ID][fid]
|
|
||||||
if ok {
|
|
||||||
// Override group perms
|
|
||||||
//log.Print("Overriding permissions for forum #" + strconv.Itoa(fid))
|
|
||||||
group.Forums = append(group.Forums, forumPerm)
|
|
||||||
} else {
|
|
||||||
// Inherit from Group
|
|
||||||
//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, fid)
|
|
||||||
}
|
|
||||||
} else if group.Perms.ViewTopic {
|
|
||||||
group.CanSee = append(group.CanSee, fid)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dev.SuperDebug {
|
|
||||||
log.Print("group.ID: ", group.ID)
|
|
||||||
log.Printf("forumPerm: %+v\n", forumPerm)
|
|
||||||
log.Print("group.CanSee: ", group.CanSee)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if dev.SuperDebug {
|
if dev.SuperDebug {
|
||||||
log.Printf("group.CanSee %+v\n", group.CanSee)
|
log.Printf("group.CanSee %+v\n", group.CanSee)
|
||||||
log.Printf("group.Forums %+v\n", group.Forums)
|
log.Printf("group.Forums %+v\n", group.Forums)
|
||||||
log.Print("len(group.CanSee)", len(group.CanSee))
|
log.Print("len(group.CanSee): ", len(group.CanSee))
|
||||||
log.Print("len(group.Forums)", len(group.Forums)) // This counts blank aka 0
|
log.Print("len(group.Forums): ", len(group.Forums)) // This counts blank aka 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
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)
|
||||||
|
}
|
||||||
|
forumPerm, ok := forumPerms[group.ID][fid]
|
||||||
|
if ok {
|
||||||
|
// Override group perms
|
||||||
|
//log.Print("Overriding permissions for forum #" + strconv.Itoa(fid))
|
||||||
|
group.Forums = append(group.Forums, forumPerm)
|
||||||
|
} else {
|
||||||
|
// Inherit from Group
|
||||||
|
//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, fid)
|
||||||
|
}
|
||||||
|
} else if group.Perms.ViewTopic {
|
||||||
|
group.CanSee = append(group.CanSee, fid)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dev.SuperDebug {
|
||||||
|
log.Print("group.ID: ", group.ID)
|
||||||
|
log.Printf("forumPerm: %+v\n", forumPerm)
|
||||||
|
log.Print("group.CanSee: ", group.CanSee)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func forumPermsToGroupForumPreset(fperms ForumPerms) string {
|
func forumPermsToGroupForumPreset(fperms ForumPerms) string {
|
||||||
if !fperms.Overrides {
|
if !fperms.Overrides {
|
||||||
return "default"
|
return "default"
|
||||||
|
|
|
@ -67,7 +67,7 @@ type GuildListPage struct {
|
||||||
Title string
|
Title string
|
||||||
CurrentUser User
|
CurrentUser User
|
||||||
Header *HeaderVars
|
Header *HeaderVars
|
||||||
GroupList []*Guild
|
GuildList []*Guild
|
||||||
}
|
}
|
||||||
|
|
||||||
type GuildMemberListPage struct {
|
type GuildMemberListPage struct {
|
||||||
|
|
|
@ -18,6 +18,7 @@ type PluginMeta struct {
|
||||||
Settings string
|
Settings string
|
||||||
Tag string
|
Tag string
|
||||||
|
|
||||||
|
Skip bool // Skip this folder?
|
||||||
Main string // The main file
|
Main string // The main file
|
||||||
Hooks map[string]string // Hooks mapped to functions
|
Hooks map[string]string // Hooks mapped to functions
|
||||||
}
|
}
|
||||||
|
@ -62,6 +63,9 @@ func InitPluginLangs() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if plugin.Skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if plugin.UName == "" {
|
if plugin.UName == "" {
|
||||||
return errors.New("The UName field must not be blank on plugin '" + pluginItem + "'")
|
return errors.New("The UName field must not be blank on plugin '" + pluginItem + "'")
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
<div class="rowblock rowhead">
|
<div class="rowblock rowhead">
|
||||||
<div class="rowitem"><h1>Create Group</h1></div>
|
<div class="rowitem"><h1>Create Guild</h1></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="rowblock">
|
<div class="rowblock">
|
||||||
<form action="/group/create/submit/" method="post">
|
<form action="/guild/create/submit/" method="post">
|
||||||
<div class="formrow">
|
<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 class="formitem"><input name="group_name" type="text" placeholder="Group Name" /></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="formrow">
|
<div class="formrow">
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="formrow">
|
<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>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<main>
|
<main>
|
||||||
<div class="rowblock opthead">
|
<div class="rowblock opthead">
|
||||||
<div class="rowitem"><a>Group List</a></div>
|
<div class="rowitem"><a>Guild List</a></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="rowblock">
|
<div class="rowblock">
|
||||||
{{range .GroupList}}<div class="rowitem datarow">
|
{{range .GuildList}}<div class="rowitem datarow">
|
||||||
<span style="float: left;">
|
<span style="float: left;">
|
||||||
<a href="{{.Link}}" style="">{{.Name}}</a>
|
<a href="{{.Link}}" style="">{{.Name}}</a>
|
||||||
<br /><span class="rowsmall">{{.Desc}}</span>
|
<br /><span class="rowsmall">{{.Desc}}</span>
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
</span>
|
</span>
|
||||||
<div style="clear: both;"></div>
|
<div style="clear: both;"></div>
|
||||||
</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>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
{{/** TODO: Move this into a CSS file **/}}
|
{{/** TODO: Move this into a per-theme CSS file **/}}
|
||||||
{{template "socialgroups_css.html" . }}
|
{{template "guilds_css.html" . }}
|
||||||
|
|
||||||
|
{{/** TODO: Add <link> next / prev bits **/}}
|
||||||
{{/** TODO: Port the page template functions to the template interpreter **/}}
|
{{/** 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 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="/group/members/{{.SocialGroup.ID}}?page={{add .Page 1}}" />
|
{{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="/group/members/{{.SocialGroup.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
<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">
|
<div class="sgBackdrop">
|
||||||
<nav class="miniMenu">
|
<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="#">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="#">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>
|
</nav>
|
||||||
<div style="clear: both;"></div>
|
<div style="clear: both;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,7 +26,7 @@
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<a class="rowtopic" href="{{.Link}}">{{.User.Name}}</a>
|
<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>
|
<br /><span class="rowsmall postCount">{{.PostCount}} posts</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,18 +3,18 @@
|
||||||
{{template "socialgroups_css.html" . }}
|
{{template "socialgroups_css.html" . }}
|
||||||
|
|
||||||
{{/** TODO: Port the page template functions to the template interpreter **/}}
|
{{/** 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 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="/group/{{.SocialGroup.ID}}?page={{add .Page 1}}" />
|
{{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="/group/{{.SocialGroup.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
<div id="nextFloat" class="next_button"><a class="next_link" href="/guild/{{.Guild.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
||||||
|
|
||||||
<div class="sgBackdrop">
|
<div class="sgBackdrop">
|
||||||
<nav class="miniMenu">
|
<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="#">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="#">Edit</a></div>
|
||||||
<div class="menuItem rightMenu"><a href="/topics/create/{{.Forum.ID}}">Reply</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>
|
</nav>
|
||||||
<div style="clear: both;"></div>
|
<div style="clear: both;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,7 +9,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
@ -63,18 +62,18 @@ type MemoryTopicStore struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMemoryTopicStore gives you a new instance of MemoryTopicStore
|
// 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 = ?", "", "")
|
getStmt, err := qgen.Builder.SimpleSelect("topics", "title, content, createdBy, createdAt, lastReplyAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data", "tid = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
existsStmt, err := qgen.Builder.SimpleSelect("topics", "tid", "tid = ?", "", "")
|
existsStmt, err := qgen.Builder.SimpleSelect("topics", "tid", "tid = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
topicCountStmt, err := qgen.Builder.SimpleCount("topics", "", "")
|
topicCountStmt, err := qgen.Builder.SimpleCount("topics", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
return &MemoryTopicStore{
|
return &MemoryTopicStore{
|
||||||
items: make(map[int]*Topic),
|
items: make(map[int]*Topic),
|
||||||
|
@ -82,7 +81,7 @@ func NewMemoryTopicStore(capacity int) *MemoryTopicStore {
|
||||||
get: getStmt,
|
get: getStmt,
|
||||||
exists: existsStmt,
|
exists: existsStmt,
|
||||||
topicCount: topicCountStmt,
|
topicCount: topicCountStmt,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) CacheGet(id int) (*Topic, error) {
|
func (mts *MemoryTopicStore) CacheGet(id int) (*Topic, error) {
|
||||||
|
@ -267,24 +266,24 @@ type SQLTopicStore struct {
|
||||||
topicCount *sql.Stmt
|
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 = ?", "", "")
|
getStmt, err := qgen.Builder.SimpleSelect("topics", "title, content, createdBy, createdAt, lastReplyAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data", "tid = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
existsStmt, err := qgen.Builder.SimpleSelect("topics", "tid", "tid = ?", "", "")
|
existsStmt, err := qgen.Builder.SimpleSelect("topics", "tid", "tid = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
topicCountStmt, err := qgen.Builder.SimpleCount("topics", "", "")
|
topicCountStmt, err := qgen.Builder.SimpleCount("topics", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
return &SQLTopicStore{
|
return &SQLTopicStore{
|
||||||
get: getStmt,
|
get: getStmt,
|
||||||
exists: existsStmt,
|
exists: existsStmt,
|
||||||
topicCount: topicCountStmt,
|
topicCount: topicCountStmt,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sts *SQLTopicStore) Get(id int) (*Topic, error) {
|
func (sts *SQLTopicStore) Get(id int) (*Topic, error) {
|
||||||
|
|
14
user.go
14
user.go
|
@ -12,6 +12,7 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"./query_gen/lib"
|
"./query_gen/lib"
|
||||||
|
@ -65,6 +66,19 @@ type Email struct {
|
||||||
Token string
|
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 {
|
func (user *User) Ban(duration time.Duration, issuedBy int) error {
|
||||||
return user.ScheduleGroupUpdate(banGroup, issuedBy, duration)
|
return user.ScheduleGroupUpdate(banGroup, issuedBy, duration)
|
||||||
}
|
}
|
||||||
|
|
110
user_store.go
110
user_store.go
|
@ -5,7 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
@ -56,32 +55,32 @@ type MemoryUserStore struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMemoryUserStore gives you a new instance of MemoryUserStore
|
// 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 = ?", "", "")
|
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 {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
existsStmt, err := qgen.Builder.SimpleSelect("users", "uid", "uid = ?", "", "")
|
existsStmt, err := qgen.Builder.SimpleSelect("users", "uid", "uid = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an admin version of register_stmt with more flexibility?
|
// Add an admin version of register_stmt with more flexibility?
|
||||||
// create_account_stmt, err = db.Prepare("INSERT INTO
|
// 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()")
|
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 {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
usernameExistsStmt, err := qgen.Builder.SimpleSelect("users", "name", "name = ?", "", "")
|
usernameExistsStmt, err := qgen.Builder.SimpleSelect("users", "name", "name = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
userCountStmt, err := qgen.Builder.SimpleCount("users", "", "")
|
userCountStmt, err := qgen.Builder.SimpleCount("users", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &MemoryUserStore{
|
return &MemoryUserStore{
|
||||||
|
@ -92,7 +91,7 @@ func NewMemoryUserStore(capacity int) *MemoryUserStore {
|
||||||
register: registerStmt,
|
register: registerStmt,
|
||||||
usernameExists: usernameExistsStmt,
|
usernameExists: usernameExistsStmt,
|
||||||
userCount: userCountStmt,
|
userCount: userCountStmt,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mus *MemoryUserStore) CacheGet(id int) (*User, error) {
|
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}
|
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)
|
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
|
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), id)
|
|
||||||
user.Tag = gstore.DirtyGet(user.Group).Tag
|
|
||||||
user.initPerms()
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
mus.CacheSet(user)
|
mus.CacheSet(user)
|
||||||
}
|
}
|
||||||
|
@ -203,18 +192,8 @@ func (mus *MemoryUserStore) BulkGetMap(ids []int) (list map[int]*User, err error
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
|
||||||
// Initialise the user
|
// Initialise the user
|
||||||
if user.Avatar != "" {
|
user.Init()
|
||||||
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()
|
|
||||||
|
|
||||||
// Add it to the cache...
|
// Add it to the cache...
|
||||||
_ = mus.CacheSet(user)
|
_ = mus.CacheSet(user)
|
||||||
|
@ -255,17 +234,7 @@ func (mus *MemoryUserStore) BypassGet(id int) (*User, error) {
|
||||||
user := &User{ID: id, Loggedin: true}
|
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)
|
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
|
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), id)
|
|
||||||
user.Tag = gstore.DirtyGet(user.Group).Tag
|
|
||||||
user.initPerms()
|
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,17 +246,7 @@ func (mus *MemoryUserStore) Reload(id int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add an init method to User rather than writing this same bit of code over and over
|
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), id)
|
|
||||||
user.Tag = gstore.DirtyGet(user.Group).Tag
|
|
||||||
user.initPerms()
|
|
||||||
_ = mus.CacheSet(user)
|
_ = mus.CacheSet(user)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -425,32 +384,32 @@ type SQLUserStore struct {
|
||||||
userCount *sql.Stmt
|
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 = ?", "", "")
|
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 {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
existsStmt, err := qgen.Builder.SimpleSelect("users", "uid", "uid = ?", "", "")
|
existsStmt, err := qgen.Builder.SimpleSelect("users", "uid", "uid = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an admin version of register_stmt with more flexibility?
|
// Add an admin version of register_stmt with more flexibility?
|
||||||
// create_account_stmt, err = db.Prepare("INSERT INTO
|
// 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()")
|
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 {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
usernameExistsStmt, err := qgen.Builder.SimpleSelect("users", "name", "name = ?", "", "")
|
usernameExistsStmt, err := qgen.Builder.SimpleSelect("users", "name", "name = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
userCountStmt, err := qgen.Builder.SimpleCount("users", "", "")
|
userCountStmt, err := qgen.Builder.SimpleCount("users", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SQLUserStore{
|
return &SQLUserStore{
|
||||||
|
@ -459,23 +418,14 @@ func NewSQLUserStore() *SQLUserStore {
|
||||||
register: registerStmt,
|
register: registerStmt,
|
||||||
usernameExists: usernameExistsStmt,
|
usernameExists: usernameExistsStmt,
|
||||||
userCount: userCountStmt,
|
userCount: userCountStmt,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mus *SQLUserStore) Get(id int) (*User, error) {
|
func (mus *SQLUserStore) Get(id int) (*User, error) {
|
||||||
user := &User{ID: id, Loggedin: true}
|
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)
|
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 != "" {
|
user.Init()
|
||||||
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()
|
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -508,16 +458,7 @@ func (mus *SQLUserStore) BulkGetMap(ids []int) (list map[int]*User, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise the user
|
// Initialise the user
|
||||||
if user.Avatar != "" {
|
user.Init()
|
||||||
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()
|
|
||||||
|
|
||||||
// Add it to the list to be returned
|
// Add it to the list to be returned
|
||||||
list[user.ID] = user
|
list[user.ID] = user
|
||||||
|
@ -530,16 +471,7 @@ func (mus *SQLUserStore) BypassGet(id int) (*User, error) {
|
||||||
user := &User{ID: id, Loggedin: true}
|
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)
|
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 != "" {
|
user.Init()
|
||||||
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()
|
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue