Added the AboutSegment feature, you can see this in use on Cosora, it's a little raw right now, but I'm planning to polish it in the next commit.
Refactored the code to use switches instead of if blocks in some places. Refactored the Dashboard to make it easier to add icons to it like I did with Cosora. You can now use maps in transpiled templates. Made progress on Cosora's footer. Swapped out the ThemeName property in the HeaderVars struct for a more general and flexible Theme property. Added the colstack CSS class to make it easier to style the layouts for the Control Panel and profile. Renamed the FStore variable to Forums. Renamed the Fpstore variable to FPStore. Renamed the Gstore variable to Groups. Split the MemoryTopicStore into DefaultTopicStore and MemoryTopicCache. Split the MemoryUserStore into DefaultUserStore and MemoryUserCache. Removed the NullUserStore, SQLUserStore, and SQLTopicStore. Added the NullTopicCache and NullUserCache. Moved the Reload method out of the TopicCache interface and into the TopicStore one. Moved the Reload method out of the UserCache interface and into the UserStore one. Added the SetCache and GetCache methods to the TopicStore and UserStore. Added the BypassGetAll method to the WordFilterMap type. Renamed routePanelSetting to routePanelSettingEdit. Renamed routePanelSettingEdit to routePanelSettingEditSubmit. Moved the page titles into the english language pack. Split main() into main and afterDBInit to avoid code duplication in general_test.go Added the ReqIsJson method so that we don't have to sniff the headers every time. Added the LogStore interface. Added the SQLModLogStore and the SQLAdminLogStore. Refactored the phrase system to use getPhrasePlaceholder instead of hard-coding the string to return in a bunch of functions. Removed a redundant rank check. Added the GuildStore to plugin_guilds. Added the about_segment_title and about_segment_body settings. Refactored the setting system to use predefined errors to make it easier for an upstream caller to filter out sensitive error messages as opposed to safe errors. Added the BypassGetAll method to the SettingMap type. Added the Update method to the SettingMap type. BulkGet is now exposed via the MemoryUserCache. Refactored more logs in the template transpiler to reduce the amount of indentation. Refactored the tests to take up fewer lines. Further improved the Cosora theme's colours, padding, and profiles. Added styling for the Control Panel Dashboard to the Cosora Theme. Reduced the amount of code duplication in the installer query generator and opened the door to certain types of auto-migrations. Refactored the Control Panel Dashboard to reduce the amount of code duplication. Refactored the modlog route to reduce the amount of code duplication and string concatenation.
This commit is contained in:
parent
8d0479f4b2
commit
381ce3083a
|
@ -192,6 +192,8 @@ More images in the /images/ folder. Beware though, some of them are *really* out
|
||||||
|
|
||||||
* ithub.com/denisenkom/go-mssqldb For interfacing with MSSQL. You will be able to pick this instead of MSSQL soon.
|
* ithub.com/denisenkom/go-mssqldb For interfacing with MSSQL. You will be able to pick this instead of MSSQL soon.
|
||||||
|
|
||||||
|
* github.com/go-ego/riot A search engine library.
|
||||||
|
|
||||||
# Bundled Plugins
|
# Bundled Plugins
|
||||||
|
|
||||||
There are several plugins which are bundled with the software by default. These cover various common tasks which aren't common enough to clutter the core with or which have competing implementation methods (E.g. plugin_markdown vs plugin_bbcode for post mark-up).
|
There are several plugins which are bundled with the software by default. These cover various common tasks which aren't common enough to clutter the core with or which have competing implementation methods (E.g. plugin_markdown vs plugin_bbcode for post mark-up).
|
||||||
|
|
|
@ -6,31 +6,64 @@ import (
|
||||||
"../query_gen/lib"
|
"../query_gen/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LogStmts struct {
|
var ModLogs LogStore
|
||||||
addModLogEntry *sql.Stmt
|
var AdminLogs LogStore
|
||||||
addAdminLogEntry *sql.Stmt
|
|
||||||
|
type LogStore interface {
|
||||||
|
Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error)
|
||||||
|
GlobalCount() int
|
||||||
}
|
}
|
||||||
|
|
||||||
var logStmts LogStmts
|
type SQLModLogStore struct {
|
||||||
|
create *sql.Stmt
|
||||||
func init() {
|
count *sql.Stmt
|
||||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
|
||||||
logStmts = LogStmts{
|
|
||||||
addModLogEntry: acc.Insert("moderation_logs").Columns("action, elementID, elementType, ipaddress, actorID, doneAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
|
||||||
addAdminLogEntry: acc.Insert("administration_logs").Columns("action, elementID, elementType, ipaddress, actorID, doneAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
|
||||||
})
|
func NewModLogStore() (*SQLModLogStore, error) {
|
||||||
|
acc := qgen.Builder.Accumulator()
|
||||||
|
return &SQLModLogStore{
|
||||||
|
create: acc.Insert("moderation_logs").Columns("action, elementID, elementType, ipaddress, actorID, doneAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
||||||
|
count: acc.Count("moderation_logs").Prepare(),
|
||||||
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make a store for this?
|
// TODO: Make a store for this?
|
||||||
func AddModLog(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) {
|
func (store *SQLModLogStore) Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) {
|
||||||
_, err = logStmts.addModLogEntry.Exec(action, elementID, elementType, ipaddress, actorID)
|
_, err = store.create.Exec(action, elementID, elementType, ipaddress, actorID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (store *SQLModLogStore) GlobalCount() (logCount int) {
|
||||||
|
err := store.count.QueryRow().Scan(&logCount)
|
||||||
|
if err != nil {
|
||||||
|
LogError(err)
|
||||||
|
}
|
||||||
|
return logCount
|
||||||
|
}
|
||||||
|
|
||||||
|
type SQLAdminLogStore struct {
|
||||||
|
create *sql.Stmt
|
||||||
|
count *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdminLogStore() (*SQLAdminLogStore, error) {
|
||||||
|
acc := qgen.Builder.Accumulator()
|
||||||
|
return &SQLAdminLogStore{
|
||||||
|
create: acc.Insert("administration_logs").Columns("action, elementID, elementType, ipaddress, actorID, doneAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
||||||
|
count: acc.Count("administration_logs").Prepare(),
|
||||||
|
}, acc.FirstError()
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Make a store for this?
|
// TODO: Make a store for this?
|
||||||
func AddAdminLog(action string, elementID string, elementType int, ipaddress string, actorID int) (err error) {
|
func (store *SQLAdminLogStore) Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) {
|
||||||
_, err = logStmts.addAdminLogEntry.Exec(action, elementID, elementType, ipaddress, actorID)
|
_, err = store.create.Exec(action, elementID, elementType, ipaddress, actorID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (store *SQLAdminLogStore) GlobalCount() (logCount int) {
|
||||||
|
err := store.count.QueryRow().Scan(&logCount)
|
||||||
|
if err != nil {
|
||||||
|
LogError(err)
|
||||||
|
}
|
||||||
|
return logCount
|
||||||
|
}
|
||||||
|
|
|
@ -91,9 +91,9 @@ func (auth *DefaultAuth) ForceLogout(uid int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush the user out of the cache
|
// Flush the user out of the cache
|
||||||
ucache, ok := Users.(UserCache)
|
ucache := Users.GetCache()
|
||||||
if ok {
|
if ucache != nil {
|
||||||
ucache.CacheRemove(uid)
|
ucache.Remove(uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -170,9 +170,9 @@ func (auth *DefaultAuth) CreateSession(uid int) (session string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush the user data from the cache
|
// Flush the user data from the cache
|
||||||
ucache, ok := Users.(UserCache)
|
ucache := Users.GetCache()
|
||||||
if ok {
|
if ucache != nil {
|
||||||
ucache.CacheRemove(uid)
|
ucache.Remove(uid)
|
||||||
}
|
}
|
||||||
return session, nil
|
return session, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ func (forum *Forum) Update(name string, desc string, active bool, preset string)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = Fstore.Reload(forum.ID)
|
_ = Forums.Reload(forum.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,11 +113,11 @@ func (forum *Forum) setPreset(fperms *ForumPerms, preset string, gid int) (err e
|
||||||
LogError(err)
|
LogError(err)
|
||||||
return errors.New("Unable to update the forum")
|
return errors.New("Unable to update the forum")
|
||||||
}
|
}
|
||||||
err = Fstore.Reload(forum.ID)
|
err = Forums.Reload(forum.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Unable to reload forum")
|
return errors.New("Unable to reload forum")
|
||||||
}
|
}
|
||||||
err = Fpstore.ReloadGroup(forum.ID, gid)
|
err = FPStore.ReloadGroup(forum.ID, gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("Unable to reload the forum permissions")
|
return errors.New("Unable to reload the forum permissions")
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ var LocalPermList = []string{
|
||||||
"CloseTopic",
|
"CloseTopic",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Rename this to ForumPermSet?
|
||||||
/* Inherit from group permissions for ones we don't have */
|
/* Inherit from group permissions for ones we don't have */
|
||||||
type ForumPerms struct {
|
type ForumPerms struct {
|
||||||
ViewTopic bool
|
ViewTopic bool
|
||||||
|
@ -165,7 +166,7 @@ func PermmapToQuery(permmap map[string]*ForumPerms, fid int) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return Fpstore.Reload(fid)
|
return FPStore.Reload(fid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReplaceForumPermsForGroup(gid int, presetSet map[int]string, permSets map[int]*ForumPerms) error {
|
func ReplaceForumPermsForGroup(gid int, presetSet map[int]string, permSets map[int]*ForumPerms) error {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"../query_gen/lib"
|
"../query_gen/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Fpstore ForumPermsStore
|
var FPStore ForumPermsStore
|
||||||
|
|
||||||
type ForumPermsStore interface {
|
type ForumPermsStore interface {
|
||||||
Init() error
|
Init() error
|
||||||
|
@ -41,7 +41,7 @@ func NewMemoryForumPermsStore() (*MemoryForumPermsStore, error) {
|
||||||
func (fps *MemoryForumPermsStore) Init() error {
|
func (fps *MemoryForumPermsStore) Init() error {
|
||||||
fps.updateMutex.Lock()
|
fps.updateMutex.Lock()
|
||||||
defer fps.updateMutex.Unlock()
|
defer fps.updateMutex.Unlock()
|
||||||
fids, err := Fstore.GetAllIDs()
|
fids, err := Forums.GetAllIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,6 @@ func (fps *MemoryForumPermsStore) Init() error {
|
||||||
debugLog("Adding the forum permissions")
|
debugLog("Adding the forum permissions")
|
||||||
debugDetail("forumPerms[gid][fid]")
|
debugDetail("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)
|
forumPerms = make(map[int]map[int]*ForumPerms)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var gid, fid int
|
var gid, fid int
|
||||||
|
@ -97,7 +96,7 @@ func (fps *MemoryForumPermsStore) Reload(fid int) error {
|
||||||
fps.updateMutex.Lock()
|
fps.updateMutex.Lock()
|
||||||
defer fps.updateMutex.Unlock()
|
defer fps.updateMutex.Unlock()
|
||||||
debugLogf("Reloading the forum permissions for forum #%d", fid)
|
debugLogf("Reloading the forum permissions for forum #%d", fid)
|
||||||
fids, err := Fstore.GetAllIDs()
|
fids, err := Forums.GetAllIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -143,7 +142,7 @@ func (fps *MemoryForumPermsStore) ReloadGroup(fid int, gid int) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
group, err := Gstore.Get(gid)
|
group, err := Groups.Get(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -153,7 +152,7 @@ func (fps *MemoryForumPermsStore) ReloadGroup(fid int, gid int) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fps *MemoryForumPermsStore) cascadePermSetToGroups(forumPerms map[int]map[int]*ForumPerms, fids []int) error {
|
func (fps *MemoryForumPermsStore) cascadePermSetToGroups(forumPerms map[int]map[int]*ForumPerms, fids []int) error {
|
||||||
groups, err := Gstore.GetAll()
|
groups, err := Groups.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -200,7 +199,7 @@ func (fps *MemoryForumPermsStore) cascadePermSetToGroup(forumPerms map[int]map[i
|
||||||
|
|
||||||
// TODO: Add a hook here and have plugin_guilds use it
|
// TODO: Add a hook here and have plugin_guilds use it
|
||||||
func (fps *MemoryForumPermsStore) Get(fid int, gid int) (fperms *ForumPerms, err error) {
|
func (fps *MemoryForumPermsStore) Get(fid int, gid int) (fperms *ForumPerms, err error) {
|
||||||
group, err := Gstore.Get(gid)
|
group, err := Groups.Get(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fperms, ErrNoRows
|
return fperms, ErrNoRows
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
|
|
||||||
var forumCreateMutex sync.Mutex
|
var forumCreateMutex sync.Mutex
|
||||||
var forumPerms map[int]map[int]*ForumPerms // [gid][fid]*ForumPerms // TODO: Add an abstraction around this and make it more thread-safe
|
var forumPerms map[int]map[int]*ForumPerms // [gid][fid]*ForumPerms // TODO: Add an abstraction around this and make it more thread-safe
|
||||||
var Fstore ForumStore
|
var Forums ForumStore
|
||||||
|
|
||||||
// ForumStore is an interface for accessing the forums and the metadata stored on them
|
// ForumStore is an interface for accessing the forums and the metadata stored on them
|
||||||
type ForumStore interface {
|
type ForumStore interface {
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (group *Group) ChangeRank(isAdmin bool, isMod bool, isBanned bool) (err err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
Gstore.Reload(group.ID)
|
Groups.Reload(group.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"../query_gen/lib"
|
"../query_gen/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Gstore GroupStore
|
var Groups 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?
|
||||||
type GroupStore interface {
|
type GroupStore interface {
|
||||||
|
@ -230,7 +230,7 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the forum permissions based on the presets...
|
// Generate the forum permissions based on the presets...
|
||||||
fdata, err := Fstore.GetAll()
|
fdata, err := Forums.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -279,7 +279,7 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
|
||||||
mgs.Unlock()
|
mgs.Unlock()
|
||||||
|
|
||||||
for _, forum := range fdata {
|
for _, forum := range fdata {
|
||||||
err = Fpstore.Reload(forum.ID)
|
err = FPStore.Reload(forum.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return gid, err
|
return gid, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
type NullTopicCache struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNullTopicCache gives you a new instance of NullTopicCache
|
||||||
|
func NewNullTopicCache() *NullTopicCache {
|
||||||
|
return &NullTopicCache{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *NullTopicCache) Get(id int) (*Topic, error) {
|
||||||
|
return nil, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *NullTopicCache) GetUnsafe(id int) (*Topic, error) {
|
||||||
|
return nil, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *NullTopicCache) Set(_ *Topic) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *NullTopicCache) Add(item *Topic) error {
|
||||||
|
_ = item
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make these length increments thread-safe. Ditto for the other DataStores
|
||||||
|
func (mts *NullTopicCache) AddUnsafe(item *Topic) error {
|
||||||
|
_ = item
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make these length decrements thread-safe. Ditto for the other DataStores
|
||||||
|
func (mts *NullTopicCache) Remove(id int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *NullTopicCache) RemoveUnsafe(id int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *NullTopicCache) Flush() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *NullTopicCache) Length() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *NullTopicCache) SetCapacity(_ int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *NullTopicCache) GetCapacity() int {
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
type NullUserCache struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNullUserCache gives you a new instance of NullUserCache
|
||||||
|
func NewNullUserCache() *NullUserCache {
|
||||||
|
return &NullUserCache{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullUserCache) Get(id int) (*User, error) {
|
||||||
|
return nil, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullUserCache) BulkGet(ids []int) (list []*User) {
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullUserCache) GetUnsafe(id int) (*User, error) {
|
||||||
|
return nil, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullUserCache) Set(_ *User) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullUserCache) Add(item *User) error {
|
||||||
|
_ = item
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullUserCache) AddUnsafe(item *User) error {
|
||||||
|
_ = item
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullUserCache) Remove(id int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullUserCache) RemoveUnsafe(id int) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullUserCache) Flush() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullUserCache) Length() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullUserCache) SetCapacity(_ int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *NullUserCache) GetCapacity() int {
|
||||||
|
return 0
|
||||||
|
}
|
|
@ -13,8 +13,8 @@ type HeaderVars struct {
|
||||||
Widgets PageWidgets
|
Widgets PageWidgets
|
||||||
Site *site
|
Site *site
|
||||||
Settings SettingMap
|
Settings SettingMap
|
||||||
Themes map[string]Theme // TODO: Use a slice containing every theme instead of the main map for speed
|
Themes map[string]Theme // TODO: Use a slice containing every theme instead of the main map for speed?
|
||||||
ThemeName string
|
Theme Theme
|
||||||
//TemplateName string // TODO: Use this to move template calls to the router rather than duplicating them over and over and over?
|
//TemplateName string // TODO: Use this to move template calls to the router rather than duplicating them over and over and over?
|
||||||
ExtData ExtData
|
ExtData ExtData
|
||||||
}
|
}
|
||||||
|
@ -282,5 +282,5 @@ type AreYouSure struct {
|
||||||
// This is mostly for errors.go, please create *HeaderVars on the spot instead of relying on this or the atomic store underlying it, if possible
|
// This is mostly for errors.go, please create *HeaderVars on the spot instead of relying on this or the atomic store underlying it, if possible
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
func DefaultHeaderVar() *HeaderVars {
|
func DefaultHeaderVar() *HeaderVars {
|
||||||
return &HeaderVars{Site: Site, ThemeName: fallbackTheme}
|
return &HeaderVars{Site: Site, Theme: Themes[fallbackTheme]}
|
||||||
}
|
}
|
||||||
|
|
|
@ -219,7 +219,7 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
||||||
i += intLen
|
i += intLen
|
||||||
|
|
||||||
topic, err := Topics.Get(tid)
|
topic, err := Topics.Get(tid)
|
||||||
if err != nil || !Fstore.Exists(topic.ParentID) {
|
if err != nil || !Forums.Exists(topic.ParentID) {
|
||||||
outbytes = append(outbytes, InvalidTopic...)
|
outbytes = append(outbytes, InvalidTopic...)
|
||||||
lastItem = i
|
lastItem = i
|
||||||
continue
|
continue
|
||||||
|
@ -250,7 +250,7 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
||||||
reply := BlankReply()
|
reply := BlankReply()
|
||||||
reply.ID = rid
|
reply.ID = rid
|
||||||
topic, err := reply.Topic()
|
topic, err := reply.Topic()
|
||||||
if err != nil || !Fstore.Exists(topic.ParentID) {
|
if err != nil || !Forums.Exists(topic.ParentID) {
|
||||||
outbytes = append(outbytes, InvalidTopic...)
|
outbytes = append(outbytes, InvalidTopic...)
|
||||||
lastItem = i
|
lastItem = i
|
||||||
continue
|
continue
|
||||||
|
@ -271,7 +271,7 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
||||||
fid, intLen := CoerceIntBytes(msgbytes[start:])
|
fid, intLen := CoerceIntBytes(msgbytes[start:])
|
||||||
i += intLen
|
i += intLen
|
||||||
|
|
||||||
if !Fstore.Exists(fid) {
|
if !Forums.Exists(fid) {
|
||||||
outbytes = append(outbytes, InvalidForum...)
|
outbytes = append(outbytes, InvalidForum...)
|
||||||
lastItem = i
|
lastItem = i
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -189,7 +189,7 @@ func RebuildGroupPermissions(gid int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
group, err := Gstore.Get(gid)
|
group, err := Groups.Get(gid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: Add a phrase store?
|
||||||
// TODO: Let the admin edit phrases from inside the Control Panel? How should we persist these? Should we create a copy of the langpack or edit the primaries? Use the changeLangpack mutex for this?
|
// TODO: Let the admin edit phrases from inside the Control Panel? How should we persist these? Should we create a copy of the langpack or edit the primaries? Use the changeLangpack mutex for this?
|
||||||
// nolint Be quiet megacheck, this *is* used
|
// nolint Be quiet megacheck, this *is* used
|
||||||
var currentLangPack atomic.Value
|
var currentLangPack atomic.Value
|
||||||
|
@ -41,6 +42,8 @@ type LanguagePack struct {
|
||||||
SettingLabels map[string]string
|
SettingLabels map[string]string
|
||||||
PermPresets map[string]string
|
PermPresets map[string]string
|
||||||
Accounts map[string]string // TODO: Apply these phrases in the software proper
|
Accounts map[string]string // TODO: Apply these phrases in the software proper
|
||||||
|
Errors map[string]map[string]string // map[category]map[name]value
|
||||||
|
PageTitles map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add the ability to edit language JSON files from the Control Panel and automatically scan the files for changes
|
// TODO: Add the ability to edit language JSON files from the Control Panel and automatically scan the files for changes
|
||||||
|
@ -114,7 +117,7 @@ func GetPhrase(name string) (string, bool) {
|
||||||
func GetGlobalPermPhrase(name string) string {
|
func GetGlobalPermPhrase(name string) string {
|
||||||
res, ok := currentLangPack.Load().(*LanguagePack).GlobalPerms[name]
|
res, ok := currentLangPack.Load().(*LanguagePack).GlobalPerms[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "{name}"
|
return getPhrasePlaceholder()
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@ -122,7 +125,7 @@ func GetGlobalPermPhrase(name string) string {
|
||||||
func GetLocalPermPhrase(name string) string {
|
func GetLocalPermPhrase(name string) string {
|
||||||
res, ok := currentLangPack.Load().(*LanguagePack).LocalPerms[name]
|
res, ok := currentLangPack.Load().(*LanguagePack).LocalPerms[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "{name}"
|
return getPhrasePlaceholder()
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@ -130,7 +133,7 @@ func GetLocalPermPhrase(name string) string {
|
||||||
func GetSettingLabel(name string) string {
|
func GetSettingLabel(name string) string {
|
||||||
res, ok := currentLangPack.Load().(*LanguagePack).SettingLabels[name]
|
res, ok := currentLangPack.Load().(*LanguagePack).SettingLabels[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "{name}"
|
return getPhrasePlaceholder()
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@ -146,11 +149,32 @@ func GetAllPermPresets() map[string]string {
|
||||||
func GetAccountPhrase(name string) string {
|
func GetAccountPhrase(name string) string {
|
||||||
res, ok := currentLangPack.Load().(*LanguagePack).Accounts[name]
|
res, ok := currentLangPack.Load().(*LanguagePack).Accounts[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "{name}"
|
return getPhrasePlaceholder()
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Does comma ok work with multi-dimensional maps?
|
||||||
|
func GetErrorPhrase(category string, name string) string {
|
||||||
|
res, ok := currentLangPack.Load().(*LanguagePack).Errors[category][name]
|
||||||
|
if !ok {
|
||||||
|
return getPhrasePlaceholder()
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTitlePhrase(name string) string {
|
||||||
|
res, ok := currentLangPack.Load().(*LanguagePack).PageTitles[name]
|
||||||
|
if !ok {
|
||||||
|
return getPhrasePlaceholder()
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPhrasePlaceholder() string {
|
||||||
|
return "{name}"
|
||||||
|
}
|
||||||
|
|
||||||
// ? - Use runtime reflection for updating phrases?
|
// ? - Use runtime reflection for updating phrases?
|
||||||
// TODO: Implement these
|
// TODO: Implement these
|
||||||
func AddPhrase() {
|
func AddPhrase() {
|
||||||
|
|
|
@ -112,9 +112,9 @@ func (reply *Reply) Delete() error {
|
||||||
}
|
}
|
||||||
// TODO: Move this bit to *Topic
|
// TODO: Move this bit to *Topic
|
||||||
_, err = replyStmts.removeRepliesFromTopic.Exec(1, reply.ParentID)
|
_, err = replyStmts.removeRepliesFromTopic.Exec(1, reply.ParentID)
|
||||||
tcache, ok := Topics.(TopicCache)
|
tcache := Topics.GetCache()
|
||||||
if ok {
|
if tcache != nil {
|
||||||
tcache.CacheRemove(reply.ParentID)
|
tcache.Remove(reply.ParentID)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,7 @@ func BuildWidgets(zone string, data interface{}, headerVars *HeaderVars, r *http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.Print("Themes[headerVars.ThemeName].Sidebars", Themes[headerVars.ThemeName].Sidebars)
|
if Themes[headerVars.Theme.Name].Sidebars == "right" {
|
||||||
if Themes[headerVars.ThemeName].Sidebars == "right" {
|
|
||||||
if len(Docks.RightSidebar) != 0 {
|
if len(Docks.RightSidebar) != 0 {
|
||||||
var sbody string
|
var sbody string
|
||||||
for _, widget := range Docks.RightSidebar {
|
for _, widget := range Docks.RightSidebar {
|
||||||
|
@ -48,7 +47,7 @@ func BuildWidgets(zone string, data interface{}, headerVars *HeaderVars, r *http
|
||||||
}
|
}
|
||||||
|
|
||||||
func simpleForumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerLite *HeaderLite, rerr RouteError) {
|
func simpleForumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerLite *HeaderLite, rerr RouteError) {
|
||||||
if !Fstore.Exists(fid) {
|
if !Forums.Exists(fid) {
|
||||||
return nil, PreError("The target forum doesn't exist.", w, r)
|
return nil, PreError("The target forum doesn't exist.", w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +60,7 @@ func simpleForumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fperms, err := Fpstore.Get(fid, user.Group)
|
fperms, err := FPStore.Get(fid, user.Group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: Refactor this
|
// TODO: Refactor this
|
||||||
log.Printf("Unable to get the forum perms for Group #%d for User #%d", user.Group, user.ID)
|
log.Printf("Unable to get the forum perms for Group #%d for User #%d", user.Group, user.ID)
|
||||||
|
@ -76,7 +75,7 @@ func forumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int)
|
||||||
if rerr != nil {
|
if rerr != nil {
|
||||||
return headerVars, rerr
|
return headerVars, rerr
|
||||||
}
|
}
|
||||||
if !Fstore.Exists(fid) {
|
if !Forums.Exists(fid) {
|
||||||
return headerVars, NotFound(w, r)
|
return headerVars, NotFound(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,14 +87,12 @@ func forumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fperms, err := Fpstore.Get(fid, user.Group)
|
fperms, err := FPStore.Get(fid, user.Group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: Refactor this
|
// TODO: Refactor this
|
||||||
log.Printf("Unable to get the forum perms for Group #%d for User #%d", user.Group, user.ID)
|
log.Printf("Unable to get the forum perms for Group #%d for User #%d", user.Group, user.ID)
|
||||||
return nil, PreError("Something weird happened", w, r)
|
return nil, PreError("Something weird happened", w, r)
|
||||||
}
|
}
|
||||||
//log.Printf("user.Perms: %+v\n", user.Perms)
|
|
||||||
//log.Printf("fperms: %+v\n", fperms)
|
|
||||||
cascadeForumPerms(fperms, user)
|
cascadeForumPerms(fperms, user)
|
||||||
return headerVars, rerr
|
return headerVars, rerr
|
||||||
}
|
}
|
||||||
|
@ -125,28 +122,30 @@ func cascadeForumPerms(fperms *ForumPerms, user *User) {
|
||||||
// Even if they have the right permissions, the control panel is only open to supermods+. There are many areas without subpermissions which assume that the current user is a supermod+ and admins are extremely unlikely to give these permissions to someone who isn't at-least a supermod to begin with
|
// Even if they have the right permissions, the control panel is only open to supermods+. There are many areas without subpermissions which assume that the current user is a supermod+ and admins are extremely unlikely to give these permissions to someone who isn't at-least a supermod to begin with
|
||||||
// TODO: Do a panel specific theme?
|
// TODO: Do a panel specific theme?
|
||||||
func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, stats PanelStats, rerr RouteError) {
|
func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, stats PanelStats, rerr RouteError) {
|
||||||
var themeName = DefaultThemeBox.Load().(string)
|
var theme Theme
|
||||||
|
|
||||||
cookie, err := r.Cookie("current_theme")
|
cookie, err := r.Cookie("current_theme")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cookie := html.EscapeString(cookie.Value)
|
inTheme, ok := Themes[html.EscapeString(cookie.Value)]
|
||||||
theme, ok := Themes[cookie]
|
|
||||||
if ok && !theme.HideFromThemes {
|
if ok && !theme.HideFromThemes {
|
||||||
themeName = cookie
|
theme = inTheme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if theme.Name == "" {
|
||||||
|
theme = Themes[DefaultThemeBox.Load().(string)]
|
||||||
|
}
|
||||||
|
|
||||||
headerVars = &HeaderVars{
|
headerVars = &HeaderVars{
|
||||||
Site: Site,
|
Site: Site,
|
||||||
Settings: SettingBox.Load().(SettingMap),
|
Settings: SettingBox.Load().(SettingMap),
|
||||||
Themes: Themes,
|
Themes: Themes,
|
||||||
ThemeName: themeName,
|
Theme: theme,
|
||||||
}
|
}
|
||||||
// TODO: We should probably initialise headerVars.ExtData
|
// TODO: We should probably initialise headerVars.ExtData
|
||||||
|
|
||||||
headerVars.Stylesheets = append(headerVars.Stylesheets, headerVars.ThemeName+"/panel.css")
|
headerVars.Stylesheets = append(headerVars.Stylesheets, theme.Name+"/panel.css")
|
||||||
if len(Themes[headerVars.ThemeName].Resources) > 0 {
|
if len(theme.Resources) > 0 {
|
||||||
rlist := Themes[headerVars.ThemeName].Resources
|
rlist := theme.Resources
|
||||||
for _, resource := range rlist {
|
for _, resource := range rlist {
|
||||||
if resource.Location == "global" || resource.Location == "panel" {
|
if resource.Location == "global" || resource.Location == "panel" {
|
||||||
extarr := strings.Split(resource.Name, ".")
|
extarr := strings.Split(resource.Name, ".")
|
||||||
|
@ -161,8 +160,8 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerV
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.Users = Users.GlobalCount()
|
stats.Users = Users.GlobalCount()
|
||||||
stats.Groups = Gstore.GlobalCount()
|
stats.Groups = Groups.GlobalCount()
|
||||||
stats.Forums = Fstore.GlobalCount() // TODO: Stop it from showing the blanked forums
|
stats.Forums = Forums.GlobalCount() // TODO: Stop it from showing the blanked forums
|
||||||
stats.Settings = len(headerVars.Settings)
|
stats.Settings = len(headerVars.Settings)
|
||||||
stats.WordFilters = len(WordFilterBox.Load().(WordFilterMap))
|
stats.WordFilters = len(WordFilterBox.Load().(WordFilterMap))
|
||||||
stats.Themes = len(Themes)
|
stats.Themes = len(Themes)
|
||||||
|
@ -170,12 +169,17 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerV
|
||||||
|
|
||||||
pusher, ok := w.(http.Pusher)
|
pusher, ok := w.(http.Pusher)
|
||||||
if ok {
|
if ok {
|
||||||
pusher.Push("/static/"+headerVars.ThemeName+"/main.css", nil)
|
pusher.Push("/static/"+theme.Name+"/main.css", nil)
|
||||||
pusher.Push("/static/"+headerVars.ThemeName+"/panel.css", nil)
|
pusher.Push("/static/"+theme.Name+"/panel.css", nil)
|
||||||
pusher.Push("/static/global.js", nil)
|
pusher.Push("/static/global.js", nil)
|
||||||
pusher.Push("/static/jquery-3.1.1.min.js", nil)
|
pusher.Push("/static/jquery-3.1.1.min.js", nil)
|
||||||
// TODO: Push the theme CSS files
|
// TODO: Test these
|
||||||
// TODO: Push the theme scripts
|
for _, sheet := range headerVars.Stylesheets {
|
||||||
|
pusher.Push("/static/"+sheet, nil)
|
||||||
|
}
|
||||||
|
for _, script := range headerVars.Scripts {
|
||||||
|
pusher.Push("/static/"+script, nil)
|
||||||
|
}
|
||||||
// TODO: Push avatars?
|
// TODO: Push avatars?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,30 +213,32 @@ func simpleUserCheck(w http.ResponseWriter, r *http.Request, user *User) (header
|
||||||
|
|
||||||
// TODO: Add the ability for admins to restrict certain themes to certain groups?
|
// TODO: Add the ability for admins to restrict certain themes to certain groups?
|
||||||
func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, rerr RouteError) {
|
func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, rerr RouteError) {
|
||||||
var themeName = DefaultThemeBox.Load().(string)
|
var theme Theme
|
||||||
|
|
||||||
cookie, err := r.Cookie("current_theme")
|
cookie, err := r.Cookie("current_theme")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
cookie := html.EscapeString(cookie.Value)
|
inTheme, ok := Themes[html.EscapeString(cookie.Value)]
|
||||||
theme, ok := Themes[cookie]
|
|
||||||
if ok && !theme.HideFromThemes {
|
if ok && !theme.HideFromThemes {
|
||||||
themeName = cookie
|
theme = inTheme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if theme.Name == "" {
|
||||||
|
theme = Themes[DefaultThemeBox.Load().(string)]
|
||||||
|
}
|
||||||
|
|
||||||
headerVars = &HeaderVars{
|
headerVars = &HeaderVars{
|
||||||
Site: Site,
|
Site: Site,
|
||||||
Settings: SettingBox.Load().(SettingMap),
|
Settings: SettingBox.Load().(SettingMap),
|
||||||
Themes: Themes,
|
Themes: Themes,
|
||||||
ThemeName: themeName,
|
Theme: theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.IsBanned {
|
if user.IsBanned {
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, "Your account has been suspended. Some of your permissions may have been revoked.")
|
headerVars.NoticeList = append(headerVars.NoticeList, "Your account has been suspended. Some of your permissions may have been revoked.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(Themes[headerVars.ThemeName].Resources) > 0 {
|
if len(theme.Resources) > 0 {
|
||||||
rlist := Themes[headerVars.ThemeName].Resources
|
rlist := theme.Resources
|
||||||
for _, resource := range rlist {
|
for _, resource := range rlist {
|
||||||
if resource.Location == "global" || resource.Location == "frontend" {
|
if resource.Location == "global" || resource.Location == "frontend" {
|
||||||
extarr := strings.Split(resource.Name, ".")
|
extarr := strings.Split(resource.Name, ".")
|
||||||
|
@ -248,11 +254,16 @@ func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *
|
||||||
|
|
||||||
pusher, ok := w.(http.Pusher)
|
pusher, ok := w.(http.Pusher)
|
||||||
if ok {
|
if ok {
|
||||||
pusher.Push("/static/"+headerVars.ThemeName+"/main.css", nil)
|
pusher.Push("/static/"+theme.Name+"/main.css", nil)
|
||||||
pusher.Push("/static/global.js", nil)
|
pusher.Push("/static/global.js", nil)
|
||||||
pusher.Push("/static/jquery-3.1.1.min.js", nil)
|
pusher.Push("/static/jquery-3.1.1.min.js", nil)
|
||||||
// TODO: Push the theme CSS files
|
// TODO: Test these
|
||||||
// TODO: Push the theme scripts
|
for _, sheet := range headerVars.Stylesheets {
|
||||||
|
pusher.Push("/static/"+sheet, nil)
|
||||||
|
}
|
||||||
|
for _, script := range headerVars.Scripts {
|
||||||
|
pusher.Push("/static/"+script, nil)
|
||||||
|
}
|
||||||
// TODO: Push avatars?
|
// TODO: Push avatars?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,3 +355,7 @@ func NoSessionMismatch(w http.ResponseWriter, r *http.Request, user User) RouteE
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReqIsJson(r *http.Request) bool {
|
||||||
|
return r.Header.Get("Content-type") == "application/json"
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
@ -17,6 +18,7 @@ type SettingMap map[string]interface{}
|
||||||
type SettingStore interface {
|
type SettingStore interface {
|
||||||
ParseSetting(sname string, scontent string, stype string, sconstraint string) string
|
ParseSetting(sname string, scontent string, stype string, sconstraint string) string
|
||||||
BypassGet(name string) (*Setting, error)
|
BypassGet(name string) (*Setting, error)
|
||||||
|
BypassGetAll(name string) ([]*Setting, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type OptionLabel struct {
|
type OptionLabel struct {
|
||||||
|
@ -35,6 +37,7 @@ type Setting struct {
|
||||||
type SettingStmts struct {
|
type SettingStmts struct {
|
||||||
getAll *sql.Stmt
|
getAll *sql.Stmt
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
|
update *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
var settingStmts SettingStmts
|
var settingStmts SettingStmts
|
||||||
|
@ -45,80 +48,83 @@ func init() {
|
||||||
settingStmts = SettingStmts{
|
settingStmts = SettingStmts{
|
||||||
getAll: acc.Select("settings").Columns("name, content, type, constraints").Prepare(),
|
getAll: acc.Select("settings").Columns("name, content, type, constraints").Prepare(),
|
||||||
get: acc.Select("settings").Columns("content, type, constraints").Where("name = ?").Prepare(),
|
get: acc.Select("settings").Columns("content, type, constraints").Where("name = ?").Prepare(),
|
||||||
|
update: acc.Update("settings").Set("content = ?").Where("name = ?").Prepare(),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadSettings() error {
|
func LoadSettings() error {
|
||||||
rows, err := settingStmts.getAll.Query()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var sBox = SettingMap(make(map[string]interface{}))
|
var sBox = SettingMap(make(map[string]interface{}))
|
||||||
var sname, scontent, stype, sconstraints string
|
settings, err := sBox.BypassGetAll()
|
||||||
for rows.Next() {
|
|
||||||
err = rows.Scan(&sname, &scontent, &stype, &sconstraints)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
errmsg := sBox.ParseSetting(sname, scontent, stype, sconstraints)
|
|
||||||
if errmsg != "" {
|
for _, setting := range settings {
|
||||||
return err
|
err = sBox.ParseSetting(setting.Name, setting.Content, setting.Type, setting.Constraint)
|
||||||
}
|
|
||||||
}
|
|
||||||
err = rows.Err()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SettingBox.Store(sBox)
|
SettingBox.Store(sBox)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
|
var ErrNotInteger = errors.New("You were supposed to enter an integer x.x")
|
||||||
|
var ErrSettingNotInteger = errors.New("Only integers are allowed in this setting x.x")
|
||||||
|
var ErrBadConstraintNotInteger = errors.New("Invalid contraint! The constraint field wasn't an integer!")
|
||||||
|
var ErrBadSettingRange = errors.New("Only integers between a certain range are allowed in this setting")
|
||||||
|
|
||||||
|
// To avoid leaking internal state to the user
|
||||||
|
// TODO: We need to add some sort of DualError interface
|
||||||
|
func SafeSettingError(err error) bool {
|
||||||
|
return err == ErrNotInteger || err == ErrSettingNotInteger || err == ErrBadConstraintNotInteger || err == ErrBadSettingRange || err == ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions.
|
// TODO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions.
|
||||||
func (sBox SettingMap) ParseSetting(sname string, scontent string, stype string, constraint string) string {
|
func (sBox SettingMap) ParseSetting(sname string, scontent string, stype string, constraint string) (err error) {
|
||||||
var err error
|
|
||||||
var ssBox = map[string]interface{}(sBox)
|
var ssBox = map[string]interface{}(sBox)
|
||||||
if stype == "bool" {
|
switch stype {
|
||||||
|
case "bool":
|
||||||
ssBox[sname] = (scontent == "1")
|
ssBox[sname] = (scontent == "1")
|
||||||
} else if stype == "int" {
|
case "int":
|
||||||
ssBox[sname], err = strconv.Atoi(scontent)
|
ssBox[sname], err = strconv.Atoi(scontent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "You were supposed to enter an integer x.x\nType mismatch in " + sname
|
return ErrNotInteger
|
||||||
}
|
}
|
||||||
} else if stype == "int64" {
|
case "int64":
|
||||||
ssBox[sname], err = strconv.ParseInt(scontent, 10, 64)
|
ssBox[sname], err = strconv.ParseInt(scontent, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "You were supposed to enter an integer x.x\nType mismatch in " + sname
|
return ErrNotInteger
|
||||||
}
|
}
|
||||||
} else if stype == "list" {
|
case "list":
|
||||||
cons := strings.Split(constraint, "-")
|
cons := strings.Split(constraint, "-")
|
||||||
if len(cons) < 2 {
|
if len(cons) < 2 {
|
||||||
return "Invalid constraint! The second field wasn't set!"
|
return errors.New("Invalid constraint! The second field wasn't set!")
|
||||||
}
|
}
|
||||||
|
|
||||||
con1, err := strconv.Atoi(cons[0])
|
con1, err := strconv.Atoi(cons[0])
|
||||||
con2, err2 := strconv.Atoi(cons[1])
|
con2, err2 := strconv.Atoi(cons[1])
|
||||||
if err != nil || err2 != nil {
|
if err != nil || err2 != nil {
|
||||||
return "Invalid contraint! The constraint field wasn't an integer!"
|
return ErrBadConstraintNotInteger
|
||||||
}
|
}
|
||||||
|
|
||||||
value, err := strconv.Atoi(scontent)
|
value, err := strconv.Atoi(scontent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "Only integers are allowed in this setting x.x\nType mismatch in " + sname
|
return ErrSettingNotInteger
|
||||||
}
|
}
|
||||||
|
|
||||||
if value < con1 || value > con2 {
|
if value < con1 || value > con2 {
|
||||||
return "Only integers between a certain range are allowed in this setting"
|
return ErrBadSettingRange
|
||||||
}
|
}
|
||||||
ssBox[sname] = value
|
ssBox[sname] = value
|
||||||
} else {
|
default:
|
||||||
ssBox[sname] = scontent
|
ssBox[sname] = scontent
|
||||||
}
|
}
|
||||||
return ""
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sBox SettingMap) BypassGet(name string) (*Setting, error) {
|
func (sBox SettingMap) BypassGet(name string) (*Setting, error) {
|
||||||
|
@ -126,3 +132,51 @@ func (sBox SettingMap) BypassGet(name string) (*Setting, error) {
|
||||||
err := settingStmts.get.QueryRow(name).Scan(&setting.Content, &setting.Type, &setting.Constraint)
|
err := settingStmts.get.QueryRow(name).Scan(&setting.Content, &setting.Type, &setting.Constraint)
|
||||||
return setting, err
|
return setting, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sBox SettingMap) BypassGetAll() (settingList []*Setting, err error) {
|
||||||
|
rows, err := settingStmts.getAll.Query()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
setting := &Setting{Name: ""}
|
||||||
|
err := rows.Scan(&setting.Name, &setting.Content, &setting.Type, &setting.Constraint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
settingList = append(settingList, setting)
|
||||||
|
}
|
||||||
|
return settingList, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sBox SettingMap) Update(name string, content string) error {
|
||||||
|
setting, err := sBox.BypassGet(name)
|
||||||
|
if err == ErrNoRows {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Why is this here and not in a common function?
|
||||||
|
if setting.Type == "bool" {
|
||||||
|
if content == "on" || content == "1" {
|
||||||
|
content = "1"
|
||||||
|
} else {
|
||||||
|
content = "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make this a method or function?
|
||||||
|
_, err = settingStmts.update.Exec(content, name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sBox.ParseSetting(name, content, setting.Type, setting.Constraint)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO: Do a reload instead?
|
||||||
|
SettingBox.Store(sBox)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ func ProcessConfig() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerifyConfig() error {
|
func VerifyConfig() error {
|
||||||
if !Fstore.Exists(Config.DefaultForum) {
|
if !Forums.Exists(Config.DefaultForum) {
|
||||||
return errors.New("Invalid default forum")
|
return errors.New("Invalid default forum")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -67,7 +67,7 @@ func HandleServerSync() error {
|
||||||
|
|
||||||
if lastUpdate.After(lastSync) {
|
if lastUpdate.After(lastSync) {
|
||||||
// TODO: A more granular sync
|
// TODO: A more granular sync
|
||||||
err = Fstore.LoadForums()
|
err = Forums.LoadForums()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print("Unable to reload the forums")
|
log.Print("Unable to reload the forums")
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -96,7 +96,7 @@ func compileTemplates() error {
|
||||||
Site: Site,
|
Site: Site,
|
||||||
Settings: SettingBox.Load().(SettingMap),
|
Settings: SettingBox.Load().(SettingMap),
|
||||||
Themes: Themes,
|
Themes: Themes,
|
||||||
ThemeName: DefaultThemeBox.Load().(string),
|
Theme: Themes[DefaultThemeBox.Load().(string)],
|
||||||
NoticeList: []string{"test"},
|
NoticeList: []string{"test"},
|
||||||
Stylesheets: []string{"panel"},
|
Stylesheets: []string{"panel"},
|
||||||
Scripts: []string{"whatever"},
|
Scripts: []string{"whatever"},
|
||||||
|
@ -132,7 +132,7 @@ func compileTemplates() error {
|
||||||
|
|
||||||
// TODO: Use a dummy forum list to avoid o(n) problems
|
// TODO: Use a dummy forum list to avoid o(n) problems
|
||||||
var forumList []Forum
|
var forumList []Forum
|
||||||
forums, err := Fstore.GetAll()
|
forums, err := Forums.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,8 @@ type CTemplateSet struct {
|
||||||
FragOut string
|
FragOut string
|
||||||
varList map[string]VarItem
|
varList map[string]VarItem
|
||||||
localVars map[string]map[string]VarItemReflect
|
localVars map[string]map[string]VarItemReflect
|
||||||
|
hasDispInt bool
|
||||||
|
localDispStructIndex int
|
||||||
stats map[string]int
|
stats map[string]int
|
||||||
pVarList string
|
pVarList string
|
||||||
pVarPosition int
|
pVarPosition int
|
||||||
|
@ -102,6 +104,8 @@ func (c *CTemplateSet) Compile(name string, dir string, expects string, expectsI
|
||||||
}
|
}
|
||||||
|
|
||||||
c.varList = varList
|
c.varList = varList
|
||||||
|
c.hasDispInt = false
|
||||||
|
c.localDispStructIndex = 0
|
||||||
//c.pVarList = ""
|
//c.pVarList = ""
|
||||||
//c.pVarPosition = 0
|
//c.pVarPosition = 0
|
||||||
c.stats = make(map[string]int)
|
c.stats = make(map[string]int)
|
||||||
|
@ -187,7 +191,6 @@ w.Write([]byte(`, " + ", -1)
|
||||||
|
|
||||||
c.log("Output!")
|
c.log("Output!")
|
||||||
c.log(fout)
|
c.log(fout)
|
||||||
//log.Fatal("remove the log.Fatal line")
|
|
||||||
return fout, nil
|
return fout, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,10 +337,8 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
|
||||||
varbit += ".(" + cur.Type().Name() + ")"
|
varbit += ".(" + cur.Type().Name() + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, id := range n.Ident {
|
// ! Might not work so well for non-struct pointers
|
||||||
c.log("Data Kind:", cur.Kind().String())
|
skipPointers := func(cur reflect.Value, id string) reflect.Value {
|
||||||
c.log("Field Bit:", id)
|
|
||||||
|
|
||||||
if cur.Kind() == reflect.Ptr {
|
if cur.Kind() == reflect.Ptr {
|
||||||
c.log("Looping over pointer")
|
c.log("Looping over pointer")
|
||||||
for cur.Kind() == reflect.Ptr {
|
for cur.Kind() == reflect.Ptr {
|
||||||
|
@ -346,40 +347,87 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
|
||||||
c.log("Data Kind:", cur.Kind().String())
|
c.log("Data Kind:", cur.Kind().String())
|
||||||
c.log("Field Bit:", id)
|
c.log("Field Bit:", id)
|
||||||
}
|
}
|
||||||
|
return cur
|
||||||
|
}
|
||||||
|
|
||||||
|
var assLines string
|
||||||
|
var multiline = false
|
||||||
|
for _, id := range n.Ident {
|
||||||
|
c.log("Data Kind:", cur.Kind().String())
|
||||||
|
c.log("Field Bit:", id)
|
||||||
|
cur = skipPointers(cur, id)
|
||||||
|
|
||||||
if !cur.IsValid() {
|
if !cur.IsValid() {
|
||||||
if c.debug {
|
c.error("Debug Data:")
|
||||||
fmt.Println("Debug Data:")
|
c.error("Holdreflect:", holdreflect)
|
||||||
fmt.Println("Holdreflect:", holdreflect)
|
c.error("Holdreflect.Kind():", holdreflect.Kind())
|
||||||
fmt.Println("Holdreflect.Kind():", holdreflect.Kind())
|
|
||||||
if !c.superDebug {
|
if !c.superDebug {
|
||||||
fmt.Println("cur.Kind():", cur.Kind().String())
|
c.error("cur.Kind():", cur.Kind().String())
|
||||||
}
|
}
|
||||||
fmt.Println("")
|
c.error("")
|
||||||
}
|
if !multiline {
|
||||||
|
|
||||||
panic(varholder + varbit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
panic(varholder + varbit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||||
|
} else {
|
||||||
|
panic(varbit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cur = cur.FieldByName(id)
|
c.log("in-loop varbit: " + varbit)
|
||||||
|
if cur.Kind() == reflect.Map {
|
||||||
|
cur = cur.MapIndex(reflect.ValueOf(id))
|
||||||
|
varbit += "[\"" + id + "\"]"
|
||||||
|
cur = skipPointers(cur, id)
|
||||||
|
|
||||||
|
if cur.Kind() == reflect.Struct || cur.Kind() == reflect.Interface {
|
||||||
|
// TODO: Move the newVarByte declaration to the top level or to the if level, if a dispInt is only used in a particular if statement
|
||||||
|
var dispStr, newVarByte string
|
||||||
if cur.Kind() == reflect.Interface {
|
if cur.Kind() == reflect.Interface {
|
||||||
cur = cur.Elem()
|
dispStr = "Int"
|
||||||
// TODO: Surely, there's a better way of detecting this?
|
if !c.hasDispInt {
|
||||||
/*if cur.Kind() == reflect.String && cur.Type().Name() != "string" {
|
newVarByte = ":"
|
||||||
varbit = "string(" + varbit + "." + id + ")"*/
|
c.hasDispInt = true
|
||||||
//if cur.Kind() == reflect.String && cur.Type().Name() != "string" {
|
|
||||||
if cur.Type().PkgPath() != "main" && cur.Type().PkgPath() != "" {
|
|
||||||
c.importMap["html/template"] = "html/template"
|
|
||||||
varbit += "." + id + ".(" + strings.TrimPrefix(cur.Type().PkgPath(), "html/") + "." + cur.Type().Name() + ")"
|
|
||||||
} else {
|
|
||||||
varbit += "." + id + ".(" + cur.Type().Name() + ")"
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
// TODO: De-dupe identical struct types rather than allocating a variable for each one
|
||||||
|
if cur.Kind() == reflect.Struct {
|
||||||
|
dispStr = "Struct" + strconv.Itoa(c.localDispStructIndex)
|
||||||
|
newVarByte = ":"
|
||||||
|
c.localDispStructIndex++
|
||||||
|
}
|
||||||
|
varbit = "disp" + dispStr + " " + newVarByte + "= " + varholder + varbit + "\n"
|
||||||
|
varholder = "disp" + dispStr
|
||||||
|
multiline = true
|
||||||
} else {
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cur.Kind() != reflect.Interface {
|
||||||
|
cur = cur.FieldByName(id)
|
||||||
varbit += "." + id
|
varbit += "." + id
|
||||||
}
|
}
|
||||||
c.log("End Cycle")
|
|
||||||
|
// TODO: Handle deeply nested pointers mixed with interfaces mixed with pointers better
|
||||||
|
if cur.Kind() == reflect.Interface {
|
||||||
|
cur = cur.Elem()
|
||||||
|
varbit += ".("
|
||||||
|
// TODO: Surely, there's a better way of doing this?
|
||||||
|
if cur.Type().PkgPath() != "main" && cur.Type().PkgPath() != "" {
|
||||||
|
c.importMap["html/template"] = "html/template"
|
||||||
|
varbit += strings.TrimPrefix(cur.Type().PkgPath(), "html/") + "."
|
||||||
}
|
}
|
||||||
out = c.compileVarsub(varholder+varbit, cur)
|
varbit += cur.Type().Name() + ")"
|
||||||
|
}
|
||||||
|
c.log("End Cycle: ", varbit)
|
||||||
|
}
|
||||||
|
|
||||||
|
if multiline {
|
||||||
|
assSplit := strings.Split(varbit, "\n")
|
||||||
|
varbit = assSplit[len(assSplit)-1]
|
||||||
|
assSplit = assSplit[:len(assSplit)-1]
|
||||||
|
assLines = strings.Join(assSplit, "\n") + "\n"
|
||||||
|
}
|
||||||
|
varbit = varholder + varbit
|
||||||
|
out = c.compileVarsub(varbit, cur, assLines)
|
||||||
|
|
||||||
for _, varItem := range c.varList {
|
for _, varItem := range c.varList {
|
||||||
if strings.HasPrefix(out, varItem.Destination) {
|
if strings.HasPrefix(out, varItem.Destination) {
|
||||||
|
@ -389,20 +437,21 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
|
||||||
return out
|
return out
|
||||||
case *parse.DotNode:
|
case *parse.DotNode:
|
||||||
c.log("Dot Node:", node.String())
|
c.log("Dot Node:", node.String())
|
||||||
return c.compileVarsub(varholder, holdreflect)
|
return c.compileVarsub(varholder, holdreflect, "")
|
||||||
case *parse.NilNode:
|
case *parse.NilNode:
|
||||||
panic("Nil is not a command x.x")
|
panic("Nil is not a command x.x")
|
||||||
case *parse.VariableNode:
|
case *parse.VariableNode:
|
||||||
c.log("Variable Node:", n.String())
|
c.log("Variable Node:", n.String())
|
||||||
c.log(n.Ident)
|
c.log(n.Ident)
|
||||||
varname, reflectVal := c.compileIfVarsub(n.String(), varholder, templateName, holdreflect)
|
varname, reflectVal := c.compileIfVarsub(n.String(), varholder, templateName, holdreflect)
|
||||||
return c.compileVarsub(varname, reflectVal)
|
return c.compileVarsub(varname, reflectVal, "")
|
||||||
case *parse.StringNode:
|
case *parse.StringNode:
|
||||||
return n.Quoted
|
return n.Quoted
|
||||||
case *parse.IdentifierNode:
|
case *parse.IdentifierNode:
|
||||||
c.log("Identifier Node:", node)
|
c.log("Identifier Node:", node)
|
||||||
c.log("Identifier Node Args:", node.Args)
|
c.log("Identifier Node Args:", node.Args)
|
||||||
return c.compileVarsub(c.compileIdentSwitch(varholder, holdreflect, templateName, node))
|
out, outval := c.compileIdentSwitch(varholder, holdreflect, templateName, node)
|
||||||
|
return c.compileVarsub(out, outval, "")
|
||||||
default:
|
default:
|
||||||
return c.unknownNode(node)
|
return c.unknownNode(node)
|
||||||
}
|
}
|
||||||
|
@ -525,7 +574,6 @@ func (c *CTemplateSet) compareJoin(varholder string, holdreflect reflect.Value,
|
||||||
|
|
||||||
func (c *CTemplateSet) compileIdentSwitch(varholder string, holdreflect reflect.Value, templateName string, node *parse.CommandNode) (out string, val reflect.Value) {
|
func (c *CTemplateSet) compileIdentSwitch(varholder string, holdreflect reflect.Value, templateName string, node *parse.CommandNode) (out string, val reflect.Value) {
|
||||||
c.log("in compileIdentSwitch")
|
c.log("in compileIdentSwitch")
|
||||||
//var outbuf map[int]string
|
|
||||||
ArgLoop:
|
ArgLoop:
|
||||||
for pos := 0; pos < len(node.Args); pos++ {
|
for pos := 0; pos < len(node.Args); pos++ {
|
||||||
id := node.Args[pos]
|
id := node.Args[pos]
|
||||||
|
@ -728,7 +776,7 @@ func (c *CTemplateSet) compileBoolsub(varname string, varholder string, template
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) compileVarsub(varname string, val reflect.Value) string {
|
func (c *CTemplateSet) compileVarsub(varname string, val reflect.Value, assLines string) (out string) {
|
||||||
c.log("in compileVarsub")
|
c.log("in compileVarsub")
|
||||||
for _, varItem := range c.varList {
|
for _, varItem := range c.varList {
|
||||||
if strings.HasPrefix(varname, varItem.Destination) {
|
if strings.HasPrefix(varname, varItem.Destination) {
|
||||||
|
@ -747,29 +795,33 @@ func (c *CTemplateSet) compileVarsub(varname string, val reflect.Value) string {
|
||||||
val = val.Elem()
|
val = val.Elem()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.log("varname: ", varname)
|
||||||
|
c.log("assLines: ", assLines)
|
||||||
switch val.Kind() {
|
switch val.Kind() {
|
||||||
case reflect.Int:
|
case reflect.Int:
|
||||||
c.importMap["strconv"] = "strconv"
|
c.importMap["strconv"] = "strconv"
|
||||||
return "w.Write([]byte(strconv.Itoa(" + varname + ")))\n"
|
out = "w.Write([]byte(strconv.Itoa(" + varname + ")))\n"
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
return "if " + varname + " {\nw.Write([]byte(\"true\"))} else {\nw.Write([]byte(\"false\"))\n}\n"
|
out = "if " + varname + " {\nw.Write([]byte(\"true\"))} else {\nw.Write([]byte(\"false\"))\n}\n"
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
if val.Type().Name() != "string" && !strings.HasPrefix(varname, "string(") {
|
if val.Type().Name() != "string" && !strings.HasPrefix(varname, "string(") {
|
||||||
return "w.Write([]byte(string(" + varname + ")))\n"
|
varname = "string(" + varname + ")"
|
||||||
}
|
}
|
||||||
return "w.Write([]byte(" + varname + "))\n"
|
out = "w.Write([]byte(" + varname + "))\n"
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
c.importMap["strconv"] = "strconv"
|
c.importMap["strconv"] = "strconv"
|
||||||
return "w.Write([]byte(strconv.FormatInt(" + varname + ", 10)))"
|
out = "w.Write([]byte(strconv.FormatInt(" + varname + ", 10)))"
|
||||||
default:
|
default:
|
||||||
if !val.IsValid() {
|
if !val.IsValid() {
|
||||||
panic(varname + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
panic(assLines + varname + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||||
}
|
}
|
||||||
fmt.Println("Unknown Variable Name:", varname)
|
fmt.Println("Unknown Variable Name:", varname)
|
||||||
fmt.Println("Unknown Kind:", val.Kind())
|
fmt.Println("Unknown Kind:", val.Kind())
|
||||||
fmt.Println("Unknown Type:", val.Type().Name())
|
fmt.Println("Unknown Type:", val.Type().Name())
|
||||||
panic("// I don't know what this variable's type is o.o\n")
|
panic("// I don't know what this variable's type is o.o\n")
|
||||||
}
|
}
|
||||||
|
c.log("out: ", out)
|
||||||
|
return assLines + out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflect.Value, node *parse.TemplateNode) (out string) {
|
func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflect.Value, node *parse.TemplateNode) (out string) {
|
||||||
|
@ -831,6 +883,12 @@ func (c *CTemplateSet) log(args ...interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CTemplateSet) error(args ...interface{}) {
|
||||||
|
if c.debug {
|
||||||
|
fmt.Println(args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *CTemplateSet) compileCommand(*parse.CommandNode) (out string) {
|
func (c *CTemplateSet) compileCommand(*parse.CommandNode) (out string) {
|
||||||
panic("Uh oh! Something went wrong!")
|
panic("Uh oh! Something went wrong!")
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"../query_gen/lib"
|
"../query_gen/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ThemeList map[string]Theme
|
type ThemeList map[string]Theme // ? Use pointers instead?
|
||||||
|
|
||||||
var Themes ThemeList = make(map[string]Theme)
|
var Themes ThemeList = make(map[string]Theme)
|
||||||
var DefaultThemeBox atomic.Value
|
var DefaultThemeBox atomic.Value
|
||||||
|
@ -47,6 +47,7 @@ type Theme struct {
|
||||||
Tag string
|
Tag string
|
||||||
URL string
|
URL string
|
||||||
Sidebars string // Allowed Values: left, right, both, false
|
Sidebars string // Allowed Values: left, right, both, false
|
||||||
|
AboutSegment bool // ? - Should this be a theme var instead?
|
||||||
//DisableMinifier // Is this really a good idea? I don't think themes should be fighting against the minifier
|
//DisableMinifier // Is this really a good idea? I don't think themes should be fighting against the minifier
|
||||||
Settings map[string]ThemeSetting
|
Settings map[string]ThemeSetting
|
||||||
Templates []TemplateMapping
|
Templates []TemplateMapping
|
||||||
|
|
|
@ -149,9 +149,9 @@ func init() {
|
||||||
// Flush the topic out of the cache
|
// Flush the topic out of the cache
|
||||||
// ? - We do a CacheRemove() here instead of mutating the pointer to avoid creating a race condition
|
// ? - We do a CacheRemove() here instead of mutating the pointer to avoid creating a race condition
|
||||||
func (topic *Topic) cacheRemove() {
|
func (topic *Topic) cacheRemove() {
|
||||||
tcache, ok := Topics.(TopicCache)
|
tcache := Topics.(TopicCache)
|
||||||
if ok {
|
if tcache != nil {
|
||||||
tcache.CacheRemove(topic.ID)
|
tcache.Remove(topic.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ func (topic *Topic) Delete() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = Fstore.RemoveTopic(topic.ParentID)
|
err = Forums.RemoveTopic(topic.ParentID)
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -263,10 +263,10 @@ func (topic *Topic) Copy() Topic {
|
||||||
|
|
||||||
// TODO: Refactor the caller to take a Topic and a User rather than a combined TopicUser
|
// TODO: Refactor the caller to take a Topic and a User rather than a combined TopicUser
|
||||||
func GetTopicUser(tid int) (TopicUser, error) {
|
func GetTopicUser(tid int) (TopicUser, error) {
|
||||||
tcache, tok := Topics.(TopicCache)
|
tcache := Topics.GetCache()
|
||||||
ucache, uok := Users.(UserCache)
|
ucache := Users.GetCache()
|
||||||
if tok && uok {
|
if tcache != nil && ucache != nil {
|
||||||
topic, err := tcache.CacheGet(tid)
|
topic, err := tcache.Get(tid)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
user, err := Users.Get(topic.CreatedBy)
|
user, err := Users.Get(topic.CreatedBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -292,12 +292,12 @@ func GetTopicUser(tid int) (TopicUser, error) {
|
||||||
err := topicStmts.getTopicUser.QueryRow(tid).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IPAddress, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level)
|
err := topicStmts.getTopicUser.QueryRow(tid).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IPAddress, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.URLPrefix, &tu.URLName, &tu.Level)
|
||||||
tu.Link = BuildTopicURL(NameToSlug(tu.Title), tu.ID)
|
tu.Link = BuildTopicURL(NameToSlug(tu.Title), tu.ID)
|
||||||
tu.UserLink = BuildProfileURL(NameToSlug(tu.CreatedByName), tu.CreatedBy)
|
tu.UserLink = BuildProfileURL(NameToSlug(tu.CreatedByName), tu.CreatedBy)
|
||||||
tu.Tag = Gstore.DirtyGet(tu.Group).Tag
|
tu.Tag = Groups.DirtyGet(tu.Group).Tag
|
||||||
|
|
||||||
if tok {
|
if tcache != nil {
|
||||||
theTopic := Topic{ID: tu.ID, Link: tu.Link, Title: tu.Title, Content: tu.Content, CreatedBy: tu.CreatedBy, IsClosed: tu.IsClosed, Sticky: tu.Sticky, CreatedAt: tu.CreatedAt, LastReplyAt: tu.LastReplyAt, ParentID: tu.ParentID, IPAddress: tu.IPAddress, PostCount: tu.PostCount, LikeCount: tu.LikeCount}
|
theTopic := Topic{ID: tu.ID, Link: tu.Link, Title: tu.Title, Content: tu.Content, CreatedBy: tu.CreatedBy, IsClosed: tu.IsClosed, Sticky: tu.Sticky, CreatedAt: tu.CreatedAt, LastReplyAt: tu.LastReplyAt, ParentID: tu.ParentID, IPAddress: tu.IPAddress, PostCount: tu.PostCount, LikeCount: tu.LikeCount}
|
||||||
//log.Printf("theTopic: %+v\n", theTopic)
|
//log.Printf("theTopic: %+v\n", theTopic)
|
||||||
_ = tcache.CacheAdd(&theTopic)
|
_ = tcache.Add(&theTopic)
|
||||||
}
|
}
|
||||||
return tu, err
|
return tu, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TopicCache interface {
|
||||||
|
Get(id int) (*Topic, error)
|
||||||
|
GetUnsafe(id int) (*Topic, error)
|
||||||
|
Set(item *Topic) error
|
||||||
|
Add(item *Topic) error
|
||||||
|
AddUnsafe(item *Topic) error
|
||||||
|
Remove(id int) error
|
||||||
|
RemoveUnsafe(id int) error
|
||||||
|
Flush()
|
||||||
|
Length() int
|
||||||
|
SetCapacity(capacity int)
|
||||||
|
GetCapacity() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemoryTopicCache struct {
|
||||||
|
items map[int]*Topic
|
||||||
|
length int64 // sync/atomic only lets us operate on int32s and int64s
|
||||||
|
capacity int
|
||||||
|
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMemoryTopicCache gives you a new instance of MemoryTopicCache
|
||||||
|
func NewMemoryTopicCache(capacity int) *MemoryTopicCache {
|
||||||
|
return &MemoryTopicCache{
|
||||||
|
items: make(map[int]*Topic),
|
||||||
|
capacity: capacity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *MemoryTopicCache) Get(id int) (*Topic, error) {
|
||||||
|
mts.RLock()
|
||||||
|
item, ok := mts.items[id]
|
||||||
|
mts.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
return item, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *MemoryTopicCache) GetUnsafe(id int) (*Topic, error) {
|
||||||
|
item, ok := mts.items[id]
|
||||||
|
if ok {
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
return item, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *MemoryTopicCache) Set(item *Topic) error {
|
||||||
|
mts.Lock()
|
||||||
|
_, ok := mts.items[item.ID]
|
||||||
|
if ok {
|
||||||
|
mts.items[item.ID] = item
|
||||||
|
} else if int(mts.length) >= mts.capacity {
|
||||||
|
mts.Unlock()
|
||||||
|
return ErrStoreCapacityOverflow
|
||||||
|
} else {
|
||||||
|
mts.items[item.ID] = item
|
||||||
|
atomic.AddInt64(&mts.length, 1)
|
||||||
|
}
|
||||||
|
mts.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *MemoryTopicCache) Add(item *Topic) error {
|
||||||
|
if int(mts.length) >= mts.capacity {
|
||||||
|
return ErrStoreCapacityOverflow
|
||||||
|
}
|
||||||
|
mts.Lock()
|
||||||
|
mts.items[item.ID] = item
|
||||||
|
mts.Unlock()
|
||||||
|
atomic.AddInt64(&mts.length, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make these length increments thread-safe. Ditto for the other DataStores
|
||||||
|
func (mts *MemoryTopicCache) AddUnsafe(item *Topic) error {
|
||||||
|
if int(mts.length) >= mts.capacity {
|
||||||
|
return ErrStoreCapacityOverflow
|
||||||
|
}
|
||||||
|
mts.items[item.ID] = item
|
||||||
|
atomic.AddInt64(&mts.length, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make these length decrements thread-safe. Ditto for the other DataStores
|
||||||
|
func (mts *MemoryTopicCache) Remove(id int) error {
|
||||||
|
mts.Lock()
|
||||||
|
delete(mts.items, id)
|
||||||
|
mts.Unlock()
|
||||||
|
atomic.AddInt64(&mts.length, -1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *MemoryTopicCache) RemoveUnsafe(id int) error {
|
||||||
|
delete(mts.items, id)
|
||||||
|
atomic.AddInt64(&mts.length, -1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *MemoryTopicCache) Flush() {
|
||||||
|
mts.Lock()
|
||||||
|
mts.items = make(map[int]*Topic)
|
||||||
|
mts.length = 0
|
||||||
|
mts.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ! Is this concurrent?
|
||||||
|
// Length returns the number of topics in the memory cache
|
||||||
|
func (mts *MemoryTopicCache) Length() int {
|
||||||
|
return int(mts.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *MemoryTopicCache) SetCapacity(capacity int) {
|
||||||
|
mts.capacity = capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mts *MemoryTopicCache) GetCapacity() int {
|
||||||
|
return mts.capacity
|
||||||
|
}
|
|
@ -10,8 +10,6 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"../query_gen/lib"
|
"../query_gen/lib"
|
||||||
)
|
)
|
||||||
|
@ -31,44 +29,33 @@ type TopicStore interface {
|
||||||
Exists(id int) bool
|
Exists(id int) bool
|
||||||
Create(fid int, topicName string, content string, uid int, ipaddress string) (tid int, err error)
|
Create(fid int, topicName string, content string, uid int, ipaddress string) (tid int, err error)
|
||||||
AddLastTopic(item *Topic, fid int) error // unimplemented
|
AddLastTopic(item *Topic, fid int) error // unimplemented
|
||||||
|
Reload(id int) error // Too much SQL logic to move into TopicCache
|
||||||
// TODO: Implement these two methods
|
// TODO: Implement these two methods
|
||||||
//Replies(tid int) ([]*Reply, error)
|
//Replies(tid int) ([]*Reply, error)
|
||||||
//RepliesRange(tid int, lower int, higher int) ([]*Reply, error)
|
//RepliesRange(tid int, lower int, higher int) ([]*Reply, error)
|
||||||
GlobalCount() int
|
GlobalCount() int
|
||||||
|
|
||||||
|
SetCache(cache TopicCache)
|
||||||
|
GetCache() TopicCache
|
||||||
}
|
}
|
||||||
|
|
||||||
type TopicCache interface {
|
type DefaultTopicStore struct {
|
||||||
CacheGet(id int) (*Topic, error)
|
cache TopicCache
|
||||||
CacheGetUnsafe(id int) (*Topic, error)
|
|
||||||
CacheSet(item *Topic) error
|
|
||||||
CacheAdd(item *Topic) error
|
|
||||||
CacheAddUnsafe(item *Topic) error
|
|
||||||
CacheRemove(id int) error
|
|
||||||
CacheRemoveUnsafe(id int) error
|
|
||||||
Flush()
|
|
||||||
Reload(id int) error
|
|
||||||
Length() int
|
|
||||||
SetCapacity(capacity int)
|
|
||||||
GetCapacity() int
|
|
||||||
}
|
|
||||||
|
|
||||||
type MemoryTopicStore struct {
|
|
||||||
items map[int]*Topic
|
|
||||||
length int64 // sync/atomic only lets us operate on int32s and int64s
|
|
||||||
capacity int
|
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
exists *sql.Stmt
|
exists *sql.Stmt
|
||||||
topicCount *sql.Stmt
|
topicCount *sql.Stmt
|
||||||
create *sql.Stmt
|
create *sql.Stmt
|
||||||
sync.RWMutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMemoryTopicStore gives you a new instance of MemoryTopicStore
|
// NewDefaultTopicStore gives you a new instance of DefaultTopicStore
|
||||||
func NewMemoryTopicStore(capacity int) (*MemoryTopicStore, error) {
|
func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.Builder.Accumulator()
|
||||||
return &MemoryTopicStore{
|
if cache == nil {
|
||||||
items: make(map[int]*Topic),
|
cache = NewNullTopicCache()
|
||||||
capacity: capacity,
|
}
|
||||||
|
return &DefaultTopicStore{
|
||||||
|
cache: cache,
|
||||||
get: acc.Select("topics").Columns("title, content, createdBy, createdAt, lastReplyAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data").Where("tid = ?").Prepare(),
|
get: acc.Select("topics").Columns("title, content, createdBy, createdAt, lastReplyAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data").Where("tid = ?").Prepare(),
|
||||||
exists: acc.Select("topics").Columns("tid").Where("tid = ?").Prepare(),
|
exists: acc.Select("topics").Columns("tid").Where("tid = ?").Prepare(),
|
||||||
topicCount: acc.Count("topics").Prepare(),
|
topicCount: acc.Count("topics").Prepare(),
|
||||||
|
@ -76,84 +63,63 @@ func NewMemoryTopicStore(capacity int) (*MemoryTopicStore, error) {
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) CacheGet(id int) (*Topic, error) {
|
func (mts *DefaultTopicStore) DirtyGet(id int) *Topic {
|
||||||
mts.RLock()
|
topic, err := mts.cache.Get(id)
|
||||||
item, ok := mts.items[id]
|
if err == nil {
|
||||||
mts.RUnlock()
|
|
||||||
if ok {
|
|
||||||
return item, nil
|
|
||||||
}
|
|
||||||
return item, ErrNoRows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) CacheGetUnsafe(id int) (*Topic, error) {
|
|
||||||
item, ok := mts.items[id]
|
|
||||||
if ok {
|
|
||||||
return item, nil
|
|
||||||
}
|
|
||||||
return item, ErrNoRows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) DirtyGet(id int) *Topic {
|
|
||||||
mts.RLock()
|
|
||||||
topic, ok := mts.items[id]
|
|
||||||
mts.RUnlock()
|
|
||||||
if ok {
|
|
||||||
return topic
|
return topic
|
||||||
}
|
}
|
||||||
|
|
||||||
topic = &Topic{ID: id}
|
topic = &Topic{ID: id}
|
||||||
err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
err = mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
||||||
_ = mts.CacheAdd(topic)
|
_ = mts.cache.Add(topic)
|
||||||
return topic
|
return topic
|
||||||
}
|
}
|
||||||
return BlankTopic()
|
return BlankTopic()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) Get(id int) (*Topic, error) {
|
// TODO: Log weird cache errors?
|
||||||
mts.RLock()
|
func (mts *DefaultTopicStore) Get(id int) (topic *Topic, err error) {
|
||||||
topic, ok := mts.items[id]
|
topic, err = mts.cache.Get(id)
|
||||||
mts.RUnlock()
|
if err == nil {
|
||||||
if ok {
|
|
||||||
return topic, nil
|
return topic, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
topic = &Topic{ID: id}
|
topic = &Topic{ID: id}
|
||||||
err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
err = mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
||||||
_ = mts.CacheAdd(topic)
|
_ = mts.cache.Add(topic)
|
||||||
}
|
}
|
||||||
return topic, err
|
return topic, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// BypassGet will always bypass the cache and pull the topic directly from the database
|
// BypassGet will always bypass the cache and pull the topic directly from the database
|
||||||
func (mts *MemoryTopicStore) BypassGet(id int) (*Topic, error) {
|
func (mts *DefaultTopicStore) BypassGet(id int) (*Topic, error) {
|
||||||
topic := &Topic{ID: id}
|
topic := &Topic{ID: id}
|
||||||
err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
||||||
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
||||||
return topic, err
|
return topic, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) Reload(id int) error {
|
func (mts *DefaultTopicStore) Reload(id int) error {
|
||||||
topic := &Topic{ID: id}
|
topic := &Topic{ID: id}
|
||||||
err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
err := mts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
||||||
_ = mts.CacheSet(topic)
|
_ = mts.cache.Set(topic)
|
||||||
} else {
|
} else {
|
||||||
_ = mts.CacheRemove(id)
|
_ = mts.cache.Remove(id)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) Exists(id int) bool {
|
func (mts *DefaultTopicStore) Exists(id int) bool {
|
||||||
return mts.exists.QueryRow(id).Scan(&id) == nil
|
return mts.exists.QueryRow(id).Scan(&id) == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) Create(fid int, topicName string, content string, uid int, ipaddress string) (tid int, err error) {
|
func (mts *DefaultTopicStore) Create(fid int, topicName string, content string, uid int, ipaddress string) (tid int, err error) {
|
||||||
topicName = strings.TrimSpace(topicName)
|
topicName = strings.TrimSpace(topicName)
|
||||||
if topicName == "" {
|
if topicName == "" {
|
||||||
return 0, ErrNoBody
|
return 0, ErrNoBody
|
||||||
|
@ -177,91 +143,17 @@ func (mts *MemoryTopicStore) Create(fid int, topicName string, content string, u
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return int(lastID), Fstore.AddTopic(int(lastID), uid, fid)
|
return int(lastID), Forums.AddTopic(int(lastID), uid, fid)
|
||||||
}
|
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) CacheSet(item *Topic) error {
|
|
||||||
mts.Lock()
|
|
||||||
_, ok := mts.items[item.ID]
|
|
||||||
if ok {
|
|
||||||
mts.items[item.ID] = item
|
|
||||||
} else if int(mts.length) >= mts.capacity {
|
|
||||||
mts.Unlock()
|
|
||||||
return ErrStoreCapacityOverflow
|
|
||||||
} else {
|
|
||||||
mts.items[item.ID] = item
|
|
||||||
atomic.AddInt64(&mts.length, 1)
|
|
||||||
}
|
|
||||||
mts.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) CacheAdd(item *Topic) error {
|
|
||||||
if int(mts.length) >= mts.capacity {
|
|
||||||
return ErrStoreCapacityOverflow
|
|
||||||
}
|
|
||||||
mts.Lock()
|
|
||||||
mts.items[item.ID] = item
|
|
||||||
mts.Unlock()
|
|
||||||
atomic.AddInt64(&mts.length, 1)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Make these length increments thread-safe. Ditto for the other DataStores
|
|
||||||
func (mts *MemoryTopicStore) CacheAddUnsafe(item *Topic) error {
|
|
||||||
if int(mts.length) >= mts.capacity {
|
|
||||||
return ErrStoreCapacityOverflow
|
|
||||||
}
|
|
||||||
mts.items[item.ID] = item
|
|
||||||
atomic.AddInt64(&mts.length, 1)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Make these length decrements thread-safe. Ditto for the other DataStores
|
|
||||||
func (mts *MemoryTopicStore) CacheRemove(id int) error {
|
|
||||||
mts.Lock()
|
|
||||||
delete(mts.items, id)
|
|
||||||
mts.Unlock()
|
|
||||||
atomic.AddInt64(&mts.length, -1)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) CacheRemoveUnsafe(id int) error {
|
|
||||||
delete(mts.items, id)
|
|
||||||
atomic.AddInt64(&mts.length, -1)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ? - What is this? Do we need it? Should it be in the main store interface?
|
// ? - What is this? Do we need it? Should it be in the main store interface?
|
||||||
func (mts *MemoryTopicStore) AddLastTopic(item *Topic, fid int) error {
|
func (mts *DefaultTopicStore) AddLastTopic(item *Topic, fid int) error {
|
||||||
// Coming Soon...
|
// Coming Soon...
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) Flush() {
|
|
||||||
mts.Lock()
|
|
||||||
mts.items = make(map[int]*Topic)
|
|
||||||
mts.length = 0
|
|
||||||
mts.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ! Is this concurrent?
|
|
||||||
// Length returns the number of topics in the memory cache
|
|
||||||
func (mts *MemoryTopicStore) Length() int {
|
|
||||||
return int(mts.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) SetCapacity(capacity int) {
|
|
||||||
mts.capacity = capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mts *MemoryTopicStore) GetCapacity() int {
|
|
||||||
return mts.capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalCount returns the total number of topics on these forums
|
// GlobalCount returns the total number of topics on these forums
|
||||||
func (mts *MemoryTopicStore) GlobalCount() int {
|
func (mts *DefaultTopicStore) GlobalCount() (tcount int) {
|
||||||
var tcount int
|
|
||||||
err := mts.topicCount.QueryRow().Scan(&tcount)
|
err := mts.topicCount.QueryRow().Scan(&tcount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
|
@ -269,91 +161,15 @@ func (mts *MemoryTopicStore) GlobalCount() int {
|
||||||
return tcount
|
return tcount
|
||||||
}
|
}
|
||||||
|
|
||||||
type SQLTopicStore struct {
|
func (mts *DefaultTopicStore) SetCache(cache TopicCache) {
|
||||||
get *sql.Stmt
|
mts.cache = cache
|
||||||
exists *sql.Stmt
|
|
||||||
topicCount *sql.Stmt
|
|
||||||
create *sql.Stmt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSQLTopicStore() (*SQLTopicStore, error) {
|
// TODO: We're temporarily doing this so that you can do tcache != nil in getTopicUser. Refactor it.
|
||||||
acc := qgen.Builder.Accumulator()
|
func (mts *DefaultTopicStore) GetCache() TopicCache {
|
||||||
return &SQLTopicStore{
|
_, ok := mts.cache.(*NullTopicCache)
|
||||||
get: acc.Select("topics").Columns("title, content, createdBy, createdAt, lastReplyAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data").Where("tid = ?").Prepare(),
|
if ok {
|
||||||
exists: acc.Select("topics").Columns("tid").Where("tid = ?").Prepare(),
|
|
||||||
topicCount: acc.Count("topics").Prepare(),
|
|
||||||
create: acc.Insert("topics").Columns("parentID, title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, ipaddress, words, createdBy").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?").Prepare(),
|
|
||||||
}, acc.FirstError()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sts *SQLTopicStore) DirtyGet(id int) *Topic {
|
|
||||||
topic := &Topic{ID: id}
|
|
||||||
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
|
||||||
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
|
||||||
if err != nil {
|
|
||||||
return BlankTopic()
|
|
||||||
}
|
|
||||||
return topic
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sts *SQLTopicStore) Get(id int) (*Topic, error) {
|
|
||||||
topic := Topic{ID: id}
|
|
||||||
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
|
||||||
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
|
||||||
return &topic, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// BypassGet is an alias of Get(), as we don't have a cache for SQLTopicStore
|
|
||||||
func (sts *SQLTopicStore) BypassGet(id int) (*Topic, error) {
|
|
||||||
topic := &Topic{ID: id}
|
|
||||||
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.LastReplyAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
|
||||||
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
|
||||||
return topic, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sts *SQLTopicStore) Exists(id int) bool {
|
|
||||||
return sts.exists.QueryRow(id).Scan(&id) == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sts *SQLTopicStore) Create(fid int, topicName string, content string, uid int, ipaddress string) (tid int, err error) {
|
|
||||||
topicName = strings.TrimSpace(topicName)
|
|
||||||
if topicName == "" {
|
|
||||||
return 0, ErrNoBody
|
|
||||||
}
|
|
||||||
|
|
||||||
content = strings.TrimSpace(content)
|
|
||||||
parsedContent := ParseMessage(content, fid, "forums")
|
|
||||||
if strings.TrimSpace(parsedContent) == "" {
|
|
||||||
return 0, ErrNoBody
|
|
||||||
}
|
|
||||||
|
|
||||||
wcount := WordCount(content)
|
|
||||||
// TODO: Move this statement into the topic store
|
|
||||||
res, err := sts.create.Exec(fid, topicName, content, parsedContent, uid, ipaddress, wcount, uid)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
lastID, err := res.LastInsertId()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return int(lastID), Fstore.AddTopic(int(lastID), uid, fid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ? - What're we going to do about this?
|
|
||||||
func (sts *SQLTopicStore) AddLastTopic(item *Topic, fid int) error {
|
|
||||||
// Coming Soon...
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return mts.cache
|
||||||
// GlobalCount returns the total number of topics on these forums
|
|
||||||
func (sts *SQLTopicStore) GlobalCount() int {
|
|
||||||
var tcount int
|
|
||||||
err := sts.topicCount.QueryRow().Scan(&tcount)
|
|
||||||
if err != nil {
|
|
||||||
LogError(err)
|
|
||||||
}
|
|
||||||
return tcount
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
//"log"
|
|
||||||
//"fmt"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -111,14 +109,15 @@ func (user *User) Init() {
|
||||||
user.Avatar = strings.Replace(Config.Noavatar, "{id}", strconv.Itoa(user.ID), 1)
|
user.Avatar = strings.Replace(Config.Noavatar, "{id}", strconv.Itoa(user.ID), 1)
|
||||||
}
|
}
|
||||||
user.Link = BuildProfileURL(NameToSlug(user.Name), user.ID)
|
user.Link = BuildProfileURL(NameToSlug(user.Name), user.ID)
|
||||||
user.Tag = Gstore.DirtyGet(user.Group).Tag
|
user.Tag = Groups.DirtyGet(user.Group).Tag
|
||||||
user.InitPerms()
|
user.InitPerms()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Refactor this idiom into something shorter, maybe with a NullUserCache when one isn't set?
|
||||||
func (user *User) CacheRemove() {
|
func (user *User) CacheRemove() {
|
||||||
ucache, ok := Users.(UserCache)
|
ucache := Users.GetCache()
|
||||||
if ok {
|
if ucache != nil {
|
||||||
ucache.CacheRemove(user.ID)
|
ucache.Remove(user.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,7 +336,7 @@ func (user *User) InitPerms() {
|
||||||
user.Group = user.TempGroup
|
user.Group = user.TempGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
group := Gstore.DirtyGet(user.Group)
|
group := Groups.DirtyGet(user.Group)
|
||||||
if user.IsSuperAdmin {
|
if user.IsSuperAdmin {
|
||||||
user.Perms = AllPerms
|
user.Perms = AllPerms
|
||||||
user.PluginPerms = AllPluginPerms
|
user.PluginPerms = AllPluginPerms
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserCache interface {
|
||||||
|
Get(id int) (*User, error)
|
||||||
|
GetUnsafe(id int) (*User, error)
|
||||||
|
BulkGet(ids []int) (list []*User)
|
||||||
|
Set(item *User) error
|
||||||
|
Add(item *User) error
|
||||||
|
AddUnsafe(item *User) error
|
||||||
|
Remove(id int) error
|
||||||
|
RemoveUnsafe(id int) error
|
||||||
|
Flush()
|
||||||
|
Length() int
|
||||||
|
SetCapacity(capacity int)
|
||||||
|
GetCapacity() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemoryUserCache struct {
|
||||||
|
items map[int]*User
|
||||||
|
length int64
|
||||||
|
capacity int
|
||||||
|
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMemoryUserCache gives you a new instance of MemoryUserCache
|
||||||
|
func NewMemoryUserCache(capacity int) *MemoryUserCache {
|
||||||
|
return &MemoryUserCache{
|
||||||
|
items: make(map[int]*User),
|
||||||
|
capacity: capacity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryUserCache) Get(id int) (*User, error) {
|
||||||
|
mus.RLock()
|
||||||
|
item, ok := mus.items[id]
|
||||||
|
mus.RUnlock()
|
||||||
|
if ok {
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
return item, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryUserCache) BulkGet(ids []int) (list []*User) {
|
||||||
|
list = make([]*User, len(ids))
|
||||||
|
mus.RLock()
|
||||||
|
for i, id := range ids {
|
||||||
|
list[i] = mus.items[id]
|
||||||
|
}
|
||||||
|
mus.RUnlock()
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryUserCache) GetUnsafe(id int) (*User, error) {
|
||||||
|
item, ok := mus.items[id]
|
||||||
|
if ok {
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
return item, ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryUserCache) Set(item *User) error {
|
||||||
|
mus.Lock()
|
||||||
|
user, ok := mus.items[item.ID]
|
||||||
|
if ok {
|
||||||
|
mus.Unlock()
|
||||||
|
*user = *item
|
||||||
|
} else if int(mus.length) >= mus.capacity {
|
||||||
|
mus.Unlock()
|
||||||
|
return ErrStoreCapacityOverflow
|
||||||
|
} else {
|
||||||
|
mus.items[item.ID] = item
|
||||||
|
mus.Unlock()
|
||||||
|
atomic.AddInt64(&mus.length, 1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryUserCache) Add(item *User) error {
|
||||||
|
if int(mus.length) >= mus.capacity {
|
||||||
|
return ErrStoreCapacityOverflow
|
||||||
|
}
|
||||||
|
mus.Lock()
|
||||||
|
mus.items[item.ID] = item
|
||||||
|
mus.length = int64(len(mus.items))
|
||||||
|
mus.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryUserCache) AddUnsafe(item *User) error {
|
||||||
|
if int(mus.length) >= mus.capacity {
|
||||||
|
return ErrStoreCapacityOverflow
|
||||||
|
}
|
||||||
|
mus.items[item.ID] = item
|
||||||
|
mus.length = int64(len(mus.items))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryUserCache) Remove(id int) error {
|
||||||
|
mus.Lock()
|
||||||
|
_, ok := mus.items[id]
|
||||||
|
if !ok {
|
||||||
|
mus.Unlock()
|
||||||
|
return ErrNoRows
|
||||||
|
}
|
||||||
|
delete(mus.items, id)
|
||||||
|
mus.Unlock()
|
||||||
|
atomic.AddInt64(&mus.length, -1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryUserCache) RemoveUnsafe(id int) error {
|
||||||
|
_, ok := mus.items[id]
|
||||||
|
if !ok {
|
||||||
|
return ErrNoRows
|
||||||
|
}
|
||||||
|
delete(mus.items, id)
|
||||||
|
atomic.AddInt64(&mus.length, -1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryUserCache) Flush() {
|
||||||
|
mus.Lock()
|
||||||
|
mus.items = make(map[int]*User)
|
||||||
|
mus.length = 0
|
||||||
|
mus.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ! Is this concurrent?
|
||||||
|
// Length returns the number of users in the memory cache
|
||||||
|
func (mus *MemoryUserCache) Length() int {
|
||||||
|
return int(mus.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryUserCache) SetCapacity(capacity int) {
|
||||||
|
mus.capacity = capacity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mus *MemoryUserCache) GetCapacity() int {
|
||||||
|
return mus.capacity
|
||||||
|
}
|
|
@ -5,8 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"../query_gen/lib"
|
"../query_gen/lib"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
@ -25,43 +23,32 @@ type UserStore interface {
|
||||||
BulkGetMap(ids []int) (map[int]*User, error)
|
BulkGetMap(ids []int) (map[int]*User, error)
|
||||||
BypassGet(id int) (*User, error)
|
BypassGet(id int) (*User, error)
|
||||||
Create(username string, password string, email string, group int, active bool) (int, error)
|
Create(username string, password string, email string, group int, active bool) (int, error)
|
||||||
GlobalCount() int
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserCache interface {
|
|
||||||
CacheGet(id int) (*User, error)
|
|
||||||
CacheGetUnsafe(id int) (*User, error)
|
|
||||||
CacheSet(item *User) error
|
|
||||||
CacheAdd(item *User) error
|
|
||||||
CacheAddUnsafe(item *User) error
|
|
||||||
CacheRemove(id int) error
|
|
||||||
CacheRemoveUnsafe(id int) error
|
|
||||||
Flush()
|
|
||||||
Reload(id int) error
|
Reload(id int) error
|
||||||
Length() int
|
GlobalCount() int
|
||||||
SetCapacity(capacity int)
|
|
||||||
GetCapacity() int
|
SetCache(cache UserCache)
|
||||||
|
GetCache() UserCache
|
||||||
}
|
}
|
||||||
|
|
||||||
type MemoryUserStore struct {
|
type DefaultUserStore struct {
|
||||||
items map[int]*User
|
cache UserCache
|
||||||
length int64
|
|
||||||
capacity int
|
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
exists *sql.Stmt
|
exists *sql.Stmt
|
||||||
register *sql.Stmt
|
register *sql.Stmt
|
||||||
usernameExists *sql.Stmt
|
usernameExists *sql.Stmt
|
||||||
userCount *sql.Stmt
|
userCount *sql.Stmt
|
||||||
sync.RWMutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMemoryUserStore gives you a new instance of MemoryUserStore
|
// NewDefaultUserStore gives you a new instance of DefaultUserStore
|
||||||
func NewMemoryUserStore(capacity int) (*MemoryUserStore, error) {
|
func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.Builder.Accumulator()
|
||||||
|
if cache == nil {
|
||||||
|
cache = NewNullUserCache()
|
||||||
|
}
|
||||||
// TODO: Add an admin version of registerStmt with more flexibility?
|
// TODO: Add an admin version of registerStmt with more flexibility?
|
||||||
return &MemoryUserStore{
|
return &DefaultUserStore{
|
||||||
items: make(map[int]*User),
|
cache: cache,
|
||||||
capacity: capacity,
|
|
||||||
get: acc.SimpleSelect("users", "name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group", "uid = ?", "", ""),
|
get: acc.SimpleSelect("users", "name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group", "uid = ?", "", ""),
|
||||||
exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""),
|
exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""),
|
||||||
register: acc.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt", "?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()"),
|
register: acc.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt", "?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()"),
|
||||||
|
@ -70,75 +57,43 @@ func NewMemoryUserStore(capacity int) (*MemoryUserStore, error) {
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mus *MemoryUserStore) CacheGet(id int) (*User, error) {
|
func (mus *DefaultUserStore) DirtyGet(id int) *User {
|
||||||
mus.RLock()
|
user, err := mus.cache.Get(id)
|
||||||
item, ok := mus.items[id]
|
if err == nil {
|
||||||
mus.RUnlock()
|
|
||||||
if ok {
|
|
||||||
return item, nil
|
|
||||||
}
|
|
||||||
return item, ErrNoRows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mus *MemoryUserStore) CacheGetUnsafe(id int) (*User, error) {
|
|
||||||
item, ok := mus.items[id]
|
|
||||||
if ok {
|
|
||||||
return item, nil
|
|
||||||
}
|
|
||||||
return item, ErrNoRows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mus *MemoryUserStore) DirtyGet(id int) *User {
|
|
||||||
mus.RLock()
|
|
||||||
user, ok := mus.items[id]
|
|
||||||
mus.RUnlock()
|
|
||||||
if ok {
|
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
user.Init()
|
user.Init()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
mus.CacheSet(user)
|
mus.cache.Set(user)
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
return BlankUser()
|
return BlankUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mus *MemoryUserStore) Get(id int) (*User, error) {
|
// TODO: Log weird cache errors? Not just here but in every *Cache?
|
||||||
mus.RLock()
|
func (mus *DefaultUserStore) Get(id int) (*User, error) {
|
||||||
user, ok := mus.items[id]
|
user, err := mus.cache.Get(id)
|
||||||
mus.RUnlock()
|
if err == nil {
|
||||||
if ok {
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
user.Init()
|
user.Init()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
mus.CacheSet(user)
|
mus.cache.Set(user)
|
||||||
}
|
}
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING: We did a little hack to make this as thin and quick as possible to reduce lock contention, use the * Cascade* methods instead for normal use
|
|
||||||
func (mus *MemoryUserStore) bulkGet(ids []int) (list []*User) {
|
|
||||||
list = make([]*User, len(ids))
|
|
||||||
mus.RLock()
|
|
||||||
for i, id := range ids {
|
|
||||||
list[i] = mus.items[id]
|
|
||||||
}
|
|
||||||
mus.RUnlock()
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
||||||
// TODO: ID of 0 should always error?
|
// TODO: ID of 0 should always error?
|
||||||
func (mus *MemoryUserStore) BulkGetMap(ids []int) (list map[int]*User, err error) {
|
func (mus *DefaultUserStore) BulkGetMap(ids []int) (list map[int]*User, err error) {
|
||||||
var idCount = len(ids)
|
var idCount = len(ids)
|
||||||
list = make(map[int]*User)
|
list = make(map[int]*User)
|
||||||
if idCount == 0 {
|
if idCount == 0 {
|
||||||
|
@ -146,7 +101,7 @@ func (mus *MemoryUserStore) BulkGetMap(ids []int) (list map[int]*User, err error
|
||||||
}
|
}
|
||||||
|
|
||||||
var stillHere []int
|
var stillHere []int
|
||||||
sliceList := mus.bulkGet(ids)
|
sliceList := mus.cache.BulkGet(ids)
|
||||||
for i, sliceItem := range sliceList {
|
for i, sliceItem := range sliceList {
|
||||||
if sliceItem != nil {
|
if sliceItem != nil {
|
||||||
list[sliceItem.ID] = sliceItem
|
list[sliceItem.ID] = sliceItem
|
||||||
|
@ -184,7 +139,7 @@ func (mus *MemoryUserStore) BulkGetMap(ids []int) (list map[int]*User, err error
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Init()
|
user.Init()
|
||||||
mus.CacheSet(user)
|
mus.cache.Set(user)
|
||||||
list[user.ID] = user
|
list[user.ID] = user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +171,7 @@ func (mus *MemoryUserStore) BulkGetMap(ids []int) (list map[int]*User, err error
|
||||||
return list, err
|
return list, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mus *MemoryUserStore) BypassGet(id int) (*User, error) {
|
func (mus *DefaultUserStore) 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)
|
||||||
|
|
||||||
|
@ -224,20 +179,20 @@ func (mus *MemoryUserStore) BypassGet(id int) (*User, error) {
|
||||||
return user, err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mus *MemoryUserStore) Reload(id int) error {
|
func (mus *DefaultUserStore) Reload(id int) 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 err != nil {
|
if err != nil {
|
||||||
mus.CacheRemove(id)
|
mus.cache.Remove(id)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Init()
|
user.Init()
|
||||||
_ = mus.CacheSet(user)
|
_ = mus.cache.Set(user)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mus *MemoryUserStore) Exists(id int) bool {
|
func (mus *DefaultUserStore) Exists(id int) bool {
|
||||||
err := mus.exists.QueryRow(id).Scan(&id)
|
err := mus.exists.QueryRow(id).Scan(&id)
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
|
@ -245,210 +200,8 @@ func (mus *MemoryUserStore) Exists(id int) bool {
|
||||||
return err != ErrNoRows
|
return err != ErrNoRows
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mus *MemoryUserStore) CacheSet(item *User) error {
|
|
||||||
mus.Lock()
|
|
||||||
user, ok := mus.items[item.ID]
|
|
||||||
if ok {
|
|
||||||
mus.Unlock()
|
|
||||||
*user = *item
|
|
||||||
} else if int(mus.length) >= mus.capacity {
|
|
||||||
mus.Unlock()
|
|
||||||
return ErrStoreCapacityOverflow
|
|
||||||
} else {
|
|
||||||
mus.items[item.ID] = item
|
|
||||||
mus.Unlock()
|
|
||||||
atomic.AddInt64(&mus.length, 1)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mus *MemoryUserStore) CacheAdd(item *User) error {
|
|
||||||
if int(mus.length) >= mus.capacity {
|
|
||||||
return ErrStoreCapacityOverflow
|
|
||||||
}
|
|
||||||
mus.Lock()
|
|
||||||
mus.items[item.ID] = item
|
|
||||||
mus.length = int64(len(mus.items))
|
|
||||||
mus.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mus *MemoryUserStore) CacheAddUnsafe(item *User) error {
|
|
||||||
if int(mus.length) >= mus.capacity {
|
|
||||||
return ErrStoreCapacityOverflow
|
|
||||||
}
|
|
||||||
mus.items[item.ID] = item
|
|
||||||
mus.length = int64(len(mus.items))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mus *MemoryUserStore) CacheRemove(id int) error {
|
|
||||||
mus.Lock()
|
|
||||||
_, ok := mus.items[id]
|
|
||||||
if !ok {
|
|
||||||
mus.Unlock()
|
|
||||||
return ErrNoRows
|
|
||||||
}
|
|
||||||
delete(mus.items, id)
|
|
||||||
mus.Unlock()
|
|
||||||
atomic.AddInt64(&mus.length, -1)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mus *MemoryUserStore) CacheRemoveUnsafe(id int) error {
|
|
||||||
_, ok := mus.items[id]
|
|
||||||
if !ok {
|
|
||||||
return ErrNoRows
|
|
||||||
}
|
|
||||||
delete(mus.items, id)
|
|
||||||
atomic.AddInt64(&mus.length, -1)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Change active to a bool?
|
// TODO: Change active to a bool?
|
||||||
func (mus *MemoryUserStore) Create(username string, password string, email string, group int, active bool) (int, error) {
|
func (mus *DefaultUserStore) Create(username string, password string, email string, group int, active bool) (int, error) {
|
||||||
// Is this username already taken..?
|
|
||||||
err := mus.usernameExists.QueryRow(username).Scan(&username)
|
|
||||||
if err != ErrNoRows {
|
|
||||||
return 0, ErrAccountExists
|
|
||||||
}
|
|
||||||
|
|
||||||
salt, err := GenerateSafeString(SaltLength)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password+salt), bcrypt.DefaultCost)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := mus.register.Exec(username, email, string(hashedPassword), salt, group, active)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
lastID, err := res.LastInsertId()
|
|
||||||
return int(lastID), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mus *MemoryUserStore) Flush() {
|
|
||||||
mus.Lock()
|
|
||||||
mus.items = make(map[int]*User)
|
|
||||||
mus.length = 0
|
|
||||||
mus.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ! Is this concurrent?
|
|
||||||
// Length returns the number of users in the memory cache
|
|
||||||
func (mus *MemoryUserStore) Length() int {
|
|
||||||
return int(mus.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mus *MemoryUserStore) SetCapacity(capacity int) {
|
|
||||||
mus.capacity = capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mus *MemoryUserStore) GetCapacity() int {
|
|
||||||
return mus.capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalCount returns the total number of users registered on the forums
|
|
||||||
func (mus *MemoryUserStore) GlobalCount() (ucount int) {
|
|
||||||
err := mus.userCount.QueryRow().Scan(&ucount)
|
|
||||||
if err != nil {
|
|
||||||
LogError(err)
|
|
||||||
}
|
|
||||||
return ucount
|
|
||||||
}
|
|
||||||
|
|
||||||
type SQLUserStore struct {
|
|
||||||
get *sql.Stmt
|
|
||||||
exists *sql.Stmt
|
|
||||||
register *sql.Stmt
|
|
||||||
usernameExists *sql.Stmt
|
|
||||||
userCount *sql.Stmt
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSQLUserStore() (*SQLUserStore, error) {
|
|
||||||
acc := qgen.Builder.Accumulator()
|
|
||||||
// TODO: Add an admin version of registerStmt with more flexibility?
|
|
||||||
return &SQLUserStore{
|
|
||||||
get: acc.SimpleSelect("users", "name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group", "uid = ?", "", ""),
|
|
||||||
exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""),
|
|
||||||
register: acc.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt", "?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()"),
|
|
||||||
usernameExists: acc.SimpleSelect("users", "name", "name = ?", "", ""),
|
|
||||||
userCount: acc.SimpleCount("users", "", ""),
|
|
||||||
}, acc.FirstError()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mus *SQLUserStore) DirtyGet(id int) *User {
|
|
||||||
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)
|
|
||||||
|
|
||||||
user.Init()
|
|
||||||
if err != nil {
|
|
||||||
return BlankUser()
|
|
||||||
}
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
user.Init()
|
|
||||||
return user, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
|
||||||
func (mus *SQLUserStore) BulkGetMap(ids []int) (list map[int]*User, err error) {
|
|
||||||
var qlist string
|
|
||||||
var uidList []interface{}
|
|
||||||
for _, id := range ids {
|
|
||||||
uidList = append(uidList, strconv.Itoa(id))
|
|
||||||
qlist += "?,"
|
|
||||||
}
|
|
||||||
qlist = qlist[0 : len(qlist)-1]
|
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
|
||||||
rows, err := acc.Select("users").Columns("uid, name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group").Where("uid IN(" + qlist + ")").Query(uidList...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
list = make(map[int]*User)
|
|
||||||
for rows.Next() {
|
|
||||||
user := &User{Loggedin: true}
|
|
||||||
err := rows.Scan(&user.ID, &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 err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
user.Init()
|
|
||||||
list[user.ID] = user
|
|
||||||
}
|
|
||||||
|
|
||||||
return list, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
user.Init()
|
|
||||||
return user, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mus *SQLUserStore) Exists(id int) bool {
|
|
||||||
err := mus.exists.QueryRow(id).Scan(&id)
|
|
||||||
if err != nil && err != ErrNoRows {
|
|
||||||
LogError(err)
|
|
||||||
}
|
|
||||||
return err != ErrNoRows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mus *SQLUserStore) Create(username string, password string, email string, group int, active bool) (int, error) {
|
|
||||||
// Is this username already taken..?
|
// Is this username already taken..?
|
||||||
err := mus.usernameExists.QueryRow(username).Scan(&username)
|
err := mus.usernameExists.QueryRow(username).Scan(&username)
|
||||||
if err != ErrNoRows {
|
if err != ErrNoRows {
|
||||||
|
@ -475,7 +228,7 @@ func (mus *SQLUserStore) Create(username string, password string, email string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalCount returns the total number of users registered on the forums
|
// GlobalCount returns the total number of users registered on the forums
|
||||||
func (mus *SQLUserStore) GlobalCount() (ucount int) {
|
func (mus *DefaultUserStore) GlobalCount() (ucount int) {
|
||||||
err := mus.userCount.QueryRow().Scan(&ucount)
|
err := mus.userCount.QueryRow().Scan(&ucount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
|
@ -483,54 +236,15 @@ func (mus *SQLUserStore) GlobalCount() (ucount int) {
|
||||||
return ucount
|
return ucount
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: MockUserStore
|
func (mus *DefaultUserStore) SetCache(cache UserCache) {
|
||||||
|
mus.cache = cache
|
||||||
// NullUserStore is here for tests because Go doesn't have short-circuiting
|
|
||||||
type NullUserStore struct {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nus *NullUserStore) CacheGet(_ int) (*User, error) {
|
// TODO: We're temporarily doing this so that you can do ucache != nil in getTopicUser. Refactor it.
|
||||||
return nil, ErrNoRows
|
func (mus *DefaultUserStore) GetCache() UserCache {
|
||||||
|
_, ok := mus.cache.(*NullUserCache)
|
||||||
|
if ok {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
return mus.cache
|
||||||
func (nus *NullUserStore) CacheGetUnsafe(_ int) (*User, error) {
|
|
||||||
return nil, ErrNoRows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nus *NullUserStore) CacheSet(_ *User) error {
|
|
||||||
return ErrStoreCapacityOverflow
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nus *NullUserStore) CacheAdd(_ *User) error {
|
|
||||||
return ErrStoreCapacityOverflow
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nus *NullUserStore) CacheAddUnsafe(_ *User) error {
|
|
||||||
return ErrStoreCapacityOverflow
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nus *NullUserStore) CacheRemove(_ int) error {
|
|
||||||
return ErrNoRows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nus *NullUserStore) CacheRemoveUnsafe(_ int) error {
|
|
||||||
return ErrNoRows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nus *NullUserStore) Flush() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nus *NullUserStore) Reload(_ int) error {
|
|
||||||
return ErrNoRows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nus *NullUserStore) Length() int {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nus *NullUserStore) SetCapacity(_ int) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (nus *NullUserStore) GetCapacity() int {
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,26 +33,37 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadWordFilters() error {
|
func LoadWordFilters() error {
|
||||||
rows, err := filterStmts.getWordFilters.Query()
|
var wordFilters = WordFilterMap(make(map[int]WordFilter))
|
||||||
|
filters, err := wordFilters.BypassGetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, filter := range filters {
|
||||||
|
wordFilters[filter.ID] = filter
|
||||||
|
}
|
||||||
|
|
||||||
|
WordFilterBox.Store(wordFilters)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Return pointers to word filters intead to save memory?
|
||||||
|
func (wBox WordFilterMap) BypassGetAll() (filters []WordFilter, err error) {
|
||||||
|
rows, err := filterStmts.getWordFilters.Query()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var wordFilters = WordFilterMap(make(map[int]WordFilter))
|
|
||||||
var wfid int
|
|
||||||
var find string
|
|
||||||
var replacement string
|
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err := rows.Scan(&wfid, &find, &replacement)
|
filter := WordFilter{ID: 0}
|
||||||
|
err := rows.Scan(&filter.ID, &filter.Find, &filter.Replacement)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return filters, err
|
||||||
}
|
}
|
||||||
wordFilters[wfid] = WordFilter{ID: wfid, Find: find, Replacement: replacement}
|
filters = append(filters, filter)
|
||||||
}
|
}
|
||||||
WordFilterBox.Store(wordFilters)
|
return filters, rows.Err()
|
||||||
return rows.Err()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddWordFilter(id int, find string, replacement string) {
|
func AddWordFilter(id int, find string, replacement string) {
|
||||||
|
|
27
database.go
27
database.go
|
@ -35,47 +35,50 @@ func InitDatabase() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Loading the usergroups.")
|
log.Print("Loading the usergroups.")
|
||||||
common.Gstore, err = common.NewMemoryGroupStore()
|
common.Groups, err = common.NewMemoryGroupStore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err2 := common.Gstore.LoadGroups()
|
err2 := common.Groups.LoadGroups()
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
return err2
|
return err2
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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")
|
||||||
|
|
||||||
|
var ucache common.UserCache
|
||||||
|
var tcache common.TopicCache
|
||||||
if common.Config.CacheTopicUser == common.CACHE_STATIC {
|
if common.Config.CacheTopicUser == common.CACHE_STATIC {
|
||||||
common.Users, err = common.NewMemoryUserStore(common.Config.UserCacheCapacity)
|
ucache = common.NewMemoryUserCache(common.Config.UserCacheCapacity)
|
||||||
common.Topics, err2 = common.NewMemoryTopicStore(common.Config.TopicCacheCapacity)
|
tcache = common.NewMemoryTopicCache(common.Config.TopicCacheCapacity)
|
||||||
} else {
|
|
||||||
common.Users, err = common.NewSQLUserStore()
|
|
||||||
common.Topics, err2 = common.NewSQLTopicStore()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
common.Users, err = common.NewDefaultUserStore(ucache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err2 != nil {
|
common.Topics, err = common.NewDefaultTopicStore(tcache)
|
||||||
|
if err != nil {
|
||||||
return err2
|
return err2
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Loading the forums.")
|
log.Print("Loading the forums.")
|
||||||
common.Fstore, err = common.NewMemoryForumStore()
|
common.Forums, err = common.NewMemoryForumStore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = common.Fstore.LoadForums()
|
err = common.Forums.LoadForums()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Loading the forum permissions.")
|
log.Print("Loading the forum permissions.")
|
||||||
common.Fpstore, err = common.NewMemoryForumPermsStore()
|
common.FPStore, err = common.NewMemoryForumPermsStore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = common.Fpstore.Init()
|
err = common.FPStore.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package guilds
|
||||||
|
|
||||||
|
import "database/sql"
|
||||||
|
import "../../../query_gen/lib"
|
||||||
|
|
||||||
|
var Gstore GuildStore
|
||||||
|
|
||||||
|
type GuildStore interface {
|
||||||
|
Get(guildID int) (guild *Guild, err error)
|
||||||
|
Create(name string, desc string, active bool, privacy int, uid int, fid int) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SQLGuildStore struct {
|
||||||
|
get *sql.Stmt
|
||||||
|
create *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSQLGuildStore() (*SQLGuildStore, error) {
|
||||||
|
acc := qgen.Builder.Accumulator()
|
||||||
|
return &SQLGuildStore{
|
||||||
|
get: acc.Select("guilds").Columns("name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime").Where("guildID = ?").Prepare(),
|
||||||
|
create: acc.Insert("guilds").Columns("name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime").Fields("?,?,?,?,1,?,1,?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(),
|
||||||
|
}, acc.FirstError()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *SQLGuildStore) Close() {
|
||||||
|
_ = store.get.Close()
|
||||||
|
_ = store.create.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *SQLGuildStore) Get(guildID int) (guild *Guild, err error) {
|
||||||
|
guild = &Guild{ID: guildID}
|
||||||
|
err = store.get.QueryRow(guildID).Scan(&guild.Name, &guild.Desc, &guild.Active, &guild.Privacy, &guild.Joinable, &guild.Owner, &guild.MemberCount, &guild.MainForumID, &guild.Backdrop, &guild.CreatedAt, &guild.LastUpdateTime)
|
||||||
|
return guild, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (store *SQLGuildStore) Create(name string, desc string, active bool, privacy int, uid int, fid int) (int, error) {
|
||||||
|
res, err := store.create.Exec(name, desc, active, privacy, uid, fid)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
lastID, err := res.LastInsertId()
|
||||||
|
return int(lastID), err
|
||||||
|
}
|
|
@ -21,8 +21,6 @@ var ListStmt *sql.Stmt
|
||||||
var MemberListStmt *sql.Stmt
|
var MemberListStmt *sql.Stmt
|
||||||
var MemberListJoinStmt *sql.Stmt
|
var MemberListJoinStmt *sql.Stmt
|
||||||
var GetMemberStmt *sql.Stmt
|
var GetMemberStmt *sql.Stmt
|
||||||
var GetGuildStmt *sql.Stmt
|
|
||||||
var CreateGuildStmt *sql.Stmt
|
|
||||||
var AttachForumStmt *sql.Stmt
|
var AttachForumStmt *sql.Stmt
|
||||||
var UnattachForumStmt *sql.Stmt
|
var UnattachForumStmt *sql.Stmt
|
||||||
var AddMemberStmt *sql.Stmt
|
var AddMemberStmt *sql.Stmt
|
||||||
|
@ -105,8 +103,8 @@ func PrebuildTmplList(user common.User, headerVars *common.HeaderVars) common.CT
|
||||||
CreatedAt: "date",
|
CreatedAt: "date",
|
||||||
LastUpdateTime: "date",
|
LastUpdateTime: "date",
|
||||||
MainForumID: 1,
|
MainForumID: 1,
|
||||||
MainForum: common.Fstore.DirtyGet(1),
|
MainForum: common.Forums.DirtyGet(1),
|
||||||
Forums: []*common.Forum{common.Fstore.DirtyGet(1)},
|
Forums: []*common.Forum{common.Forums.DirtyGet(1)},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
listPage := ListPage{"Guild List", user, headerVars, guildList}
|
listPage := ListPage{"Guild List", user, headerVars, guildList}
|
||||||
|
@ -127,9 +125,9 @@ func CommonAreaWidgets(headerVars *common.HeaderVars) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if common.Themes[headerVars.ThemeName].Sidebars == "left" {
|
if common.Themes[headerVars.Theme.Name].Sidebars == "left" {
|
||||||
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
||||||
} else if common.Themes[headerVars.ThemeName].Sidebars == "right" || common.Themes[headerVars.ThemeName].Sidebars == "both" {
|
} else if common.Themes[headerVars.Theme.Name].Sidebars == "right" || common.Themes[headerVars.Theme.Name].Sidebars == "both" {
|
||||||
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,9 +149,9 @@ func GuildWidgets(headerVars *common.HeaderVars, guildItem *Guild) (success bool
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if themes[headerVars.ThemeName].Sidebars == "left" {
|
if themes[headerVars.Theme.Name].Sidebars == "left" {
|
||||||
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
||||||
} else if themes[headerVars.ThemeName].Sidebars == "right" || themes[headerVars.ThemeName].Sidebars == "both" {
|
} else if themes[headerVars.Theme.Name].Sidebars == "right" || themes[headerVars.Theme.Name].Sidebars == "both" {
|
||||||
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
@ -194,19 +192,13 @@ func RouteGuildList(w http.ResponseWriter, r *http.Request, user common.User) co
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := ListPage{"Guild List", user, headerVars, guildList}
|
pi := ListPage{"Guild List", user, headerVars, guildList}
|
||||||
err = common.RunThemeTemplate(headerVars.ThemeName, "guilds_guild_list", pi, w)
|
err = common.RunThemeTemplate(headerVars.Theme.Name, "guilds_guild_list", pi, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetGuild(guildID int) (guildItem *Guild, err error) {
|
|
||||||
guildItem = &Guild{ID: guildID}
|
|
||||||
err = GetGuildStmt.QueryRow(guildID).Scan(&guildItem.Name, &guildItem.Desc, &guildItem.Active, &guildItem.Privacy, &guildItem.Joinable, &guildItem.Owner, &guildItem.MemberCount, &guildItem.MainForumID, &guildItem.Backdrop, &guildItem.CreatedAt, &guildItem.LastUpdateTime)
|
|
||||||
return guildItem, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func MiddleViewGuild(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func MiddleViewGuild(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
// SEO URLs...
|
// SEO URLs...
|
||||||
halves := strings.Split(r.URL.Path[len("/guild/"):], ".")
|
halves := strings.Split(r.URL.Path[len("/guild/"):], ".")
|
||||||
|
@ -218,7 +210,7 @@ func MiddleViewGuild(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
return common.PreError("Not a valid guild ID", w, r)
|
return common.PreError("Not a valid guild ID", w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
guildItem, err := GetGuild(guildID)
|
guildItem, err := Gstore.Get(guildID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError("Bad guild", w, r, user)
|
return common.LocalError("Bad guild", w, r, user)
|
||||||
}
|
}
|
||||||
|
@ -277,32 +269,28 @@ func RouteCreateGuildSubmit(w http.ResponseWriter, r *http.Request, user common.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the backing forum
|
// Create the backing forum
|
||||||
fid, err := common.Fstore.Create(guildName, "", true, "")
|
fid, err := common.Forums.Create(guildName, "", true, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := CreateGuildStmt.Exec(guildName, guildDesc, guildActive, guildPrivacy, user.ID, fid)
|
gid, err := Gstore.Create(guildName, guildDesc, guildActive, guildPrivacy, user.ID, fid)
|
||||||
if err != nil {
|
|
||||||
return common.InternalError(err, w, r)
|
|
||||||
}
|
|
||||||
lastID, err := res.LastInsertId()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the main backing forum to the forum list
|
// Add the main backing forum to the forum list
|
||||||
err = AttachForum(int(lastID), fid)
|
err = AttachForum(gid, fid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = AddMemberStmt.Exec(lastID, user.ID, 2)
|
_, err = AddMemberStmt.Exec(gid, user.ID, 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, BuildGuildURL(common.NameToSlug(guildName), int(lastID)), http.StatusSeeOther)
|
http.Redirect(w, r, BuildGuildURL(common.NameToSlug(guildName), gid), http.StatusSeeOther)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,9 +310,7 @@ func RouteMemberList(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
return common.PreError("Not a valid group ID", w, r)
|
return common.PreError("Not a valid group ID", w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
var guildItem = &Guild{ID: guildID}
|
guildItem, err := Gstore.Get(guildID)
|
||||||
var mainForum int // Unused
|
|
||||||
err = GetGuildStmt.QueryRow(guildID).Scan(&guildItem.Name, &guildItem.Desc, &guildItem.Active, &guildItem.Privacy, &guildItem.Joinable, &guildItem.Owner, &guildItem.MemberCount, &mainForum, &guildItem.Backdrop, &guildItem.CreatedAt, &guildItem.LastUpdateTime)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError("Bad group", w, r, user)
|
return common.LocalError("Bad group", w, r, user)
|
||||||
}
|
}
|
||||||
|
@ -380,7 +366,7 @@ func RouteMemberList(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = common.RunThemeTemplate(headerVars.ThemeName, "guilds_member_list", pi, w)
|
err = common.RunThemeTemplate(headerVars.Theme.Name, "guilds_member_list", pi, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -439,7 +425,7 @@ func TrowAssign(args ...interface{}) interface{} {
|
||||||
// TODO: It would be nice, if you could select one of the boards in the group from that drop-down rather than just the one you got linked from
|
// TODO: It would be nice, if you could select one of the boards in the group from that drop-down rather than just the one you got linked from
|
||||||
func TopicCreatePreLoop(args ...interface{}) interface{} {
|
func TopicCreatePreLoop(args ...interface{}) interface{} {
|
||||||
var fid = args[2].(int)
|
var fid = args[2].(int)
|
||||||
if common.Fstore.DirtyGet(fid).ParentType == "guild" {
|
if common.Forums.DirtyGet(fid).ParentType == "guild" {
|
||||||
var strictmode = args[5].(*bool)
|
var strictmode = args[5].(*bool)
|
||||||
*strictmode = true
|
*strictmode = true
|
||||||
}
|
}
|
||||||
|
@ -452,14 +438,14 @@ func TopicCreatePreLoop(args ...interface{}) interface{} {
|
||||||
func ForumCheck(args ...interface{}) (skip bool, rerr common.RouteError) {
|
func ForumCheck(args ...interface{}) (skip bool, rerr common.RouteError) {
|
||||||
var r = args[1].(*http.Request)
|
var r = args[1].(*http.Request)
|
||||||
var fid = args[3].(*int)
|
var fid = args[3].(*int)
|
||||||
var forum = common.Fstore.DirtyGet(*fid)
|
var forum = common.Forums.DirtyGet(*fid)
|
||||||
|
|
||||||
if forum.ParentType == "guild" {
|
if forum.ParentType == "guild" {
|
||||||
var err error
|
var err error
|
||||||
var w = args[0].(http.ResponseWriter)
|
var w = args[0].(http.ResponseWriter)
|
||||||
guildItem, ok := r.Context().Value("guilds_current_group").(*Guild)
|
guildItem, ok := r.Context().Value("guilds_current_group").(*Guild)
|
||||||
if !ok {
|
if !ok {
|
||||||
guildItem, err = GetGuild(forum.ParentID)
|
guildItem, err = Gstore.Get(forum.ParentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, common.InternalError(errors.New("Unable to find the parent group for a forum"), w, r)
|
return true, common.InternalError(errors.New("Unable to find the parent group for a forum"), w, r)
|
||||||
}
|
}
|
||||||
|
|
24
gen_mssql.go
24
gen_mssql.go
|
@ -10,7 +10,6 @@ import "./common"
|
||||||
// nolint
|
// nolint
|
||||||
type Stmts struct {
|
type Stmts struct {
|
||||||
getPassword *sql.Stmt
|
getPassword *sql.Stmt
|
||||||
getSettings *sql.Stmt
|
|
||||||
isPluginActive *sql.Stmt
|
isPluginActive *sql.Stmt
|
||||||
getUsersOffset *sql.Stmt
|
getUsersOffset *sql.Stmt
|
||||||
isThemeDefault *sql.Stmt
|
isThemeDefault *sql.Stmt
|
||||||
|
@ -45,7 +44,6 @@ type Stmts struct {
|
||||||
createWordFilter *sql.Stmt
|
createWordFilter *sql.Stmt
|
||||||
editReply *sql.Stmt
|
editReply *sql.Stmt
|
||||||
editProfileReply *sql.Stmt
|
editProfileReply *sql.Stmt
|
||||||
updateSetting *sql.Stmt
|
|
||||||
updatePlugin *sql.Stmt
|
updatePlugin *sql.Stmt
|
||||||
updatePluginInstall *sql.Stmt
|
updatePluginInstall *sql.Stmt
|
||||||
updateTheme *sql.Stmt
|
updateTheme *sql.Stmt
|
||||||
|
@ -61,7 +59,6 @@ type Stmts struct {
|
||||||
deleteActivityStreamMatch *sql.Stmt
|
deleteActivityStreamMatch *sql.Stmt
|
||||||
deleteWordFilter *sql.Stmt
|
deleteWordFilter *sql.Stmt
|
||||||
reportExists *sql.Stmt
|
reportExists *sql.Stmt
|
||||||
modlogCount *sql.Stmt
|
|
||||||
notifyWatchers *sql.Stmt
|
notifyWatchers *sql.Stmt
|
||||||
|
|
||||||
getActivityFeedByWatcher *sql.Stmt
|
getActivityFeedByWatcher *sql.Stmt
|
||||||
|
@ -90,13 +87,6 @@ func _gen_mssql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing getSettings statement.")
|
|
||||||
stmts.getSettings, err = db.Prepare("SELECT [name],[content],[type] FROM [settings]")
|
|
||||||
if err != nil {
|
|
||||||
log.Print("Bad Query: ","SELECT [name],[content],[type] FROM [settings]")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing isPluginActive statement.")
|
log.Print("Preparing isPluginActive statement.")
|
||||||
stmts.isPluginActive, err = db.Prepare("SELECT [active] FROM [plugins] WHERE [uname] = ?1")
|
stmts.isPluginActive, err = db.Prepare("SELECT [active] FROM [plugins] WHERE [uname] = ?1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -335,13 +325,6 @@ func _gen_mssql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing updateSetting statement.")
|
|
||||||
stmts.updateSetting, err = db.Prepare("UPDATE [settings] SET [content] = ? WHERE [name] = ?")
|
|
||||||
if err != nil {
|
|
||||||
log.Print("Bad Query: ","UPDATE [settings] SET [content] = ? WHERE [name] = ?")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing updatePlugin statement.")
|
log.Print("Preparing updatePlugin statement.")
|
||||||
stmts.updatePlugin, err = db.Prepare("UPDATE [plugins] SET [active] = ? WHERE [uname] = ?")
|
stmts.updatePlugin, err = db.Prepare("UPDATE [plugins] SET [active] = ? WHERE [uname] = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -447,13 +430,6 @@ func _gen_mssql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing modlogCount statement.")
|
|
||||||
stmts.modlogCount, err = db.Prepare("SELECT COUNT(*) AS [count] FROM [moderation_logs]")
|
|
||||||
if err != nil {
|
|
||||||
log.Print("Bad Query: ","SELECT COUNT(*) AS [count] FROM [moderation_logs]")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing notifyWatchers statement.")
|
log.Print("Preparing notifyWatchers statement.")
|
||||||
stmts.notifyWatchers, err = db.Prepare("INSERT INTO [activity_stream_matches] ([watcher],[asid]) SELECT [activity_subscriptions].[user],[activity_stream].[asid] FROM [activity_stream] INNER JOIN [activity_subscriptions] ON [activity_subscriptions].[targetType] = [activity_stream].[elementType] AND [activity_subscriptions].[targetID] = [activity_stream].[elementID] AND [activity_subscriptions].[user] != [activity_stream].[actor] WHERE [asid] = ?1")
|
stmts.notifyWatchers, err = db.Prepare("INSERT INTO [activity_stream_matches] ([watcher],[asid]) SELECT [activity_subscriptions].[user],[activity_stream].[asid] FROM [activity_stream] INNER JOIN [activity_subscriptions] ON [activity_subscriptions].[targetType] = [activity_stream].[elementType] AND [activity_subscriptions].[targetID] = [activity_stream].[elementID] AND [activity_subscriptions].[user] != [activity_stream].[actor] WHERE [asid] = ?1")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
21
gen_mysql.go
21
gen_mysql.go
|
@ -12,7 +12,6 @@ import "./common"
|
||||||
// nolint
|
// nolint
|
||||||
type Stmts struct {
|
type Stmts struct {
|
||||||
getPassword *sql.Stmt
|
getPassword *sql.Stmt
|
||||||
getSettings *sql.Stmt
|
|
||||||
isPluginActive *sql.Stmt
|
isPluginActive *sql.Stmt
|
||||||
getUsersOffset *sql.Stmt
|
getUsersOffset *sql.Stmt
|
||||||
isThemeDefault *sql.Stmt
|
isThemeDefault *sql.Stmt
|
||||||
|
@ -47,7 +46,6 @@ type Stmts struct {
|
||||||
createWordFilter *sql.Stmt
|
createWordFilter *sql.Stmt
|
||||||
editReply *sql.Stmt
|
editReply *sql.Stmt
|
||||||
editProfileReply *sql.Stmt
|
editProfileReply *sql.Stmt
|
||||||
updateSetting *sql.Stmt
|
|
||||||
updatePlugin *sql.Stmt
|
updatePlugin *sql.Stmt
|
||||||
updatePluginInstall *sql.Stmt
|
updatePluginInstall *sql.Stmt
|
||||||
updateTheme *sql.Stmt
|
updateTheme *sql.Stmt
|
||||||
|
@ -63,7 +61,6 @@ type Stmts struct {
|
||||||
deleteActivityStreamMatch *sql.Stmt
|
deleteActivityStreamMatch *sql.Stmt
|
||||||
deleteWordFilter *sql.Stmt
|
deleteWordFilter *sql.Stmt
|
||||||
reportExists *sql.Stmt
|
reportExists *sql.Stmt
|
||||||
modlogCount *sql.Stmt
|
|
||||||
notifyWatchers *sql.Stmt
|
notifyWatchers *sql.Stmt
|
||||||
|
|
||||||
getActivityFeedByWatcher *sql.Stmt
|
getActivityFeedByWatcher *sql.Stmt
|
||||||
|
@ -91,12 +88,6 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing getSettings statement.")
|
|
||||||
stmts.getSettings, err = db.Prepare("SELECT `name`,`content`,`type` FROM `settings`")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing isPluginActive statement.")
|
log.Print("Preparing isPluginActive statement.")
|
||||||
stmts.isPluginActive, err = db.Prepare("SELECT `active` FROM `plugins` WHERE `uname` = ?")
|
stmts.isPluginActive, err = db.Prepare("SELECT `active` FROM `plugins` WHERE `uname` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -301,12 +292,6 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing updateSetting statement.")
|
|
||||||
stmts.updateSetting, err = db.Prepare("UPDATE `settings` SET `content` = ? WHERE `name` = ?")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing updatePlugin statement.")
|
log.Print("Preparing updatePlugin statement.")
|
||||||
stmts.updatePlugin, err = db.Prepare("UPDATE `plugins` SET `active` = ? WHERE `uname` = ?")
|
stmts.updatePlugin, err = db.Prepare("UPDATE `plugins` SET `active` = ? WHERE `uname` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -397,12 +382,6 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing modlogCount statement.")
|
|
||||||
stmts.modlogCount, err = db.Prepare("SELECT COUNT(*) AS `count` FROM `moderation_logs`")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing notifyWatchers statement.")
|
log.Print("Preparing notifyWatchers statement.")
|
||||||
stmts.notifyWatchers, err = db.Prepare("INSERT INTO `activity_stream_matches`(`watcher`,`asid`) SELECT `activity_subscriptions`.`user`, `activity_stream`.`asid` FROM `activity_stream` INNER JOIN `activity_subscriptions` ON `activity_subscriptions`.`targetType` = `activity_stream`.`elementType` AND `activity_subscriptions`.`targetID` = `activity_stream`.`elementID` AND `activity_subscriptions`.`user` != `activity_stream`.`actor` WHERE `asid` = ?")
|
stmts.notifyWatchers, err = db.Prepare("INSERT INTO `activity_stream_matches`(`watcher`,`asid`) SELECT `activity_subscriptions`.`user`, `activity_stream`.`asid` FROM `activity_stream` INNER JOIN `activity_subscriptions` ON `activity_subscriptions`.`targetType` = `activity_stream`.`elementType` AND `activity_subscriptions`.`targetID` = `activity_stream`.`elementID` AND `activity_subscriptions`.`user` != `activity_stream`.`actor` WHERE `asid` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -11,7 +11,6 @@ import "./common"
|
||||||
type Stmts struct {
|
type Stmts struct {
|
||||||
editReply *sql.Stmt
|
editReply *sql.Stmt
|
||||||
editProfileReply *sql.Stmt
|
editProfileReply *sql.Stmt
|
||||||
updateSetting *sql.Stmt
|
|
||||||
updatePlugin *sql.Stmt
|
updatePlugin *sql.Stmt
|
||||||
updatePluginInstall *sql.Stmt
|
updatePluginInstall *sql.Stmt
|
||||||
updateTheme *sql.Stmt
|
updateTheme *sql.Stmt
|
||||||
|
@ -55,12 +54,6 @@ func _gen_pgsql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing updateSetting statement.")
|
|
||||||
stmts.updateSetting, err = db.Prepare("UPDATE `settings` SET `content` = ? WHERE `name` = ?")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing updatePlugin statement.")
|
log.Print("Preparing updatePlugin statement.")
|
||||||
stmts.updatePlugin, err = db.Prepare("UPDATE `plugins` SET `active` = ? WHERE `uname` = ?")
|
stmts.updatePlugin, err = db.Prepare("UPDATE `plugins` SET `active` = ? WHERE `uname` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -246,7 +246,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
case "/panel/settings/":
|
case "/panel/settings/":
|
||||||
err = routePanelSettings(w,req,user)
|
err = routePanelSettings(w,req,user)
|
||||||
case "/panel/settings/edit/":
|
case "/panel/settings/edit/":
|
||||||
err = routePanelSetting(w,req,user,extra_data)
|
err = routePanelSettingEdit(w,req,user,extra_data)
|
||||||
case "/panel/settings/edit/submit/":
|
case "/panel/settings/edit/submit/":
|
||||||
err = common.NoSessionMismatch(w,req,user)
|
err = common.NoSessionMismatch(w,req,user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -254,7 +254,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = routePanelSettingEdit(w,req,user,extra_data)
|
err = routePanelSettingEditSubmit(w,req,user,extra_data)
|
||||||
case "/panel/settings/word-filters/":
|
case "/panel/settings/word-filters/":
|
||||||
err = routePanelWordFilters(w,req,user)
|
err = routePanelWordFilters(w,req,user)
|
||||||
case "/panel/settings/word-filters/create/":
|
case "/panel/settings/word-filters/create/":
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
"./common"
|
"./common"
|
||||||
"./install/install"
|
"./install/install"
|
||||||
"./query_gen/lib"
|
"./query_gen/lib"
|
||||||
//"runtime/pprof"
|
|
||||||
//"github.com/husobee/vestigo"
|
//"github.com/husobee/vestigo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -77,44 +76,7 @@ func gloinit() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
common.Rstore, err = common.NewSQLReplyStore()
|
err = afterDBInit()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
common.Prstore, err = common.NewSQLProfileReplyStore()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
dbProd = db
|
|
||||||
//db_test, err = sql.Open("testdb","")
|
|
||||||
//if err != nil {
|
|
||||||
// return err
|
|
||||||
//}
|
|
||||||
|
|
||||||
err = common.InitTemplates()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
dbProd.SetMaxOpenConns(64)
|
|
||||||
|
|
||||||
err = common.InitPhrases()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Loading the static files.")
|
|
||||||
err = common.StaticFiles.Init()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
common.Auth, err = common.NewDefaultAuth()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = common.LoadWordFilters()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Gosora MSSQL Interface
|
* Gosora MSSQL Interface
|
||||||
* Under heavy development
|
|
||||||
* Copyright Azareal 2017 - 2018
|
* Copyright Azareal 2017 - 2018
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -80,14 +79,9 @@ func (ins *MssqlInstaller) InitDatabase() (err error) {
|
||||||
// TODO: Create the database, if it doesn't exist
|
// TODO: Create the database, if it doesn't exist
|
||||||
|
|
||||||
// Ready the query builder
|
// Ready the query builder
|
||||||
qgen.Builder.SetConn(db)
|
|
||||||
err = qgen.Builder.SetAdapter("mssql")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ins.db = db
|
ins.db = db
|
||||||
|
qgen.Builder.SetConn(db)
|
||||||
return nil
|
return qgen.Builder.SetAdapter("mssql")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ins *MssqlInstaller) TableDefs() (err error) {
|
func (ins *MssqlInstaller) TableDefs() (err error) {
|
||||||
|
@ -126,7 +120,6 @@ func (ins *MssqlInstaller) TableDefs() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//fmt.Println("Finished creating the tables")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,8 +143,6 @@ func (ins *MssqlInstaller) InitialData() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Println("Finished inserting the database data")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,5 +55,36 @@
|
||||||
"Accounts": {
|
"Accounts": {
|
||||||
"VerifyEmailSubject": "Validate Your Email @ {{name}}",
|
"VerifyEmailSubject": "Validate Your Email @ {{name}}",
|
||||||
"VerifyEmailBody": "Dear {{username}}, following your registration on our forums, we ask you to validate your email, so that we can confirm that this email actually belongs to you.\n\nClick on the following link to do so. {{schema}}://{{url}}/user/edit/token/{{token}}\n\nIf you haven't created an account here, then please feel free to ignore this email.\nWe're sorry for the inconvenience this may have caused."
|
"VerifyEmailBody": "Dear {{username}}, following your registration on our forums, we ask you to validate your email, so that we can confirm that this email actually belongs to you.\n\nClick on the following link to do so. {{schema}}://{{url}}/user/edit/token/{{token}}\n\nIf you haven't created an account here, then please feel free to ignore this email.\nWe're sorry for the inconvenience this may have caused."
|
||||||
|
},
|
||||||
|
"Errors": {
|
||||||
|
"NoPerms": {
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"PageTitles": {
|
||||||
|
"overview":"Overview",
|
||||||
|
"page":"Page",
|
||||||
|
"topics":"All Topics",
|
||||||
|
"forums":"Forum List",
|
||||||
|
"login":"Login",
|
||||||
|
"register":"Registration",
|
||||||
|
"ip-search":"IP Search",
|
||||||
|
|
||||||
|
"panel-dashboard":"Control Panel Dashboard",
|
||||||
|
"panel-forums":"Forum Manager",
|
||||||
|
"panel-delete-forum":"Delete Forum",
|
||||||
|
"panel-edit-forum":"Forum Editor",
|
||||||
|
"panel-settings":"Setting Manager",
|
||||||
|
"panel-edit-setting":"Edit Setting",
|
||||||
|
"panel-word-filters":"Word Filter Manager",
|
||||||
|
"panel-edit-word-filter":"Edit Word Filter",
|
||||||
|
"panel-plugins":"Plugin Manager",
|
||||||
|
"panel-users":"User Manager",
|
||||||
|
"panel-edit-user":"User Editor",
|
||||||
|
"panel-groups":"Group Manager",
|
||||||
|
"panel-edit-group":"Group Editor",
|
||||||
|
"panel-themes":"Theme Manager",
|
||||||
|
"panel-backups":"Backups",
|
||||||
|
"panel-mod-logs":"Moderation Logs",
|
||||||
|
"panel-debug":"Debug"
|
||||||
}
|
}
|
||||||
}
|
}
|
94
main.go
94
main.go
|
@ -29,6 +29,61 @@ type Globs struct {
|
||||||
stmts *Stmts
|
stmts *Stmts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func afterDBInit() (err error) {
|
||||||
|
common.Rstore, err = common.NewSQLReplyStore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
common.Prstore, err = common.NewSQLProfileReplyStore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = common.InitTemplates()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = common.InitPhrases()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Print("Loading the static files.")
|
||||||
|
err = common.StaticFiles.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Print("Initialising the widgets")
|
||||||
|
err = common.InitWidgets()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Print("Initialising the authentication system")
|
||||||
|
common.Auth, err = common.NewDefaultAuth()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = common.LoadWordFilters()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
common.ModLogs, err = common.NewModLogStore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
common.AdminLogs, err = common.NewAdminLogStore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Split this function up
|
// TODO: Split this function up
|
||||||
func main() {
|
func main() {
|
||||||
// TODO: Recover from panics
|
// TODO: Recover from panics
|
||||||
|
@ -90,44 +145,7 @@ func main() {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
common.Rstore, err = common.NewSQLReplyStore()
|
err = afterDBInit()
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
common.Prstore, err = common.NewSQLProfileReplyStore()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = common.InitTemplates()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = common.InitPhrases()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Loading the static files.")
|
|
||||||
err = common.StaticFiles.Init()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Initialising the widgets")
|
|
||||||
err = common.InitWidgets()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Initialising the authentication system")
|
|
||||||
common.Auth, err = common.NewDefaultAuth()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = common.LoadWordFilters()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,12 +55,12 @@ func routeTopicCreate(w http.ResponseWriter, r *http.Request, user common.User,
|
||||||
var forumList []common.Forum
|
var forumList []common.Forum
|
||||||
var canSee []int
|
var canSee []int
|
||||||
if user.IsSuperAdmin {
|
if user.IsSuperAdmin {
|
||||||
canSee, err = common.Fstore.GetAllVisibleIDs()
|
canSee, err = common.Forums.GetAllVisibleIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
group, err := common.Gstore.Get(user.Group)
|
group, err := common.Groups.Get(user.Group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: Refactor this
|
// TODO: Refactor this
|
||||||
common.LocalError("Something weird happened behind the scenes", w, r, user)
|
common.LocalError("Something weird happened behind the scenes", w, r, user)
|
||||||
|
@ -78,7 +78,7 @@ func routeTopicCreate(w http.ResponseWriter, r *http.Request, user common.User,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do a bulk forum fetch, just in case it's the SqlForumStore?
|
// Do a bulk forum fetch, just in case it's the SqlForumStore?
|
||||||
forum := common.Fstore.DirtyGet(ffid)
|
forum := common.Forums.DirtyGet(ffid)
|
||||||
if forum.Name != "" && forum.Active {
|
if forum.Name != "" && forum.Active {
|
||||||
fcopy := forum.Copy()
|
fcopy := forum.Copy()
|
||||||
if common.Hooks["topic_create_frow_assign"] != nil {
|
if common.Hooks["topic_create_frow_assign"] != nil {
|
||||||
|
@ -98,7 +98,7 @@ func routeTopicCreate(w http.ResponseWriter, r *http.Request, user common.User,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.RunThemeTemplate(headerVars.ThemeName, "create-topic", ctpage, w)
|
err = common.RunThemeTemplate(headerVars.Theme.Name, "create-topic", ctpage, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -340,7 +340,7 @@ func routeCreateReply(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.Fstore.UpdateLastTopic(tid, user.ID, topic.ParentID)
|
err = common.Forums.UpdateLastTopic(tid, user.ID, topic.ParentID)
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -623,7 +623,7 @@ func routeReportSubmit(w http.ResponseWriter, r *http.Request, user common.User,
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.Fstore.AddTopic(int(lastID), user.ID, fid)
|
err = common.Forums.AddTopic(int(lastID), user.ID, fid)
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
This file is here so that Git will include this folder in the repository.
|
185
misc_test.go
185
misc_test.go
|
@ -5,7 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -40,29 +39,39 @@ func TestUserStore(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
common.Users, err = common.NewMemoryUserStore(common.Config.UserCacheCapacity)
|
ucache := common.NewMemoryUserCache(common.Config.UserCacheCapacity)
|
||||||
|
common.Users, err = common.NewDefaultUserStore(ucache)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
common.Users.(common.UserCache).Flush()
|
ucache.Flush()
|
||||||
userStoreTest(t, 2)
|
userStoreTest(t, 2)
|
||||||
common.Users, err = common.NewSQLUserStore()
|
common.Users, err = common.NewDefaultUserStore(nil)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
userStoreTest(t, 3)
|
userStoreTest(t, 3)
|
||||||
}
|
}
|
||||||
func userStoreTest(t *testing.T, newUserID int) {
|
func userStoreTest(t *testing.T, newUserID int) {
|
||||||
ucache, hasCache := common.Users.(common.UserCache)
|
ucache := common.Users.GetCache()
|
||||||
// Go doesn't have short-circuiting, so this'll allow us to do one liner tests
|
// Go doesn't have short-circuiting, so this'll allow us to do one liner tests
|
||||||
if !hasCache {
|
isCacheLengthZero := func(ucache common.UserCache) bool {
|
||||||
ucache = &common.NullUserStore{}
|
if ucache == nil {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
expect(t, (!hasCache || ucache.Length() == 0), fmt.Sprintf("The initial ucache length should be zero, not %d", ucache.Length()))
|
return ucache.Length() == 0
|
||||||
|
}
|
||||||
|
cacheLength := func(ucache common.UserCache) int {
|
||||||
|
if ucache == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return ucache.Length()
|
||||||
|
}
|
||||||
|
expect(t, isCacheLengthZero(ucache), fmt.Sprintf("The initial ucache length should be zero, not %d", cacheLength(ucache)))
|
||||||
|
|
||||||
_, err := common.Users.Get(-1)
|
_, err := common.Users.Get(-1)
|
||||||
recordMustNotExist(t, err, "UID #-1 shouldn't exist")
|
recordMustNotExist(t, err, "UID #-1 shouldn't exist")
|
||||||
expect(t, !hasCache || ucache.Length() == 0, fmt.Sprintf("We found %d items in the user cache and it's supposed to be empty", ucache.Length()))
|
expect(t, isCacheLengthZero(ucache), fmt.Sprintf("We found %d items in the user cache and it's supposed to be empty", cacheLength(ucache)))
|
||||||
|
|
||||||
_, err = common.Users.Get(0)
|
_, err = common.Users.Get(0)
|
||||||
recordMustNotExist(t, err, "UID #0 shouldn't exist")
|
recordMustNotExist(t, err, "UID #0 shouldn't exist")
|
||||||
expect(t, !hasCache || ucache.Length() == 0, fmt.Sprintf("We found %d items in the user cache and it's supposed to be empty", ucache.Length()))
|
expect(t, isCacheLengthZero(ucache), fmt.Sprintf("We found %d items in the user cache and it's supposed to be empty", cacheLength(ucache)))
|
||||||
|
|
||||||
user, err := common.Users.Get(1)
|
user, err := common.Users.Get(1)
|
||||||
recordMustExist(t, err, "Couldn't find UID #1")
|
recordMustExist(t, err, "Couldn't find UID #1")
|
||||||
|
@ -79,24 +88,20 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
_, err = common.Users.Get(newUserID)
|
_, err = common.Users.Get(newUserID)
|
||||||
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't exist", newUserID))
|
recordMustNotExist(t, err, fmt.Sprintf("UID #%d shouldn't exist", newUserID))
|
||||||
|
|
||||||
if hasCache {
|
if ucache != nil {
|
||||||
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
|
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
|
||||||
|
|
||||||
_, err = ucache.CacheGet(-1)
|
_, err = ucache.Get(-1)
|
||||||
recordMustNotExist(t, err, "UID #-1 shouldn't exist, even in the cache")
|
recordMustNotExist(t, err, "UID #-1 shouldn't exist, even in the cache")
|
||||||
_, err = ucache.CacheGet(0)
|
_, err = ucache.Get(0)
|
||||||
recordMustNotExist(t, err, "UID #0 shouldn't exist, even in the cache")
|
recordMustNotExist(t, err, "UID #0 shouldn't exist, even in the cache")
|
||||||
user, err = ucache.CacheGet(1)
|
user, err = ucache.Get(1)
|
||||||
recordMustExist(t, err, "Couldn't find UID #1 in the cache")
|
recordMustExist(t, err, "Couldn't find UID #1 in the cache")
|
||||||
|
|
||||||
if user.ID != 1 {
|
expect(t, user.ID == 1, fmt.Sprintf("user.ID does not match the requested UID. Got '%d' instead.", user.ID))
|
||||||
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
expect(t, user.Name == "Admin", fmt.Sprintf("user.Name should be 'Admin', not '%s'", user.Name))
|
||||||
}
|
|
||||||
if user.Name != "Admin" {
|
|
||||||
t.Error("user.Name should be 'Admin', not '" + user.Name + "'")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = ucache.CacheGet(newUserID)
|
_, err = ucache.Get(newUserID)
|
||||||
recordMustNotExist(t, err, "UID #%d shouldn't exist, even in the cache", newUserID)
|
recordMustNotExist(t, err, "UID #%d shouldn't exist, even in the cache", newUserID)
|
||||||
|
|
||||||
ucache.Flush()
|
ucache.Flush()
|
||||||
|
@ -106,22 +111,12 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
// TODO: Lock onto the specific error type. Is this even possible without sacrificing the detailed information in the error message?
|
// TODO: Lock onto the specific error type. Is this even possible without sacrificing the detailed information in the error message?
|
||||||
var userList map[int]*common.User
|
var userList map[int]*common.User
|
||||||
userList, _ = common.Users.BulkGetMap([]int{-1})
|
userList, _ = common.Users.BulkGetMap([]int{-1})
|
||||||
if len(userList) > 0 {
|
expect(t, len(userList) == 0, fmt.Sprintf("The userList length should be 0, not %d", len(userList)))
|
||||||
t.Error("There shouldn't be any results for UID #-1")
|
expect(t, isCacheLengthZero(ucache), fmt.Sprintf("User cache length should be 0, not %d", cacheLength(ucache)))
|
||||||
}
|
|
||||||
|
|
||||||
if hasCache {
|
|
||||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
|
||||||
}
|
|
||||||
|
|
||||||
userList, _ = common.Users.BulkGetMap([]int{0})
|
userList, _ = common.Users.BulkGetMap([]int{0})
|
||||||
if len(userList) > 0 {
|
expect(t, len(userList) == 0, fmt.Sprintf("The userList length should be 0, not %d", len(userList)))
|
||||||
t.Error("There shouldn't be any results for UID #0")
|
expect(t, isCacheLengthZero(ucache), fmt.Sprintf("User cache length should be 0, not %d", cacheLength(ucache)))
|
||||||
}
|
|
||||||
|
|
||||||
if hasCache {
|
|
||||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
|
||||||
}
|
|
||||||
|
|
||||||
userList, _ = common.Users.BulkGetMap([]int{1})
|
userList, _ = common.Users.BulkGetMap([]int{1})
|
||||||
if len(userList) == 0 {
|
if len(userList) == 0 {
|
||||||
|
@ -135,18 +130,14 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
t.Error("We couldn't find UID #1 in the returned map")
|
t.Error("We couldn't find UID #1 in the returned map")
|
||||||
t.Error("userList", userList)
|
t.Error("userList", userList)
|
||||||
}
|
}
|
||||||
if user.ID != 1 {
|
expect(t, user.ID == 1, fmt.Sprintf("user.ID does not match the requested UID. Got '%d' instead.", user.ID))
|
||||||
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasCache {
|
if ucache != nil {
|
||||||
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
|
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
|
||||||
user, err = ucache.CacheGet(1)
|
user, err = ucache.Get(1)
|
||||||
recordMustExist(t, err, "Couldn't find UID #1 in the cache")
|
recordMustExist(t, err, "Couldn't find UID #1 in the cache")
|
||||||
|
|
||||||
if user.ID != 1 {
|
expect(t, user.ID == 1, fmt.Sprintf("user.ID does not match the requested UID. Got '%d' instead.", user.ID))
|
||||||
t.Errorf("user.ID does not match the requested UID. Got '%d' instead.", user.ID)
|
|
||||||
}
|
|
||||||
ucache.Flush()
|
ucache.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +146,7 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
expect(t, common.Users.Exists(1), "UID #1 should exist")
|
expect(t, common.Users.Exists(1), "UID #1 should exist")
|
||||||
expect(t, !common.Users.Exists(newUserID), fmt.Sprintf("UID #%d shouldn't exist", newUserID))
|
expect(t, !common.Users.Exists(newUserID), fmt.Sprintf("UID #%d shouldn't exist", newUserID))
|
||||||
|
|
||||||
expect(t, !hasCache || ucache.Length() == 0, fmt.Sprintf("User cache length should be 0, not %d", ucache.Length()))
|
expect(t, isCacheLengthZero(ucache), fmt.Sprintf("User cache length should be 0, not %d", cacheLength(ucache)))
|
||||||
expectIntToBeX(t, common.Users.GlobalCount(), 1, "The number of users should be one, not %d")
|
expectIntToBeX(t, common.Users.GlobalCount(), 1, "The number of users should be one, not %d")
|
||||||
|
|
||||||
var awaitingActivation = 5
|
var awaitingActivation = 5
|
||||||
|
@ -166,9 +157,7 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
|
|
||||||
user, err = common.Users.Get(newUserID)
|
user, err = common.Users.Get(newUserID)
|
||||||
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||||
if user.ID != newUserID {
|
expect(t, user.ID == newUserID, fmt.Sprintf("The UID of the user record should be %d", newUserID))
|
||||||
t.Errorf("The UID of the user record should be %d", newUserID)
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(t, user.Name == "Sam", "The user should be named Sam")
|
expect(t, user.Name == "Sam", "The user should be named Sam")
|
||||||
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
||||||
|
@ -178,9 +167,9 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
expect(t, !user.IsBanned, "Sam should not be banned")
|
expect(t, !user.IsBanned, "Sam should not be banned")
|
||||||
expectIntToBeX(t, user.Group, 5, "Sam should be in group 5")
|
expectIntToBeX(t, user.Group, 5, "Sam should be in group 5")
|
||||||
|
|
||||||
if hasCache {
|
if ucache != nil {
|
||||||
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
|
expectIntToBeX(t, ucache.Length(), 1, "User cache length should be 1, not %d")
|
||||||
user, err = ucache.CacheGet(newUserID)
|
user, err = ucache.Get(newUserID)
|
||||||
recordMustExist(t, err, "Couldn't find UID #%d in the cache", newUserID)
|
recordMustExist(t, err, "Couldn't find UID #%d in the cache", newUserID)
|
||||||
expect(t, user.ID == newUserID, fmt.Sprintf("user.ID does not match the requested UID. Got '%d' instead.", user.ID))
|
expect(t, user.ID == newUserID, fmt.Sprintf("user.ID does not match the requested UID. Got '%d' instead.", user.ID))
|
||||||
}
|
}
|
||||||
|
@ -190,9 +179,9 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
expectIntToBeX(t, user.Group, 5, "Sam should still be in group 5 in this copy")
|
expectIntToBeX(t, user.Group, 5, "Sam should still be in group 5 in this copy")
|
||||||
|
|
||||||
// ? - What if we change the caching mechanism so it isn't hard purged and reloaded? We'll deal with that when we come to it, but for now, this is a sign of a cache bug
|
// ? - What if we change the caching mechanism so it isn't hard purged and reloaded? We'll deal with that when we come to it, but for now, this is a sign of a cache bug
|
||||||
if hasCache {
|
if ucache != nil {
|
||||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||||
_, err = ucache.CacheGet(newUserID)
|
_, err = ucache.Get(newUserID)
|
||||||
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,18 +205,16 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, user.Group == common.Config.DefaultGroup, fmt.Sprintf("Sam should be in group %d, not %d", common.Config.DefaultGroup, user.Group))
|
expect(t, user.Group == common.Config.DefaultGroup, fmt.Sprintf("Sam should be in group %d, not %d", common.Config.DefaultGroup, user.Group))
|
||||||
|
|
||||||
if hasCache {
|
if ucache != nil {
|
||||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||||
_, err = ucache.CacheGet(2)
|
_, err = ucache.Get(2)
|
||||||
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err = common.Users.Get(newUserID)
|
user, err = common.Users.Get(newUserID)
|
||||||
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
recordMustExist(t, err, "Couldn't find UID #%d", newUserID)
|
||||||
if user.ID != newUserID {
|
|
||||||
t.Errorf("The UID of the user record should be %d", newUserID)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
expect(t, user.ID == newUserID, fmt.Sprintf("The UID of the user record should be %d", newUserID))
|
||||||
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
expect(t, !user.IsSuperAdmin, "Sam should not be a super admin")
|
||||||
expect(t, !user.IsAdmin, "Sam should not be an admin")
|
expect(t, !user.IsAdmin, "Sam should not be an admin")
|
||||||
expect(t, !user.IsSuperMod, "Sam should not be a super mod")
|
expect(t, !user.IsSuperMod, "Sam should not be a super mod")
|
||||||
|
@ -242,9 +229,9 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expectIntToBeX(t, user.Group, common.BanGroup, "Sam should still be in the ban group in this copy")
|
expectIntToBeX(t, user.Group, common.BanGroup, "Sam should still be in the ban group in this copy")
|
||||||
|
|
||||||
if hasCache {
|
if ucache != nil {
|
||||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||||
_, err = ucache.CacheGet(newUserID)
|
_, err = ucache.Get(newUserID)
|
||||||
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,9 +401,9 @@ func userStoreTest(t *testing.T, newUserID int) {
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, !common.Users.Exists(newUserID), fmt.Sprintf("UID #%d should no longer exist", newUserID))
|
expect(t, !common.Users.Exists(newUserID), fmt.Sprintf("UID #%d should no longer exist", newUserID))
|
||||||
|
|
||||||
if hasCache {
|
if ucache != nil {
|
||||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||||
_, err = ucache.CacheGet(newUserID)
|
_, err = ucache.Get(newUserID)
|
||||||
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,10 +492,11 @@ func TestTopicStore(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
common.Topics, err = common.NewMemoryTopicStore(common.Config.TopicCacheCapacity)
|
tcache := common.NewMemoryTopicCache(common.Config.TopicCacheCapacity)
|
||||||
|
common.Topics, err = common.NewDefaultTopicStore(tcache)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
topicStoreTest(t)
|
topicStoreTest(t)
|
||||||
common.Topics, err = common.NewSQLTopicStore()
|
common.Topics, err = common.NewDefaultTopicStore(nil)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
topicStoreTest(t)
|
topicStoreTest(t)
|
||||||
}
|
}
|
||||||
|
@ -526,25 +514,19 @@ func topicStoreTest(t *testing.T) {
|
||||||
recordMustExist(t, err, "Couldn't find TID #1")
|
recordMustExist(t, err, "Couldn't find TID #1")
|
||||||
|
|
||||||
if topic.ID != 1 {
|
if topic.ID != 1 {
|
||||||
t.Error("topic.ID does not match the requested TID. Got '" + strconv.Itoa(topic.ID) + "' instead.")
|
t.Error("topic.ID does not match the requested TID. Got '%d' instead.", topic.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add BulkGetMap() to the TopicStore
|
// TODO: Add BulkGetMap() to the TopicStore
|
||||||
|
|
||||||
ok := common.Topics.Exists(-1)
|
ok := common.Topics.Exists(-1)
|
||||||
if ok {
|
expect(t, !ok, "TID #-1 shouldn't exist")
|
||||||
t.Error("TID #-1 shouldn't exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = common.Topics.Exists(0)
|
ok = common.Topics.Exists(0)
|
||||||
if ok {
|
expect(t, !ok, "TID #0 shouldn't exist")
|
||||||
t.Error("TID #0 shouldn't exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = common.Topics.Exists(1)
|
ok = common.Topics.Exists(1)
|
||||||
if !ok {
|
expect(t, ok, "TID #1 should exist")
|
||||||
t.Error("TID #1 should exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
count := common.Topics.GlobalCount()
|
count := common.Topics.GlobalCount()
|
||||||
if count <= 0 {
|
if count <= 0 {
|
||||||
|
@ -563,17 +545,17 @@ func TestForumStore(t *testing.T) {
|
||||||
common.InitPlugins()
|
common.InitPlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := common.Fstore.Get(-1)
|
_, err := common.Forums.Get(-1)
|
||||||
recordMustNotExist(t, err, "FID #-1 shouldn't exist")
|
recordMustNotExist(t, err, "FID #-1 shouldn't exist")
|
||||||
|
|
||||||
_, err = common.Fstore.Get(0)
|
_, err = common.Forums.Get(0)
|
||||||
recordMustNotExist(t, err, "FID #0 shouldn't exist")
|
recordMustNotExist(t, err, "FID #0 shouldn't exist")
|
||||||
|
|
||||||
forum, err := common.Fstore.Get(1)
|
forum, err := common.Forums.Get(1)
|
||||||
recordMustExist(t, err, "Couldn't find FID #1")
|
recordMustExist(t, err, "Couldn't find FID #1")
|
||||||
|
|
||||||
if forum.ID != 1 {
|
if forum.ID != 1 {
|
||||||
t.Error("forum.ID doesn't not match the requested FID. Got '" + strconv.Itoa(forum.ID) + "' instead.'")
|
t.Error("forum.ID doesn't not match the requested FID. Got '%d' instead.'", forum.ID)
|
||||||
}
|
}
|
||||||
// TODO: Check the preset and forum permissions
|
// TODO: Check the preset and forum permissions
|
||||||
expect(t, forum.Name == "Reports", fmt.Sprintf("FID #0 is named '%s' and not 'Reports'", forum.Name))
|
expect(t, forum.Name == "Reports", fmt.Sprintf("FID #0 is named '%s' and not 'Reports'", forum.Name))
|
||||||
|
@ -581,7 +563,7 @@ func TestForumStore(t *testing.T) {
|
||||||
var expectDesc = "All the reports go here"
|
var expectDesc = "All the reports go here"
|
||||||
expect(t, forum.Desc == expectDesc, fmt.Sprintf("The forum description should be '%s' not '%s'", expectDesc, forum.Desc))
|
expect(t, forum.Desc == expectDesc, fmt.Sprintf("The forum description should be '%s' not '%s'", expectDesc, forum.Desc))
|
||||||
|
|
||||||
forum, err = common.Fstore.Get(2)
|
forum, err = common.Forums.Get(2)
|
||||||
recordMustExist(t, err, "Couldn't find FID #1")
|
recordMustExist(t, err, "Couldn't find FID #1")
|
||||||
|
|
||||||
expect(t, forum.ID == 2, fmt.Sprintf("The FID should be 2 not %d", forum.ID))
|
expect(t, forum.ID == 2, fmt.Sprintf("The FID should be 2 not %d", forum.ID))
|
||||||
|
@ -590,11 +572,11 @@ func TestForumStore(t *testing.T) {
|
||||||
expectDesc = "A place for general discussions which don't fit elsewhere"
|
expectDesc = "A place for general discussions which don't fit elsewhere"
|
||||||
expect(t, forum.Desc == expectDesc, fmt.Sprintf("The forum description should be '%s' not '%s'", expectDesc, forum.Desc))
|
expect(t, forum.Desc == expectDesc, fmt.Sprintf("The forum description should be '%s' not '%s'", expectDesc, forum.Desc))
|
||||||
|
|
||||||
ok := common.Fstore.Exists(-1)
|
ok := common.Forums.Exists(-1)
|
||||||
expect(t, !ok, "FID #-1 shouldn't exist")
|
expect(t, !ok, "FID #-1 shouldn't exist")
|
||||||
ok = common.Fstore.Exists(0)
|
ok = common.Forums.Exists(0)
|
||||||
expect(t, !ok, "FID #0 shouldn't exist")
|
expect(t, !ok, "FID #0 shouldn't exist")
|
||||||
ok = common.Fstore.Exists(1)
|
ok = common.Forums.Exists(1)
|
||||||
expect(t, ok, "FID #1 should exist")
|
expect(t, ok, "FID #1 should exist")
|
||||||
|
|
||||||
// TODO: Test forum creation
|
// TODO: Test forum creation
|
||||||
|
@ -621,43 +603,38 @@ func TestGroupStore(t *testing.T) {
|
||||||
common.InitPlugins()
|
common.InitPlugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := common.Gstore.Get(-1)
|
_, err := common.Groups.Get(-1)
|
||||||
recordMustNotExist(t, err, "GID #-1 shouldn't exist")
|
recordMustNotExist(t, err, "GID #-1 shouldn't exist")
|
||||||
|
|
||||||
// TODO: Refactor the group store to remove GID #0
|
// TODO: Refactor the group store to remove GID #0
|
||||||
group, err := common.Gstore.Get(0)
|
group, err := common.Groups.Get(0)
|
||||||
recordMustExist(t, err, "Couldn't find GID #0")
|
recordMustExist(t, err, "Couldn't find GID #0")
|
||||||
|
|
||||||
if group.ID != 0 {
|
expect(t, group.ID == 0, fmt.Sprintf("group.ID doesn't not match the requested GID. Got '%d' instead.", group.ID))
|
||||||
t.Errorf("group.ID doesn't not match the requested GID. Got '%d' instead.", group.ID)
|
|
||||||
}
|
|
||||||
expect(t, group.Name == "Unknown", fmt.Sprintf("GID #0 is named '%s' and not 'Unknown'", group.Name))
|
expect(t, group.Name == "Unknown", fmt.Sprintf("GID #0 is named '%s' and not 'Unknown'", group.Name))
|
||||||
|
|
||||||
group, err = common.Gstore.Get(1)
|
group, err = common.Groups.Get(1)
|
||||||
recordMustExist(t, err, "Couldn't find GID #1")
|
recordMustExist(t, err, "Couldn't find GID #1")
|
||||||
|
expect(t, group.ID == 1, fmt.Sprintf("group.ID doesn't not match the requested GID. Got '%d' instead.'", group.ID))
|
||||||
|
|
||||||
if group.ID != 1 {
|
ok := common.Groups.Exists(-1)
|
||||||
t.Errorf("group.ID doesn't not match the requested GID. Got '%d' instead.'", group.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
ok := common.Gstore.Exists(-1)
|
|
||||||
expect(t, !ok, "GID #-1 shouldn't exist")
|
expect(t, !ok, "GID #-1 shouldn't exist")
|
||||||
|
|
||||||
// 0 aka Unknown, for system posts and other oddities
|
// 0 aka Unknown, for system posts and other oddities
|
||||||
ok = common.Gstore.Exists(0)
|
ok = common.Groups.Exists(0)
|
||||||
expect(t, ok, "GID #0 should exist")
|
expect(t, ok, "GID #0 should exist")
|
||||||
|
|
||||||
ok = common.Gstore.Exists(1)
|
ok = common.Groups.Exists(1)
|
||||||
expect(t, ok, "GID #1 should exist")
|
expect(t, ok, "GID #1 should exist")
|
||||||
|
|
||||||
var isAdmin = true
|
var isAdmin = true
|
||||||
var isMod = true
|
var isMod = true
|
||||||
var isBanned = false
|
var isBanned = false
|
||||||
gid, err := common.Gstore.Create("Testing", "Test", isAdmin, isMod, isBanned)
|
gid, err := common.Groups.Create("Testing", "Test", isAdmin, isMod, isBanned)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, common.Gstore.Exists(gid), "The group we just made doesn't exist")
|
expect(t, common.Groups.Exists(gid), "The group we just made doesn't exist")
|
||||||
|
|
||||||
group, err = common.Gstore.Get(gid)
|
group, err = common.Groups.Get(gid)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||||
expect(t, group.IsAdmin, "This should be an admin group")
|
expect(t, group.IsAdmin, "This should be an admin group")
|
||||||
|
@ -667,11 +644,11 @@ func TestGroupStore(t *testing.T) {
|
||||||
isAdmin = false
|
isAdmin = false
|
||||||
isMod = true
|
isMod = true
|
||||||
isBanned = true
|
isBanned = true
|
||||||
gid, err = common.Gstore.Create("Testing 2", "Test", isAdmin, isMod, isBanned)
|
gid, err = common.Groups.Create("Testing 2", "Test", isAdmin, isMod, isBanned)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, common.Gstore.Exists(gid), "The group we just made doesn't exist")
|
expect(t, common.Groups.Exists(gid), "The group we just made doesn't exist")
|
||||||
|
|
||||||
group, err = common.Gstore.Get(gid)
|
group, err = common.Groups.Get(gid)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||||
expect(t, !group.IsAdmin, "This should not be an admin group")
|
expect(t, !group.IsAdmin, "This should not be an admin group")
|
||||||
|
@ -682,7 +659,7 @@ func TestGroupStore(t *testing.T) {
|
||||||
err = group.ChangeRank(false, false, true)
|
err = group.ChangeRank(false, false, true)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
|
|
||||||
group, err = common.Gstore.Get(gid)
|
group, err = common.Groups.Get(gid)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||||
expect(t, !group.IsAdmin, "This shouldn't be an admin group")
|
expect(t, !group.IsAdmin, "This shouldn't be an admin group")
|
||||||
|
@ -692,7 +669,7 @@ func TestGroupStore(t *testing.T) {
|
||||||
err = group.ChangeRank(true, true, true)
|
err = group.ChangeRank(true, true, true)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
|
|
||||||
group, err = common.Gstore.Get(gid)
|
group, err = common.Groups.Get(gid)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||||
expect(t, group.IsAdmin, "This should be an admin group")
|
expect(t, group.IsAdmin, "This should be an admin group")
|
||||||
|
@ -702,7 +679,7 @@ func TestGroupStore(t *testing.T) {
|
||||||
err = group.ChangeRank(false, true, true)
|
err = group.ChangeRank(false, true, true)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
|
|
||||||
group, err = common.Gstore.Get(gid)
|
group, err = common.Groups.Get(gid)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||||
expect(t, !group.IsAdmin, "This shouldn't be an admin group")
|
expect(t, !group.IsAdmin, "This shouldn't be an admin group")
|
||||||
|
@ -710,9 +687,9 @@ func TestGroupStore(t *testing.T) {
|
||||||
expect(t, !group.IsBanned, "This shouldn't be a ban group")
|
expect(t, !group.IsBanned, "This shouldn't be a ban group")
|
||||||
|
|
||||||
// Make sure the data is static
|
// Make sure the data is static
|
||||||
common.Gstore.Reload(gid)
|
common.Groups.Reload(gid)
|
||||||
|
|
||||||
group, err = common.Gstore.Get(gid)
|
group, err = common.Groups.Get(gid)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||||
expect(t, !group.IsAdmin, "This shouldn't be an admin group")
|
expect(t, !group.IsAdmin, "This shouldn't be an admin group")
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes
|
// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes
|
||||||
// TODO: Disable stat updates in posts handled by plugin_guilds
|
// TODO: Disable stat updates in posts handled by plugin_guilds
|
||||||
|
// TODO: Make sure this route is member only
|
||||||
func routeEditTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func routeEditTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -50,7 +51,7 @@ func routeEditTopic(w http.ResponseWriter, r *http.Request, user common.User) co
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.Fstore.UpdateLastTopic(topic.ID, user.ID, topic.ParentID)
|
err = common.Forums.UpdateLastTopic(topic.ID, user.ID, topic.ParentID)
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
@ -65,11 +66,12 @@ func routeEditTopic(w http.ResponseWriter, r *http.Request, user common.User) co
|
||||||
|
|
||||||
// TODO: Add support for soft-deletion and add a permission for hard delete in addition to the usual
|
// TODO: Add support for soft-deletion and add a permission for hard delete in addition to the usual
|
||||||
// TODO: Disable stat updates in posts handled by plugin_guilds
|
// TODO: Disable stat updates in posts handled by plugin_guilds
|
||||||
|
// TODO: Make sure this route is member only
|
||||||
func routeDeleteTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func routeDeleteTopic(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
// TODO: Move this to some sort of middleware
|
// TODO: Move this to some sort of middleware
|
||||||
var tids []int
|
var tids []int
|
||||||
var isJs = false
|
var isJs = false
|
||||||
if r.Header.Get("Content-type") == "application/json" {
|
if common.ReqIsJson(r) {
|
||||||
if r.Body == nil {
|
if r.Body == nil {
|
||||||
return common.PreErrorJS("No request body", w, r)
|
return common.PreErrorJS("No request body", w, r)
|
||||||
}
|
}
|
||||||
|
@ -114,7 +116,7 @@ func routeDeleteTopic(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.AddModLog("delete", tid, "topic", user.LastIP, user.ID)
|
err = common.ModLogs.Create("delete", tid, "topic", user.LastIP, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
@ -158,7 +160,7 @@ func routeStickTopic(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.AddModLog("stick", tid, "topic", user.LastIP, user.ID)
|
err = common.ModLogs.Create("stick", tid, "topic", user.LastIP, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -197,7 +199,7 @@ func routeUnstickTopic(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.AddModLog("unstick", tid, "topic", user.LastIP, user.ID)
|
err = common.ModLogs.Create("unstick", tid, "topic", user.LastIP, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -214,7 +216,7 @@ func routeLockTopic(w http.ResponseWriter, r *http.Request, user common.User) co
|
||||||
// TODO: Move this to some sort of middleware
|
// TODO: Move this to some sort of middleware
|
||||||
var tids []int
|
var tids []int
|
||||||
var isJs = false
|
var isJs = false
|
||||||
if r.Header.Get("Content-type") == "application/json" {
|
if common.ReqIsJson(r) {
|
||||||
if r.Body == nil {
|
if r.Body == nil {
|
||||||
return common.PreErrorJS("No request body", w, r)
|
return common.PreErrorJS("No request body", w, r)
|
||||||
}
|
}
|
||||||
|
@ -256,7 +258,7 @@ func routeLockTopic(w http.ResponseWriter, r *http.Request, user common.User) co
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.AddModLog("lock", tid, "topic", user.LastIP, user.ID)
|
err = common.ModLogs.Create("lock", tid, "topic", user.LastIP, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
@ -299,7 +301,7 @@ func routeUnlockTopic(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.AddModLog("unlock", tid, "topic", user.LastIP, user.ID)
|
err = common.ModLogs.Create("unlock", tid, "topic", user.LastIP, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -425,7 +427,7 @@ func routeReplyDeleteSubmit(w http.ResponseWriter, r *http.Request, user common.
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.AddModLog("delete", reply.ParentID, "reply", user.LastIP, user.ID)
|
err = common.ModLogs.Create("delete", reply.ParentID, "reply", user.LastIP, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalErrorJSQ(err, w, r, isJs)
|
return common.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
@ -588,7 +590,7 @@ func routeIps(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.IPSearchPage{"IP Search", user, headerVars, userList, ip}
|
pi := common.IPSearchPage{common.GetTitlePhrase("ip-search"), user, headerVars, userList, ip}
|
||||||
if common.PreRenderHooks["pre_render_ips"] != nil {
|
if common.PreRenderHooks["pre_render_ips"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_ips", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_ips", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -610,9 +612,9 @@ func routeBanSubmit(w http.ResponseWriter, r *http.Request, user common.User) co
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError("The provided common.User ID is not a valid number.", w, r, user)
|
return common.LocalError("The provided common.User ID is not a valid number.", w, r, user)
|
||||||
}
|
}
|
||||||
/*if uid == -2 {
|
if uid == -2 {
|
||||||
return common.LocalError("Stop trying to ban Merlin! Ban admin! Bad! No!",w,r,user)
|
return common.LocalError("Why don't you like Merlin?", w, r, user)
|
||||||
}*/
|
}
|
||||||
|
|
||||||
targetUser, err := common.Users.Get(uid)
|
targetUser, err := common.Users.Get(uid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
|
@ -622,7 +624,7 @@ func routeBanSubmit(w http.ResponseWriter, r *http.Request, user common.User) co
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Is there a difference between IsMod and IsSuperMod? Should we delete the redundant one?
|
// TODO: Is there a difference between IsMod and IsSuperMod? Should we delete the redundant one?
|
||||||
if targetUser.IsSuperAdmin || targetUser.IsAdmin || targetUser.IsMod {
|
if targetUser.IsMod {
|
||||||
return common.LocalError("You may not ban another staff member.", w, r, user)
|
return common.LocalError("You may not ban another staff member.", w, r, user)
|
||||||
}
|
}
|
||||||
if uid == user.ID {
|
if uid == user.ID {
|
||||||
|
@ -665,7 +667,7 @@ func routeBanSubmit(w http.ResponseWriter, r *http.Request, user common.User) co
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.AddModLog("ban", uid, "user", user.LastIP, user.ID)
|
err = common.ModLogs.Create("ban", uid, "user", user.LastIP, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -704,7 +706,7 @@ func routeUnban(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.AddModLog("unban", uid, "user", user.LastIP, user.ID)
|
err = common.ModLogs.Create("unban", uid, "user", user.LastIP, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -738,7 +740,7 @@ func routeActivate(w http.ResponseWriter, r *http.Request, user common.User) com
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.AddModLog("activate", targetUser.ID, "user", user.LastIP, user.ID)
|
err = common.ModLogs.Create("activate", targetUser.ID, "user", user.LastIP, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
319
panel_routes.go
319
panel_routes.go
|
@ -28,6 +28,16 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||||
var cpustr = "Unknown"
|
var cpustr = "Unknown"
|
||||||
var cpuColour string
|
var cpuColour string
|
||||||
|
|
||||||
|
lessThanSwitch := func(number int, lowerBound int, midBound int) string {
|
||||||
|
switch {
|
||||||
|
case number < lowerBound:
|
||||||
|
return "stat_green"
|
||||||
|
case number < midBound:
|
||||||
|
return "stat_orange"
|
||||||
|
}
|
||||||
|
return "stat_red"
|
||||||
|
}
|
||||||
|
|
||||||
var ramstr, ramColour string
|
var ramstr, ramColour string
|
||||||
memres, err := mem.VirtualMemory()
|
memres, err := mem.VirtualMemory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -37,7 +47,6 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||||
usedCount := common.ConvertByteInUnit(float64(memres.Total-memres.Available), totalUnit)
|
usedCount := common.ConvertByteInUnit(float64(memres.Total-memres.Available), totalUnit)
|
||||||
|
|
||||||
// Round totals with .9s up, it's how most people see it anyway. Floats are notoriously imprecise, so do it off 0.85
|
// Round totals with .9s up, it's how most people see it anyway. Floats are notoriously imprecise, so do it off 0.85
|
||||||
//log.Print("pre used_count",used_count)
|
|
||||||
var totstr string
|
var totstr string
|
||||||
if (totalCount - float64(int(totalCount))) > 0.85 {
|
if (totalCount - float64(int(totalCount))) > 0.85 {
|
||||||
usedCount += 1.0 - (totalCount - float64(int(totalCount)))
|
usedCount += 1.0 - (totalCount - float64(int(totalCount)))
|
||||||
|
@ -45,7 +54,6 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||||
} else {
|
} else {
|
||||||
totstr = fmt.Sprintf("%.1f", totalCount)
|
totstr = fmt.Sprintf("%.1f", totalCount)
|
||||||
}
|
}
|
||||||
//log.Print("post used_count",used_count)
|
|
||||||
|
|
||||||
if usedCount > totalCount {
|
if usedCount > totalCount {
|
||||||
usedCount = totalCount
|
usedCount = totalCount
|
||||||
|
@ -53,31 +61,27 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||||
ramstr = fmt.Sprintf("%.1f", usedCount) + " / " + totstr + totalUnit
|
ramstr = fmt.Sprintf("%.1f", usedCount) + " / " + totstr + totalUnit
|
||||||
|
|
||||||
ramperc := ((memres.Total - memres.Available) * 100) / memres.Total
|
ramperc := ((memres.Total - memres.Available) * 100) / memres.Total
|
||||||
//log.Print("ramperc",ramperc)
|
ramColour = lessThanSwitch(int(ramperc), 50, 75)
|
||||||
if ramperc < 50 {
|
|
||||||
ramColour = "stat_green"
|
|
||||||
} else if ramperc < 75 {
|
|
||||||
ramColour = "stat_orange"
|
|
||||||
} else {
|
|
||||||
ramColour = "stat_red"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
greaterThanSwitch := func(number int, lowerBound int, midBound int) string {
|
||||||
|
switch {
|
||||||
|
case number > midBound:
|
||||||
|
return "stat_green"
|
||||||
|
case number > lowerBound:
|
||||||
|
return "stat_orange"
|
||||||
|
}
|
||||||
|
return "stat_red"
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add a stat store for this?
|
||||||
var postCount int
|
var postCount int
|
||||||
err = stmts.todaysPostCount.QueryRow().Scan(&postCount)
|
err = stmts.todaysPostCount.QueryRow().Scan(&postCount)
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
var postInterval = "day"
|
var postInterval = "day"
|
||||||
|
var postColour = greaterThanSwitch(postCount, 5, 25)
|
||||||
var postColour string
|
|
||||||
if postCount > 25 {
|
|
||||||
postColour = "stat_green"
|
|
||||||
} else if postCount > 5 {
|
|
||||||
postColour = "stat_orange"
|
|
||||||
} else {
|
|
||||||
postColour = "stat_red"
|
|
||||||
}
|
|
||||||
|
|
||||||
var topicCount int
|
var topicCount int
|
||||||
err = stmts.todaysTopicCount.QueryRow().Scan(&topicCount)
|
err = stmts.todaysTopicCount.QueryRow().Scan(&topicCount)
|
||||||
|
@ -85,15 +89,7 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
var topicInterval = "day"
|
var topicInterval = "day"
|
||||||
|
var topicColour = greaterThanSwitch(topicCount, 0, 8)
|
||||||
var topicColour string
|
|
||||||
if topicCount > 8 {
|
|
||||||
topicColour = "stat_green"
|
|
||||||
} else if topicCount > 0 {
|
|
||||||
topicColour = "stat_orange"
|
|
||||||
} else {
|
|
||||||
topicColour = "stat_red"
|
|
||||||
}
|
|
||||||
|
|
||||||
var reportCount int
|
var reportCount int
|
||||||
err = stmts.todaysReportCount.QueryRow().Scan(&reportCount)
|
err = stmts.todaysReportCount.QueryRow().Scan(&reportCount)
|
||||||
|
@ -120,32 +116,9 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||||
gonline := wsHub.guestCount()
|
gonline := wsHub.guestCount()
|
||||||
totonline := uonline + gonline
|
totonline := uonline + gonline
|
||||||
|
|
||||||
var onlineColour string
|
var onlineColour = greaterThanSwitch(totonline, 3, 10)
|
||||||
if totonline > 10 {
|
var onlineGuestsColour = greaterThanSwitch(gonline, 1, 10)
|
||||||
onlineColour = "stat_green"
|
var onlineUsersColour = greaterThanSwitch(uonline, 1, 5)
|
||||||
} else if totonline > 3 {
|
|
||||||
onlineColour = "stat_orange"
|
|
||||||
} else {
|
|
||||||
onlineColour = "stat_red"
|
|
||||||
}
|
|
||||||
|
|
||||||
var onlineGuestsColour string
|
|
||||||
if gonline > 10 {
|
|
||||||
onlineGuestsColour = "stat_green"
|
|
||||||
} else if gonline > 1 {
|
|
||||||
onlineGuestsColour = "stat_orange"
|
|
||||||
} else {
|
|
||||||
onlineGuestsColour = "stat_red"
|
|
||||||
}
|
|
||||||
|
|
||||||
var onlineUsersColour string
|
|
||||||
if uonline > 5 {
|
|
||||||
onlineUsersColour = "stat_green"
|
|
||||||
} else if uonline > 1 {
|
|
||||||
onlineUsersColour = "stat_orange"
|
|
||||||
} else {
|
|
||||||
onlineUsersColour = "stat_red"
|
|
||||||
}
|
|
||||||
|
|
||||||
totonline, totunit := common.ConvertFriendlyUnit(totonline)
|
totonline, totunit := common.ConvertFriendlyUnit(totonline)
|
||||||
uonline, uunit := common.ConvertFriendlyUnit(uonline)
|
uonline, uunit := common.ConvertFriendlyUnit(uonline)
|
||||||
|
@ -168,7 +141,7 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-visitorsperweek", "2 visitors / week", 13, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The number of unique visitors we've had over the last 7 days"*/})
|
gridElements = append(gridElements, common.GridElement{"dash-visitorsperweek", "2 visitors / week", 13, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The number of unique visitors we've had over the last 7 days"*/})
|
||||||
gridElements = append(gridElements, common.GridElement{"dash-postsperuser", "5 posts / user / week", 14, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The average number of posts made by each active user over the past week"*/})
|
gridElements = append(gridElements, common.GridElement{"dash-postsperuser", "5 posts / user / week", 14, "grid_stat stat_disabled", "", "", "Coming Soon!" /*"The average number of posts made by each active user over the past week"*/})
|
||||||
|
|
||||||
pi := common.PanelDashboardPage{"Control Panel Dashboard", user, headerVars, stats, gridElements}
|
pi := common.PanelDashboardPage{common.GetTitlePhrase("panel-dashboard"), user, headerVars, stats, gridElements}
|
||||||
if common.PreRenderHooks["pre_render_panel_dashboard"] != nil {
|
if common.PreRenderHooks["pre_render_panel_dashboard"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_dashboard", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_dashboard", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -192,7 +165,7 @@ func routePanelForums(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
|
|
||||||
// TODO: Paginate this?
|
// TODO: Paginate this?
|
||||||
var forumList []interface{}
|
var forumList []interface{}
|
||||||
forums, err := common.Fstore.GetAll()
|
forums, err := common.Forums.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -207,7 +180,7 @@ func routePanelForums(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
forumList = append(forumList, fadmin)
|
forumList = append(forumList, fadmin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pi := common.PanelPage{"Forum Manager", user, headerVars, stats, forumList, nil}
|
pi := common.PanelPage{common.GetTitlePhrase("panel-forums"), user, headerVars, stats, forumList, nil}
|
||||||
if common.PreRenderHooks["pre_render_panel_forums"] != nil {
|
if common.PreRenderHooks["pre_render_panel_forums"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_forums", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_forums", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -217,6 +190,7 @@ func routePanelForums(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +209,7 @@ func routePanelForumsCreateSubmit(w http.ResponseWriter, r *http.Request, user c
|
||||||
factive := r.PostFormValue("forum-name")
|
factive := r.PostFormValue("forum-name")
|
||||||
active := (factive == "on" || factive == "1")
|
active := (factive == "on" || factive == "1")
|
||||||
|
|
||||||
_, err := common.Fstore.Create(fname, fdesc, active, fpreset)
|
_, err := common.Forums.Create(fname, fdesc, active, fpreset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -259,17 +233,18 @@ func routePanelForumsDelete(w http.ResponseWriter, r *http.Request, user common.
|
||||||
return common.LocalError("The provided Forum ID is not a valid number.", w, r, user)
|
return common.LocalError("The provided Forum ID is not a valid number.", w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
forum, err := common.Fstore.Get(fid)
|
forum, err := common.Forums.Get(fid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
return common.LocalError("The forum you're trying to delete doesn't exist.", w, r, user)
|
return common.LocalError("The forum you're trying to delete doesn't exist.", w, r, user)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Make this a phrase
|
||||||
confirmMsg := "Are you sure you want to delete the '" + forum.Name + "' forum?"
|
confirmMsg := "Are you sure you want to delete the '" + forum.Name + "' forum?"
|
||||||
yousure := common.AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid), confirmMsg}
|
yousure := common.AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid), confirmMsg}
|
||||||
|
|
||||||
pi := common.PanelPage{"Delete Forum", user, headerVars, stats, tList, yousure}
|
pi := common.PanelPage{common.GetTitlePhrase("panel-delete-forum"), user, headerVars, stats, tList, yousure}
|
||||||
if common.PreRenderHooks["pre_render_panel_delete_forum"] != nil {
|
if common.PreRenderHooks["pre_render_panel_delete_forum"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_delete_forum", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_delete_forum", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -296,7 +271,7 @@ func routePanelForumsDeleteSubmit(w http.ResponseWriter, r *http.Request, user c
|
||||||
return common.LocalError("The provided Forum ID is not a valid number.", w, r, user)
|
return common.LocalError("The provided Forum ID is not a valid number.", w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.Fstore.Delete(fid)
|
err = common.Forums.Delete(fid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
return common.LocalError("The forum you're trying to delete doesn't exist.", w, r, user)
|
return common.LocalError("The forum you're trying to delete doesn't exist.", w, r, user)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -321,7 +296,7 @@ func routePanelForumsEdit(w http.ResponseWriter, r *http.Request, user common.Us
|
||||||
return common.LocalError("The provided Forum ID is not a valid number.", w, r, user)
|
return common.LocalError("The provided Forum ID is not a valid number.", w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
forum, err := common.Fstore.Get(fid)
|
forum, err := common.Forums.Get(fid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
return common.LocalError("The forum you're trying to edit doesn't exist.", w, r, user)
|
return common.LocalError("The forum you're trying to edit doesn't exist.", w, r, user)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -332,7 +307,7 @@ func routePanelForumsEdit(w http.ResponseWriter, r *http.Request, user common.Us
|
||||||
forum.Preset = "custom"
|
forum.Preset = "custom"
|
||||||
}
|
}
|
||||||
|
|
||||||
glist, err := common.Gstore.GetAll()
|
glist, err := common.Groups.GetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -345,7 +320,7 @@ func routePanelForumsEdit(w http.ResponseWriter, r *http.Request, user common.Us
|
||||||
gplist = append(gplist, common.GroupForumPermPreset{group, common.ForumPermsToGroupForumPreset(group.Forums[fid])})
|
gplist = append(gplist, common.GroupForumPermPreset{group, common.ForumPermsToGroupForumPreset(group.Forums[fid])})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelEditForumPage{"Forum Editor", user, headerVars, stats, forum.ID, forum.Name, forum.Desc, forum.Active, forum.Preset, gplist}
|
pi := common.PanelEditForumPage{common.GetTitlePhrase("panel-edit-forum"), user, headerVars, stats, forum.ID, forum.Name, forum.Desc, forum.Active, forum.Preset, gplist}
|
||||||
if common.PreRenderHooks["pre_render_panel_edit_forum"] != nil {
|
if common.PreRenderHooks["pre_render_panel_edit_forum"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_edit_forum", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_edit_forum", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -355,6 +330,7 @@ func routePanelForumsEdit(w http.ResponseWriter, r *http.Request, user common.Us
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -373,7 +349,7 @@ func routePanelForumsEditSubmit(w http.ResponseWriter, r *http.Request, user com
|
||||||
return common.LocalErrorJSQ("The provided Forum ID is not a valid number.", w, r, user, isJs)
|
return common.LocalErrorJSQ("The provided Forum ID is not a valid number.", w, r, user, isJs)
|
||||||
}
|
}
|
||||||
|
|
||||||
forum, err := common.Fstore.Get(fid)
|
forum, err := common.Forums.Get(fid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
return common.LocalErrorJSQ("The forum you're trying to edit doesn't exist.", w, r, user, isJs)
|
return common.LocalErrorJSQ("The forum you're trying to edit doesn't exist.", w, r, user, isJs)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -425,7 +401,7 @@ func routePanelForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, use
|
||||||
return common.LocalErrorJSQ("Invalid Group ID", w, r, user, isJs)
|
return common.LocalErrorJSQ("Invalid Group ID", w, r, user, isJs)
|
||||||
}
|
}
|
||||||
|
|
||||||
forum, err := common.Fstore.Get(fid)
|
forum, err := common.Forums.Get(fid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
return common.LocalErrorJSQ("This forum doesn't exist", w, r, user, isJs)
|
return common.LocalErrorJSQ("This forum doesn't exist", w, r, user, isJs)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -454,46 +430,35 @@ func routePanelSettings(w http.ResponseWriter, r *http.Request, user common.User
|
||||||
if !user.Perms.EditSettings {
|
if !user.Perms.EditSettings {
|
||||||
return common.NoPermissions(w, r, user)
|
return common.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
var settingList = make(map[string]interface{})
|
var settingList = make(map[string]interface{})
|
||||||
rows, err := stmts.getSettings.Query()
|
|
||||||
|
settings, err := headerVars.Settings.BypassGetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
// nolint need the type so people viewing this file understand what it returns without visiting setting.go
|
// nolint need the type so people viewing this file understand what it returns without visiting phrases.go
|
||||||
var settingLabels map[string]string = common.GetAllSettingLabels()
|
var settingLabels map[string]string = common.GetAllSettingLabels()
|
||||||
var sname, scontent, stype string
|
for _, setting := range settings {
|
||||||
for rows.Next() {
|
if setting.Type == "list" {
|
||||||
err := rows.Scan(&sname, &scontent, &stype)
|
llist := settingLabels[setting.Name]
|
||||||
if err != nil {
|
|
||||||
return common.InternalError(err, w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
if stype == "list" {
|
|
||||||
llist := settingLabels[sname]
|
|
||||||
labels := strings.Split(llist, ",")
|
labels := strings.Split(llist, ",")
|
||||||
conv, err := strconv.Atoi(scontent)
|
conv, err := strconv.Atoi(setting.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.LocalError("The setting '"+sname+"' can't be converted to an integer", w, r, user)
|
return common.LocalError("The setting '"+setting.Name+"' can't be converted to an integer", w, r, user)
|
||||||
}
|
}
|
||||||
scontent = labels[conv-1]
|
setting.Content = labels[conv-1]
|
||||||
} else if stype == "bool" {
|
} else if setting.Type == "bool" {
|
||||||
if scontent == "1" {
|
if setting.Content == "1" {
|
||||||
scontent = "Yes"
|
setting.Content = "Yes"
|
||||||
} else {
|
} else {
|
||||||
scontent = "No"
|
setting.Content = "No"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
settingList[sname] = scontent
|
settingList[setting.Name] = setting.Content
|
||||||
}
|
|
||||||
err = rows.Err()
|
|
||||||
if err != nil {
|
|
||||||
return common.InternalError(err, w, r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelPage{"Setting Manager", user, headerVars, stats, tList, settingList}
|
pi := common.PanelPage{common.GetTitlePhrase("panel-settings"), user, headerVars, stats, tList, settingList}
|
||||||
if common.PreRenderHooks["pre_render_panel_settings"] != nil {
|
if common.PreRenderHooks["pre_render_panel_settings"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_settings", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_settings", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -506,7 +471,7 @@ func routePanelSettings(w http.ResponseWriter, r *http.Request, user common.User
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func routePanelSetting(w http.ResponseWriter, r *http.Request, user common.User, sname string) common.RouteError {
|
func routePanelSettingEdit(w http.ResponseWriter, r *http.Request, user common.User, sname string) common.RouteError {
|
||||||
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
headerVars, stats, ferr := common.PanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
|
@ -539,7 +504,7 @@ func routePanelSetting(w http.ResponseWriter, r *http.Request, user common.User,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelPage{"Edit Setting", user, headerVars, stats, itemList, setting}
|
pi := common.PanelPage{common.GetTitlePhrase("panel-edit-setting"), user, headerVars, stats, itemList, setting}
|
||||||
if common.PreRenderHooks["pre_render_panel_setting"] != nil {
|
if common.PreRenderHooks["pre_render_panel_setting"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_setting", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_setting", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -552,7 +517,7 @@ func routePanelSetting(w http.ResponseWriter, r *http.Request, user common.User,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func routePanelSettingEdit(w http.ResponseWriter, r *http.Request, user common.User, sname string) common.RouteError {
|
func routePanelSettingEditSubmit(w http.ResponseWriter, r *http.Request, user common.User, sname string) common.RouteError {
|
||||||
headerLite, ferr := common.SimplePanelUserCheck(w, r, &user)
|
headerLite, ferr := common.SimplePanelUserCheck(w, r, &user)
|
||||||
if ferr != nil {
|
if ferr != nil {
|
||||||
return ferr
|
return ferr
|
||||||
|
@ -562,34 +527,14 @@ func routePanelSettingEdit(w http.ResponseWriter, r *http.Request, user common.U
|
||||||
}
|
}
|
||||||
|
|
||||||
scontent := r.PostFormValue("setting-value")
|
scontent := r.PostFormValue("setting-value")
|
||||||
setting, err := headerLite.Settings.BypassGet(sname)
|
err := headerLite.Settings.Update(sname, scontent)
|
||||||
if err == ErrNoRows {
|
|
||||||
return common.LocalError("The setting you want to edit doesn't exist.", w, r, user)
|
|
||||||
} else if err != nil {
|
|
||||||
return common.InternalError(err, w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
if setting.Type == "bool" {
|
|
||||||
if scontent == "on" || scontent == "1" {
|
|
||||||
scontent = "1"
|
|
||||||
} else {
|
|
||||||
scontent = "0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Make this a method or function?
|
|
||||||
_, err = stmts.updateSetting.Exec(scontent, sname)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if common.SafeSettingError(err) {
|
||||||
|
return common.LocalError(err.Error(), w, r, user)
|
||||||
|
}
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
errmsg := headerLite.Settings.ParseSetting(sname, scontent, setting.Type, setting.Constraint)
|
|
||||||
if errmsg != "" {
|
|
||||||
return common.LocalError(errmsg, w, r, user)
|
|
||||||
}
|
|
||||||
// TODO: Do a reload instead?
|
|
||||||
common.SettingBox.Store(headerLite.Settings)
|
|
||||||
|
|
||||||
http.Redirect(w, r, "/panel/settings/", http.StatusSeeOther)
|
http.Redirect(w, r, "/panel/settings/", http.StatusSeeOther)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -604,7 +549,7 @@ func routePanelWordFilters(w http.ResponseWriter, r *http.Request, user common.U
|
||||||
}
|
}
|
||||||
|
|
||||||
var filterList = common.WordFilterBox.Load().(common.WordFilterMap)
|
var filterList = common.WordFilterBox.Load().(common.WordFilterMap)
|
||||||
pi := common.PanelPage{"Word Filter Manager", user, headerVars, stats, tList, filterList}
|
pi := common.PanelPage{common.GetTitlePhrase("panel-word-filters"), user, headerVars, stats, tList, filterList}
|
||||||
if common.PreRenderHooks["pre_render_panel_word_filters"] != nil {
|
if common.PreRenderHooks["pre_render_panel_word_filters"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_word_filters", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_word_filters", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -665,7 +610,7 @@ func routePanelWordFiltersEdit(w http.ResponseWriter, r *http.Request, user comm
|
||||||
|
|
||||||
_ = wfid
|
_ = wfid
|
||||||
|
|
||||||
pi := common.PanelPage{"Edit Word Filter", user, headerVars, stats, tList, nil}
|
pi := common.PanelPage{common.GetTitlePhrase("panel-edit-word-filter"), user, headerVars, stats, tList, nil}
|
||||||
if common.PreRenderHooks["pre_render_panel_word_filters_edit"] != nil {
|
if common.PreRenderHooks["pre_render_panel_word_filters_edit"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_word_filters_edit", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_word_filters_edit", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -758,7 +703,7 @@ func routePanelPlugins(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
pluginList = append(pluginList, plugin)
|
pluginList = append(pluginList, plugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelPage{"Plugin Manager", user, headerVars, stats, pluginList, nil}
|
pi := common.PanelPage{common.GetTitlePhrase("panel-plugins"), user, headerVars, stats, pluginList, nil}
|
||||||
if common.PreRenderHooks["pre_render_panel_plugins"] != nil {
|
if common.PreRenderHooks["pre_render_panel_plugins"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_plugins", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_plugins", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -966,8 +911,8 @@ func routePanelUsers(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
puser.Avatar = strings.Replace(common.Config.Noavatar, "{id}", strconv.Itoa(puser.ID), 1)
|
puser.Avatar = strings.Replace(common.Config.Noavatar, "{id}", strconv.Itoa(puser.ID), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if common.Gstore.DirtyGet(puser.Group).Tag != "" {
|
if common.Groups.DirtyGet(puser.Group).Tag != "" {
|
||||||
puser.Tag = common.Gstore.DirtyGet(puser.Group).Tag
|
puser.Tag = common.Groups.DirtyGet(puser.Group).Tag
|
||||||
} else {
|
} else {
|
||||||
puser.Tag = ""
|
puser.Tag = ""
|
||||||
}
|
}
|
||||||
|
@ -979,7 +924,7 @@ func routePanelUsers(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
}
|
}
|
||||||
|
|
||||||
pageList := common.Paginate(stats.Users, perPage, 5)
|
pageList := common.Paginate(stats.Users, perPage, 5)
|
||||||
pi := common.PanelUserPage{"User Manager", user, headerVars, stats, userList, pageList, page, lastPage}
|
pi := common.PanelUserPage{common.GetTitlePhrase("panel-users"), user, headerVars, stats, userList, pageList, page, lastPage}
|
||||||
if common.PreRenderHooks["pre_render_panel_users"] != nil {
|
if common.PreRenderHooks["pre_render_panel_users"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_users", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_users", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1018,7 +963,7 @@ func routePanelUsersEdit(w http.ResponseWriter, r *http.Request, user common.Use
|
||||||
}
|
}
|
||||||
|
|
||||||
// ? - Should we stop admins from deleting all the groups? Maybe, protect the group they're currently using?
|
// ? - Should we stop admins from deleting all the groups? Maybe, protect the group they're currently using?
|
||||||
groups, err := common.Gstore.GetRange(1, 0) // ? - 0 = Go to the end
|
groups, err := common.Groups.GetRange(1, 0) // ? - 0 = Go to the end
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -1034,7 +979,7 @@ func routePanelUsersEdit(w http.ResponseWriter, r *http.Request, user common.Use
|
||||||
groupList = append(groupList, group)
|
groupList = append(groupList, group)
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelPage{"User Editor", user, headerVars, stats, groupList, targetUser}
|
pi := common.PanelPage{common.GetTitlePhrase("panel-edit-user"), user, headerVars, stats, groupList, targetUser}
|
||||||
if common.PreRenderHooks["pre_render_panel_edit_user"] != nil {
|
if common.PreRenderHooks["pre_render_panel_edit_user"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_edit_user", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_edit_user", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1095,7 +1040,7 @@ func routePanelUsersEditSubmit(w http.ResponseWriter, r *http.Request, user comm
|
||||||
return common.LocalError("You need to provide a whole number for the group ID", w, r, user)
|
return common.LocalError("You need to provide a whole number for the group ID", w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
group, err := common.Gstore.Get(newgroup)
|
group, err := common.Groups.Get(newgroup)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
return common.LocalError("The group you're trying to place this user in doesn't exist.", w, r, user)
|
return common.LocalError("The group you're trying to place this user in doesn't exist.", w, r, user)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -1109,6 +1054,7 @@ func routePanelUsersEditSubmit(w http.ResponseWriter, r *http.Request, user comm
|
||||||
return common.LocalError("You need the EditUserGroupSuperMod permission to assign someone to a super mod group.", w, r, user)
|
return common.LocalError("You need the EditUserGroupSuperMod permission to assign someone to a super mod group.", w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move this query into common
|
||||||
_, err = stmts.updateUser.Exec(newname, newemail, newgroup, targetUser.ID)
|
_, err = stmts.updateUser.Exec(newname, newemail, newgroup, targetUser.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
|
@ -1138,7 +1084,7 @@ func routePanelGroups(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
|
|
||||||
var count int
|
var count int
|
||||||
var groupList []common.GroupAdmin
|
var groupList []common.GroupAdmin
|
||||||
groups, _ := common.Gstore.GetRange(offset, 0)
|
groups, _ := common.Groups.GetRange(offset, 0)
|
||||||
for _, group := range groups {
|
for _, group := range groups {
|
||||||
if count == perPage {
|
if count == perPage {
|
||||||
break
|
break
|
||||||
|
@ -1149,6 +1095,7 @@ func routePanelGroups(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
var canEdit bool
|
var canEdit bool
|
||||||
var canDelete = false
|
var canDelete = false
|
||||||
|
|
||||||
|
// TODO: Use a switch for this
|
||||||
if group.IsAdmin {
|
if group.IsAdmin {
|
||||||
rank = "Admin"
|
rank = "Admin"
|
||||||
rankClass = "admin"
|
rankClass = "admin"
|
||||||
|
@ -1173,7 +1120,7 @@ func routePanelGroups(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
//log.Printf("groupList: %+v\n", groupList)
|
//log.Printf("groupList: %+v\n", groupList)
|
||||||
|
|
||||||
pageList := common.Paginate(stats.Groups, perPage, 5)
|
pageList := common.Paginate(stats.Groups, perPage, 5)
|
||||||
pi := common.PanelGroupPage{"Group Manager", user, headerVars, stats, groupList, pageList, page, lastPage}
|
pi := common.PanelGroupPage{common.GetTitlePhrase("panel-groups"), user, headerVars, stats, groupList, pageList, page, lastPage}
|
||||||
if common.PreRenderHooks["pre_render_panel_groups"] != nil {
|
if common.PreRenderHooks["pre_render_panel_groups"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_groups", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_groups", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1201,7 +1148,7 @@ func routePanelGroupsEdit(w http.ResponseWriter, r *http.Request, user common.Us
|
||||||
return common.LocalError("You need to provide a whole number for the group ID", w, r, user)
|
return common.LocalError("You need to provide a whole number for the group ID", w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
group, err := common.Gstore.Get(gid)
|
group, err := common.Groups.Get(gid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
//log.Print("aaaaa monsters")
|
//log.Print("aaaaa monsters")
|
||||||
return common.NotFound(w, r)
|
return common.NotFound(w, r)
|
||||||
|
@ -1232,7 +1179,7 @@ func routePanelGroupsEdit(w http.ResponseWriter, r *http.Request, user common.Us
|
||||||
|
|
||||||
disableRank := !user.Perms.EditGroupGlobalPerms || (group.ID == 6)
|
disableRank := !user.Perms.EditGroupGlobalPerms || (group.ID == 6)
|
||||||
|
|
||||||
pi := common.PanelEditGroupPage{"Group Editor", user, headerVars, stats, group.ID, group.Name, group.Tag, rank, disableRank}
|
pi := common.PanelEditGroupPage{common.GetTitlePhrase("panel-edit-group"), user, headerVars, stats, group.ID, group.Name, group.Tag, rank, disableRank}
|
||||||
if common.PreRenderHooks["pre_render_panel_edit_group"] != nil {
|
if common.PreRenderHooks["pre_render_panel_edit_group"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_edit_group", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_edit_group", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1259,7 +1206,7 @@ func routePanelGroupsEditPerms(w http.ResponseWriter, r *http.Request, user comm
|
||||||
return common.LocalError("The Group ID is not a valid integer.", w, r, user)
|
return common.LocalError("The Group ID is not a valid integer.", w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
group, err := common.Gstore.Get(gid)
|
group, err := common.Groups.Get(gid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
//log.Print("aaaaa monsters")
|
//log.Print("aaaaa monsters")
|
||||||
return common.NotFound(w, r)
|
return common.NotFound(w, r)
|
||||||
|
@ -1310,7 +1257,7 @@ func routePanelGroupsEditPerms(w http.ResponseWriter, r *http.Request, user comm
|
||||||
globalPerms = append(globalPerms, common.NameLangToggle{"ViewIPs", common.GetGlobalPermPhrase("ViewIPs"), group.Perms.ViewIPs})
|
globalPerms = append(globalPerms, common.NameLangToggle{"ViewIPs", common.GetGlobalPermPhrase("ViewIPs"), group.Perms.ViewIPs})
|
||||||
globalPerms = append(globalPerms, common.NameLangToggle{"UploadFiles", common.GetGlobalPermPhrase("UploadFiles"), group.Perms.UploadFiles})
|
globalPerms = append(globalPerms, common.NameLangToggle{"UploadFiles", common.GetGlobalPermPhrase("UploadFiles"), group.Perms.UploadFiles})
|
||||||
|
|
||||||
pi := common.PanelEditGroupPermsPage{"Group Editor", user, headerVars, stats, group.ID, group.Name, localPerms, globalPerms}
|
pi := common.PanelEditGroupPermsPage{common.GetTitlePhrase("panel-edit-group"), user, headerVars, stats, group.ID, group.Name, localPerms, globalPerms}
|
||||||
if common.PreRenderHooks["pre_render_panel_edit_group_perms"] != nil {
|
if common.PreRenderHooks["pre_render_panel_edit_group_perms"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_edit_group_perms", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_edit_group_perms", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1337,7 +1284,7 @@ func routePanelGroupsEditSubmit(w http.ResponseWriter, r *http.Request, user com
|
||||||
return common.LocalError("You need to provide a whole number for the group ID", w, r, user)
|
return common.LocalError("You need to provide a whole number for the group ID", w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
group, err := common.Gstore.Get(gid)
|
group, err := common.Groups.Get(gid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
//log.Print("aaaaa monsters")
|
//log.Print("aaaaa monsters")
|
||||||
return common.NotFound(w, r)
|
return common.NotFound(w, r)
|
||||||
|
@ -1360,6 +1307,7 @@ func routePanelGroupsEditSubmit(w http.ResponseWriter, r *http.Request, user com
|
||||||
rank := r.FormValue("group-type")
|
rank := r.FormValue("group-type")
|
||||||
|
|
||||||
var originalRank string
|
var originalRank string
|
||||||
|
// TODO: Use a switch for this
|
||||||
if group.IsAdmin {
|
if group.IsAdmin {
|
||||||
originalRank = "Admin"
|
originalRank = "Admin"
|
||||||
} else if group.IsMod {
|
} else if group.IsMod {
|
||||||
|
@ -1407,7 +1355,7 @@ func routePanelGroupsEditSubmit(w http.ResponseWriter, r *http.Request, user com
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
common.Gstore.Reload(gid)
|
common.Groups.Reload(gid)
|
||||||
|
|
||||||
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
|
||||||
|
@ -1427,7 +1375,7 @@ func routePanelGroupsEditPermsSubmit(w http.ResponseWriter, r *http.Request, use
|
||||||
return common.LocalError("The Group ID is not a valid integer.", w, r, user)
|
return common.LocalError("The Group ID is not a valid integer.", w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
group, err := common.Gstore.Get(gid)
|
group, err := common.Groups.Get(gid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
//log.Print("aaaaa monsters o.o")
|
//log.Print("aaaaa monsters o.o")
|
||||||
return common.NotFound(w, r)
|
return common.NotFound(w, r)
|
||||||
|
@ -1508,7 +1456,7 @@ func routePanelGroupsCreateSubmit(w http.ResponseWriter, r *http.Request, user c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gid, err := common.Gstore.Create(groupName, groupTag, isAdmin, isMod, isBanned)
|
gid, err := common.Groups.Create(groupName, groupTag, isAdmin, isMod, isBanned)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -1538,7 +1486,7 @@ func routePanelThemes(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelThemesPage{"Theme Manager", user, headerVars, stats, pThemeList, vThemeList}
|
pi := common.PanelThemesPage{common.GetTitlePhrase("panel-themes"), user, headerVars, stats, pThemeList, vThemeList}
|
||||||
if common.PreRenderHooks["pre_render_panel_themes"] != nil {
|
if common.PreRenderHooks["pre_render_panel_themes"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_themes", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_themes", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1658,7 +1606,7 @@ func routePanelBackups(w http.ResponseWriter, r *http.Request, user common.User,
|
||||||
backupList = append(backupList, common.BackupItem{backupFile.Name(), backupFile.ModTime()})
|
backupList = append(backupList, common.BackupItem{backupFile.Name(), backupFile.ModTime()})
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.PanelBackupPage{"Backups", user, headerVars, stats, backupList}
|
pi := common.PanelBackupPage{common.GetTitlePhrase("panel-backups"), user, headerVars, stats, backupList}
|
||||||
err = common.Templates.ExecuteTemplate(w, "panel-backups.html", pi)
|
err = common.Templates.ExecuteTemplate(w, "panel-backups.html", pi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
|
@ -1672,12 +1620,7 @@ func routePanelLogsMod(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
|
||||||
var logCount int
|
logCount := common.ModLogs.GlobalCount()
|
||||||
err := stmts.modlogCount.QueryRow().Scan(&logCount)
|
|
||||||
if err != nil {
|
|
||||||
return common.InternalError(err, w, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
perPage := 10
|
perPage := 10
|
||||||
offset, page, lastPage := common.PageOffset(logCount, page, perPage)
|
offset, page, lastPage := common.PageOffset(logCount, page, perPage)
|
||||||
|
@ -1688,6 +1631,21 @@ func routePanelLogsMod(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
|
// TODO: Log errors when something really screwy is going on?
|
||||||
|
handleUnknownUser := func(user *common.User, err error) *common.User {
|
||||||
|
if err != nil {
|
||||||
|
return &common.User{Name: "Unknown", Link: common.BuildProfileURL("unknown", 0)}
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUnknownTopic := func(topic *common.Topic, err error) *common.Topic {
|
||||||
|
if err != nil {
|
||||||
|
return &common.Topic{Title: "Unknown", Link: common.BuildProfileURL("unknown", 0)}
|
||||||
|
}
|
||||||
|
return topic
|
||||||
|
}
|
||||||
|
|
||||||
var logs []common.LogItem
|
var logs []common.LogItem
|
||||||
var action, elementType, ipaddress, doneAt string
|
var action, elementType, ipaddress, doneAt string
|
||||||
var elementID, actorID int
|
var elementID, actorID int
|
||||||
|
@ -1697,68 +1655,41 @@ func routePanelLogsMod(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
actor, err := common.Users.Get(actorID)
|
actor := handleUnknownUser(common.Users.Get(actorID))
|
||||||
if err != nil {
|
|
||||||
actor = &common.User{Name: "Unknown", Link: common.BuildProfileURL("unknown", 0)}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case "lock":
|
case "lock":
|
||||||
topic, err := common.Topics.Get(elementID)
|
topic := handleUnknownTopic(common.Topics.Get(elementID))
|
||||||
if err != nil {
|
action = fmt.Sprintf("<a href='%s'>%s</a> was locked by <a href='%s'>%s</a>", topic.Link, topic.Title, actor.Link, actor.Name)
|
||||||
topic = &common.Topic{Title: "Unknown", Link: common.BuildProfileURL("unknown", 0)}
|
|
||||||
}
|
|
||||||
action = "<a href='" + topic.Link + "'>" + topic.Title + "</a> was locked by <a href='" + actor.Link + "'>" + actor.Name + "</a>"
|
|
||||||
case "unlock":
|
case "unlock":
|
||||||
topic, err := common.Topics.Get(elementID)
|
topic := handleUnknownTopic(common.Topics.Get(elementID))
|
||||||
if err != nil {
|
action = fmt.Sprintf("<a href='%s'>%s</a> was reopened by <a href='%s'>%s</a>", topic.Link, topic.Title, actor.Link, actor.Name)
|
||||||
topic = &common.Topic{Title: "Unknown", Link: common.BuildProfileURL("unknown", 0)}
|
|
||||||
}
|
|
||||||
action = "<a href='" + topic.Link + "'>" + topic.Title + "</a> was reopened by <a href='" + actor.Link + "'>" + actor.Name + "</a>"
|
|
||||||
case "stick":
|
case "stick":
|
||||||
topic, err := common.Topics.Get(elementID)
|
topic := handleUnknownTopic(common.Topics.Get(elementID))
|
||||||
if err != nil {
|
action = fmt.Sprintf("<a href='%s'>%s</a> was pinned by <a href='%s'>%s</a>", topic.Link, topic.Title, actor.Link, actor.Name)
|
||||||
topic = &common.Topic{Title: "Unknown", Link: common.BuildProfileURL("unknown", 0)}
|
|
||||||
}
|
|
||||||
action = "<a href='" + topic.Link + "'>" + topic.Title + "</a> was pinned by <a href='" + actor.Link + "'>" + actor.Name + "</a>"
|
|
||||||
case "unstick":
|
case "unstick":
|
||||||
topic, err := common.Topics.Get(elementID)
|
topic := handleUnknownTopic(common.Topics.Get(elementID))
|
||||||
if err != nil {
|
action = fmt.Sprintf("<a href='%s'>%s</a> was unpinned by <a href='%s'>%s</a>", topic.Link, topic.Title, actor.Link, actor.Name)
|
||||||
topic = &common.Topic{Title: "Unknown", Link: common.BuildProfileURL("unknown", 0)}
|
|
||||||
}
|
|
||||||
action = "<a href='" + topic.Link + "'>" + topic.Title + "</a> was unpinned by <a href='" + actor.Link + "'>" + actor.Name + "</a>"
|
|
||||||
case "delete":
|
case "delete":
|
||||||
if elementType == "topic" {
|
if elementType == "topic" {
|
||||||
action = "Topic #" + strconv.Itoa(elementID) + " was deleted by <a href='" + actor.Link + "'>" + actor.Name + "</a>"
|
action = fmt.Sprintf("Topic #%d was deleted by <a href='%s'>%s</a>", elementID, actor.Link, actor.Name)
|
||||||
} else {
|
} else {
|
||||||
reply := common.BlankReply()
|
reply := common.BlankReply()
|
||||||
reply.ID = elementID
|
reply.ID = elementID
|
||||||
topic, err := reply.Topic()
|
topic := handleUnknownTopic(reply.Topic())
|
||||||
if err != nil {
|
action = fmt.Sprintf("A reply in <a href='%s'>%s</a> was deleted by <a href='%s'>%s</a>", topic.Link, topic.Title, actor.Link, actor.Name)
|
||||||
topic = &common.Topic{Title: "Unknown", Link: common.BuildProfileURL("unknown", 0)}
|
|
||||||
}
|
|
||||||
action = "A reply in <a href='" + topic.Link + "'>" + topic.Title + "</a> was deleted by <a href='" + actor.Link + "'>" + actor.Name + "</a>"
|
|
||||||
}
|
}
|
||||||
case "ban":
|
case "ban":
|
||||||
targetUser, err := common.Users.Get(elementID)
|
targetUser := handleUnknownUser(common.Users.Get(elementID))
|
||||||
if err != nil {
|
action = fmt.Sprintf("<a href='%s'>%s</a> was banned by <a href='%s'>%s</a>", targetUser.Link, targetUser.Name, actor.Link, actor.Name)
|
||||||
targetUser = &common.User{Name: "Unknown", Link: common.BuildProfileURL("unknown", 0)}
|
|
||||||
}
|
|
||||||
action = "<a href='" + targetUser.Link + "'>" + targetUser.Name + "</a> was banned by <a href='" + actor.Link + "'>" + actor.Name + "</a>"
|
|
||||||
case "unban":
|
case "unban":
|
||||||
targetUser, err := common.Users.Get(elementID)
|
targetUser := handleUnknownUser(common.Users.Get(elementID))
|
||||||
if err != nil {
|
action = fmt.Sprintf("<a href='%s'>%s</a> was unbanned by <a href='%s'>%s</a>", targetUser.Link, targetUser.Name, actor.Link, actor.Name)
|
||||||
targetUser = &common.User{Name: "Unknown", Link: common.BuildProfileURL("unknown", 0)}
|
|
||||||
}
|
|
||||||
action = "<a href='" + targetUser.Link + "'>" + targetUser.Name + "</a> was unbanned by <a href='" + actor.Link + "'>" + actor.Name + "</a>"
|
|
||||||
case "activate":
|
case "activate":
|
||||||
targetUser, err := common.Users.Get(elementID)
|
targetUser := handleUnknownUser(common.Users.Get(elementID))
|
||||||
if err != nil {
|
action = fmt.Sprintf("<a href='%s'>%s</a> was activated by <a href='%s'>%s</a>", targetUser.Link, targetUser.Name, actor.Link, actor.Name)
|
||||||
targetUser = &common.User{Name: "Unknown", Link: common.BuildProfileURL("unknown", 0)}
|
|
||||||
}
|
|
||||||
action = "<a href='" + targetUser.Link + "'>" + targetUser.Name + "</a> was activated by <a href='" + actor.Link + "'>" + actor.Name + "</a>"
|
|
||||||
default:
|
default:
|
||||||
action = "Unknown action '" + action + "' by <a href='" + actor.Link + "'>" + actor.Name + "</a>"
|
action = fmt.Sprintf("Unknown action '%s' by <a href='%s'>%s</a>", action, actor.Link, actor.Name)
|
||||||
}
|
}
|
||||||
logs = append(logs, common.LogItem{Action: template.HTML(action), IPAddress: ipaddress, DoneAt: doneAt})
|
logs = append(logs, common.LogItem{Action: template.HTML(action), IPAddress: ipaddress, DoneAt: doneAt})
|
||||||
}
|
}
|
||||||
|
@ -1768,7 +1699,7 @@ func routePanelLogsMod(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
}
|
}
|
||||||
|
|
||||||
pageList := common.Paginate(logCount, perPage, 5)
|
pageList := common.Paginate(logCount, perPage, 5)
|
||||||
pi := common.PanelLogsPage{"Moderation Logs", user, headerVars, stats, logs, pageList, page, lastPage}
|
pi := common.PanelLogsPage{common.GetTitlePhrase("panel-mod-logs"), user, headerVars, stats, logs, pageList, page, lastPage}
|
||||||
if common.PreRenderHooks["pre_render_panel_mod_log"] != nil {
|
if common.PreRenderHooks["pre_render_panel_mod_log"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_panel_mod_log", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_panel_mod_log", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1792,7 +1723,7 @@ func routePanelDebug(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
openConnCount := dbStats.OpenConnections
|
openConnCount := dbStats.OpenConnections
|
||||||
// Disk I/O?
|
// Disk I/O?
|
||||||
|
|
||||||
pi := common.PanelDebugPage{"Debug", user, headerVars, stats, uptime, openConnCount, dbAdapter}
|
pi := common.PanelDebugPage{common.GetTitlePhrase("panel-debug"), user, headerVars, stats, uptime, openConnCount, dbAdapter}
|
||||||
err := common.Templates.ExecuteTemplate(w, "panel-debug.html", pi)
|
err := common.Templates.ExecuteTemplate(w, "panel-debug.html", pi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
|
|
|
@ -33,20 +33,21 @@ func initGuilds() (err error) {
|
||||||
router.HandleFunc("/guild/create/submit/", guilds.RouteCreateGuildSubmit)
|
router.HandleFunc("/guild/create/submit/", guilds.RouteCreateGuildSubmit)
|
||||||
router.HandleFunc("/guild/members/", guilds.RouteMemberList)
|
router.HandleFunc("/guild/members/", guilds.RouteMemberList)
|
||||||
|
|
||||||
|
guilds.Gstore, err = guilds.NewSQLGuildStore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
acc := qgen.Builder.Accumulator()
|
acc := qgen.Builder.Accumulator()
|
||||||
|
|
||||||
guilds.ListStmt = acc.Select("guilds").Columns("guildID, name, desc, active, privacy, joinable, owner, memberCount, createdAt, lastUpdateTime").Prepare()
|
guilds.ListStmt = acc.Select("guilds").Columns("guildID, name, desc, active, privacy, joinable, owner, memberCount, createdAt, lastUpdateTime").Prepare()
|
||||||
|
|
||||||
guilds.GetGuildStmt = acc.Select("guilds").Columns("name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime").Where("guildID = ?").Prepare()
|
|
||||||
|
|
||||||
guilds.MemberListStmt = acc.Select("guilds_members").Columns("guildID, uid, rank, posts, joinedAt").Prepare()
|
guilds.MemberListStmt = acc.Select("guilds_members").Columns("guildID, uid, rank, posts, joinedAt").Prepare()
|
||||||
|
|
||||||
guilds.MemberListJoinStmt = acc.SimpleLeftJoin("guilds_members", "users", "users.uid, guilds_members.rank, guilds_members.posts, guilds_members.joinedAt, users.name, users.avatar", "guilds_members.uid = users.uid", "guilds_members.guildID = ?", "guilds_members.rank DESC, guilds_members.joinedat ASC", "")
|
guilds.MemberListJoinStmt = acc.SimpleLeftJoin("guilds_members", "users", "users.uid, guilds_members.rank, guilds_members.posts, guilds_members.joinedAt, users.name, users.avatar", "guilds_members.uid = users.uid", "guilds_members.guildID = ?", "guilds_members.rank DESC, guilds_members.joinedat ASC", "")
|
||||||
|
|
||||||
guilds.GetMemberStmt = acc.Select("guilds_members").Columns("rank, posts, joinedAt").Where("guildID = ? AND uid = ?").Prepare()
|
guilds.GetMemberStmt = acc.Select("guilds_members").Columns("rank, posts, joinedAt").Where("guildID = ? AND uid = ?").Prepare()
|
||||||
|
|
||||||
guilds.CreateGuildStmt = acc.Insert("guilds").Columns("name, desc, active, privacy, joinable, owner, memberCount, mainForum, backdrop, createdAt, lastUpdateTime").Fields("?,?,?,?,1,?,1,?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare()
|
|
||||||
|
|
||||||
guilds.AttachForumStmt = acc.Update("forums").Set("parentID = ?, parentType = 'guild'").Where("fid = ?").Prepare()
|
guilds.AttachForumStmt = acc.Update("forums").Set("parentID = ?, parentType = 'guild'").Where("fid = ?").Prepare()
|
||||||
|
|
||||||
guilds.UnattachForumStmt = acc.Update("forums").Set("parentID = 0, parentType = ''").Where("fid = ?").Prepare()
|
guilds.UnattachForumStmt = acc.Update("forums").Set("parentID = 0, parentType = ''").Where("fid = ?").Prepare()
|
||||||
|
@ -72,8 +73,6 @@ func deactivateGuilds() {
|
||||||
_ = guilds.MemberListStmt.Close()
|
_ = guilds.MemberListStmt.Close()
|
||||||
_ = guilds.MemberListJoinStmt.Close()
|
_ = guilds.MemberListJoinStmt.Close()
|
||||||
_ = guilds.GetMemberStmt.Close()
|
_ = guilds.GetMemberStmt.Close()
|
||||||
_ = guilds.GetGuildStmt.Close()
|
|
||||||
_ = guilds.CreateGuildStmt.Close()
|
|
||||||
_ = guilds.AttachForumStmt.Close()
|
_ = guilds.AttachForumStmt.Close()
|
||||||
_ = guilds.UnattachForumStmt.Close()
|
_ = guilds.UnattachForumStmt.Close()
|
||||||
_ = guilds.AddMemberStmt.Close()
|
_ = guilds.AddMemberStmt.Close()
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
/* WIP Under Construction */
|
|
||||||
package qgen
|
package qgen
|
||||||
|
|
||||||
var Install *installer
|
var Install *installer
|
||||||
|
@ -13,11 +12,21 @@ type DB_Install_Instruction struct {
|
||||||
Type string
|
Type string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add methods to this to construct it OO-like
|
||||||
|
type DB_Install_Table struct {
|
||||||
|
Name string
|
||||||
|
Charset string
|
||||||
|
Collation string
|
||||||
|
Columns []DBTableColumn
|
||||||
|
Keys []DBTableKey
|
||||||
|
}
|
||||||
|
|
||||||
// A set of wrappers around the generator methods, so we can use this in the installer
|
// A set of wrappers around the generator methods, so we can use this in the installer
|
||||||
// TODO: Re-implement the query generation, query builder and installer adapters as layers on-top of a query text adapter
|
// TODO: Re-implement the query generation, query builder and installer adapters as layers on-top of a query text adapter
|
||||||
type installer struct {
|
type installer struct {
|
||||||
adapter Adapter
|
adapter Adapter
|
||||||
instructions []DB_Install_Instruction
|
instructions []DB_Install_Instruction
|
||||||
|
tables []*DB_Install_Table // TODO: Use this in Record() in the next commit to allow us to auto-migrate settings rather than manually patching them in on upgrade
|
||||||
plugins []QueryPlugin
|
plugins []QueryPlugin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,8 +35,7 @@ func (install *installer) SetAdapter(name string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
install.adapter = adap
|
install.SetAdapterInstance(adap)
|
||||||
install.instructions = []DB_Install_Instruction{}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,49 +44,54 @@ func (install *installer) SetAdapterInstance(adapter Adapter) {
|
||||||
install.instructions = []DB_Install_Instruction{}
|
install.instructions = []DB_Install_Instruction{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (install *installer) RegisterPlugin(plugin QueryPlugin) {
|
func (install *installer) AddPlugins(plugins ...QueryPlugin) {
|
||||||
install.plugins = append(install.plugins, plugin)
|
install.plugins = append(install.plugins, plugins...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (install *installer) CreateTable(table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) error {
|
func (install *installer) CreateTable(table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) error {
|
||||||
for _, plugin := range install.plugins {
|
tableStruct := &DB_Install_Table{table, charset, collation, columns, keys}
|
||||||
err := plugin.Hook("CreateTableStart", table, charset, collation, columns, keys)
|
err := install.RunHook("CreateTableStart", tableStruct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
res, err := install.adapter.CreateTable("_installer", table, charset, collation, columns, keys)
|
res, err := install.adapter.CreateTable("_installer", table, charset, collation, columns, keys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, plugin := range install.plugins {
|
err = install.RunHook("CreateTableAfter", tableStruct)
|
||||||
err := plugin.Hook("CreateTableAfter", table, charset, collation, columns, keys, res)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
install.instructions = append(install.instructions, DB_Install_Instruction{table, res, "create-table"})
|
install.instructions = append(install.instructions, DB_Install_Instruction{table, res, "create-table"})
|
||||||
|
install.tables = append(install.tables, tableStruct)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Let plugins manipulate the parameters like in CreateTable
|
||||||
func (install *installer) SimpleInsert(table string, columns string, fields string) error {
|
func (install *installer) SimpleInsert(table string, columns string, fields string) error {
|
||||||
for _, plugin := range install.plugins {
|
err := install.RunHook("SimpleInsertStart", table, columns, fields)
|
||||||
err := plugin.Hook("SimpleInsertStart", table, columns, fields)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
res, err := install.adapter.SimpleInsert("_installer", table, columns, fields)
|
res, err := install.adapter.SimpleInsert("_installer", table, columns, fields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = install.RunHook("SimpleInsertAfter", table, columns, fields, res)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
install.instructions = append(install.instructions, DB_Install_Instruction{table, res, "insert"})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (install *installer) RunHook(name string, args ...interface{}) error {
|
||||||
for _, plugin := range install.plugins {
|
for _, plugin := range install.plugins {
|
||||||
err := plugin.Hook("SimpleInsertAfter", table, columns, fields, res)
|
err := plugin.Hook(name, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
install.instructions = append(install.instructions, DB_Install_Instruction{table, res, "insert"})
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ func main() {
|
||||||
for _, adapter := range qgen.Registry {
|
for _, adapter := range qgen.Registry {
|
||||||
log.Printf("Building the queries for the %s adapter", adapter.GetName())
|
log.Printf("Building the queries for the %s adapter", adapter.GetName())
|
||||||
qgen.Install.SetAdapterInstance(adapter)
|
qgen.Install.SetAdapterInstance(adapter)
|
||||||
qgen.Install.RegisterPlugin(NewPrimaryKeySpitter()) // TODO: Do we really need to fill the spitter for every adapter?
|
qgen.Install.AddPlugins(NewPrimaryKeySpitter()) // TODO: Do we really need to fill the spitter for every adapter?
|
||||||
|
|
||||||
err := writeStatements(adapter)
|
err := writeStatements(adapter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -75,16 +75,6 @@ func writeStatements(adapter qgen.Adapter) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
/*err = writeReplaces(adapter)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = writeUpserts(adapter)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}*/
|
|
||||||
|
|
||||||
err = writeUpdates(adapter)
|
err = writeUpdates(adapter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -125,6 +115,8 @@ func seedTables(adapter qgen.Adapter) error {
|
||||||
qgen.Install.SimpleInsert("settings", "name, content, type, constraints", "'activation_type','1','list','1-3'")
|
qgen.Install.SimpleInsert("settings", "name, content, type, constraints", "'activation_type','1','list','1-3'")
|
||||||
qgen.Install.SimpleInsert("settings", "name, content, type", "'bigpost_min_words','250','int'")
|
qgen.Install.SimpleInsert("settings", "name, content, type", "'bigpost_min_words','250','int'")
|
||||||
qgen.Install.SimpleInsert("settings", "name, content, type", "'megapost_min_words','1000','int'")
|
qgen.Install.SimpleInsert("settings", "name, content, type", "'megapost_min_words','1000','int'")
|
||||||
|
qgen.Install.SimpleInsert("settings", "name, content, type", "'about_segment_title','','text'")
|
||||||
|
qgen.Install.SimpleInsert("settings", "name, content, type", "'about_segment_body','','text'")
|
||||||
qgen.Install.SimpleInsert("themes", "uname, default", "'tempra-simple',1")
|
qgen.Install.SimpleInsert("themes", "uname, default", "'tempra-simple',1")
|
||||||
qgen.Install.SimpleInsert("emails", "email, uid, validated", "'admin@localhost',1,1") // ? - Use a different default email or let the admin input it during installation?
|
qgen.Install.SimpleInsert("emails", "email, uid, validated", "'admin@localhost',1,1") // ? - Use a different default email or let the admin input it during installation?
|
||||||
|
|
||||||
|
@ -168,6 +160,7 @@ func seedTables(adapter qgen.Adapter) error {
|
||||||
CloseTopic
|
CloseTopic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO: Set the permissions on a struct and then serialize the struct and insert that instead of writing raw JSON
|
||||||
qgen.Install.SimpleInsert("users_groups", "name, permissions, plugin_perms, is_mod, is_admin, tag", `'Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"EditGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}','{}',1,1,"Admin"`)
|
qgen.Install.SimpleInsert("users_groups", "name, permissions, plugin_perms, is_mod, is_admin, tag", `'Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"EditGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}','{}',1,1,"Admin"`)
|
||||||
|
|
||||||
qgen.Install.SimpleInsert("users_groups", "name, permissions, plugin_perms, is_mod, tag", `'Moderator','{"BanUsers":true,"ActivateUsers":false,"EditUser":true,"EditUserEmail":false,"EditUserGroup":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}','{}',1,"Mod"`)
|
qgen.Install.SimpleInsert("users_groups", "name, permissions, plugin_perms, is_mod, tag", `'Moderator','{"BanUsers":true,"ActivateUsers":false,"EditUser":true,"EditUserEmail":false,"EditUserGroup":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}','{}',1,"Mod"`)
|
||||||
|
@ -226,8 +219,6 @@ func writeSelects(adapter qgen.Adapter) error {
|
||||||
|
|
||||||
build.Select("getPassword").Table("users").Columns("password, salt").Where("uid = ?").Parse()
|
build.Select("getPassword").Table("users").Columns("password, salt").Where("uid = ?").Parse()
|
||||||
|
|
||||||
build.Select("getSettings").Table("settings").Columns("name, content, type").Parse()
|
|
||||||
|
|
||||||
build.Select("isPluginActive").Table("plugins").Columns("active").Where("uname = ?").Parse()
|
build.Select("isPluginActive").Table("plugins").Columns("active").Where("uname = ?").Parse()
|
||||||
|
|
||||||
//build.Select("isPluginInstalled").Table("plugins").Columns("installed").Where("uname = ?").Parse()
|
//build.Select("isPluginInstalled").Table("plugins").Columns("installed").Where("uname = ?").Parse()
|
||||||
|
@ -314,25 +305,6 @@ func writeInserts(adapter qgen.Adapter) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeReplaces(adapter qgen.Adapter) (err error) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ! Upserts are broken atm
|
|
||||||
/*func writeUpserts(adapter qgen.Adapter) (err error) {
|
|
||||||
_, err = adapter.SimpleUpsert("addForumPermsToGroup", "forums_permissions", "gid, fid, preset, permissions", "?,?,?,?", "gid = ? AND fid = ?")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = adapter.SimpleUpsert("replaceScheduleGroup", "users_groups_scheduler", "uid, set_group, issued_by, issued_at, revert_at, temporary", "?,?,?,UTC_TIMESTAMP(),?,?", "uid = ?")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}*/
|
|
||||||
|
|
||||||
func writeUpdates(adapter qgen.Adapter) error {
|
func writeUpdates(adapter qgen.Adapter) error {
|
||||||
build := adapter.Builder()
|
build := adapter.Builder()
|
||||||
|
|
||||||
|
@ -340,8 +312,6 @@ func writeUpdates(adapter qgen.Adapter) error {
|
||||||
|
|
||||||
build.Update("editProfileReply").Table("users_replies").Set("content = ?, parsed_content = ?").Where("rid = ?").Parse()
|
build.Update("editProfileReply").Table("users_replies").Set("content = ?, parsed_content = ?").Where("rid = ?").Parse()
|
||||||
|
|
||||||
build.Update("updateSetting").Table("settings").Set("content = ?").Where("name = ?").Parse()
|
|
||||||
|
|
||||||
build.Update("updatePlugin").Table("plugins").Set("active = ?").Where("uname = ?").Parse()
|
build.Update("updatePlugin").Table("plugins").Set("active = ?").Where("uname = ?").Parse()
|
||||||
|
|
||||||
build.Update("updatePluginInstall").Table("plugins").Set("installed = ?").Where("uname = ?").Parse()
|
build.Update("updatePluginInstall").Table("plugins").Set("installed = ?").Where("uname = ?").Parse()
|
||||||
|
@ -385,8 +355,6 @@ func writeDeletes(adapter qgen.Adapter) error {
|
||||||
func writeSimpleCounts(adapter qgen.Adapter) error {
|
func writeSimpleCounts(adapter qgen.Adapter) error {
|
||||||
adapter.SimpleCount("reportExists", "topics", "data = ? AND data != '' AND parentID = 1", "")
|
adapter.SimpleCount("reportExists", "topics", "data = ? AND data != '' AND parentID = 1", "")
|
||||||
|
|
||||||
adapter.SimpleCount("modlogCount", "moderation_logs", "", "")
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,8 @@ func NewPrimaryKeySpitter() *PrimaryKeySpitter {
|
||||||
func (spit *PrimaryKeySpitter) Hook(name string, args ...interface{}) error {
|
func (spit *PrimaryKeySpitter) Hook(name string, args ...interface{}) error {
|
||||||
if name == "CreateTableStart" {
|
if name == "CreateTableStart" {
|
||||||
var found string
|
var found string
|
||||||
for _, key := range args[4].([]qgen.DBTableKey) {
|
var table = args[0].(*qgen.DB_Install_Table)
|
||||||
|
for _, key := range table.Keys {
|
||||||
if key.Type == "primary" {
|
if key.Type == "primary" {
|
||||||
expl := strings.Split(key.Columns, ",")
|
expl := strings.Split(key.Columns, ",")
|
||||||
if len(expl) > 1 {
|
if len(expl) > 1 {
|
||||||
|
@ -23,7 +24,7 @@ func (spit *PrimaryKeySpitter) Hook(name string, args ...interface{}) error {
|
||||||
found = key.Columns
|
found = key.Columns
|
||||||
}
|
}
|
||||||
if found != "" {
|
if found != "" {
|
||||||
table := args[0].(string)
|
table := table.Name
|
||||||
spit.keys[table] = found
|
spit.keys[table] = found
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,8 +68,8 @@ func buildPanelRoutes() {
|
||||||
Action("routePanelForumsEditPermsSubmit", "/panel/forums/edit/perms/submit/", "extra_data"),
|
Action("routePanelForumsEditPermsSubmit", "/panel/forums/edit/perms/submit/", "extra_data"),
|
||||||
|
|
||||||
View("routePanelSettings", "/panel/settings/"),
|
View("routePanelSettings", "/panel/settings/"),
|
||||||
View("routePanelSetting", "/panel/settings/edit/", "extra_data"),
|
View("routePanelSettingEdit", "/panel/settings/edit/", "extra_data"),
|
||||||
Action("routePanelSettingEdit", "/panel/settings/edit/submit/", "extra_data"),
|
Action("routePanelSettingEditSubmit", "/panel/settings/edit/submit/", "extra_data"),
|
||||||
|
|
||||||
View("routePanelWordFilters", "/panel/settings/word-filters/"),
|
View("routePanelWordFilters", "/panel/settings/word-filters/"),
|
||||||
Action("routePanelWordFiltersCreate", "/panel/settings/word-filters/create/"),
|
Action("routePanelWordFiltersCreate", "/panel/settings/word-filters/create/"),
|
||||||
|
|
61
routes.go
61
routes.go
|
@ -46,7 +46,7 @@ func routeStatic(w http.ResponseWriter, r *http.Request) {
|
||||||
file, ok := common.StaticFiles[r.URL.Path]
|
file, ok := common.StaticFiles[r.URL.Path]
|
||||||
if !ok {
|
if !ok {
|
||||||
if common.Dev.DebugMode {
|
if common.Dev.DebugMode {
|
||||||
log.Print("Failed to find '" + r.URL.Path + "'")
|
log.Printf("Failed to find '%s'", r.URL.Path)
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
|
@ -103,7 +103,7 @@ func routeOverview(w http.ResponseWriter, r *http.Request, user common.User) com
|
||||||
}
|
}
|
||||||
common.BuildWidgets("overview", nil, headerVars, r)
|
common.BuildWidgets("overview", nil, headerVars, r)
|
||||||
|
|
||||||
pi := common.Page{"Overview", user, headerVars, tList, nil}
|
pi := common.Page{common.GetTitlePhrase("overview"), user, headerVars, tList, nil}
|
||||||
if common.PreRenderHooks["pre_render_overview"] != nil {
|
if common.PreRenderHooks["pre_render_overview"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_overview", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_overview", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -129,7 +129,7 @@ func routeCustomPage(w http.ResponseWriter, r *http.Request, user common.User) c
|
||||||
}
|
}
|
||||||
common.BuildWidgets("custom_page", name, headerVars, r)
|
common.BuildWidgets("custom_page", name, headerVars, r)
|
||||||
|
|
||||||
pi := common.Page{"Page", user, headerVars, tList, nil}
|
pi := common.Page{common.GetTitlePhrase("page"), user, headerVars, tList, nil}
|
||||||
if common.PreRenderHooks["pre_render_custom_page"] != nil {
|
if common.PreRenderHooks["pre_render_custom_page"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_custom_page", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_custom_page", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -152,7 +152,7 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) commo
|
||||||
|
|
||||||
// TODO: Add a function for the qlist stuff
|
// TODO: Add a function for the qlist stuff
|
||||||
var qlist string
|
var qlist string
|
||||||
group, err := common.Gstore.Get(user.Group)
|
group, err := common.Groups.Get(user.Group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Group #%d doesn't exist despite being used by common.User #%d", user.Group, user.ID)
|
log.Printf("Group #%d doesn't exist despite being used by common.User #%d", user.Group, user.ID)
|
||||||
return common.LocalError("Something weird happened", w, r, user)
|
return common.LocalError("Something weird happened", w, r, user)
|
||||||
|
@ -161,7 +161,7 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) commo
|
||||||
// TODO: Make CanSee a method on *Group with a canSee field?
|
// TODO: Make CanSee a method on *Group with a canSee field?
|
||||||
var canSee []int
|
var canSee []int
|
||||||
if user.IsSuperAdmin {
|
if user.IsSuperAdmin {
|
||||||
canSee, err = common.Fstore.GetAllVisibleIDs()
|
canSee, err = common.Forums.GetAllVisibleIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -174,7 +174,7 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) commo
|
||||||
var argList []interface{}
|
var argList []interface{}
|
||||||
|
|
||||||
for _, fid := range canSee {
|
for _, fid := range canSee {
|
||||||
forum := common.Fstore.DirtyGet(fid)
|
forum := common.Forums.DirtyGet(fid)
|
||||||
if forum.Name != "" && forum.Active {
|
if forum.Name != "" && forum.Active {
|
||||||
if forum.ParentType == "" || forum.ParentType == "forum" {
|
if forum.ParentType == "" || forum.ParentType == "forum" {
|
||||||
// Optimise Quick Topic away for guests
|
// Optimise Quick Topic away for guests
|
||||||
|
@ -251,7 +251,7 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) commo
|
||||||
|
|
||||||
topicItem.Link = common.BuildTopicURL(common.NameToSlug(topicItem.Title), topicItem.ID)
|
topicItem.Link = common.BuildTopicURL(common.NameToSlug(topicItem.Title), topicItem.ID)
|
||||||
|
|
||||||
forum := common.Fstore.DirtyGet(topicItem.ParentID)
|
forum := common.Forums.DirtyGet(topicItem.ParentID)
|
||||||
topicItem.ForumName = forum.Name
|
topicItem.ForumName = forum.Name
|
||||||
topicItem.ForumLink = forum.Link
|
topicItem.ForumLink = forum.Link
|
||||||
|
|
||||||
|
@ -291,13 +291,13 @@ func routeTopics(w http.ResponseWriter, r *http.Request, user common.User) commo
|
||||||
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.TopicsPage{"All Topics", user, headerVars, topicList, forumList, common.Config.DefaultForum}
|
pi := common.TopicsPage{common.GetTitlePhrase("topics"), user, headerVars, topicList, forumList, common.Config.DefaultForum}
|
||||||
if common.PreRenderHooks["pre_render_topic_list"] != nil {
|
if common.PreRenderHooks["pre_render_topic_list"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_topic_list", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_topic_list", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = common.RunThemeTemplate(headerVars.ThemeName, "topics", pi, w)
|
err = common.RunThemeTemplate(headerVars.Theme.Name, "topics", pi, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -327,7 +327,7 @@ func routeForum(w http.ResponseWriter, r *http.Request, user common.User, sfid s
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Fix this double-check
|
// TODO: Fix this double-check
|
||||||
forum, err := common.Fstore.Get(fid)
|
forum, err := common.Forums.Get(fid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
return common.NotFound(w, r)
|
return common.NotFound(w, r)
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -408,7 +408,7 @@ func routeForum(w http.ResponseWriter, r *http.Request, user common.User, sfid s
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = common.RunThemeTemplate(headerVars.ThemeName, "forum", pi, w)
|
err = common.RunThemeTemplate(headerVars.Theme.Name, "forum", pi, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -426,13 +426,12 @@ func routeForums(w http.ResponseWriter, r *http.Request, user common.User) commo
|
||||||
var forumList []common.Forum
|
var forumList []common.Forum
|
||||||
var canSee []int
|
var canSee []int
|
||||||
if user.IsSuperAdmin {
|
if user.IsSuperAdmin {
|
||||||
canSee, err = common.Fstore.GetAllVisibleIDs()
|
canSee, err = common.Forums.GetAllVisibleIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
//log.Print("canSee ", canSee)
|
|
||||||
} else {
|
} else {
|
||||||
group, err := common.Gstore.Get(user.Group)
|
group, err := common.Groups.Get(user.Group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Group #%d doesn't exist despite being used by common.User #%d", user.Group, user.ID)
|
log.Printf("Group #%d doesn't exist despite being used by common.User #%d", user.Group, user.ID)
|
||||||
return common.LocalError("Something weird happened", w, r, user)
|
return common.LocalError("Something weird happened", w, r, user)
|
||||||
|
@ -442,7 +441,7 @@ func routeForums(w http.ResponseWriter, r *http.Request, user common.User) commo
|
||||||
|
|
||||||
for _, fid := range canSee {
|
for _, fid := range canSee {
|
||||||
// Avoid data races by copying the struct into something we can freely mold without worrying about breaking something somewhere else
|
// Avoid data races by copying the struct into something we can freely mold without worrying about breaking something somewhere else
|
||||||
var forum = common.Fstore.DirtyGet(fid).Copy()
|
var forum = common.Forums.DirtyGet(fid).Copy()
|
||||||
if forum.ParentID == 0 && forum.Name != "" && forum.Active {
|
if forum.ParentID == 0 && forum.Name != "" && forum.Active {
|
||||||
if forum.LastTopicID != 0 {
|
if forum.LastTopicID != 0 {
|
||||||
if forum.LastTopic.ID != 0 && forum.LastReplyer.ID != 0 {
|
if forum.LastTopic.ID != 0 && forum.LastReplyer.ID != 0 {
|
||||||
|
@ -460,13 +459,13 @@ func routeForums(w http.ResponseWriter, r *http.Request, user common.User) commo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pi := common.ForumsPage{"Forum List", user, headerVars, forumList}
|
pi := common.ForumsPage{common.GetTitlePhrase("forums"), user, headerVars, forumList}
|
||||||
if common.PreRenderHooks["pre_render_forum_list"] != nil {
|
if common.PreRenderHooks["pre_render_forum_list"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_forum_list", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_forum_list", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = common.RunThemeTemplate(headerVars.ThemeName, "forums", pi, w)
|
err = common.RunThemeTemplate(headerVars.Theme.Name, "forums", pi, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -481,6 +480,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user common.User) comm
|
||||||
page, _ = strconv.Atoi(r.FormValue("page"))
|
page, _ = strconv.Atoi(r.FormValue("page"))
|
||||||
|
|
||||||
// SEO URLs...
|
// SEO URLs...
|
||||||
|
// TODO: Make a shared function for this
|
||||||
halves := strings.Split(r.URL.Path[len("/topic/"):], ".")
|
halves := strings.Split(r.URL.Path[len("/topic/"):], ".")
|
||||||
if len(halves) < 2 {
|
if len(halves) < 2 {
|
||||||
halves = append(halves, halves[0])
|
halves = append(halves, halves[0])
|
||||||
|
@ -520,7 +520,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user common.User) comm
|
||||||
user.Perms.CreateReply = false
|
user.Perms.CreateReply = false
|
||||||
}
|
}
|
||||||
|
|
||||||
postGroup, err := common.Gstore.Get(topic.Group)
|
postGroup, err := common.Groups.Get(topic.Group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -574,7 +574,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user common.User) comm
|
||||||
replyItem.ContentHtml = common.ParseMessage(replyItem.Content, topic.ParentID, "forums")
|
replyItem.ContentHtml = common.ParseMessage(replyItem.Content, topic.ParentID, "forums")
|
||||||
replyItem.ContentLines = strings.Count(replyItem.Content, "\n")
|
replyItem.ContentLines = strings.Count(replyItem.Content, "\n")
|
||||||
|
|
||||||
postGroup, err = common.Gstore.Get(replyItem.Group)
|
postGroup, err = common.Groups.Get(replyItem.Group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -635,7 +635,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user common.User) comm
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = common.RunThemeTemplate(headerVars.ThemeName, "topic", tpage, w)
|
err = common.RunThemeTemplate(headerVars.Theme.Name, "topic", tpage, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -671,6 +671,7 @@ func routeProfile(w http.ResponseWriter, r *http.Request, user common.User) comm
|
||||||
puser = &user
|
puser = &user
|
||||||
} else {
|
} else {
|
||||||
// Fetch the user data
|
// Fetch the user data
|
||||||
|
// TODO: Add a shared function for checking for ErrNoRows and internal erroring if it's not that case?
|
||||||
puser, err = common.Users.Get(pid)
|
puser, err = common.Users.Get(pid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
return common.NotFound(w, r)
|
return common.NotFound(w, r)
|
||||||
|
@ -692,7 +693,7 @@ func routeProfile(w http.ResponseWriter, r *http.Request, user common.User) comm
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
group, err := common.Gstore.Get(replyGroup)
|
group, err := common.Groups.Get(replyGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -733,6 +734,7 @@ func routeProfile(w http.ResponseWriter, r *http.Request, user common.User) comm
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add a phrase for this title
|
||||||
ppage := common.ProfilePage{puser.Name + "'s Profile", user, headerVars, replyList, *puser}
|
ppage := common.ProfilePage{puser.Name + "'s Profile", user, headerVars, replyList, *puser}
|
||||||
if common.PreRenderHooks["pre_render_profile"] != nil {
|
if common.PreRenderHooks["pre_render_profile"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_profile", w, r, &user, &ppage) {
|
if common.RunPreRenderHook("pre_render_profile", w, r, &user, &ppage) {
|
||||||
|
@ -740,7 +742,7 @@ func routeProfile(w http.ResponseWriter, r *http.Request, user common.User) comm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = common.RunThemeTemplate(headerVars.ThemeName, "profile", ppage, w)
|
err = common.RunThemeTemplate(headerVars.Theme.Name, "profile", ppage, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.InternalError(err, w, r)
|
return common.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
@ -755,7 +757,7 @@ func routeLogin(w http.ResponseWriter, r *http.Request, user common.User) common
|
||||||
if user.Loggedin {
|
if user.Loggedin {
|
||||||
return common.LocalError("You're already logged in.", w, r, user)
|
return common.LocalError("You're already logged in.", w, r, user)
|
||||||
}
|
}
|
||||||
pi := common.Page{"Login", user, headerVars, tList, nil}
|
pi := common.Page{common.GetTitlePhrase("login"), user, headerVars, tList, nil}
|
||||||
if common.PreRenderHooks["pre_render_login"] != nil {
|
if common.PreRenderHooks["pre_render_login"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_login", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_login", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -803,7 +805,7 @@ func routeLoginSubmit(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
|
|
||||||
common.Auth.SetCookies(w, uid, session)
|
common.Auth.SetCookies(w, uid, session)
|
||||||
if user.IsAdmin {
|
if user.IsAdmin {
|
||||||
// Is this error check reundant? We already check for the error in PreRoute for the same IP
|
// Is this error check redundant? We already check for the error in PreRoute for the same IP
|
||||||
// TODO: Should we be logging this?
|
// TODO: Should we be logging this?
|
||||||
log.Printf("#%d has logged in with IP %s", uid, user.LastIP)
|
log.Printf("#%d has logged in with IP %s", uid, user.LastIP)
|
||||||
}
|
}
|
||||||
|
@ -819,7 +821,7 @@ func routeRegister(w http.ResponseWriter, r *http.Request, user common.User) com
|
||||||
if user.Loggedin {
|
if user.Loggedin {
|
||||||
return common.LocalError("You're already logged in.", w, r, user)
|
return common.LocalError("You're already logged in.", w, r, user)
|
||||||
}
|
}
|
||||||
pi := common.Page{"Registration", user, headerVars, tList, nil}
|
pi := common.Page{common.GetTitlePhrase("register"), user, headerVars, tList, nil}
|
||||||
if common.PreRenderHooks["pre_render_register"] != nil {
|
if common.PreRenderHooks["pre_render_register"] != nil {
|
||||||
if common.RunPreRenderHook("pre_render_register", w, r, &user, &pi) {
|
if common.RunPreRenderHook("pre_render_register", w, r, &user, &pi) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -937,10 +939,12 @@ func routeChangeTheme(w http.ResponseWriter, r *http.Request, user common.User)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We don't need support XML here to support sitemaps, we could handle those elsewhere
|
// TODO: Refactor this
|
||||||
var phraseLoginAlerts = []byte(`{"msgs":[{"msg":"Login to see your alerts","path":"/accounts/login"}]}`)
|
var phraseLoginAlerts = []byte(`{"msgs":[{"msg":"Login to see your alerts","path":"/accounts/login"}]}`)
|
||||||
|
|
||||||
|
// TODO: Refactor this endpoint
|
||||||
func routeAPI(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
func routeAPI(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError {
|
||||||
|
// TODO: Don't make this too JSON dependent so that we can swap in newer more efficient formats
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1008,11 +1012,6 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user common.User) common.R
|
||||||
msglist = msglist[0 : len(msglist)-1]
|
msglist = msglist[0 : len(msglist)-1]
|
||||||
}
|
}
|
||||||
_, _ = w.Write([]byte(`{"msgs":[` + msglist + `],"msgCount":` + strconv.Itoa(msgCount) + `}`))
|
_, _ = w.Write([]byte(`{"msgs":[` + msglist + `],"msgCount":` + strconv.Itoa(msgCount) + `}`))
|
||||||
//log.Print(`{"msgs":[` + msglist + `],"msgCount":` + strconv.Itoa(msgCount) + `}`)
|
|
||||||
//case "topics":
|
|
||||||
//case "forums":
|
|
||||||
//case "users":
|
|
||||||
//case "pages":
|
|
||||||
default:
|
default:
|
||||||
return common.PreErrorJS("Invalid Module", w, r)
|
return common.PreErrorJS("Invalid Module", w, r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ INSERT INTO [settings] ([name],[content],[type]) VALUES ('url_tags','1','bool');
|
||||||
INSERT INTO [settings] ([name],[content],[type],[constraints]) VALUES ('activation_type','1','list','1-3');
|
INSERT INTO [settings] ([name],[content],[type],[constraints]) VALUES ('activation_type','1','list','1-3');
|
||||||
INSERT INTO [settings] ([name],[content],[type]) VALUES ('bigpost_min_words','250','int');
|
INSERT INTO [settings] ([name],[content],[type]) VALUES ('bigpost_min_words','250','int');
|
||||||
INSERT INTO [settings] ([name],[content],[type]) VALUES ('megapost_min_words','1000','int');
|
INSERT INTO [settings] ([name],[content],[type]) VALUES ('megapost_min_words','1000','int');
|
||||||
|
INSERT INTO [settings] ([name],[content],[type]) VALUES ('about_segment_title','','text');
|
||||||
|
INSERT INTO [settings] ([name],[content],[type]) VALUES ('about_segment_body','','text');
|
||||||
INSERT INTO [themes] ([uname],[default]) VALUES ('tempra-simple',1);
|
INSERT INTO [themes] ([uname],[default]) VALUES ('tempra-simple',1);
|
||||||
INSERT INTO [emails] ([email],[uid],[validated]) VALUES ('admin@localhost',1,1);
|
INSERT INTO [emails] ([email],[uid],[validated]) VALUES ('admin@localhost',1,1);
|
||||||
INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[tag]) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"EditGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}','{}',1,1,'Admin');
|
INSERT INTO [users_groups] ([name],[permissions],[plugin_perms],[is_mod],[is_admin],[tag]) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"EditGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}','{}',1,1,'Admin');
|
||||||
|
|
|
@ -3,6 +3,8 @@ INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('url_tags','1','bool');
|
||||||
INSERT INTO `settings`(`name`,`content`,`type`,`constraints`) VALUES ('activation_type','1','list','1-3');
|
INSERT INTO `settings`(`name`,`content`,`type`,`constraints`) VALUES ('activation_type','1','list','1-3');
|
||||||
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('bigpost_min_words','250','int');
|
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('bigpost_min_words','250','int');
|
||||||
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('megapost_min_words','1000','int');
|
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('megapost_min_words','1000','int');
|
||||||
|
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('about_segment_title','','text');
|
||||||
|
INSERT INTO `settings`(`name`,`content`,`type`) VALUES ('about_segment_body','','text');
|
||||||
INSERT INTO `themes`(`uname`,`default`) VALUES ('tempra-simple',1);
|
INSERT INTO `themes`(`uname`,`default`) VALUES ('tempra-simple',1);
|
||||||
INSERT INTO `emails`(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,1);
|
INSERT INTO `emails`(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,1);
|
||||||
INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`tag`) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"EditGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}','{}',1,1,'Admin');
|
INSERT INTO `users_groups`(`name`,`permissions`,`plugin_perms`,`is_mod`,`is_admin`,`tag`) VALUES ('Administrator','{"BanUsers":true,"ActivateUsers":true,"EditUser":true,"EditUserEmail":true,"EditUserPassword":true,"EditUserGroup":true,"EditUserGroupSuperMod":true,"EditUserGroupAdmin":false,"EditGroup":true,"EditGroupLocalPerms":true,"EditGroupGlobalPerms":true,"EditGroupSuperMod":true,"EditGroupAdmin":false,"ManageForums":true,"EditSettings":true,"ManageThemes":true,"ManagePlugins":true,"ViewAdminLogs":true,"ViewIPs":true,"UploadFiles":true,"ViewTopic":true,"LikeItem":true,"CreateTopic":true,"EditTopic":true,"DeleteTopic":true,"CreateReply":true,"EditReply":true,"DeleteReply":true,"PinTopic":true,"CloseTopic":true}','{}',1,1,'Admin');
|
||||||
|
|
|
@ -27,3 +27,5 @@
|
||||||
;
|
;
|
||||||
;
|
;
|
||||||
;
|
;
|
||||||
|
;
|
||||||
|
;
|
||||||
|
|
|
@ -22,7 +22,7 @@ w.Write([]byte(tmpl_forum_vars.Title))
|
||||||
w.Write(header_1)
|
w.Write(header_1)
|
||||||
w.Write([]byte(tmpl_forum_vars.Header.Site.Name))
|
w.Write([]byte(tmpl_forum_vars.Header.Site.Name))
|
||||||
w.Write(header_2)
|
w.Write(header_2)
|
||||||
w.Write([]byte(tmpl_forum_vars.Header.ThemeName))
|
w.Write([]byte(tmpl_forum_vars.Header.Theme.Name))
|
||||||
w.Write(header_3)
|
w.Write(header_3)
|
||||||
if len(tmpl_forum_vars.Header.Stylesheets) != 0 {
|
if len(tmpl_forum_vars.Header.Stylesheets) != 0 {
|
||||||
for _, item := range tmpl_forum_vars.Header.Stylesheets {
|
for _, item := range tmpl_forum_vars.Header.Stylesheets {
|
||||||
|
@ -203,28 +203,37 @@ w.Write(forum_57)
|
||||||
w.Write(forum_58)
|
w.Write(forum_58)
|
||||||
}
|
}
|
||||||
w.Write(forum_59)
|
w.Write(forum_59)
|
||||||
|
if tmpl_forum_vars.Header.Theme.AboutSegment {
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
|
dispInt := tmpl_forum_vars.Header.Settings["about_segment_title"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_1)
|
||||||
|
dispInt = tmpl_forum_vars.Header.Settings["about_segment_body"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_2)
|
||||||
|
}
|
||||||
|
w.Write(footer_3)
|
||||||
if len(tmpl_forum_vars.Header.Themes) != 0 {
|
if len(tmpl_forum_vars.Header.Themes) != 0 {
|
||||||
for _, item := range tmpl_forum_vars.Header.Themes {
|
for _, item := range tmpl_forum_vars.Header.Themes {
|
||||||
if !item.HideFromThemes {
|
if !item.HideFromThemes {
|
||||||
w.Write(footer_1)
|
|
||||||
w.Write([]byte(item.Name))
|
|
||||||
w.Write(footer_2)
|
|
||||||
if tmpl_forum_vars.Header.ThemeName == item.Name {
|
|
||||||
w.Write(footer_3)
|
|
||||||
}
|
|
||||||
w.Write(footer_4)
|
w.Write(footer_4)
|
||||||
w.Write([]byte(item.FriendlyName))
|
w.Write([]byte(item.Name))
|
||||||
w.Write(footer_5)
|
w.Write(footer_5)
|
||||||
}
|
if tmpl_forum_vars.Header.Theme.Name == item.Name {
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write(footer_6)
|
w.Write(footer_6)
|
||||||
if tmpl_forum_vars.Header.Widgets.RightSidebar != "" {
|
}
|
||||||
w.Write(footer_7)
|
w.Write(footer_7)
|
||||||
w.Write([]byte(string(tmpl_forum_vars.Header.Widgets.RightSidebar)))
|
w.Write([]byte(item.FriendlyName))
|
||||||
w.Write(footer_8)
|
w.Write(footer_8)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
w.Write(footer_9)
|
w.Write(footer_9)
|
||||||
|
if tmpl_forum_vars.Header.Widgets.RightSidebar != "" {
|
||||||
|
w.Write(footer_10)
|
||||||
|
w.Write([]byte(string(tmpl_forum_vars.Header.Widgets.RightSidebar)))
|
||||||
|
w.Write(footer_11)
|
||||||
|
}
|
||||||
|
w.Write(footer_12)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ w.Write([]byte(tmpl_forums_vars.Title))
|
||||||
w.Write(header_1)
|
w.Write(header_1)
|
||||||
w.Write([]byte(tmpl_forums_vars.Header.Site.Name))
|
w.Write([]byte(tmpl_forums_vars.Header.Site.Name))
|
||||||
w.Write(header_2)
|
w.Write(header_2)
|
||||||
w.Write([]byte(tmpl_forums_vars.Header.ThemeName))
|
w.Write([]byte(tmpl_forums_vars.Header.Theme.Name))
|
||||||
w.Write(header_3)
|
w.Write(header_3)
|
||||||
if len(tmpl_forums_vars.Header.Stylesheets) != 0 {
|
if len(tmpl_forums_vars.Header.Stylesheets) != 0 {
|
||||||
for _, item := range tmpl_forums_vars.Header.Stylesheets {
|
for _, item := range tmpl_forums_vars.Header.Stylesheets {
|
||||||
|
@ -118,28 +118,37 @@ w.Write(forums_18)
|
||||||
w.Write(forums_19)
|
w.Write(forums_19)
|
||||||
}
|
}
|
||||||
w.Write(forums_20)
|
w.Write(forums_20)
|
||||||
|
if tmpl_forums_vars.Header.Theme.AboutSegment {
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
|
dispInt := tmpl_forums_vars.Header.Settings["about_segment_title"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_1)
|
||||||
|
dispInt = tmpl_forums_vars.Header.Settings["about_segment_body"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_2)
|
||||||
|
}
|
||||||
|
w.Write(footer_3)
|
||||||
if len(tmpl_forums_vars.Header.Themes) != 0 {
|
if len(tmpl_forums_vars.Header.Themes) != 0 {
|
||||||
for _, item := range tmpl_forums_vars.Header.Themes {
|
for _, item := range tmpl_forums_vars.Header.Themes {
|
||||||
if !item.HideFromThemes {
|
if !item.HideFromThemes {
|
||||||
w.Write(footer_1)
|
|
||||||
w.Write([]byte(item.Name))
|
|
||||||
w.Write(footer_2)
|
|
||||||
if tmpl_forums_vars.Header.ThemeName == item.Name {
|
|
||||||
w.Write(footer_3)
|
|
||||||
}
|
|
||||||
w.Write(footer_4)
|
w.Write(footer_4)
|
||||||
w.Write([]byte(item.FriendlyName))
|
w.Write([]byte(item.Name))
|
||||||
w.Write(footer_5)
|
w.Write(footer_5)
|
||||||
}
|
if tmpl_forums_vars.Header.Theme.Name == item.Name {
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write(footer_6)
|
w.Write(footer_6)
|
||||||
if tmpl_forums_vars.Header.Widgets.RightSidebar != "" {
|
}
|
||||||
w.Write(footer_7)
|
w.Write(footer_7)
|
||||||
w.Write([]byte(string(tmpl_forums_vars.Header.Widgets.RightSidebar)))
|
w.Write([]byte(item.FriendlyName))
|
||||||
w.Write(footer_8)
|
w.Write(footer_8)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
w.Write(footer_9)
|
w.Write(footer_9)
|
||||||
|
if tmpl_forums_vars.Header.Widgets.RightSidebar != "" {
|
||||||
|
w.Write(footer_10)
|
||||||
|
w.Write([]byte(string(tmpl_forums_vars.Header.Widgets.RightSidebar)))
|
||||||
|
w.Write(footer_11)
|
||||||
|
}
|
||||||
|
w.Write(footer_12)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ w.Write([]byte(tmpl_guilds_guild_list_vars.Title))
|
||||||
w.Write(header_1)
|
w.Write(header_1)
|
||||||
w.Write([]byte(tmpl_guilds_guild_list_vars.Header.Site.Name))
|
w.Write([]byte(tmpl_guilds_guild_list_vars.Header.Site.Name))
|
||||||
w.Write(header_2)
|
w.Write(header_2)
|
||||||
w.Write([]byte(tmpl_guilds_guild_list_vars.Header.ThemeName))
|
w.Write([]byte(tmpl_guilds_guild_list_vars.Header.Theme.Name))
|
||||||
w.Write(header_3)
|
w.Write(header_3)
|
||||||
if len(tmpl_guilds_guild_list_vars.Header.Stylesheets) != 0 {
|
if len(tmpl_guilds_guild_list_vars.Header.Stylesheets) != 0 {
|
||||||
for _, item := range tmpl_guilds_guild_list_vars.Header.Stylesheets {
|
for _, item := range tmpl_guilds_guild_list_vars.Header.Stylesheets {
|
||||||
|
@ -91,28 +91,37 @@ w.Write(guilds_guild_list_6)
|
||||||
w.Write(guilds_guild_list_7)
|
w.Write(guilds_guild_list_7)
|
||||||
}
|
}
|
||||||
w.Write(guilds_guild_list_8)
|
w.Write(guilds_guild_list_8)
|
||||||
|
if tmpl_guilds_guild_list_vars.Header.Theme.AboutSegment {
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
|
dispInt := tmpl_guilds_guild_list_vars.Header.Settings["about_segment_title"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_1)
|
||||||
|
dispInt = tmpl_guilds_guild_list_vars.Header.Settings["about_segment_body"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_2)
|
||||||
|
}
|
||||||
|
w.Write(footer_3)
|
||||||
if len(tmpl_guilds_guild_list_vars.Header.Themes) != 0 {
|
if len(tmpl_guilds_guild_list_vars.Header.Themes) != 0 {
|
||||||
for _, item := range tmpl_guilds_guild_list_vars.Header.Themes {
|
for _, item := range tmpl_guilds_guild_list_vars.Header.Themes {
|
||||||
if !item.HideFromThemes {
|
if !item.HideFromThemes {
|
||||||
w.Write(footer_1)
|
|
||||||
w.Write([]byte(item.Name))
|
|
||||||
w.Write(footer_2)
|
|
||||||
if tmpl_guilds_guild_list_vars.Header.ThemeName == item.Name {
|
|
||||||
w.Write(footer_3)
|
|
||||||
}
|
|
||||||
w.Write(footer_4)
|
w.Write(footer_4)
|
||||||
w.Write([]byte(item.FriendlyName))
|
w.Write([]byte(item.Name))
|
||||||
w.Write(footer_5)
|
w.Write(footer_5)
|
||||||
}
|
if tmpl_guilds_guild_list_vars.Header.Theme.Name == item.Name {
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write(footer_6)
|
w.Write(footer_6)
|
||||||
if tmpl_guilds_guild_list_vars.Header.Widgets.RightSidebar != "" {
|
}
|
||||||
w.Write(footer_7)
|
w.Write(footer_7)
|
||||||
w.Write([]byte(string(tmpl_guilds_guild_list_vars.Header.Widgets.RightSidebar)))
|
w.Write([]byte(item.FriendlyName))
|
||||||
w.Write(footer_8)
|
w.Write(footer_8)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
w.Write(footer_9)
|
w.Write(footer_9)
|
||||||
|
if tmpl_guilds_guild_list_vars.Header.Widgets.RightSidebar != "" {
|
||||||
|
w.Write(footer_10)
|
||||||
|
w.Write([]byte(string(tmpl_guilds_guild_list_vars.Header.Widgets.RightSidebar)))
|
||||||
|
w.Write(footer_11)
|
||||||
|
}
|
||||||
|
w.Write(footer_12)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -264,27 +264,36 @@ var topic_100 = []byte(`
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
`)
|
`)
|
||||||
var footer_0 = []byte(`<div class="footer">
|
var footer_0 = []byte(`<div class="about">
|
||||||
<div id="poweredBy">Powered by Gosora - <span>Made with love by Azareal</span></div>
|
<a id="aboutTitle">`)
|
||||||
|
var footer_1 = []byte(`</a>
|
||||||
|
<span id="aboutDesc">`)
|
||||||
|
var footer_2 = []byte(`</span>
|
||||||
|
</div>`)
|
||||||
|
var footer_3 = []byte(`
|
||||||
|
<div class="footer">
|
||||||
|
<div id="poweredBy">
|
||||||
|
<a id="poweredByName" href="https://github.com/Azareal/Gosora">Powered by Gosora</a><span id="poweredByDash"> - </span><span id="poweredByMaker">Made with love by Azareal</span>
|
||||||
|
</div>
|
||||||
<form action="/theme/" method="post">
|
<form action="/theme/" method="post">
|
||||||
<div id="themeSelector" style="float: right;">
|
<div id="themeSelector" style="float: right;">
|
||||||
<select id="themeSelectorSelect" name="themeSelector" aria-label="Change the site's appearance">
|
<select id="themeSelectorSelect" name="themeSelector" aria-label="Change the site's appearance">
|
||||||
`)
|
`)
|
||||||
var footer_1 = []byte(`<option val="`)
|
var footer_4 = []byte(`<option val="`)
|
||||||
var footer_2 = []byte(`"`)
|
var footer_5 = []byte(`"`)
|
||||||
var footer_3 = []byte(` selected`)
|
var footer_6 = []byte(` selected`)
|
||||||
var footer_4 = []byte(`>`)
|
var footer_7 = []byte(`>`)
|
||||||
var footer_5 = []byte(`</option>`)
|
var footer_8 = []byte(`</option>`)
|
||||||
var footer_6 = []byte(`
|
var footer_9 = []byte(`
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
var footer_7 = []byte(`<aside class="sidebar">`)
|
var footer_10 = []byte(`<aside class="sidebar">`)
|
||||||
var footer_8 = []byte(`</aside>`)
|
var footer_11 = []byte(`</aside>`)
|
||||||
var footer_9 = []byte(`
|
var footer_12 = []byte(`
|
||||||
<div style="clear: both;"></div>
|
<div style="clear: both;"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -498,7 +507,7 @@ var topic_alt_104 = []byte(`
|
||||||
`)
|
`)
|
||||||
var profile_0 = []byte(`
|
var profile_0 = []byte(`
|
||||||
|
|
||||||
<div id="profile_container">
|
<div id="profile_container" class="colstack">
|
||||||
|
|
||||||
<div id="profile_left_lane" class="colstack_left">
|
<div id="profile_left_lane" class="colstack_left">
|
||||||
<!--<header class="colstack_item colstack_head rowhead">
|
<!--<header class="colstack_item colstack_head rowhead">
|
||||||
|
@ -885,7 +894,8 @@ var forum_8 = []byte(`
|
||||||
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block">
|
<div id="forum_head_block" class="rowblock rowhead topic_list_title_block">
|
||||||
<div class="rowitem forum_title`)
|
<div class="rowitem forum_title`)
|
||||||
var forum_9 = []byte(` has_opt`)
|
var forum_9 = []byte(` has_opt`)
|
||||||
var forum_10 = []byte(`"><h1>`)
|
var forum_10 = []byte(`">
|
||||||
|
<h1>`)
|
||||||
var forum_11 = []byte(`</h1>
|
var forum_11 = []byte(`</h1>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
|
@ -921,7 +931,6 @@ var forum_18 = []byte(`
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
`)
|
`)
|
||||||
var forum_19 = []byte(`
|
var forum_19 = []byte(`
|
||||||
<div id="forum_topic_create_form" class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
|
<div id="forum_topic_create_form" class="rowblock topic_create_form quick_create_form" style="display: none;" aria-label="Quick Topic Form">
|
||||||
|
@ -1018,7 +1027,6 @@ var forum_57 = []byte(`">Start one?</a>`)
|
||||||
var forum_58 = []byte(`</div>`)
|
var forum_58 = []byte(`</div>`)
|
||||||
var forum_59 = []byte(`
|
var forum_59 = []byte(`
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
`)
|
`)
|
||||||
var guilds_guild_list_0 = []byte(`
|
var guilds_guild_list_0 = []byte(`
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
// Code generated by Gosora. More below:
|
// Code generated by Gosora. More below:
|
||||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||||
package main
|
package main
|
||||||
|
import "net/http"
|
||||||
import "./common"
|
import "./common"
|
||||||
import "strconv"
|
import "strconv"
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -22,7 +22,7 @@ w.Write([]byte(tmpl_profile_vars.Title))
|
||||||
w.Write(header_1)
|
w.Write(header_1)
|
||||||
w.Write([]byte(tmpl_profile_vars.Header.Site.Name))
|
w.Write([]byte(tmpl_profile_vars.Header.Site.Name))
|
||||||
w.Write(header_2)
|
w.Write(header_2)
|
||||||
w.Write([]byte(tmpl_profile_vars.Header.ThemeName))
|
w.Write([]byte(tmpl_profile_vars.Header.Theme.Name))
|
||||||
w.Write(header_3)
|
w.Write(header_3)
|
||||||
if len(tmpl_profile_vars.Header.Stylesheets) != 0 {
|
if len(tmpl_profile_vars.Header.Stylesheets) != 0 {
|
||||||
for _, item := range tmpl_profile_vars.Header.Stylesheets {
|
for _, item := range tmpl_profile_vars.Header.Stylesheets {
|
||||||
|
@ -161,28 +161,37 @@ w.Write(profile_41)
|
||||||
}
|
}
|
||||||
w.Write(profile_42)
|
w.Write(profile_42)
|
||||||
w.Write(profile_43)
|
w.Write(profile_43)
|
||||||
|
if tmpl_profile_vars.Header.Theme.AboutSegment {
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
|
dispInt := tmpl_profile_vars.Header.Settings["about_segment_title"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_1)
|
||||||
|
dispInt = tmpl_profile_vars.Header.Settings["about_segment_body"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_2)
|
||||||
|
}
|
||||||
|
w.Write(footer_3)
|
||||||
if len(tmpl_profile_vars.Header.Themes) != 0 {
|
if len(tmpl_profile_vars.Header.Themes) != 0 {
|
||||||
for _, item := range tmpl_profile_vars.Header.Themes {
|
for _, item := range tmpl_profile_vars.Header.Themes {
|
||||||
if !item.HideFromThemes {
|
if !item.HideFromThemes {
|
||||||
w.Write(footer_1)
|
|
||||||
w.Write([]byte(item.Name))
|
|
||||||
w.Write(footer_2)
|
|
||||||
if tmpl_profile_vars.Header.ThemeName == item.Name {
|
|
||||||
w.Write(footer_3)
|
|
||||||
}
|
|
||||||
w.Write(footer_4)
|
w.Write(footer_4)
|
||||||
w.Write([]byte(item.FriendlyName))
|
w.Write([]byte(item.Name))
|
||||||
w.Write(footer_5)
|
w.Write(footer_5)
|
||||||
}
|
if tmpl_profile_vars.Header.Theme.Name == item.Name {
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write(footer_6)
|
w.Write(footer_6)
|
||||||
if tmpl_profile_vars.Header.Widgets.RightSidebar != "" {
|
}
|
||||||
w.Write(footer_7)
|
w.Write(footer_7)
|
||||||
w.Write([]byte(string(tmpl_profile_vars.Header.Widgets.RightSidebar)))
|
w.Write([]byte(item.FriendlyName))
|
||||||
w.Write(footer_8)
|
w.Write(footer_8)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
w.Write(footer_9)
|
w.Write(footer_9)
|
||||||
|
if tmpl_profile_vars.Header.Widgets.RightSidebar != "" {
|
||||||
|
w.Write(footer_10)
|
||||||
|
w.Write([]byte(string(tmpl_profile_vars.Header.Widgets.RightSidebar)))
|
||||||
|
w.Write(footer_11)
|
||||||
|
}
|
||||||
|
w.Write(footer_12)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
// Code generated by Gosora. More below:
|
// Code generated by Gosora. More below:
|
||||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||||
package main
|
package main
|
||||||
|
import "strconv"
|
||||||
import "net/http"
|
import "net/http"
|
||||||
import "./common"
|
import "./common"
|
||||||
import "strconv"
|
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -22,7 +22,7 @@ w.Write([]byte(tmpl_topic_vars.Title))
|
||||||
w.Write(header_1)
|
w.Write(header_1)
|
||||||
w.Write([]byte(tmpl_topic_vars.Header.Site.Name))
|
w.Write([]byte(tmpl_topic_vars.Header.Site.Name))
|
||||||
w.Write(header_2)
|
w.Write(header_2)
|
||||||
w.Write([]byte(tmpl_topic_vars.Header.ThemeName))
|
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
||||||
w.Write(header_3)
|
w.Write(header_3)
|
||||||
if len(tmpl_topic_vars.Header.Stylesheets) != 0 {
|
if len(tmpl_topic_vars.Header.Stylesheets) != 0 {
|
||||||
for _, item := range tmpl_topic_vars.Header.Stylesheets {
|
for _, item := range tmpl_topic_vars.Header.Stylesheets {
|
||||||
|
@ -125,7 +125,7 @@ if tmpl_topic_vars.Topic.Avatar != "" {
|
||||||
w.Write(topic_22)
|
w.Write(topic_22)
|
||||||
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
|
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
|
||||||
w.Write(topic_23)
|
w.Write(topic_23)
|
||||||
w.Write([]byte(tmpl_topic_vars.Header.ThemeName))
|
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
||||||
w.Write(topic_24)
|
w.Write(topic_24)
|
||||||
if tmpl_topic_vars.Topic.ContentLines <= 5 {
|
if tmpl_topic_vars.Topic.ContentLines <= 5 {
|
||||||
w.Write(topic_25)
|
w.Write(topic_25)
|
||||||
|
@ -223,7 +223,7 @@ if item.Avatar != "" {
|
||||||
w.Write(topic_65)
|
w.Write(topic_65)
|
||||||
w.Write([]byte(item.Avatar))
|
w.Write([]byte(item.Avatar))
|
||||||
w.Write(topic_66)
|
w.Write(topic_66)
|
||||||
w.Write([]byte(tmpl_topic_vars.Header.ThemeName))
|
w.Write([]byte(tmpl_topic_vars.Header.Theme.Name))
|
||||||
w.Write(topic_67)
|
w.Write(topic_67)
|
||||||
if item.ContentLines <= 5 {
|
if item.ContentLines <= 5 {
|
||||||
w.Write(topic_68)
|
w.Write(topic_68)
|
||||||
|
@ -296,28 +296,37 @@ w.Write(topic_98)
|
||||||
w.Write(topic_99)
|
w.Write(topic_99)
|
||||||
}
|
}
|
||||||
w.Write(topic_100)
|
w.Write(topic_100)
|
||||||
|
if tmpl_topic_vars.Header.Theme.AboutSegment {
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
|
dispInt := tmpl_topic_vars.Header.Settings["about_segment_title"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_1)
|
||||||
|
dispInt = tmpl_topic_vars.Header.Settings["about_segment_body"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_2)
|
||||||
|
}
|
||||||
|
w.Write(footer_3)
|
||||||
if len(tmpl_topic_vars.Header.Themes) != 0 {
|
if len(tmpl_topic_vars.Header.Themes) != 0 {
|
||||||
for _, item := range tmpl_topic_vars.Header.Themes {
|
for _, item := range tmpl_topic_vars.Header.Themes {
|
||||||
if !item.HideFromThemes {
|
if !item.HideFromThemes {
|
||||||
w.Write(footer_1)
|
|
||||||
w.Write([]byte(item.Name))
|
|
||||||
w.Write(footer_2)
|
|
||||||
if tmpl_topic_vars.Header.ThemeName == item.Name {
|
|
||||||
w.Write(footer_3)
|
|
||||||
}
|
|
||||||
w.Write(footer_4)
|
w.Write(footer_4)
|
||||||
w.Write([]byte(item.FriendlyName))
|
w.Write([]byte(item.Name))
|
||||||
w.Write(footer_5)
|
w.Write(footer_5)
|
||||||
}
|
if tmpl_topic_vars.Header.Theme.Name == item.Name {
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write(footer_6)
|
w.Write(footer_6)
|
||||||
if tmpl_topic_vars.Header.Widgets.RightSidebar != "" {
|
}
|
||||||
w.Write(footer_7)
|
w.Write(footer_7)
|
||||||
w.Write([]byte(string(tmpl_topic_vars.Header.Widgets.RightSidebar)))
|
w.Write([]byte(item.FriendlyName))
|
||||||
w.Write(footer_8)
|
w.Write(footer_8)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
w.Write(footer_9)
|
w.Write(footer_9)
|
||||||
|
if tmpl_topic_vars.Header.Widgets.RightSidebar != "" {
|
||||||
|
w.Write(footer_10)
|
||||||
|
w.Write([]byte(string(tmpl_topic_vars.Header.Widgets.RightSidebar)))
|
||||||
|
w.Write(footer_11)
|
||||||
|
}
|
||||||
|
w.Write(footer_12)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
// Code generated by Gosora. More below:
|
// Code generated by Gosora. More below:
|
||||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||||
package main
|
package main
|
||||||
import "strconv"
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
import "./common"
|
import "./common"
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -22,7 +22,7 @@ w.Write([]byte(tmpl_topic_alt_vars.Title))
|
||||||
w.Write(header_1)
|
w.Write(header_1)
|
||||||
w.Write([]byte(tmpl_topic_alt_vars.Header.Site.Name))
|
w.Write([]byte(tmpl_topic_alt_vars.Header.Site.Name))
|
||||||
w.Write(header_2)
|
w.Write(header_2)
|
||||||
w.Write([]byte(tmpl_topic_alt_vars.Header.ThemeName))
|
w.Write([]byte(tmpl_topic_alt_vars.Header.Theme.Name))
|
||||||
w.Write(header_3)
|
w.Write(header_3)
|
||||||
if len(tmpl_topic_alt_vars.Header.Stylesheets) != 0 {
|
if len(tmpl_topic_alt_vars.Header.Stylesheets) != 0 {
|
||||||
for _, item := range tmpl_topic_alt_vars.Header.Stylesheets {
|
for _, item := range tmpl_topic_alt_vars.Header.Stylesheets {
|
||||||
|
@ -302,28 +302,37 @@ w.Write(topic_alt_102)
|
||||||
w.Write(topic_alt_103)
|
w.Write(topic_alt_103)
|
||||||
}
|
}
|
||||||
w.Write(topic_alt_104)
|
w.Write(topic_alt_104)
|
||||||
|
if tmpl_topic_alt_vars.Header.Theme.AboutSegment {
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
|
dispInt := tmpl_topic_alt_vars.Header.Settings["about_segment_title"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_1)
|
||||||
|
dispInt = tmpl_topic_alt_vars.Header.Settings["about_segment_body"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_2)
|
||||||
|
}
|
||||||
|
w.Write(footer_3)
|
||||||
if len(tmpl_topic_alt_vars.Header.Themes) != 0 {
|
if len(tmpl_topic_alt_vars.Header.Themes) != 0 {
|
||||||
for _, item := range tmpl_topic_alt_vars.Header.Themes {
|
for _, item := range tmpl_topic_alt_vars.Header.Themes {
|
||||||
if !item.HideFromThemes {
|
if !item.HideFromThemes {
|
||||||
w.Write(footer_1)
|
|
||||||
w.Write([]byte(item.Name))
|
|
||||||
w.Write(footer_2)
|
|
||||||
if tmpl_topic_alt_vars.Header.ThemeName == item.Name {
|
|
||||||
w.Write(footer_3)
|
|
||||||
}
|
|
||||||
w.Write(footer_4)
|
w.Write(footer_4)
|
||||||
w.Write([]byte(item.FriendlyName))
|
w.Write([]byte(item.Name))
|
||||||
w.Write(footer_5)
|
w.Write(footer_5)
|
||||||
}
|
if tmpl_topic_alt_vars.Header.Theme.Name == item.Name {
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write(footer_6)
|
w.Write(footer_6)
|
||||||
if tmpl_topic_alt_vars.Header.Widgets.RightSidebar != "" {
|
}
|
||||||
w.Write(footer_7)
|
w.Write(footer_7)
|
||||||
w.Write([]byte(string(tmpl_topic_alt_vars.Header.Widgets.RightSidebar)))
|
w.Write([]byte(item.FriendlyName))
|
||||||
w.Write(footer_8)
|
w.Write(footer_8)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
w.Write(footer_9)
|
w.Write(footer_9)
|
||||||
|
if tmpl_topic_alt_vars.Header.Widgets.RightSidebar != "" {
|
||||||
|
w.Write(footer_10)
|
||||||
|
w.Write([]byte(string(tmpl_topic_alt_vars.Header.Widgets.RightSidebar)))
|
||||||
|
w.Write(footer_11)
|
||||||
|
}
|
||||||
|
w.Write(footer_12)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ w.Write([]byte(tmpl_topics_vars.Title))
|
||||||
w.Write(header_1)
|
w.Write(header_1)
|
||||||
w.Write([]byte(tmpl_topics_vars.Header.Site.Name))
|
w.Write([]byte(tmpl_topics_vars.Header.Site.Name))
|
||||||
w.Write(header_2)
|
w.Write(header_2)
|
||||||
w.Write([]byte(tmpl_topics_vars.Header.ThemeName))
|
w.Write([]byte(tmpl_topics_vars.Header.Theme.Name))
|
||||||
w.Write(header_3)
|
w.Write(header_3)
|
||||||
if len(tmpl_topics_vars.Header.Stylesheets) != 0 {
|
if len(tmpl_topics_vars.Header.Stylesheets) != 0 {
|
||||||
for _, item := range tmpl_topics_vars.Header.Stylesheets {
|
for _, item := range tmpl_topics_vars.Header.Stylesheets {
|
||||||
|
@ -199,28 +199,37 @@ w.Write(topics_55)
|
||||||
w.Write(topics_56)
|
w.Write(topics_56)
|
||||||
}
|
}
|
||||||
w.Write(topics_57)
|
w.Write(topics_57)
|
||||||
|
if tmpl_topics_vars.Header.Theme.AboutSegment {
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
|
dispInt := tmpl_topics_vars.Header.Settings["about_segment_title"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_1)
|
||||||
|
dispInt = tmpl_topics_vars.Header.Settings["about_segment_body"]
|
||||||
|
w.Write([]byte(dispInt.(string)))
|
||||||
|
w.Write(footer_2)
|
||||||
|
}
|
||||||
|
w.Write(footer_3)
|
||||||
if len(tmpl_topics_vars.Header.Themes) != 0 {
|
if len(tmpl_topics_vars.Header.Themes) != 0 {
|
||||||
for _, item := range tmpl_topics_vars.Header.Themes {
|
for _, item := range tmpl_topics_vars.Header.Themes {
|
||||||
if !item.HideFromThemes {
|
if !item.HideFromThemes {
|
||||||
w.Write(footer_1)
|
|
||||||
w.Write([]byte(item.Name))
|
|
||||||
w.Write(footer_2)
|
|
||||||
if tmpl_topics_vars.Header.ThemeName == item.Name {
|
|
||||||
w.Write(footer_3)
|
|
||||||
}
|
|
||||||
w.Write(footer_4)
|
w.Write(footer_4)
|
||||||
w.Write([]byte(item.FriendlyName))
|
w.Write([]byte(item.Name))
|
||||||
w.Write(footer_5)
|
w.Write(footer_5)
|
||||||
}
|
if tmpl_topics_vars.Header.Theme.Name == item.Name {
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write(footer_6)
|
w.Write(footer_6)
|
||||||
if tmpl_topics_vars.Header.Widgets.RightSidebar != "" {
|
}
|
||||||
w.Write(footer_7)
|
w.Write(footer_7)
|
||||||
w.Write([]byte(string(tmpl_topics_vars.Header.Widgets.RightSidebar)))
|
w.Write([]byte(item.FriendlyName))
|
||||||
w.Write(footer_8)
|
w.Write(footer_8)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
w.Write(footer_9)
|
w.Write(footer_9)
|
||||||
|
if tmpl_topics_vars.Header.Widgets.RightSidebar != "" {
|
||||||
|
w.Write(footer_10)
|
||||||
|
w.Write([]byte(string(tmpl_topics_vars.Header.Widgets.RightSidebar)))
|
||||||
|
w.Write(footer_11)
|
||||||
|
}
|
||||||
|
w.Write(footer_12)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
<nav class="colstack_left">
|
<nav class="colstack_left">
|
||||||
<div class="colstack_item colstack_head rowhead">
|
<div class="colstack_item colstack_head rowhead">
|
||||||
<div class="rowitem"><h1>My Account</h1></div>
|
<div class="rowitem">
|
||||||
|
<a href="/user/edit/critical/"><h1>My Account</h1></a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="colstack_item rowmenu">
|
<div class="colstack_item rowmenu">
|
||||||
<div class="rowitem passive"><a href="/user/edit/avatar/">Avatar</a></div>
|
<div class="rowitem passive"><a href="/user/edit/avatar/">Avatar</a></div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<main class="colstack account">
|
<div class="colstack account">
|
||||||
{{template "account-menu.html" . }}
|
{{template "account-menu.html" . }}
|
||||||
<div class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head rowhead">
|
<div class="colstack_item colstack_head rowhead">
|
||||||
<div class="rowitem"><h1>Edit Avatar</h1></div>
|
<div class="rowitem"><h1>Edit Avatar</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
<div class="rowitem"><img src="{{.CurrentUser.Avatar}}" height="128px" max-width="128px" /></div>
|
<div class="rowitem"><img src="{{.CurrentUser.Avatar}}" height="128px" max-width="128px" /></div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="colstack_item form_item">
|
<div class="colstack_item the_form">
|
||||||
<form action="/user/edit/avatar/submit/" method="post" enctype="multipart/form-data">
|
<form action="/user/edit/avatar/submit/" method="post" enctype="multipart/form-data">
|
||||||
<div class="formrow real_first_child">
|
<div class="formrow real_first_child">
|
||||||
<div class="formitem formlabel"><a>Upload Avatar</a></div>
|
<div class="formitem formlabel"><a>Upload Avatar</a></div>
|
||||||
|
@ -21,6 +21,6 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<main class="colstack account">
|
<div class="colstack account">
|
||||||
{{template "account-menu.html" . }}
|
{{template "account-menu.html" . }}
|
||||||
<div class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head rowhead">
|
<div class="colstack_item colstack_head rowhead">
|
||||||
<div class="rowitem"><h1>Emails</h1></div>
|
<div class="rowitem"><h1>Emails</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,6 +17,6 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<main class="colstack account">
|
<div class="colstack account">
|
||||||
{{template "account-menu.html" . }}
|
{{template "account-menu.html" . }}
|
||||||
<div class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head rowhead">
|
<div class="colstack_item colstack_head rowhead">
|
||||||
<div class="rowitem"><h1>Edit Username</h1></div>
|
<div class="rowitem"><h1>Edit Username</h1></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="colstack_item form_item">
|
<div class="colstack_item the_form">
|
||||||
<form action="/user/edit/username/submit/" method="post">
|
<form action="/user/edit/username/submit/" method="post">
|
||||||
<div class="formrow real_first_child">
|
<div class="formrow real_first_child">
|
||||||
<div class="formitem formlabel"><a>Current Username</a></div>
|
<div class="formitem formlabel"><a>Current Username</a></div>
|
||||||
|
@ -20,6 +20,6 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
<main class="colstack account">
|
<div class="colstack account">
|
||||||
{{template "account-menu.html" . }}
|
{{template "account-menu.html" . }}
|
||||||
<div class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head rowhead">
|
<div class="colstack_item colstack_head rowhead">
|
||||||
<div class="rowitem"><h1>Edit Password</h1></div>
|
<div class="rowitem"><h1>Edit Password</h1></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="colstack_item form_item">
|
<div class="colstack_item the_form">
|
||||||
<form action="/user/edit/critical/submit/" method="post">
|
<form action="/user/edit/critical/submit/" method="post">
|
||||||
<div class="formrow real_first_child">
|
<div class="formrow real_first_child">
|
||||||
<div class="formitem formlabel"><a>Current Password</a></div>
|
<div class="formitem formlabel"><a>Current Password</a></div>
|
||||||
|
@ -24,6 +24,6 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
|
{{if .Header.Theme.AboutSegment}}<div class="about">
|
||||||
|
<a id="aboutTitle">{{.Header.Settings.about_segment_title}}</a>
|
||||||
|
<span id="aboutDesc">{{.Header.Settings.about_segment_body}}</span>
|
||||||
|
</div>{{end}}
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div id="poweredBy">Powered by Gosora - <span>Made with love by Azareal</span></div>
|
<div id="poweredBy">
|
||||||
|
<a id="poweredByName" href="https://github.com/Azareal/Gosora">Powered by Gosora</a><span id="poweredByDash"> - </span><span id="poweredByMaker">Made with love by Azareal</span>
|
||||||
|
</div>
|
||||||
<form action="/theme/" method="post">
|
<form action="/theme/" method="post">
|
||||||
<div id="themeSelector" style="float: right;">
|
<div id="themeSelector" style="float: right;">
|
||||||
<select id="themeSelectorSelect" name="themeSelector" aria-label="Change the site's appearance">
|
<select id="themeSelectorSelect" name="themeSelector" aria-label="Change the site's appearance">
|
||||||
{{range .Header.Themes}}
|
{{range .Header.Themes}}
|
||||||
{{if not .HideFromThemes}}<option val="{{.Name}}"{{if eq $.Header.ThemeName .Name}} selected{{end}}>{{.FriendlyName}}</option>{{end}}
|
{{if not .HideFromThemes}}<option val="{{.Name}}"{{if eq $.Header.Theme.Name .Name}} selected{{end}}>{{.FriendlyName}}</option>{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>{{.Title}} | {{.Header.Site.Name}}</title>
|
<title>{{.Title}} | {{.Header.Site.Name}}</title>
|
||||||
<link href="/static/{{.Header.ThemeName}}/main.css" rel="stylesheet" type="text/css">
|
<link href="/static/{{.Header.Theme.Name}}/main.css" rel="stylesheet" type="text/css">
|
||||||
{{range .Header.Stylesheets}}
|
{{range .Header.Stylesheets}}
|
||||||
<link href="/static/{{.}}" rel="stylesheet" type="text/css">
|
<link href="/static/{{.}}" rel="stylesheet" type="text/css">
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
<nav class="colstack_left" aria-label="The control panel menu">
|
<nav class="colstack_left" aria-label="The control panel menu">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><a href="/panel/logs/mod/">Logs</a></div>
|
<div class="rowitem"><a href="/panel/logs/mod/">Logs</a></div>
|
||||||
|
@ -25,4 +26,5 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
@ -17,4 +18,5 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<main id="panel_dashboard_right" class="colstack_right">
|
<main id="panel_dashboard_right" class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
@ -7,8 +8,9 @@
|
||||||
<div id="panel_dashboard" class="colstack_grid">
|
<div id="panel_dashboard" class="colstack_grid">
|
||||||
{{range .GridItems}}
|
{{range .GridItems}}
|
||||||
<div id="{{.ID}}" class="grid_item {{.Class}}" title="{{.Note}}" style="{{if .TextColour}}color: {{.TextColour}};{{end}}
|
<div id="{{.ID}}" class="grid_item {{.Class}}" title="{{.Note}}" style="{{if .TextColour}}color: {{.TextColour}};{{end}}
|
||||||
{{if .Background}}background-color: {{.Background}};{{end}}">{{.Body}}</div>
|
{{if .Background}}background-color: {{.Background}};{{end}}"><span>{{.Body}}</span></div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<main id="panel_dashboard_right" class="colstack_right">
|
<main id="panel_dashboard_right" class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
@ -14,4 +15,5 @@
|
||||||
<div class="grid_item grid_stat">{{.DBAdapter}}</div>
|
<div class="grid_item grid_stat">{{.DBAdapter}}</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
|
||||||
|
<div class="colstack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<script>
|
<script>
|
||||||
var form_vars = {'perm_preset': ['can_moderate','can_post','read_only','no_access','default','custom']};
|
var form_vars = {'perm_preset': ['can_moderate','can_post','read_only','no_access','default','custom']};
|
||||||
|
@ -63,4 +65,5 @@ var form_vars = {'perm_preset': ['can_moderate','can_post','read_only','no_acces
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
|
||||||
|
<div class="colstack">
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<script>var form_vars = {
|
<script>var form_vars = {
|
||||||
'forum_active': ['Hide','Show'],
|
'forum_active': ['Hide','Show'],
|
||||||
|
@ -72,4 +74,5 @@
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
<nav class="colstack_left" aria-label="The control panel menu">
|
<nav class="colstack_left" aria-label="The control panel menu">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><a href="/panel/groups/edit/{{.ID}}">Group Editor</a></div>
|
<div class="rowitem"><a href="/panel/groups/edit/{{.ID}}">Group Editor</a></div>
|
||||||
|
@ -60,4 +61,5 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
<nav class="colstack_left" aria-label="The control panel menu">
|
<nav class="colstack_left" aria-label="The control panel menu">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><a href="/panel/groups/edit/{{.ID}}">Group Editor</a></div>
|
<div class="rowitem"><a href="/panel/groups/edit/{{.ID}}">Group Editor</a></div>
|
||||||
|
@ -43,4 +44,5 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
{{template "panel-menu.html" . }}
|
<div class="colstack">
|
||||||
|
|
||||||
|
{{template "panel-menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>Groups</h1></div>
|
<div class="rowitem"><h1>Groups</h1></div>
|
||||||
|
@ -57,4 +58,6 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
|
|
||||||
<nav class="colstack_left" aria-label="The control panel menu">
|
<nav class="colstack_left" aria-label="The control panel menu">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><a href="/panel/logs/mod/">Logs</a></div>
|
<div class="rowitem"><a href="/panel/logs/mod/">Logs</a></div>
|
||||||
|
@ -37,4 +39,6 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
|
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
@ -21,4 +23,6 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
|
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
@ -34,4 +36,6 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
|
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
@ -13,4 +15,6 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
|
|
||||||
<nav class="colstack_left" aria-label="The control panel menu">
|
<nav class="colstack_left" aria-label="The control panel menu">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><a href="/panel/themes/">Theme Manager</a></div>
|
<div class="rowitem"><a href="/panel/themes/">Theme Manager</a></div>
|
||||||
|
@ -56,4 +58,6 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
|
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
@ -33,4 +35,6 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
{{template "panel-menu.html" . }}
|
<div class="colstack">
|
||||||
|
|
||||||
|
{{template "panel-menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
<div class="rowitem"><h1>Users</h1></div>
|
<div class="rowitem"><h1>Users</h1></div>
|
||||||
|
@ -29,4 +30,6 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
|
@ -1,4 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
<div class="colstack">
|
||||||
|
|
||||||
{{template "panel-menu.html" . }}
|
{{template "panel-menu.html" . }}
|
||||||
<main class="colstack_right">
|
<main class="colstack_right">
|
||||||
<div class="colstack_item colstack_head">
|
<div class="colstack_item colstack_head">
|
||||||
|
@ -42,4 +44,6 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
{{template "footer.html" . }}
|
{{template "footer.html" . }}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
|
|
||||||
<div id="profile_container">
|
<div id="profile_container" class="colstack">
|
||||||
|
|
||||||
<div id="profile_left_lane" class="colstack_left">
|
<div id="profile_left_lane" class="colstack_left">
|
||||||
<!--<header class="colstack_item colstack_head rowhead">
|
<!--<header class="colstack_item colstack_head rowhead">
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="The opening post for this topic">
|
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowblock post_container top_post" aria-label="The opening post for this topic">
|
||||||
<div class="rowitem passive editable_parent post_item {{.Topic.ClassName}}" style="{{if .Topic.Avatar}}background-image: url({{.Topic.Avatar}}), url(/static/{{.Header.ThemeName}}/post-avatar-bg.jpg);background-position: 0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;{{end}}">
|
<div class="rowitem passive editable_parent post_item {{.Topic.ClassName}}" style="{{if .Topic.Avatar}}background-image: url({{.Topic.Avatar}}), url(/static/{{.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .Topic.ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;{{end}}">
|
||||||
<p class="hide_on_edit topic_content user_content" itemprop="text" style="margin:0;padding:0;">{{.Topic.ContentHTML}}</p>
|
<p class="hide_on_edit topic_content user_content" itemprop="text" style="margin:0;padding:0;">{{.Topic.ContentHTML}}</p>
|
||||||
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea>
|
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea>
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@
|
||||||
<span itemprop="text">{{.ActionType}}</span>
|
<span itemprop="text">{{.ActionType}}</span>
|
||||||
</article>
|
</article>
|
||||||
{{else}}
|
{{else}}
|
||||||
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item {{.ClassName}}" style="{{if .Avatar}}background-image: url({{.Avatar}}), url(/static/{{$.Header.ThemeName}}/post-avatar-bg.jpg);background-position: 0px {{if le .ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;{{end}}">
|
<article itemscope itemtype="http://schema.org/CreativeWork" class="rowitem passive deletable_block editable_parent post_item {{.ClassName}}" style="{{if .Avatar}}background-image: url({{.Avatar}}), url(/static/{{$.Header.Theme.Name}}/post-avatar-bg.jpg);background-position: 0px {{if le .ContentLines 5}}-1{{end}}0px;background-repeat:no-repeat, repeat-y;{{end}}">
|
||||||
{{/** TODO: We might end up with <br>s in the inline editor, fix this **/}}
|
{{/** TODO: We might end up with <br>s in the inline editor, fix this **/}}
|
||||||
<p class="editable_block user_content" itemprop="text" style="margin:0;padding:0;">{{.ContentHtml}}</p>
|
<p class="editable_block user_content" itemprop="text" style="margin:0;padding:0;">{{.ContentHtml}}</p>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
:root {
|
:root {
|
||||||
--header-border-color: hsl(0,0%,85%);
|
--header-border-color: hsl(0,0%,80%);
|
||||||
--element-border-color: hsl(0,0%,90%);
|
--element-border-color: hsl(0,0%,85%);
|
||||||
--element-background-color: white;
|
--element-background-color: white;
|
||||||
--replies-lang-string: " replies";
|
--replies-lang-string: " replies";
|
||||||
--topics-lang-string: " topics";
|
--topics-lang-string: " topics";
|
||||||
|
@ -45,8 +45,10 @@ a {
|
||||||
padding-top: 14px;
|
padding-top: 14px;
|
||||||
display: flex;
|
display: flex;
|
||||||
/*background-color: hsl(0,0%,97%);*/
|
/*background-color: hsl(0,0%,97%);*/
|
||||||
|
background-color: hsl(0,0%,98%);
|
||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
padding-right: 0px;
|
padding-right: 0px;
|
||||||
|
padding-bottom: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main {
|
#main {
|
||||||
|
@ -163,14 +165,22 @@ ul {
|
||||||
border-bottom: 2px solid var(--header-border-color);
|
border-bottom: 2px solid var(--header-border-color);
|
||||||
background-color: var(--element-background-color);
|
background-color: var(--element-background-color);
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
.rowblock {
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
}
|
}
|
||||||
|
.colstack_right {
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.rowhead, .opthead, .colstack_head {
|
.rowhead, .opthead, .colstack_head {
|
||||||
padding: 13px;
|
padding: 13px;
|
||||||
padding-top: 14px;
|
padding-top: 14px;
|
||||||
padding-bottom: 14px;
|
padding-bottom: 14px;
|
||||||
}
|
}
|
||||||
|
.rowhead:not(:first-child), .opthead:not(:first-child), .colstack_head:not(:first-child) {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
.rowhead h1, .opthead h1, .colstack_head h1 {
|
.rowhead h1, .opthead h1, .colstack_head h1 {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
|
@ -181,14 +191,18 @@ ul {
|
||||||
margin-block-end: 0;
|
margin-block-end: 0;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
.colstack_head a h1 {
|
||||||
|
font-size: 16px;
|
||||||
|
color: var(--primary-link-color);
|
||||||
|
}
|
||||||
|
|
||||||
.colstack {
|
.colstack {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
#main .colstack_left {
|
.colstack:not(#profile_container) .colstack_left {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
}
|
}
|
||||||
#main .colstack_right {
|
.colstack:not(#profile_container) .colstack_right {
|
||||||
width: calc(90% - 300px);
|
width: calc(90% - 300px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,8 +426,9 @@ label.uploadItem {
|
||||||
padding-left: 33px;
|
padding-left: 33px;
|
||||||
}
|
}
|
||||||
|
|
||||||
select, input, textarea {
|
select, input, textarea, button {
|
||||||
border: 1px solid var(--header-border-color);
|
border: 1px solid var(--header-border-color);
|
||||||
|
background: var(--element-background-color);
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
color: hsl(0,0%,30%);
|
color: hsl(0,0%,30%);
|
||||||
}
|
}
|
||||||
|
@ -713,7 +728,7 @@ select, input, textarea {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#profile_container, #profile_left_pane .topBlock {
|
#profile_left_pane .topBlock {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
#profile_left_lane {
|
#profile_left_lane {
|
||||||
|
@ -725,6 +740,7 @@ select, input, textarea {
|
||||||
padding-bottom: 18px;
|
padding-bottom: 18px;
|
||||||
border: 1px solid var(--element-border-color);
|
border: 1px solid var(--element-border-color);
|
||||||
border-bottom: 2px solid var(--element-border-color);
|
border-bottom: 2px solid var(--element-border-color);
|
||||||
|
background-color: var(--element-background-color);
|
||||||
}
|
}
|
||||||
#profile_left_pane .avatarRow {
|
#profile_left_pane .avatarRow {
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
|
@ -748,17 +764,21 @@ select, input, textarea {
|
||||||
.rowmenu .passive {
|
.rowmenu .passive {
|
||||||
border: 1px solid var(--element-border-color);
|
border: 1px solid var(--element-border-color);
|
||||||
border-bottom: 2px solid var(--element-border-color);
|
border-bottom: 2px solid var(--element-border-color);
|
||||||
|
background-color: var(--element-background-color);
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
}
|
}
|
||||||
.rowmenu {
|
.colstack:not(#profile_container) .rowmenu {
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
padding-right: 12px;
|
|
||||||
}
|
}
|
||||||
.rowmenu .passive:hover {
|
.colstack:not(#profile_container) .rowmenu .passive {
|
||||||
margin-left: 4px;
|
margin-top: 0px;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.colstack:not(#profile_container) .rowmenu .passive:last-child {
|
||||||
|
border-bottom: 2px solid var(--element-border-color);
|
||||||
}
|
}
|
||||||
#profile_left_pane .passiveBlock .passive {
|
#profile_left_pane .passiveBlock .passive {
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
|
@ -771,50 +791,158 @@ select, input, textarea {
|
||||||
#profile_right_lane .colstack_item, .colstack_right .colstack_item {
|
#profile_right_lane .colstack_item, .colstack_right .colstack_item {
|
||||||
border: 1px solid var(--element-border-color);
|
border: 1px solid var(--element-border-color);
|
||||||
border-bottom: 2px solid var(--element-border-color);
|
border-bottom: 2px solid var(--element-border-color);
|
||||||
|
}
|
||||||
|
#profile_right_lane .colstack_item, .colstack_right .colstack_item, .colstack_right .colstack_grid {
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
}
|
}
|
||||||
|
#profile_right_lane .topic_reply_form {
|
||||||
.form_item {
|
width: auto;
|
||||||
display: flex;
|
|
||||||
}
|
}
|
||||||
.form_item form, .colstack_item .formrow {
|
|
||||||
display: contents;
|
.colstack_item .formrow {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
.colstack_right .formrow {
|
.colstack_right .formrow {
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
|
border-right: 1px solid var(--element-border-color);
|
||||||
}
|
}
|
||||||
.colstack_right .formrow:first-child {
|
.colstack_right .formrow:first-child {
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
}
|
}
|
||||||
|
.colstack_right .formrow .formlabel {
|
||||||
|
padding-top: 5px;
|
||||||
|
}
|
||||||
.colstack_right .formrow:last-child {
|
.colstack_right .formrow:last-child {
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
}
|
}
|
||||||
.colstack_item:not(#profile_right_lane) .formrow .formlabel {
|
.colstack_item:not(#profile_right_lane) .formrow .formlabel {
|
||||||
width: min-content;
|
width: 40%;
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
.formitem:only-child {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.the_form .formitem:only-child button {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
.quick_reply_form, .topic_reply_form, .the_form {
|
||||||
|
background: var(--element-background-color);
|
||||||
|
}
|
||||||
|
.formrow {
|
||||||
|
border-right: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.about, .footer {
|
||||||
border-top: 1px solid var(--element-border-color);
|
border-top: 1px solid var(--element-border-color);
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
margin-top: 14px;
|
|
||||||
margin-left: -8px;
|
margin-left: -8px;
|
||||||
margin-right: -8px;
|
margin-right: -8px;
|
||||||
|
background-color: var(--element-background-color);
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
.footer #poweredBy {
|
.about, #poweredBy {
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
.footer #poweredBy span {
|
.about {
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
#poweredBy {
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
#poweredBy span {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
#aboutTitle {
|
||||||
|
font-size: 18px;
|
||||||
|
margin: 8px;
|
||||||
|
}
|
||||||
|
#poweredByName {
|
||||||
|
font-size: 17px;
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
#aboutDesc {
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
#aboutDesc p:last-child {
|
||||||
|
-webkit-margin-after: 8px;
|
||||||
|
}
|
||||||
|
#aboutDesc p:first-child {
|
||||||
|
-webkit-margin-before: 3px;
|
||||||
|
}
|
||||||
|
#poweredByDash, #poweredByMaker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#themeSelectorSelect {
|
||||||
|
padding: 3px;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colstack_grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
.grid_item {
|
||||||
|
background: var(--element-background-color);
|
||||||
|
border: 1px solid var(--element-border-color);
|
||||||
|
border-bottom: 2px solid var(--element-border-color);
|
||||||
|
margin: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
padding-left: 0px;
|
||||||
|
display: flex;
|
||||||
|
padding-top: 0px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
.grid_item span {
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#dash-version:before, #dash-cpu:before, #dash-ram:before {
|
||||||
|
display: inline-block;
|
||||||
|
background: hsl(0,0%,98%);
|
||||||
|
font: normal normal normal 14px/1 FontAwesome;
|
||||||
|
font-size: 20px;
|
||||||
|
padding-left: 17px;
|
||||||
|
padding-top: 16px;
|
||||||
|
padding-right: 19px;
|
||||||
|
color: hsl(0,0%,20%);
|
||||||
|
border-bottom: 1px solid var(--element-border-color);
|
||||||
|
}
|
||||||
|
#dash-version:before {
|
||||||
|
content: "\f126";
|
||||||
|
}
|
||||||
|
#dash-cpu:before {
|
||||||
|
content: "\f2db";
|
||||||
|
}
|
||||||
|
#dash-ram:before {
|
||||||
|
content: "\f233";
|
||||||
|
}
|
||||||
|
|
||||||
@media(max-width: 670px) {
|
@media(max-width: 670px) {
|
||||||
.topic_inner_right {
|
.topic_inner_right {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*#dash-cpu:before {
|
||||||
|
content: "\f2db";
|
||||||
|
width: 30px;
|
||||||
|
margin-right: 8px;
|
||||||
|
display: inline-block;
|
||||||
|
padding-left: 20px;
|
||||||
|
background: hsl(0,0%,98%);
|
||||||
|
font: normal normal normal 14px/1 FontAwesome;
|
||||||
|
}*/
|
|
@ -0,0 +1,6 @@
|
||||||
|
.about {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
"URL": "github.com/Azareal/Gosora",
|
"URL": "github.com/Azareal/Gosora",
|
||||||
"Tag": "WIP",
|
"Tag": "WIP",
|
||||||
"Sidebars":"right",
|
"Sidebars":"right",
|
||||||
|
"AboutSegment":true,
|
||||||
"Templates": [
|
"Templates": [
|
||||||
{
|
{
|
||||||
"Name": "topic",
|
"Name": "topic",
|
||||||
|
|
|
@ -352,11 +352,12 @@ AdminStatLoop:
|
||||||
} else {
|
} else {
|
||||||
calcperc := int(cpuPerc[0]) / runtime.NumCPU()
|
calcperc := int(cpuPerc[0]) / runtime.NumCPU()
|
||||||
cpustr = strconv.Itoa(calcperc)
|
cpustr = strconv.Itoa(calcperc)
|
||||||
if calcperc < 30 {
|
switch {
|
||||||
|
case calcperc < 30:
|
||||||
cpuColour = "stat_green"
|
cpuColour = "stat_green"
|
||||||
} else if calcperc < 75 {
|
case calcperc < 75:
|
||||||
cpuColour = "stat_orange"
|
cpuColour = "stat_orange"
|
||||||
} else {
|
default:
|
||||||
cpuColour = "stat_red"
|
cpuColour = "stat_red"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,20 +410,20 @@ AdminStatLoop:
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
if !noStatUpdates {
|
if !noStatUpdates {
|
||||||
w.Write([]byte("set #dash-totonline " + strconv.Itoa(totonline) + totunit + " online\r"))
|
w.Write([]byte("set #dash-totonline <span>" + strconv.Itoa(totonline) + totunit + " online</span>\r"))
|
||||||
w.Write([]byte("set #dash-gonline " + strconv.Itoa(gonline) + gunit + " guests online\r"))
|
w.Write([]byte("set #dash-gonline <span>" + strconv.Itoa(gonline) + gunit + " guests online</span>\r"))
|
||||||
w.Write([]byte("set #dash-uonline " + strconv.Itoa(uonline) + uunit + " users online\r"))
|
w.Write([]byte("set #dash-uonline <span>" + strconv.Itoa(uonline) + uunit + " users online</span>\r"))
|
||||||
|
|
||||||
w.Write([]byte("set-class #dash-totonline grid_item grid_stat " + onlineColour + "\r"))
|
w.Write([]byte("set-class #dash-totonline grid_item grid_stat " + onlineColour + "\r"))
|
||||||
w.Write([]byte("set-class #dash-gonline grid_item grid_stat " + onlineGuestsColour + "\r"))
|
w.Write([]byte("set-class #dash-gonline grid_item grid_stat " + onlineGuestsColour + "\r"))
|
||||||
w.Write([]byte("set-class #dash-uonline grid_item grid_stat " + onlineUsersColour + "\r"))
|
w.Write([]byte("set-class #dash-uonline grid_item grid_stat " + onlineUsersColour + "\r"))
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Write([]byte("set #dash-cpu CPU: " + cpustr + "%\r"))
|
w.Write([]byte("set #dash-cpu <span>CPU: " + cpustr + "%</span>\r"))
|
||||||
w.Write([]byte("set-class #dash-cpu grid_item grid_istat " + cpuColour + "\r"))
|
w.Write([]byte("set-class #dash-cpu grid_item grid_istat " + cpuColour + "\r"))
|
||||||
|
|
||||||
if !noRAMUpdates {
|
if !noRAMUpdates {
|
||||||
w.Write([]byte("set #dash-ram RAM: " + ramstr + "\r"))
|
w.Write([]byte("set #dash-ram <span>RAM: " + ramstr + "</span>\r"))
|
||||||
w.Write([]byte("set-class #dash-ram grid_item grid_istat " + ramColour + "\r"))
|
w.Write([]byte("set-class #dash-ram grid_item grid_istat " + ramColour + "\r"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue