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.
|
||||
|
||||
* github.com/go-ego/riot A search engine library.
|
||||
|
||||
# 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).
|
||||
|
|
|
@ -6,31 +6,64 @@ import (
|
|||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
type LogStmts struct {
|
||||
addModLogEntry *sql.Stmt
|
||||
addAdminLogEntry *sql.Stmt
|
||||
var ModLogs LogStore
|
||||
var AdminLogs LogStore
|
||||
|
||||
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
|
||||
count *sql.Stmt
|
||||
}
|
||||
|
||||
func init() {
|
||||
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(),
|
||||
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?
|
||||
func (store *SQLModLogStore) Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) {
|
||||
_, err = store.create.Exec(action, elementID, elementType, ipaddress, actorID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (store *SQLModLogStore) GlobalCount() (logCount int) {
|
||||
err := store.count.QueryRow().Scan(&logCount)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
}
|
||||
return acc.FirstError()
|
||||
})
|
||||
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?
|
||||
func AddModLog(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) {
|
||||
_, err = logStmts.addModLogEntry.Exec(action, elementID, elementType, ipaddress, actorID)
|
||||
func (store *SQLAdminLogStore) Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) {
|
||||
_, err = store.create.Exec(action, elementID, elementType, ipaddress, actorID)
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Make a store for this?
|
||||
func AddAdminLog(action string, elementID string, elementType int, ipaddress string, actorID int) (err error) {
|
||||
_, err = logStmts.addAdminLogEntry.Exec(action, elementID, elementType, ipaddress, actorID)
|
||||
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
|
||||
ucache, ok := Users.(UserCache)
|
||||
if ok {
|
||||
ucache.CacheRemove(uid)
|
||||
ucache := Users.GetCache()
|
||||
if ucache != nil {
|
||||
ucache.Remove(uid)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -170,9 +170,9 @@ func (auth *DefaultAuth) CreateSession(uid int) (session string, err error) {
|
|||
}
|
||||
|
||||
// Flush the user data from the cache
|
||||
ucache, ok := Users.(UserCache)
|
||||
if ok {
|
||||
ucache.CacheRemove(uid)
|
||||
ucache := Users.GetCache()
|
||||
if ucache != nil {
|
||||
ucache.Remove(uid)
|
||||
}
|
||||
return session, nil
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ func (forum *Forum) Update(name string, desc string, active bool, preset string)
|
|||
return err
|
||||
}
|
||||
}
|
||||
_ = Fstore.Reload(forum.ID)
|
||||
_ = Forums.Reload(forum.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -113,11 +113,11 @@ func (forum *Forum) setPreset(fperms *ForumPerms, preset string, gid int) (err e
|
|||
LogError(err)
|
||||
return errors.New("Unable to update the forum")
|
||||
}
|
||||
err = Fstore.Reload(forum.ID)
|
||||
err = Forums.Reload(forum.ID)
|
||||
if err != nil {
|
||||
return errors.New("Unable to reload forum")
|
||||
}
|
||||
err = Fpstore.ReloadGroup(forum.ID, gid)
|
||||
err = FPStore.ReloadGroup(forum.ID, gid)
|
||||
if err != nil {
|
||||
return errors.New("Unable to reload the forum permissions")
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ var LocalPermList = []string{
|
|||
"CloseTopic",
|
||||
}
|
||||
|
||||
// TODO: Rename this to ForumPermSet?
|
||||
/* Inherit from group permissions for ones we don't have */
|
||||
type ForumPerms struct {
|
||||
ViewTopic bool
|
||||
|
@ -165,7 +166,7 @@ func PermmapToQuery(permmap map[string]*ForumPerms, fid int) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Fpstore.Reload(fid)
|
||||
return FPStore.Reload(fid)
|
||||
}
|
||||
|
||||
func ReplaceForumPermsForGroup(gid int, presetSet map[int]string, permSets map[int]*ForumPerms) error {
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
var Fpstore ForumPermsStore
|
||||
var FPStore ForumPermsStore
|
||||
|
||||
type ForumPermsStore interface {
|
||||
Init() error
|
||||
|
@ -41,7 +41,7 @@ func NewMemoryForumPermsStore() (*MemoryForumPermsStore, error) {
|
|||
func (fps *MemoryForumPermsStore) Init() error {
|
||||
fps.updateMutex.Lock()
|
||||
defer fps.updateMutex.Unlock()
|
||||
fids, err := Fstore.GetAllIDs()
|
||||
fids, err := Forums.GetAllIDs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -55,7 +55,6 @@ func (fps *MemoryForumPermsStore) Init() error {
|
|||
debugLog("Adding the forum permissions")
|
||||
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)
|
||||
for rows.Next() {
|
||||
var gid, fid int
|
||||
|
@ -97,7 +96,7 @@ func (fps *MemoryForumPermsStore) Reload(fid int) error {
|
|||
fps.updateMutex.Lock()
|
||||
defer fps.updateMutex.Unlock()
|
||||
debugLogf("Reloading the forum permissions for forum #%d", fid)
|
||||
fids, err := Fstore.GetAllIDs()
|
||||
fids, err := Forums.GetAllIDs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -143,7 +142,7 @@ func (fps *MemoryForumPermsStore) ReloadGroup(fid int, gid int) (err error) {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
group, err := Gstore.Get(gid)
|
||||
group, err := Groups.Get(gid)
|
||||
if err != nil {
|
||||
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 {
|
||||
groups, err := Gstore.GetAll()
|
||||
groups, err := Groups.GetAll()
|
||||
if err != nil {
|
||||
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
|
||||
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 {
|
||||
return fperms, ErrNoRows
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
|
||||
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 Fstore ForumStore
|
||||
var Forums ForumStore
|
||||
|
||||
// ForumStore is an interface for accessing the forums and the metadata stored on them
|
||||
type ForumStore interface {
|
||||
|
|
|
@ -51,7 +51,7 @@ func (group *Group) ChangeRank(isAdmin bool, isMod bool, isBanned bool) (err err
|
|||
return err
|
||||
}
|
||||
|
||||
Gstore.Reload(group.ID)
|
||||
Groups.Reload(group.ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"../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?
|
||||
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...
|
||||
fdata, err := Fstore.GetAll()
|
||||
fdata, err := Forums.GetAll()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
|
|||
mgs.Unlock()
|
||||
|
||||
for _, forum := range fdata {
|
||||
err = Fpstore.Reload(forum.ID)
|
||||
err = FPStore.Reload(forum.ID)
|
||||
if err != nil {
|
||||
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
|
||||
Site *site
|
||||
Settings SettingMap
|
||||
Themes map[string]Theme // TODO: Use a slice containing every theme instead of the main map for speed
|
||||
ThemeName string
|
||||
Themes map[string]Theme // TODO: Use a slice containing every theme instead of the main map for speed?
|
||||
Theme Theme
|
||||
//TemplateName string // TODO: Use this to move template calls to the router rather than duplicating them over and over and over?
|
||||
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
|
||||
// TODO: Write a test for this
|
||||
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
|
||||
|
||||
topic, err := Topics.Get(tid)
|
||||
if err != nil || !Fstore.Exists(topic.ParentID) {
|
||||
if err != nil || !Forums.Exists(topic.ParentID) {
|
||||
outbytes = append(outbytes, InvalidTopic...)
|
||||
lastItem = i
|
||||
continue
|
||||
|
@ -250,7 +250,7 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
|||
reply := BlankReply()
|
||||
reply.ID = rid
|
||||
topic, err := reply.Topic()
|
||||
if err != nil || !Fstore.Exists(topic.ParentID) {
|
||||
if err != nil || !Forums.Exists(topic.ParentID) {
|
||||
outbytes = append(outbytes, InvalidTopic...)
|
||||
lastItem = i
|
||||
continue
|
||||
|
@ -271,7 +271,7 @@ func ParseMessage(msg string, sectionID int, sectionType string /*, user User*/)
|
|||
fid, intLen := CoerceIntBytes(msgbytes[start:])
|
||||
i += intLen
|
||||
|
||||
if !Fstore.Exists(fid) {
|
||||
if !Forums.Exists(fid) {
|
||||
outbytes = append(outbytes, InvalidForum...)
|
||||
lastItem = i
|
||||
continue
|
||||
|
|
|
@ -189,7 +189,7 @@ func RebuildGroupPermissions(gid int) error {
|
|||
return err
|
||||
}
|
||||
|
||||
group, err := Gstore.Get(gid)
|
||||
group, err := Groups.Get(gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"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?
|
||||
// nolint Be quiet megacheck, this *is* used
|
||||
var currentLangPack atomic.Value
|
||||
|
@ -41,6 +42,8 @@ type LanguagePack struct {
|
|||
SettingLabels map[string]string
|
||||
PermPresets map[string]string
|
||||
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
|
||||
|
@ -114,7 +117,7 @@ func GetPhrase(name string) (string, bool) {
|
|||
func GetGlobalPermPhrase(name string) string {
|
||||
res, ok := currentLangPack.Load().(*LanguagePack).GlobalPerms[name]
|
||||
if !ok {
|
||||
return "{name}"
|
||||
return getPhrasePlaceholder()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
@ -122,7 +125,7 @@ func GetGlobalPermPhrase(name string) string {
|
|||
func GetLocalPermPhrase(name string) string {
|
||||
res, ok := currentLangPack.Load().(*LanguagePack).LocalPerms[name]
|
||||
if !ok {
|
||||
return "{name}"
|
||||
return getPhrasePlaceholder()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
@ -130,7 +133,7 @@ func GetLocalPermPhrase(name string) string {
|
|||
func GetSettingLabel(name string) string {
|
||||
res, ok := currentLangPack.Load().(*LanguagePack).SettingLabels[name]
|
||||
if !ok {
|
||||
return "{name}"
|
||||
return getPhrasePlaceholder()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
@ -146,11 +149,32 @@ func GetAllPermPresets() map[string]string {
|
|||
func GetAccountPhrase(name string) string {
|
||||
res, ok := currentLangPack.Load().(*LanguagePack).Accounts[name]
|
||||
if !ok {
|
||||
return "{name}"
|
||||
return getPhrasePlaceholder()
|
||||
}
|
||||
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?
|
||||
// TODO: Implement these
|
||||
func AddPhrase() {
|
||||
|
|
|
@ -112,9 +112,9 @@ func (reply *Reply) Delete() error {
|
|||
}
|
||||
// TODO: Move this bit to *Topic
|
||||
_, err = replyStmts.removeRepliesFromTopic.Exec(1, reply.ParentID)
|
||||
tcache, ok := Topics.(TopicCache)
|
||||
if ok {
|
||||
tcache.CacheRemove(reply.ParentID)
|
||||
tcache := Topics.GetCache()
|
||||
if tcache != nil {
|
||||
tcache.Remove(reply.ParentID)
|
||||
}
|
||||
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.ThemeName].Sidebars == "right" {
|
||||
if Themes[headerVars.Theme.Name].Sidebars == "right" {
|
||||
if len(Docks.RightSidebar) != 0 {
|
||||
var sbody string
|
||||
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) {
|
||||
if !Fstore.Exists(fid) {
|
||||
if !Forums.Exists(fid) {
|
||||
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 {
|
||||
// TODO: Refactor this
|
||||
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 {
|
||||
return headerVars, rerr
|
||||
}
|
||||
if !Fstore.Exists(fid) {
|
||||
if !Forums.Exists(fid) {
|
||||
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 {
|
||||
// TODO: Refactor this
|
||||
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)
|
||||
}
|
||||
//log.Printf("user.Perms: %+v\n", user.Perms)
|
||||
//log.Printf("fperms: %+v\n", fperms)
|
||||
cascadeForumPerms(fperms, user)
|
||||
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
|
||||
// TODO: Do a panel specific theme?
|
||||
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")
|
||||
if err == nil {
|
||||
cookie := html.EscapeString(cookie.Value)
|
||||
theme, ok := Themes[cookie]
|
||||
inTheme, ok := Themes[html.EscapeString(cookie.Value)]
|
||||
if ok && !theme.HideFromThemes {
|
||||
themeName = cookie
|
||||
theme = inTheme
|
||||
}
|
||||
}
|
||||
if theme.Name == "" {
|
||||
theme = Themes[DefaultThemeBox.Load().(string)]
|
||||
}
|
||||
|
||||
headerVars = &HeaderVars{
|
||||
Site: Site,
|
||||
Settings: SettingBox.Load().(SettingMap),
|
||||
Themes: Themes,
|
||||
ThemeName: themeName,
|
||||
Theme: theme,
|
||||
}
|
||||
// TODO: We should probably initialise headerVars.ExtData
|
||||
|
||||
headerVars.Stylesheets = append(headerVars.Stylesheets, headerVars.ThemeName+"/panel.css")
|
||||
if len(Themes[headerVars.ThemeName].Resources) > 0 {
|
||||
rlist := Themes[headerVars.ThemeName].Resources
|
||||
headerVars.Stylesheets = append(headerVars.Stylesheets, theme.Name+"/panel.css")
|
||||
if len(theme.Resources) > 0 {
|
||||
rlist := theme.Resources
|
||||
for _, resource := range rlist {
|
||||
if resource.Location == "global" || resource.Location == "panel" {
|
||||
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.Groups = Gstore.GlobalCount()
|
||||
stats.Forums = Fstore.GlobalCount() // TODO: Stop it from showing the blanked forums
|
||||
stats.Groups = Groups.GlobalCount()
|
||||
stats.Forums = Forums.GlobalCount() // TODO: Stop it from showing the blanked forums
|
||||
stats.Settings = len(headerVars.Settings)
|
||||
stats.WordFilters = len(WordFilterBox.Load().(WordFilterMap))
|
||||
stats.Themes = len(Themes)
|
||||
|
@ -170,12 +169,17 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerV
|
|||
|
||||
pusher, ok := w.(http.Pusher)
|
||||
if ok {
|
||||
pusher.Push("/static/"+headerVars.ThemeName+"/main.css", nil)
|
||||
pusher.Push("/static/"+headerVars.ThemeName+"/panel.css", nil)
|
||||
pusher.Push("/static/"+theme.Name+"/main.css", nil)
|
||||
pusher.Push("/static/"+theme.Name+"/panel.css", nil)
|
||||
pusher.Push("/static/global.js", nil)
|
||||
pusher.Push("/static/jquery-3.1.1.min.js", nil)
|
||||
// TODO: Push the theme CSS files
|
||||
// TODO: Push the theme scripts
|
||||
// TODO: Test these
|
||||
for _, sheet := range headerVars.Stylesheets {
|
||||
pusher.Push("/static/"+sheet, nil)
|
||||
}
|
||||
for _, script := range headerVars.Scripts {
|
||||
pusher.Push("/static/"+script, nil)
|
||||
}
|
||||
// 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?
|
||||
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")
|
||||
if err == nil {
|
||||
cookie := html.EscapeString(cookie.Value)
|
||||
theme, ok := Themes[cookie]
|
||||
inTheme, ok := Themes[html.EscapeString(cookie.Value)]
|
||||
if ok && !theme.HideFromThemes {
|
||||
themeName = cookie
|
||||
theme = inTheme
|
||||
}
|
||||
}
|
||||
if theme.Name == "" {
|
||||
theme = Themes[DefaultThemeBox.Load().(string)]
|
||||
}
|
||||
|
||||
headerVars = &HeaderVars{
|
||||
Site: Site,
|
||||
Settings: SettingBox.Load().(SettingMap),
|
||||
Themes: Themes,
|
||||
ThemeName: themeName,
|
||||
Theme: theme,
|
||||
}
|
||||
|
||||
if user.IsBanned {
|
||||
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 {
|
||||
rlist := Themes[headerVars.ThemeName].Resources
|
||||
if len(theme.Resources) > 0 {
|
||||
rlist := theme.Resources
|
||||
for _, resource := range rlist {
|
||||
if resource.Location == "global" || resource.Location == "frontend" {
|
||||
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)
|
||||
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/jquery-3.1.1.min.js", nil)
|
||||
// TODO: Push the theme CSS files
|
||||
// TODO: Push the theme scripts
|
||||
// TODO: Test these
|
||||
for _, sheet := range headerVars.Stylesheets {
|
||||
pusher.Push("/static/"+sheet, nil)
|
||||
}
|
||||
for _, script := range headerVars.Scripts {
|
||||
pusher.Push("/static/"+script, nil)
|
||||
}
|
||||
// TODO: Push avatars?
|
||||
}
|
||||
|
||||
|
@ -344,3 +355,7 @@ func NoSessionMismatch(w http.ResponseWriter, r *http.Request, user User) RouteE
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ReqIsJson(r *http.Request) bool {
|
||||
return r.Header.Get("Content-type") == "application/json"
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package common
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
@ -17,6 +18,7 @@ type SettingMap map[string]interface{}
|
|||
type SettingStore interface {
|
||||
ParseSetting(sname string, scontent string, stype string, sconstraint string) string
|
||||
BypassGet(name string) (*Setting, error)
|
||||
BypassGetAll(name string) ([]*Setting, error)
|
||||
}
|
||||
|
||||
type OptionLabel struct {
|
||||
|
@ -35,6 +37,7 @@ type Setting struct {
|
|||
type SettingStmts struct {
|
||||
getAll *sql.Stmt
|
||||
get *sql.Stmt
|
||||
update *sql.Stmt
|
||||
}
|
||||
|
||||
var settingStmts SettingStmts
|
||||
|
@ -45,80 +48,83 @@ func init() {
|
|||
settingStmts = SettingStmts{
|
||||
getAll: acc.Select("settings").Columns("name, content, type, constraints").Prepare(),
|
||||
get: acc.Select("settings").Columns("content, type, constraints").Where("name = ?").Prepare(),
|
||||
update: acc.Update("settings").Set("content = ?").Where("name = ?").Prepare(),
|
||||
}
|
||||
return acc.FirstError()
|
||||
})
|
||||
}
|
||||
|
||||
func LoadSettings() error {
|
||||
rows, err := settingStmts.getAll.Query()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var sBox = SettingMap(make(map[string]interface{}))
|
||||
var sname, scontent, stype, sconstraints string
|
||||
for rows.Next() {
|
||||
err = rows.Scan(&sname, &scontent, &stype, &sconstraints)
|
||||
settings, err := sBox.BypassGetAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errmsg := sBox.ParseSetting(sname, scontent, stype, sconstraints)
|
||||
if errmsg != "" {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = rows.Err()
|
||||
|
||||
for _, setting := range settings {
|
||||
err = sBox.ParseSetting(setting.Name, setting.Content, setting.Type, setting.Constraint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
SettingBox.Store(sBox)
|
||||
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.
|
||||
func (sBox SettingMap) ParseSetting(sname string, scontent string, stype string, constraint string) string {
|
||||
var err error
|
||||
func (sBox SettingMap) ParseSetting(sname string, scontent string, stype string, constraint string) (err error) {
|
||||
var ssBox = map[string]interface{}(sBox)
|
||||
if stype == "bool" {
|
||||
switch stype {
|
||||
case "bool":
|
||||
ssBox[sname] = (scontent == "1")
|
||||
} else if stype == "int" {
|
||||
case "int":
|
||||
ssBox[sname], err = strconv.Atoi(scontent)
|
||||
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)
|
||||
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, "-")
|
||||
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])
|
||||
con2, err2 := strconv.Atoi(cons[1])
|
||||
if err != nil || err2 != nil {
|
||||
return "Invalid contraint! The constraint field wasn't an integer!"
|
||||
return ErrBadConstraintNotInteger
|
||||
}
|
||||
|
||||
value, err := strconv.Atoi(scontent)
|
||||
if err != nil {
|
||||
return "Only integers are allowed in this setting x.x\nType mismatch in " + sname
|
||||
return ErrSettingNotInteger
|
||||
}
|
||||
|
||||
if value < con1 || value > con2 {
|
||||
return "Only integers between a certain range are allowed in this setting"
|
||||
return ErrBadSettingRange
|
||||
}
|
||||
ssBox[sname] = value
|
||||
} else {
|
||||
default:
|
||||
ssBox[sname] = scontent
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
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 {
|
||||
if !Fstore.Exists(Config.DefaultForum) {
|
||||
if !Forums.Exists(Config.DefaultForum) {
|
||||
return errors.New("Invalid default forum")
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -67,7 +67,7 @@ func HandleServerSync() error {
|
|||
|
||||
if lastUpdate.After(lastSync) {
|
||||
// TODO: A more granular sync
|
||||
err = Fstore.LoadForums()
|
||||
err = Forums.LoadForums()
|
||||
if err != nil {
|
||||
log.Print("Unable to reload the forums")
|
||||
return err
|
||||
|
|
|
@ -96,7 +96,7 @@ func compileTemplates() error {
|
|||
Site: Site,
|
||||
Settings: SettingBox.Load().(SettingMap),
|
||||
Themes: Themes,
|
||||
ThemeName: DefaultThemeBox.Load().(string),
|
||||
Theme: Themes[DefaultThemeBox.Load().(string)],
|
||||
NoticeList: []string{"test"},
|
||||
Stylesheets: []string{"panel"},
|
||||
Scripts: []string{"whatever"},
|
||||
|
@ -132,7 +132,7 @@ func compileTemplates() error {
|
|||
|
||||
// TODO: Use a dummy forum list to avoid o(n) problems
|
||||
var forumList []Forum
|
||||
forums, err := Fstore.GetAll()
|
||||
forums, err := Forums.GetAll()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ type CTemplateSet struct {
|
|||
FragOut string
|
||||
varList map[string]VarItem
|
||||
localVars map[string]map[string]VarItemReflect
|
||||
hasDispInt bool
|
||||
localDispStructIndex int
|
||||
stats map[string]int
|
||||
pVarList string
|
||||
pVarPosition int
|
||||
|
@ -102,6 +104,8 @@ func (c *CTemplateSet) Compile(name string, dir string, expects string, expectsI
|
|||
}
|
||||
|
||||
c.varList = varList
|
||||
c.hasDispInt = false
|
||||
c.localDispStructIndex = 0
|
||||
//c.pVarList = ""
|
||||
//c.pVarPosition = 0
|
||||
c.stats = make(map[string]int)
|
||||
|
@ -187,7 +191,6 @@ w.Write([]byte(`, " + ", -1)
|
|||
|
||||
c.log("Output!")
|
||||
c.log(fout)
|
||||
//log.Fatal("remove the log.Fatal line")
|
||||
return fout, nil
|
||||
}
|
||||
|
||||
|
@ -334,10 +337,8 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
|
|||
varbit += ".(" + cur.Type().Name() + ")"
|
||||
}
|
||||
|
||||
for _, id := range n.Ident {
|
||||
c.log("Data Kind:", cur.Kind().String())
|
||||
c.log("Field Bit:", id)
|
||||
|
||||
// ! Might not work so well for non-struct pointers
|
||||
skipPointers := func(cur reflect.Value, id string) reflect.Value {
|
||||
if cur.Kind() == reflect.Ptr {
|
||||
c.log("Looping over pointer")
|
||||
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("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 c.debug {
|
||||
fmt.Println("Debug Data:")
|
||||
fmt.Println("Holdreflect:", holdreflect)
|
||||
fmt.Println("Holdreflect.Kind():", holdreflect.Kind())
|
||||
c.error("Debug Data:")
|
||||
c.error("Holdreflect:", holdreflect)
|
||||
c.error("Holdreflect.Kind():", holdreflect.Kind())
|
||||
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?")
|
||||
} 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 {
|
||||
cur = cur.Elem()
|
||||
// TODO: Surely, there's a better way of detecting this?
|
||||
/*if cur.Kind() == reflect.String && cur.Type().Name() != "string" {
|
||||
varbit = "string(" + varbit + "." + id + ")"*/
|
||||
//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() + ")"
|
||||
dispStr = "Int"
|
||||
if !c.hasDispInt {
|
||||
newVarByte = ":"
|
||||
c.hasDispInt = true
|
||||
}
|
||||
}
|
||||
// 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 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if cur.Kind() != reflect.Interface {
|
||||
cur = cur.FieldByName(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 {
|
||||
if strings.HasPrefix(out, varItem.Destination) {
|
||||
|
@ -389,20 +437,21 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
|
|||
return out
|
||||
case *parse.DotNode:
|
||||
c.log("Dot Node:", node.String())
|
||||
return c.compileVarsub(varholder, holdreflect)
|
||||
return c.compileVarsub(varholder, holdreflect, "")
|
||||
case *parse.NilNode:
|
||||
panic("Nil is not a command x.x")
|
||||
case *parse.VariableNode:
|
||||
c.log("Variable Node:", n.String())
|
||||
c.log(n.Ident)
|
||||
varname, reflectVal := c.compileIfVarsub(n.String(), varholder, templateName, holdreflect)
|
||||
return c.compileVarsub(varname, reflectVal)
|
||||
return c.compileVarsub(varname, reflectVal, "")
|
||||
case *parse.StringNode:
|
||||
return n.Quoted
|
||||
case *parse.IdentifierNode:
|
||||
c.log("Identifier Node:", node)
|
||||
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:
|
||||
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) {
|
||||
c.log("in compileIdentSwitch")
|
||||
//var outbuf map[int]string
|
||||
ArgLoop:
|
||||
for pos := 0; pos < len(node.Args); pos++ {
|
||||
id := node.Args[pos]
|
||||
|
@ -728,7 +776,7 @@ func (c *CTemplateSet) compileBoolsub(varname string, varholder string, template
|
|||
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")
|
||||
for _, varItem := range c.varList {
|
||||
if strings.HasPrefix(varname, varItem.Destination) {
|
||||
|
@ -747,29 +795,33 @@ func (c *CTemplateSet) compileVarsub(varname string, val reflect.Value) string {
|
|||
val = val.Elem()
|
||||
}
|
||||
|
||||
c.log("varname: ", varname)
|
||||
c.log("assLines: ", assLines)
|
||||
switch val.Kind() {
|
||||
case reflect.Int:
|
||||
c.importMap["strconv"] = "strconv"
|
||||
return "w.Write([]byte(strconv.Itoa(" + varname + ")))\n"
|
||||
out = "w.Write([]byte(strconv.Itoa(" + varname + ")))\n"
|
||||
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:
|
||||
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:
|
||||
c.importMap["strconv"] = "strconv"
|
||||
return "w.Write([]byte(strconv.FormatInt(" + varname + ", 10)))"
|
||||
out = "w.Write([]byte(strconv.FormatInt(" + varname + ", 10)))"
|
||||
default:
|
||||
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 Kind:", val.Kind())
|
||||
fmt.Println("Unknown Type:", val.Type().Name())
|
||||
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) {
|
||||
|
@ -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) {
|
||||
panic("Uh oh! Something went wrong!")
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import (
|
|||
"../query_gen/lib"
|
||||
)
|
||||
|
||||
type ThemeList map[string]Theme
|
||||
type ThemeList map[string]Theme // ? Use pointers instead?
|
||||
|
||||
var Themes ThemeList = make(map[string]Theme)
|
||||
var DefaultThemeBox atomic.Value
|
||||
|
@ -47,6 +47,7 @@ type Theme struct {
|
|||
Tag string
|
||||
URL string
|
||||
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
|
||||
Settings map[string]ThemeSetting
|
||||
Templates []TemplateMapping
|
||||
|
|
|
@ -149,9 +149,9 @@ func init() {
|
|||
// Flush the topic out of the cache
|
||||
// ? - We do a CacheRemove() here instead of mutating the pointer to avoid creating a race condition
|
||||
func (topic *Topic) cacheRemove() {
|
||||
tcache, ok := Topics.(TopicCache)
|
||||
if ok {
|
||||
tcache.CacheRemove(topic.ID)
|
||||
tcache := Topics.(TopicCache)
|
||||
if tcache != nil {
|
||||
tcache.Remove(topic.ID)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,7 @@ func (topic *Topic) Delete() error {
|
|||
return err
|
||||
}
|
||||
|
||||
err = Fstore.RemoveTopic(topic.ParentID)
|
||||
err = Forums.RemoveTopic(topic.ParentID)
|
||||
if err != nil && err != ErrNoRows {
|
||||
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
|
||||
func GetTopicUser(tid int) (TopicUser, error) {
|
||||
tcache, tok := Topics.(TopicCache)
|
||||
ucache, uok := Users.(UserCache)
|
||||
if tok && uok {
|
||||
topic, err := tcache.CacheGet(tid)
|
||||
tcache := Topics.GetCache()
|
||||
ucache := Users.GetCache()
|
||||
if tcache != nil && ucache != nil {
|
||||
topic, err := tcache.Get(tid)
|
||||
if err == nil {
|
||||
user, err := Users.Get(topic.CreatedBy)
|
||||
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)
|
||||
tu.Link = BuildTopicURL(NameToSlug(tu.Title), tu.ID)
|
||||
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}
|
||||
//log.Printf("theTopic: %+v\n", theTopic)
|
||||
_ = tcache.CacheAdd(&theTopic)
|
||||
_ = tcache.Add(&theTopic)
|
||||
}
|
||||
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"
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"../query_gen/lib"
|
||||
)
|
||||
|
@ -31,44 +29,33 @@ type TopicStore interface {
|
|||
Exists(id int) bool
|
||||
Create(fid int, topicName string, content string, uid int, ipaddress string) (tid int, err error)
|
||||
AddLastTopic(item *Topic, fid int) error // unimplemented
|
||||
Reload(id int) error // Too much SQL logic to move into TopicCache
|
||||
// TODO: Implement these two methods
|
||||
//Replies(tid int) ([]*Reply, error)
|
||||
//RepliesRange(tid int, lower int, higher int) ([]*Reply, error)
|
||||
GlobalCount() int
|
||||
|
||||
SetCache(cache TopicCache)
|
||||
GetCache() TopicCache
|
||||
}
|
||||
|
||||
type TopicCache interface {
|
||||
CacheGet(id int) (*Topic, error)
|
||||
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 DefaultTopicStore struct {
|
||||
cache TopicCache
|
||||
|
||||
type MemoryTopicStore struct {
|
||||
items map[int]*Topic
|
||||
length int64 // sync/atomic only lets us operate on int32s and int64s
|
||||
capacity int
|
||||
get *sql.Stmt
|
||||
exists *sql.Stmt
|
||||
topicCount *sql.Stmt
|
||||
create *sql.Stmt
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// NewMemoryTopicStore gives you a new instance of MemoryTopicStore
|
||||
func NewMemoryTopicStore(capacity int) (*MemoryTopicStore, error) {
|
||||
// NewDefaultTopicStore gives you a new instance of DefaultTopicStore
|
||||
func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
return &MemoryTopicStore{
|
||||
items: make(map[int]*Topic),
|
||||
capacity: capacity,
|
||||
if cache == nil {
|
||||
cache = NewNullTopicCache()
|
||||
}
|
||||
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(),
|
||||
exists: acc.Select("topics").Columns("tid").Where("tid = ?").Prepare(),
|
||||
topicCount: acc.Count("topics").Prepare(),
|
||||
|
@ -76,84 +63,63 @@ func NewMemoryTopicStore(capacity int) (*MemoryTopicStore, error) {
|
|||
}, acc.FirstError()
|
||||
}
|
||||
|
||||
func (mts *MemoryTopicStore) CacheGet(id int) (*Topic, error) {
|
||||
mts.RLock()
|
||||
item, ok := mts.items[id]
|
||||
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 {
|
||||
func (mts *DefaultTopicStore) DirtyGet(id int) *Topic {
|
||||
topic, err := mts.cache.Get(id)
|
||||
if err == nil {
|
||||
return topic
|
||||
}
|
||||
|
||||
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 {
|
||||
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
||||
_ = mts.CacheAdd(topic)
|
||||
_ = mts.cache.Add(topic)
|
||||
return topic
|
||||
}
|
||||
return BlankTopic()
|
||||
}
|
||||
|
||||
func (mts *MemoryTopicStore) Get(id int) (*Topic, error) {
|
||||
mts.RLock()
|
||||
topic, ok := mts.items[id]
|
||||
mts.RUnlock()
|
||||
if ok {
|
||||
// TODO: Log weird cache errors?
|
||||
func (mts *DefaultTopicStore) Get(id int) (topic *Topic, err error) {
|
||||
topic, err = mts.cache.Get(id)
|
||||
if err == nil {
|
||||
return topic, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
||||
_ = mts.CacheAdd(topic)
|
||||
_ = mts.cache.Add(topic)
|
||||
}
|
||||
return topic, err
|
||||
}
|
||||
|
||||
// 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}
|
||||
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)
|
||||
return topic, err
|
||||
}
|
||||
|
||||
func (mts *MemoryTopicStore) Reload(id int) error {
|
||||
func (mts *DefaultTopicStore) Reload(id int) error {
|
||||
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)
|
||||
if err == nil {
|
||||
topic.Link = BuildTopicURL(NameToSlug(topic.Title), id)
|
||||
_ = mts.CacheSet(topic)
|
||||
_ = mts.cache.Set(topic)
|
||||
} else {
|
||||
_ = mts.CacheRemove(id)
|
||||
_ = mts.cache.Remove(id)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (mts *MemoryTopicStore) Exists(id int) bool {
|
||||
func (mts *DefaultTopicStore) Exists(id int) bool {
|
||||
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)
|
||||
if topicName == "" {
|
||||
return 0, ErrNoBody
|
||||
|
@ -177,91 +143,17 @@ func (mts *MemoryTopicStore) Create(fid int, topicName string, content string, u
|
|||
return 0, err
|
||||
}
|
||||
|
||||
return int(lastID), Fstore.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
|
||||
return int(lastID), Forums.AddTopic(int(lastID), uid, fid)
|
||||
}
|
||||
|
||||
// ? - 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...
|
||||
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
|
||||
func (mts *MemoryTopicStore) GlobalCount() int {
|
||||
var tcount int
|
||||
func (mts *DefaultTopicStore) GlobalCount() (tcount int) {
|
||||
err := mts.topicCount.QueryRow().Scan(&tcount)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
|
@ -269,91 +161,15 @@ func (mts *MemoryTopicStore) GlobalCount() int {
|
|||
return tcount
|
||||
}
|
||||
|
||||
type SQLTopicStore struct {
|
||||
get *sql.Stmt
|
||||
exists *sql.Stmt
|
||||
topicCount *sql.Stmt
|
||||
create *sql.Stmt
|
||||
func (mts *DefaultTopicStore) SetCache(cache TopicCache) {
|
||||
mts.cache = cache
|
||||
}
|
||||
|
||||
func NewSQLTopicStore() (*SQLTopicStore, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
return &SQLTopicStore{
|
||||
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(),
|
||||
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...
|
||||
// TODO: We're temporarily doing this so that you can do tcache != nil in getTopicUser. Refactor it.
|
||||
func (mts *DefaultTopicStore) GetCache() TopicCache {
|
||||
_, ok := mts.cache.(*NullTopicCache)
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
return mts.cache
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
//"log"
|
||||
//"fmt"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
@ -111,14 +109,15 @@ func (user *User) Init() {
|
|||
user.Avatar = strings.Replace(Config.Noavatar, "{id}", strconv.Itoa(user.ID), 1)
|
||||
}
|
||||
user.Link = BuildProfileURL(NameToSlug(user.Name), user.ID)
|
||||
user.Tag = Gstore.DirtyGet(user.Group).Tag
|
||||
user.Tag = Groups.DirtyGet(user.Group).Tag
|
||||
user.InitPerms()
|
||||
}
|
||||
|
||||
// TODO: Refactor this idiom into something shorter, maybe with a NullUserCache when one isn't set?
|
||||
func (user *User) CacheRemove() {
|
||||
ucache, ok := Users.(UserCache)
|
||||
if ok {
|
||||
ucache.CacheRemove(user.ID)
|
||||
ucache := Users.GetCache()
|
||||
if ucache != nil {
|
||||
ucache.Remove(user.ID)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -337,7 +336,7 @@ func (user *User) InitPerms() {
|
|||
user.Group = user.TempGroup
|
||||
}
|
||||
|
||||
group := Gstore.DirtyGet(user.Group)
|
||||
group := Groups.DirtyGet(user.Group)
|
||||
if user.IsSuperAdmin {
|
||||
user.Perms = AllPerms
|
||||
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"
|
||||
"log"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"../query_gen/lib"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
@ -25,43 +23,32 @@ type UserStore interface {
|
|||
BulkGetMap(ids []int) (map[int]*User, error)
|
||||
BypassGet(id int) (*User, 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
|
||||
Length() int
|
||||
SetCapacity(capacity int)
|
||||
GetCapacity() int
|
||||
GlobalCount() int
|
||||
|
||||
SetCache(cache UserCache)
|
||||
GetCache() UserCache
|
||||
}
|
||||
|
||||
type MemoryUserStore struct {
|
||||
items map[int]*User
|
||||
length int64
|
||||
capacity int
|
||||
type DefaultUserStore struct {
|
||||
cache UserCache
|
||||
|
||||
get *sql.Stmt
|
||||
exists *sql.Stmt
|
||||
register *sql.Stmt
|
||||
usernameExists *sql.Stmt
|
||||
userCount *sql.Stmt
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// NewMemoryUserStore gives you a new instance of MemoryUserStore
|
||||
func NewMemoryUserStore(capacity int) (*MemoryUserStore, error) {
|
||||
// NewDefaultUserStore gives you a new instance of DefaultUserStore
|
||||
func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
|
||||
acc := qgen.Builder.Accumulator()
|
||||
if cache == nil {
|
||||
cache = NewNullUserCache()
|
||||
}
|
||||
// TODO: Add an admin version of registerStmt with more flexibility?
|
||||
return &MemoryUserStore{
|
||||
items: make(map[int]*User),
|
||||
capacity: capacity,
|
||||
return &DefaultUserStore{
|
||||
cache: cache,
|
||||
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()"),
|
||||
|
@ -70,75 +57,43 @@ func NewMemoryUserStore(capacity int) (*MemoryUserStore, error) {
|
|||
}, acc.FirstError()
|
||||
}
|
||||
|
||||
func (mus *MemoryUserStore) CacheGet(id int) (*User, error) {
|
||||
mus.RLock()
|
||||
item, ok := mus.items[id]
|
||||
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 {
|
||||
func (mus *DefaultUserStore) DirtyGet(id int) *User {
|
||||
user, err := mus.cache.Get(id)
|
||||
if err == nil {
|
||||
return 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)
|
||||
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 {
|
||||
mus.CacheSet(user)
|
||||
mus.cache.Set(user)
|
||||
return user
|
||||
}
|
||||
return BlankUser()
|
||||
}
|
||||
|
||||
func (mus *MemoryUserStore) Get(id int) (*User, error) {
|
||||
mus.RLock()
|
||||
user, ok := mus.items[id]
|
||||
mus.RUnlock()
|
||||
if ok {
|
||||
// TODO: Log weird cache errors? Not just here but in every *Cache?
|
||||
func (mus *DefaultUserStore) Get(id int) (*User, error) {
|
||||
user, err := mus.cache.Get(id)
|
||||
if err == nil {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
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()
|
||||
if err == nil {
|
||||
mus.CacheSet(user)
|
||||
mus.cache.Set(user)
|
||||
}
|
||||
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: 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)
|
||||
list = make(map[int]*User)
|
||||
if idCount == 0 {
|
||||
|
@ -146,7 +101,7 @@ func (mus *MemoryUserStore) BulkGetMap(ids []int) (list map[int]*User, err error
|
|||
}
|
||||
|
||||
var stillHere []int
|
||||
sliceList := mus.bulkGet(ids)
|
||||
sliceList := mus.cache.BulkGet(ids)
|
||||
for i, sliceItem := range sliceList {
|
||||
if sliceItem != nil {
|
||||
list[sliceItem.ID] = sliceItem
|
||||
|
@ -184,7 +139,7 @@ func (mus *MemoryUserStore) BulkGetMap(ids []int) (list map[int]*User, err error
|
|||
}
|
||||
|
||||
user.Init()
|
||||
mus.CacheSet(user)
|
||||
mus.cache.Set(user)
|
||||
list[user.ID] = user
|
||||
}
|
||||
|
||||
|
@ -216,7 +171,7 @@ func (mus *MemoryUserStore) BulkGetMap(ids []int) (list map[int]*User, err error
|
|||
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}
|
||||
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
|
||||
}
|
||||
|
||||
func (mus *MemoryUserStore) Reload(id int) error {
|
||||
func (mus *DefaultUserStore) Reload(id int) error {
|
||||
user := &User{ID: id, Loggedin: true}
|
||||
err := mus.get.QueryRow(id).Scan(&user.Name, &user.Group, &user.IsSuperAdmin, &user.Session, &user.Email, &user.Avatar, &user.Message, &user.URLPrefix, &user.URLName, &user.Level, &user.Score, &user.LastIP, &user.TempGroup)
|
||||
if err != nil {
|
||||
mus.CacheRemove(id)
|
||||
mus.cache.Remove(id)
|
||||
return err
|
||||
}
|
||||
|
||||
user.Init()
|
||||
_ = mus.CacheSet(user)
|
||||
_ = mus.cache.Set(user)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mus *MemoryUserStore) Exists(id int) bool {
|
||||
func (mus *DefaultUserStore) Exists(id int) bool {
|
||||
err := mus.exists.QueryRow(id).Scan(&id)
|
||||
if err != nil && err != ErrNoRows {
|
||||
LogError(err)
|
||||
|
@ -245,210 +200,8 @@ func (mus *MemoryUserStore) Exists(id int) bool {
|
|||
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?
|
||||
func (mus *MemoryUserStore) 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) {
|
||||
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 {
|
||||
|
@ -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
|
||||
func (mus *SQLUserStore) GlobalCount() (ucount int) {
|
||||
func (mus *DefaultUserStore) GlobalCount() (ucount int) {
|
||||
err := mus.userCount.QueryRow().Scan(&ucount)
|
||||
if err != nil {
|
||||
LogError(err)
|
||||
|
@ -483,54 +236,15 @@ func (mus *SQLUserStore) GlobalCount() (ucount int) {
|
|||
return ucount
|
||||
}
|
||||
|
||||
// TODO: MockUserStore
|
||||
|
||||
// NullUserStore is here for tests because Go doesn't have short-circuiting
|
||||
type NullUserStore struct {
|
||||
func (mus *DefaultUserStore) SetCache(cache UserCache) {
|
||||
mus.cache = cache
|
||||
}
|
||||
|
||||
func (nus *NullUserStore) CacheGet(_ int) (*User, error) {
|
||||
return nil, ErrNoRows
|
||||
}
|
||||
|
||||
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
|
||||
// TODO: We're temporarily doing this so that you can do ucache != nil in getTopicUser. Refactor it.
|
||||
func (mus *DefaultUserStore) GetCache() UserCache {
|
||||
_, ok := mus.cache.(*NullUserCache)
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
return mus.cache
|
||||
}
|
||||
|
|
|
@ -33,26 +33,37 @@ func init() {
|
|||
}
|
||||
|
||||
func LoadWordFilters() error {
|
||||
rows, err := filterStmts.getWordFilters.Query()
|
||||
var wordFilters = WordFilterMap(make(map[int]WordFilter))
|
||||
filters, err := wordFilters.BypassGetAll()
|
||||
if err != nil {
|
||||
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()
|
||||
|
||||
var wordFilters = WordFilterMap(make(map[int]WordFilter))
|
||||
var wfid int
|
||||
var find string
|
||||
var replacement string
|
||||
|
||||
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 {
|
||||
return err
|
||||
return filters, err
|
||||
}
|
||||
wordFilters[wfid] = WordFilter{ID: wfid, Find: find, Replacement: replacement}
|
||||
filters = append(filters, filter)
|
||||
}
|
||||
WordFilterBox.Store(wordFilters)
|
||||
return rows.Err()
|
||||
return filters, rows.Err()
|
||||
}
|
||||
|
||||
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.")
|
||||
common.Gstore, err = common.NewMemoryGroupStore()
|
||||
common.Groups, err = common.NewMemoryGroupStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err2 := common.Gstore.LoadGroups()
|
||||
err2 := common.Groups.LoadGroups()
|
||||
if err2 != nil {
|
||||
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
|
||||
log.Print("Initialising the user and topic stores")
|
||||
|
||||
var ucache common.UserCache
|
||||
var tcache common.TopicCache
|
||||
if common.Config.CacheTopicUser == common.CACHE_STATIC {
|
||||
common.Users, err = common.NewMemoryUserStore(common.Config.UserCacheCapacity)
|
||||
common.Topics, err2 = common.NewMemoryTopicStore(common.Config.TopicCacheCapacity)
|
||||
} else {
|
||||
common.Users, err = common.NewSQLUserStore()
|
||||
common.Topics, err2 = common.NewSQLTopicStore()
|
||||
ucache = common.NewMemoryUserCache(common.Config.UserCacheCapacity)
|
||||
tcache = common.NewMemoryTopicCache(common.Config.TopicCacheCapacity)
|
||||
}
|
||||
|
||||
common.Users, err = common.NewDefaultUserStore(ucache)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err2 != nil {
|
||||
common.Topics, err = common.NewDefaultTopicStore(tcache)
|
||||
if err != nil {
|
||||
return err2
|
||||
}
|
||||
|
||||
log.Print("Loading the forums.")
|
||||
common.Fstore, err = common.NewMemoryForumStore()
|
||||
common.Forums, err = common.NewMemoryForumStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = common.Fstore.LoadForums()
|
||||
err = common.Forums.LoadForums()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("Loading the forum permissions.")
|
||||
common.Fpstore, err = common.NewMemoryForumPermsStore()
|
||||
common.FPStore, err = common.NewMemoryForumPermsStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = common.Fpstore.Init()
|
||||
err = common.FPStore.Init()
|
||||
if err != nil {
|
||||
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 MemberListJoinStmt *sql.Stmt
|
||||
var GetMemberStmt *sql.Stmt
|
||||
var GetGuildStmt *sql.Stmt
|
||||
var CreateGuildStmt *sql.Stmt
|
||||
var AttachForumStmt *sql.Stmt
|
||||
var UnattachForumStmt *sql.Stmt
|
||||
var AddMemberStmt *sql.Stmt
|
||||
|
@ -105,8 +103,8 @@ func PrebuildTmplList(user common.User, headerVars *common.HeaderVars) common.CT
|
|||
CreatedAt: "date",
|
||||
LastUpdateTime: "date",
|
||||
MainForumID: 1,
|
||||
MainForum: common.Fstore.DirtyGet(1),
|
||||
Forums: []*common.Forum{common.Fstore.DirtyGet(1)},
|
||||
MainForum: common.Forums.DirtyGet(1),
|
||||
Forums: []*common.Forum{common.Forums.DirtyGet(1)},
|
||||
},
|
||||
}
|
||||
listPage := ListPage{"Guild List", user, headerVars, guildList}
|
||||
|
@ -127,9 +125,9 @@ func CommonAreaWidgets(headerVars *common.HeaderVars) {
|
|||
return
|
||||
}
|
||||
|
||||
if common.Themes[headerVars.ThemeName].Sidebars == "left" {
|
||||
if common.Themes[headerVars.Theme.Name].Sidebars == "left" {
|
||||
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()))
|
||||
}
|
||||
}
|
||||
|
@ -151,9 +149,9 @@ func GuildWidgets(headerVars *common.HeaderVars, guildItem *Guild) (success bool
|
|||
return false
|
||||
}
|
||||
|
||||
if themes[headerVars.ThemeName].Sidebars == "left" {
|
||||
if themes[headerVars.Theme.Name].Sidebars == "left" {
|
||||
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()))
|
||||
} else {
|
||||
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}
|
||||
err = common.RunThemeTemplate(headerVars.ThemeName, "guilds_guild_list", pi, w)
|
||||
err = common.RunThemeTemplate(headerVars.Theme.Name, "guilds_guild_list", pi, w)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
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 {
|
||||
// SEO URLs...
|
||||
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)
|
||||
}
|
||||
|
||||
guildItem, err := GetGuild(guildID)
|
||||
guildItem, err := Gstore.Get(guildID)
|
||||
if err != nil {
|
||||
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
|
||||
fid, err := common.Fstore.Create(guildName, "", true, "")
|
||||
fid, err := common.Forums.Create(guildName, "", true, "")
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
res, err := CreateGuildStmt.Exec(guildName, guildDesc, guildActive, guildPrivacy, user.ID, fid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
lastID, err := res.LastInsertId()
|
||||
gid, err := Gstore.Create(guildName, guildDesc, guildActive, guildPrivacy, user.ID, fid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// Add the main backing forum to the forum list
|
||||
err = AttachForum(int(lastID), fid)
|
||||
err = AttachForum(gid, fid)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
_, err = AddMemberStmt.Exec(lastID, user.ID, 2)
|
||||
_, err = AddMemberStmt.Exec(gid, user.ID, 2)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
var guildItem = &Guild{ID: 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)
|
||||
guildItem, err := Gstore.Get(guildID)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
}
|
||||
err = common.RunThemeTemplate(headerVars.ThemeName, "guilds_member_list", pi, w)
|
||||
err = common.RunThemeTemplate(headerVars.Theme.Name, "guilds_member_list", pi, w)
|
||||
if err != nil {
|
||||
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
|
||||
func TopicCreatePreLoop(args ...interface{}) interface{} {
|
||||
var fid = args[2].(int)
|
||||
if common.Fstore.DirtyGet(fid).ParentType == "guild" {
|
||||
if common.Forums.DirtyGet(fid).ParentType == "guild" {
|
||||
var strictmode = args[5].(*bool)
|
||||
*strictmode = true
|
||||
}
|
||||
|
@ -452,14 +438,14 @@ func TopicCreatePreLoop(args ...interface{}) interface{} {
|
|||
func ForumCheck(args ...interface{}) (skip bool, rerr common.RouteError) {
|
||||
var r = args[1].(*http.Request)
|
||||
var fid = args[3].(*int)
|
||||
var forum = common.Fstore.DirtyGet(*fid)
|
||||
var forum = common.Forums.DirtyGet(*fid)
|
||||
|
||||
if forum.ParentType == "guild" {
|
||||
var err error
|
||||
var w = args[0].(http.ResponseWriter)
|
||||
guildItem, ok := r.Context().Value("guilds_current_group").(*Guild)
|
||||
if !ok {
|
||||
guildItem, err = GetGuild(forum.ParentID)
|
||||
guildItem, err = Gstore.Get(forum.ParentID)
|
||||
if err != nil {
|
||||
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
|
||||
type Stmts struct {
|
||||
getPassword *sql.Stmt
|
||||
getSettings *sql.Stmt
|
||||
isPluginActive *sql.Stmt
|
||||
getUsersOffset *sql.Stmt
|
||||
isThemeDefault *sql.Stmt
|
||||
|
@ -45,7 +44,6 @@ type Stmts struct {
|
|||
createWordFilter *sql.Stmt
|
||||
editReply *sql.Stmt
|
||||
editProfileReply *sql.Stmt
|
||||
updateSetting *sql.Stmt
|
||||
updatePlugin *sql.Stmt
|
||||
updatePluginInstall *sql.Stmt
|
||||
updateTheme *sql.Stmt
|
||||
|
@ -61,7 +59,6 @@ type Stmts struct {
|
|||
deleteActivityStreamMatch *sql.Stmt
|
||||
deleteWordFilter *sql.Stmt
|
||||
reportExists *sql.Stmt
|
||||
modlogCount *sql.Stmt
|
||||
notifyWatchers *sql.Stmt
|
||||
|
||||
getActivityFeedByWatcher *sql.Stmt
|
||||
|
@ -90,13 +87,6 @@ func _gen_mssql() (err error) {
|
|||
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.")
|
||||
stmts.isPluginActive, err = db.Prepare("SELECT [active] FROM [plugins] WHERE [uname] = ?1")
|
||||
if err != nil {
|
||||
|
@ -335,13 +325,6 @@ func _gen_mssql() (err error) {
|
|||
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.")
|
||||
stmts.updatePlugin, err = db.Prepare("UPDATE [plugins] SET [active] = ? WHERE [uname] = ?")
|
||||
if err != nil {
|
||||
|
@ -447,13 +430,6 @@ func _gen_mssql() (err error) {
|
|||
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.")
|
||||
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 {
|
||||
|
|
21
gen_mysql.go
21
gen_mysql.go
|
@ -12,7 +12,6 @@ import "./common"
|
|||
// nolint
|
||||
type Stmts struct {
|
||||
getPassword *sql.Stmt
|
||||
getSettings *sql.Stmt
|
||||
isPluginActive *sql.Stmt
|
||||
getUsersOffset *sql.Stmt
|
||||
isThemeDefault *sql.Stmt
|
||||
|
@ -47,7 +46,6 @@ type Stmts struct {
|
|||
createWordFilter *sql.Stmt
|
||||
editReply *sql.Stmt
|
||||
editProfileReply *sql.Stmt
|
||||
updateSetting *sql.Stmt
|
||||
updatePlugin *sql.Stmt
|
||||
updatePluginInstall *sql.Stmt
|
||||
updateTheme *sql.Stmt
|
||||
|
@ -63,7 +61,6 @@ type Stmts struct {
|
|||
deleteActivityStreamMatch *sql.Stmt
|
||||
deleteWordFilter *sql.Stmt
|
||||
reportExists *sql.Stmt
|
||||
modlogCount *sql.Stmt
|
||||
notifyWatchers *sql.Stmt
|
||||
|
||||
getActivityFeedByWatcher *sql.Stmt
|
||||
|
@ -91,12 +88,6 @@ func _gen_mysql() (err error) {
|
|||
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.")
|
||||
stmts.isPluginActive, err = db.Prepare("SELECT `active` FROM `plugins` WHERE `uname` = ?")
|
||||
if err != nil {
|
||||
|
@ -301,12 +292,6 @@ func _gen_mysql() (err error) {
|
|||
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.")
|
||||
stmts.updatePlugin, err = db.Prepare("UPDATE `plugins` SET `active` = ? WHERE `uname` = ?")
|
||||
if err != nil {
|
||||
|
@ -397,12 +382,6 @@ func _gen_mysql() (err error) {
|
|||
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.")
|
||||
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 {
|
||||
|
|
|
@ -11,7 +11,6 @@ import "./common"
|
|||
type Stmts struct {
|
||||
editReply *sql.Stmt
|
||||
editProfileReply *sql.Stmt
|
||||
updateSetting *sql.Stmt
|
||||
updatePlugin *sql.Stmt
|
||||
updatePluginInstall *sql.Stmt
|
||||
updateTheme *sql.Stmt
|
||||
|
@ -55,12 +54,6 @@ func _gen_pgsql() (err error) {
|
|||
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.")
|
||||
stmts.updatePlugin, err = db.Prepare("UPDATE `plugins` SET `active` = ? WHERE `uname` = ?")
|
||||
if err != nil {
|
||||
|
|
|
@ -246,7 +246,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
case "/panel/settings/":
|
||||
err = routePanelSettings(w,req,user)
|
||||
case "/panel/settings/edit/":
|
||||
err = routePanelSetting(w,req,user,extra_data)
|
||||
err = routePanelSettingEdit(w,req,user,extra_data)
|
||||
case "/panel/settings/edit/submit/":
|
||||
err = common.NoSessionMismatch(w,req,user)
|
||||
if err != nil {
|
||||
|
@ -254,7 +254,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = routePanelSettingEdit(w,req,user,extra_data)
|
||||
err = routePanelSettingEditSubmit(w,req,user,extra_data)
|
||||
case "/panel/settings/word-filters/":
|
||||
err = routePanelWordFilters(w,req,user)
|
||||
case "/panel/settings/word-filters/create/":
|
||||
|
|
|
@ -15,7 +15,6 @@ import (
|
|||
"./common"
|
||||
"./install/install"
|
||||
"./query_gen/lib"
|
||||
//"runtime/pprof"
|
||||
//"github.com/husobee/vestigo"
|
||||
)
|
||||
|
||||
|
@ -77,44 +76,7 @@ func gloinit() (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
common.Rstore, err = common.NewSQLReplyStore()
|
||||
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()
|
||||
err = afterDBInit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/*
|
||||
*
|
||||
* Gosora MSSQL Interface
|
||||
* Under heavy development
|
||||
* Copyright Azareal 2017 - 2018
|
||||
*
|
||||
*/
|
||||
|
@ -80,14 +79,9 @@ func (ins *MssqlInstaller) InitDatabase() (err error) {
|
|||
// TODO: Create the database, if it doesn't exist
|
||||
|
||||
// Ready the query builder
|
||||
qgen.Builder.SetConn(db)
|
||||
err = qgen.Builder.SetAdapter("mssql")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ins.db = db
|
||||
|
||||
return nil
|
||||
qgen.Builder.SetConn(db)
|
||||
return qgen.Builder.SetAdapter("mssql")
|
||||
}
|
||||
|
||||
func (ins *MssqlInstaller) TableDefs() (err error) {
|
||||
|
@ -126,7 +120,6 @@ func (ins *MssqlInstaller) TableDefs() (err error) {
|
|||
return err
|
||||
}
|
||||
}
|
||||
//fmt.Println("Finished creating the tables")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -150,8 +143,6 @@ func (ins *MssqlInstaller) InitialData() (err error) {
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
//fmt.Println("Finished inserting the database data")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -55,5 +55,36 @@
|
|||
"Accounts": {
|
||||
"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."
|
||||
},
|
||||
"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
|
||||
}
|
||||
|
||||
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
|
||||
func main() {
|
||||
// TODO: Recover from panics
|
||||
|
@ -90,44 +145,7 @@ func main() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
common.Rstore, err = common.NewSQLReplyStore()
|
||||
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()
|
||||
err = afterDBInit()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -55,12 +55,12 @@ func routeTopicCreate(w http.ResponseWriter, r *http.Request, user common.User,
|
|||
var forumList []common.Forum
|
||||
var canSee []int
|
||||
if user.IsSuperAdmin {
|
||||
canSee, err = common.Fstore.GetAllVisibleIDs()
|
||||
canSee, err = common.Forums.GetAllVisibleIDs()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
} else {
|
||||
group, err := common.Gstore.Get(user.Group)
|
||||
group, err := common.Groups.Get(user.Group)
|
||||
if err != nil {
|
||||
// TODO: Refactor this
|
||||
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?
|
||||
forum := common.Fstore.DirtyGet(ffid)
|
||||
forum := common.Forums.DirtyGet(ffid)
|
||||
if forum.Name != "" && forum.Active {
|
||||
fcopy := forum.Copy()
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
err = common.Fstore.UpdateLastTopic(tid, user.ID, topic.ParentID)
|
||||
err = common.Forums.UpdateLastTopic(tid, user.ID, topic.ParentID)
|
||||
if err != nil && err != ErrNoRows {
|
||||
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)
|
||||
}
|
||||
|
||||
err = common.Fstore.AddTopic(int(lastID), user.ID, fid)
|
||||
err = common.Forums.AddTopic(int(lastID), user.ID, fid)
|
||||
if err != nil && err != ErrNoRows {
|
||||
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"
|
||||
"net/http/httptest"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -40,29 +39,39 @@ func TestUserStore(t *testing.T) {
|
|||
}
|
||||
|
||||
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)
|
||||
common.Users.(common.UserCache).Flush()
|
||||
ucache.Flush()
|
||||
userStoreTest(t, 2)
|
||||
common.Users, err = common.NewSQLUserStore()
|
||||
common.Users, err = common.NewDefaultUserStore(nil)
|
||||
expectNilErr(t, err)
|
||||
userStoreTest(t, 3)
|
||||
}
|
||||
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
|
||||
if !hasCache {
|
||||
ucache = &common.NullUserStore{}
|
||||
isCacheLengthZero := func(ucache common.UserCache) bool {
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
recordMustExist(t, err, "Couldn't find UID #1")
|
||||
|
@ -79,24 +88,20 @@ func userStoreTest(t *testing.T, newUserID int) {
|
|||
_, err = common.Users.Get(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")
|
||||
|
||||
_, err = ucache.CacheGet(-1)
|
||||
_, err = ucache.Get(-1)
|
||||
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")
|
||||
user, err = ucache.CacheGet(1)
|
||||
user, err = ucache.Get(1)
|
||||
recordMustExist(t, err, "Couldn't find UID #1 in the cache")
|
||||
|
||||
if user.ID != 1 {
|
||||
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
||||
}
|
||||
if user.Name != "Admin" {
|
||||
t.Error("user.Name should be 'Admin', not '" + user.Name + "'")
|
||||
}
|
||||
expect(t, user.ID == 1, fmt.Sprintf("user.ID does not match the requested UID. Got '%d' instead.", user.ID))
|
||||
expect(t, user.Name == "Admin", fmt.Sprintf("user.Name should be 'Admin', not '%s'", user.Name))
|
||||
|
||||
_, err = ucache.CacheGet(newUserID)
|
||||
_, err = ucache.Get(newUserID)
|
||||
recordMustNotExist(t, err, "UID #%d shouldn't exist, even in the cache", newUserID)
|
||||
|
||||
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?
|
||||
var userList map[int]*common.User
|
||||
userList, _ = common.Users.BulkGetMap([]int{-1})
|
||||
if len(userList) > 0 {
|
||||
t.Error("There shouldn't be any results for UID #-1")
|
||||
}
|
||||
|
||||
if hasCache {
|
||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||
}
|
||||
expect(t, len(userList) == 0, fmt.Sprintf("The userList length should be 0, not %d", len(userList)))
|
||||
expect(t, isCacheLengthZero(ucache), fmt.Sprintf("User cache length should be 0, not %d", cacheLength(ucache)))
|
||||
|
||||
userList, _ = common.Users.BulkGetMap([]int{0})
|
||||
if len(userList) > 0 {
|
||||
t.Error("There shouldn't be any results for UID #0")
|
||||
}
|
||||
|
||||
if hasCache {
|
||||
expectIntToBeX(t, ucache.Length(), 0, "User cache length should be 0, not %d")
|
||||
}
|
||||
expect(t, len(userList) == 0, fmt.Sprintf("The userList length should be 0, not %d", len(userList)))
|
||||
expect(t, isCacheLengthZero(ucache), fmt.Sprintf("User cache length should be 0, not %d", cacheLength(ucache)))
|
||||
|
||||
userList, _ = common.Users.BulkGetMap([]int{1})
|
||||
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("userList", userList)
|
||||
}
|
||||
if user.ID != 1 {
|
||||
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
||||
}
|
||||
expect(t, user.ID == 1, fmt.Sprintf("user.ID does not match the requested UID. Got '%d' instead.", user.ID))
|
||||
|
||||
if hasCache {
|
||||
if ucache != nil {
|
||||
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")
|
||||
|
||||
if user.ID != 1 {
|
||||
t.Errorf("user.ID does not match the requested UID. Got '%d' instead.", user.ID)
|
||||
}
|
||||
expect(t, user.ID == 1, fmt.Sprintf("user.ID does not match the requested UID. Got '%d' instead.", user.ID))
|
||||
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(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")
|
||||
|
||||
var awaitingActivation = 5
|
||||
|
@ -166,9 +157,7 @@ func userStoreTest(t *testing.T, newUserID int) {
|
|||
|
||||
user, err = common.Users.Get(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.Name == "Sam", "The user should be named Sam")
|
||||
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")
|
||||
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")
|
||||
user, err = ucache.CacheGet(newUserID)
|
||||
user, err = ucache.Get(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))
|
||||
}
|
||||
|
@ -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")
|
||||
|
||||
// ? - 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")
|
||||
_, err = ucache.CacheGet(newUserID)
|
||||
_, err = ucache.Get(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)
|
||||
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")
|
||||
_, err = ucache.CacheGet(2)
|
||||
_, err = ucache.Get(2)
|
||||
recordMustNotExist(t, err, "UID #%d shouldn't be in the cache", newUserID)
|
||||
}
|
||||
|
||||
user, err = common.Users.Get(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.IsAdmin, "Sam should not be an admin")
|
||||
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)
|
||||
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")
|
||||
_, err = ucache.CacheGet(newUserID)
|
||||
_, err = ucache.Get(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)
|
||||
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")
|
||||
_, err = ucache.CacheGet(newUserID)
|
||||
_, err = ucache.Get(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
|
||||
common.Topics, err = common.NewMemoryTopicStore(common.Config.TopicCacheCapacity)
|
||||
tcache := common.NewMemoryTopicCache(common.Config.TopicCacheCapacity)
|
||||
common.Topics, err = common.NewDefaultTopicStore(tcache)
|
||||
expectNilErr(t, err)
|
||||
topicStoreTest(t)
|
||||
common.Topics, err = common.NewSQLTopicStore()
|
||||
common.Topics, err = common.NewDefaultTopicStore(nil)
|
||||
expectNilErr(t, err)
|
||||
topicStoreTest(t)
|
||||
}
|
||||
|
@ -526,25 +514,19 @@ func topicStoreTest(t *testing.T) {
|
|||
recordMustExist(t, err, "Couldn't find TID #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
|
||||
|
||||
ok := common.Topics.Exists(-1)
|
||||
if ok {
|
||||
t.Error("TID #-1 shouldn't exist")
|
||||
}
|
||||
expect(t, !ok, "TID #-1 shouldn't exist")
|
||||
|
||||
ok = common.Topics.Exists(0)
|
||||
if ok {
|
||||
t.Error("TID #0 shouldn't exist")
|
||||
}
|
||||
expect(t, !ok, "TID #0 shouldn't exist")
|
||||
|
||||
ok = common.Topics.Exists(1)
|
||||
if !ok {
|
||||
t.Error("TID #1 should exist")
|
||||
}
|
||||
expect(t, ok, "TID #1 should exist")
|
||||
|
||||
count := common.Topics.GlobalCount()
|
||||
if count <= 0 {
|
||||
|
@ -563,17 +545,17 @@ func TestForumStore(t *testing.T) {
|
|||
common.InitPlugins()
|
||||
}
|
||||
|
||||
_, err := common.Fstore.Get(-1)
|
||||
_, err := common.Forums.Get(-1)
|
||||
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")
|
||||
|
||||
forum, err := common.Fstore.Get(1)
|
||||
forum, err := common.Forums.Get(1)
|
||||
recordMustExist(t, err, "Couldn't find FID #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
|
||||
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"
|
||||
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")
|
||||
|
||||
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"
|
||||
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")
|
||||
ok = common.Fstore.Exists(0)
|
||||
ok = common.Forums.Exists(0)
|
||||
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")
|
||||
|
||||
// TODO: Test forum creation
|
||||
|
@ -621,43 +603,38 @@ func TestGroupStore(t *testing.T) {
|
|||
common.InitPlugins()
|
||||
}
|
||||
|
||||
_, err := common.Gstore.Get(-1)
|
||||
_, err := common.Groups.Get(-1)
|
||||
recordMustNotExist(t, err, "GID #-1 shouldn't exist")
|
||||
|
||||
// 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")
|
||||
|
||||
if group.ID != 0 {
|
||||
t.Errorf("group.ID doesn't not match the requested GID. Got '%d' instead.", group.ID)
|
||||
}
|
||||
expect(t, group.ID == 0, fmt.Sprintf("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))
|
||||
|
||||
group, err = common.Gstore.Get(1)
|
||||
group, err = common.Groups.Get(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 {
|
||||
t.Errorf("group.ID doesn't not match the requested GID. Got '%d' instead.'", group.ID)
|
||||
}
|
||||
|
||||
ok := common.Gstore.Exists(-1)
|
||||
ok := common.Groups.Exists(-1)
|
||||
expect(t, !ok, "GID #-1 shouldn't exist")
|
||||
|
||||
// 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")
|
||||
|
||||
ok = common.Gstore.Exists(1)
|
||||
ok = common.Groups.Exists(1)
|
||||
expect(t, ok, "GID #1 should exist")
|
||||
|
||||
var isAdmin = true
|
||||
var isMod = true
|
||||
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)
|
||||
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)
|
||||
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||
expect(t, group.IsAdmin, "This should be an admin group")
|
||||
|
@ -667,11 +644,11 @@ func TestGroupStore(t *testing.T) {
|
|||
isAdmin = false
|
||||
isMod = 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)
|
||||
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)
|
||||
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||
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)
|
||||
expectNilErr(t, err)
|
||||
|
||||
group, err = common.Gstore.Get(gid)
|
||||
group, err = common.Groups.Get(gid)
|
||||
expectNilErr(t, err)
|
||||
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||
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)
|
||||
expectNilErr(t, err)
|
||||
|
||||
group, err = common.Gstore.Get(gid)
|
||||
group, err = common.Groups.Get(gid)
|
||||
expectNilErr(t, err)
|
||||
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||
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)
|
||||
expectNilErr(t, err)
|
||||
|
||||
group, err = common.Gstore.Get(gid)
|
||||
group, err = common.Groups.Get(gid)
|
||||
expectNilErr(t, err)
|
||||
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||
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")
|
||||
|
||||
// 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)
|
||||
expect(t, group.ID == gid, "The group ID should match the requested ID")
|
||||
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: 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 {
|
||||
err := r.ParseForm()
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
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: 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 {
|
||||
// TODO: Move this to some sort of middleware
|
||||
var tids []int
|
||||
var isJs = false
|
||||
if r.Header.Get("Content-type") == "application/json" {
|
||||
if common.ReqIsJson(r) {
|
||||
if r.Body == nil {
|
||||
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)
|
||||
}
|
||||
|
||||
err = common.AddModLog("delete", tid, "topic", user.LastIP, user.ID)
|
||||
err = common.ModLogs.Create("delete", tid, "topic", user.LastIP, user.ID)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
err = common.AddModLog("stick", tid, "topic", user.LastIP, user.ID)
|
||||
err = common.ModLogs.Create("stick", tid, "topic", user.LastIP, user.ID)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
err = common.AddModLog("unstick", tid, "topic", user.LastIP, user.ID)
|
||||
err = common.ModLogs.Create("unstick", tid, "topic", user.LastIP, user.ID)
|
||||
if err != nil {
|
||||
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
|
||||
var tids []int
|
||||
var isJs = false
|
||||
if r.Header.Get("Content-type") == "application/json" {
|
||||
if common.ReqIsJson(r) {
|
||||
if r.Body == nil {
|
||||
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)
|
||||
}
|
||||
|
||||
err = common.AddModLog("lock", tid, "topic", user.LastIP, user.ID)
|
||||
err = common.ModLogs.Create("lock", tid, "topic", user.LastIP, user.ID)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
err = common.AddModLog("unlock", tid, "topic", user.LastIP, user.ID)
|
||||
err = common.ModLogs.Create("unlock", tid, "topic", user.LastIP, user.ID)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
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.RunPreRenderHook("pre_render_ips", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -610,9 +612,9 @@ func routeBanSubmit(w http.ResponseWriter, r *http.Request, user common.User) co
|
|||
if err != nil {
|
||||
return common.LocalError("The provided common.User ID is not a valid number.", w, r, user)
|
||||
}
|
||||
/*if uid == -2 {
|
||||
return common.LocalError("Stop trying to ban Merlin! Ban admin! Bad! No!",w,r,user)
|
||||
}*/
|
||||
if uid == -2 {
|
||||
return common.LocalError("Why don't you like Merlin?", w, r, user)
|
||||
}
|
||||
|
||||
targetUser, err := common.Users.Get(uid)
|
||||
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?
|
||||
if targetUser.IsSuperAdmin || targetUser.IsAdmin || targetUser.IsMod {
|
||||
if targetUser.IsMod {
|
||||
return common.LocalError("You may not ban another staff member.", w, r, user)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
err = common.AddModLog("ban", uid, "user", user.LastIP, user.ID)
|
||||
err = common.ModLogs.Create("ban", uid, "user", user.LastIP, user.ID)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
err = common.AddModLog("unban", uid, "user", user.LastIP, user.ID)
|
||||
err = common.ModLogs.Create("unban", uid, "user", user.LastIP, user.ID)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
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 {
|
||||
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 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
|
||||
memres, err := mem.VirtualMemory()
|
||||
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)
|
||||
|
||||
// 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
|
||||
if (totalCount - float64(int(totalCount))) > 0.85 {
|
||||
usedCount += 1.0 - (totalCount - float64(int(totalCount)))
|
||||
|
@ -45,7 +54,6 @@ func routePanel(w http.ResponseWriter, r *http.Request, user common.User) common
|
|||
} else {
|
||||
totstr = fmt.Sprintf("%.1f", totalCount)
|
||||
}
|
||||
//log.Print("post used_count",used_count)
|
||||
|
||||
if 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
|
||||
|
||||
ramperc := ((memres.Total - memres.Available) * 100) / memres.Total
|
||||
//log.Print("ramperc",ramperc)
|
||||
if ramperc < 50 {
|
||||
ramColour = "stat_green"
|
||||
} else if ramperc < 75 {
|
||||
ramColour = "stat_orange"
|
||||
} else {
|
||||
ramColour = "stat_red"
|
||||
}
|
||||
ramColour = lessThanSwitch(int(ramperc), 50, 75)
|
||||
}
|
||||
|
||||
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
|
||||
err = stmts.todaysPostCount.QueryRow().Scan(&postCount)
|
||||
if err != nil && err != ErrNoRows {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
var postInterval = "day"
|
||||
|
||||
var postColour string
|
||||
if postCount > 25 {
|
||||
postColour = "stat_green"
|
||||
} else if postCount > 5 {
|
||||
postColour = "stat_orange"
|
||||
} else {
|
||||
postColour = "stat_red"
|
||||
}
|
||||
var postColour = greaterThanSwitch(postCount, 5, 25)
|
||||
|
||||
var topicCount int
|
||||
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)
|
||||
}
|
||||
var topicInterval = "day"
|
||||
|
||||
var topicColour string
|
||||
if topicCount > 8 {
|
||||
topicColour = "stat_green"
|
||||
} else if topicCount > 0 {
|
||||
topicColour = "stat_orange"
|
||||
} else {
|
||||
topicColour = "stat_red"
|
||||
}
|
||||
var topicColour = greaterThanSwitch(topicCount, 0, 8)
|
||||
|
||||
var reportCount int
|
||||
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()
|
||||
totonline := uonline + gonline
|
||||
|
||||
var onlineColour string
|
||||
if totonline > 10 {
|
||||
onlineColour = "stat_green"
|
||||
} 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"
|
||||
}
|
||||
var onlineColour = greaterThanSwitch(totonline, 3, 10)
|
||||
var onlineGuestsColour = greaterThanSwitch(gonline, 1, 10)
|
||||
var onlineUsersColour = greaterThanSwitch(uonline, 1, 5)
|
||||
|
||||
totonline, totunit := common.ConvertFriendlyUnit(totonline)
|
||||
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-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.RunPreRenderHook("pre_render_panel_dashboard", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -192,7 +165,7 @@ func routePanelForums(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
|
||||
// TODO: Paginate this?
|
||||
var forumList []interface{}
|
||||
forums, err := common.Fstore.GetAll()
|
||||
forums, err := common.Forums.GetAll()
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
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.RunPreRenderHook("pre_render_panel_forums", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -217,6 +190,7 @@ func routePanelForums(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -235,7 +209,7 @@ func routePanelForumsCreateSubmit(w http.ResponseWriter, r *http.Request, user c
|
|||
factive := r.PostFormValue("forum-name")
|
||||
active := (factive == "on" || factive == "1")
|
||||
|
||||
_, err := common.Fstore.Create(fname, fdesc, active, fpreset)
|
||||
_, err := common.Forums.Create(fname, fdesc, active, fpreset)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
forum, err := common.Fstore.Get(fid)
|
||||
forum, err := common.Forums.Get(fid)
|
||||
if err == ErrNoRows {
|
||||
return common.LocalError("The forum you're trying to delete doesn't exist.", w, r, user)
|
||||
} else if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
// TODO: Make this a phrase
|
||||
confirmMsg := "Are you sure you want to delete the '" + forum.Name + "' forum?"
|
||||
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.RunPreRenderHook("pre_render_panel_delete_forum", w, r, &user, &pi) {
|
||||
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)
|
||||
}
|
||||
|
||||
err = common.Fstore.Delete(fid)
|
||||
err = common.Forums.Delete(fid)
|
||||
if err == ErrNoRows {
|
||||
return common.LocalError("The forum you're trying to delete doesn't exist.", w, r, user)
|
||||
} 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)
|
||||
}
|
||||
|
||||
forum, err := common.Fstore.Get(fid)
|
||||
forum, err := common.Forums.Get(fid)
|
||||
if err == ErrNoRows {
|
||||
return common.LocalError("The forum you're trying to edit doesn't exist.", w, r, user)
|
||||
} else if err != nil {
|
||||
|
@ -332,7 +307,7 @@ func routePanelForumsEdit(w http.ResponseWriter, r *http.Request, user common.Us
|
|||
forum.Preset = "custom"
|
||||
}
|
||||
|
||||
glist, err := common.Gstore.GetAll()
|
||||
glist, err := common.Groups.GetAll()
|
||||
if err != nil {
|
||||
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])})
|
||||
}
|
||||
|
||||
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.RunPreRenderHook("pre_render_panel_edit_forum", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -355,6 +330,7 @@ func routePanelForumsEdit(w http.ResponseWriter, r *http.Request, user common.Us
|
|||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
forum, err := common.Fstore.Get(fid)
|
||||
forum, err := common.Forums.Get(fid)
|
||||
if err == ErrNoRows {
|
||||
return common.LocalErrorJSQ("The forum you're trying to edit doesn't exist.", w, r, user, isJs)
|
||||
} 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)
|
||||
}
|
||||
|
||||
forum, err := common.Fstore.Get(fid)
|
||||
forum, err := common.Forums.Get(fid)
|
||||
if err == ErrNoRows {
|
||||
return common.LocalErrorJSQ("This forum doesn't exist", w, r, user, isJs)
|
||||
} else if err != nil {
|
||||
|
@ -454,46 +430,35 @@ func routePanelSettings(w http.ResponseWriter, r *http.Request, user common.User
|
|||
if !user.Perms.EditSettings {
|
||||
return common.NoPermissions(w, r, user)
|
||||
}
|
||||
|
||||
var settingList = make(map[string]interface{})
|
||||
rows, err := stmts.getSettings.Query()
|
||||
|
||||
settings, err := headerVars.Settings.BypassGetAll()
|
||||
if err != nil {
|
||||
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 sname, scontent, stype string
|
||||
for rows.Next() {
|
||||
err := rows.Scan(&sname, &scontent, &stype)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
if stype == "list" {
|
||||
llist := settingLabels[sname]
|
||||
for _, setting := range settings {
|
||||
if setting.Type == "list" {
|
||||
llist := settingLabels[setting.Name]
|
||||
labels := strings.Split(llist, ",")
|
||||
conv, err := strconv.Atoi(scontent)
|
||||
conv, err := strconv.Atoi(setting.Content)
|
||||
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]
|
||||
} else if stype == "bool" {
|
||||
if scontent == "1" {
|
||||
scontent = "Yes"
|
||||
setting.Content = labels[conv-1]
|
||||
} else if setting.Type == "bool" {
|
||||
if setting.Content == "1" {
|
||||
setting.Content = "Yes"
|
||||
} else {
|
||||
scontent = "No"
|
||||
setting.Content = "No"
|
||||
}
|
||||
}
|
||||
settingList[sname] = scontent
|
||||
}
|
||||
err = rows.Err()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
settingList[setting.Name] = setting.Content
|
||||
}
|
||||
|
||||
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.RunPreRenderHook("pre_render_panel_settings", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -506,7 +471,7 @@ func routePanelSettings(w http.ResponseWriter, r *http.Request, user common.User
|
|||
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)
|
||||
if ferr != nil {
|
||||
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.RunPreRenderHook("pre_render_panel_setting", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -552,7 +517,7 @@ func routePanelSetting(w http.ResponseWriter, r *http.Request, user common.User,
|
|||
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)
|
||||
if ferr != nil {
|
||||
return ferr
|
||||
|
@ -562,34 +527,14 @@ func routePanelSettingEdit(w http.ResponseWriter, r *http.Request, user common.U
|
|||
}
|
||||
|
||||
scontent := r.PostFormValue("setting-value")
|
||||
setting, err := headerLite.Settings.BypassGet(sname)
|
||||
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)
|
||||
err := headerLite.Settings.Update(sname, scontent)
|
||||
if err != nil {
|
||||
if common.SafeSettingError(err) {
|
||||
return common.LocalError(err.Error(), w, r, user)
|
||||
}
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
|
@ -604,7 +549,7 @@ func routePanelWordFilters(w http.ResponseWriter, r *http.Request, user common.U
|
|||
}
|
||||
|
||||
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.RunPreRenderHook("pre_render_panel_word_filters", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -665,7 +610,7 @@ func routePanelWordFiltersEdit(w http.ResponseWriter, r *http.Request, user comm
|
|||
|
||||
_ = 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.RunPreRenderHook("pre_render_panel_word_filters_edit", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -758,7 +703,7 @@ func routePanelPlugins(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
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.RunPreRenderHook("pre_render_panel_plugins", w, r, &user, &pi) {
|
||||
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)
|
||||
}
|
||||
|
||||
if common.Gstore.DirtyGet(puser.Group).Tag != "" {
|
||||
puser.Tag = common.Gstore.DirtyGet(puser.Group).Tag
|
||||
if common.Groups.DirtyGet(puser.Group).Tag != "" {
|
||||
puser.Tag = common.Groups.DirtyGet(puser.Group).Tag
|
||||
} else {
|
||||
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)
|
||||
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.RunPreRenderHook("pre_render_panel_users", w, r, &user, &pi) {
|
||||
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?
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
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.RunPreRenderHook("pre_render_panel_edit_user", w, r, &user, &pi) {
|
||||
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)
|
||||
}
|
||||
|
||||
group, err := common.Gstore.Get(newgroup)
|
||||
group, err := common.Groups.Get(newgroup)
|
||||
if err == ErrNoRows {
|
||||
return common.LocalError("The group you're trying to place this user in doesn't exist.", w, r, user)
|
||||
} 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)
|
||||
}
|
||||
|
||||
// TODO: Move this query into common
|
||||
_, err = stmts.updateUser.Exec(newname, newemail, newgroup, targetUser.ID)
|
||||
if err != nil {
|
||||
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 groupList []common.GroupAdmin
|
||||
groups, _ := common.Gstore.GetRange(offset, 0)
|
||||
groups, _ := common.Groups.GetRange(offset, 0)
|
||||
for _, group := range groups {
|
||||
if count == perPage {
|
||||
break
|
||||
|
@ -1149,6 +1095,7 @@ func routePanelGroups(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
var canEdit bool
|
||||
var canDelete = false
|
||||
|
||||
// TODO: Use a switch for this
|
||||
if group.IsAdmin {
|
||||
rank = "Admin"
|
||||
rankClass = "admin"
|
||||
|
@ -1173,7 +1120,7 @@ func routePanelGroups(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
//log.Printf("groupList: %+v\n", groupList)
|
||||
|
||||
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.RunPreRenderHook("pre_render_panel_groups", w, r, &user, &pi) {
|
||||
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)
|
||||
}
|
||||
|
||||
group, err := common.Gstore.Get(gid)
|
||||
group, err := common.Groups.Get(gid)
|
||||
if err == ErrNoRows {
|
||||
//log.Print("aaaaa monsters")
|
||||
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)
|
||||
|
||||
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.RunPreRenderHook("pre_render_panel_edit_group", w, r, &user, &pi) {
|
||||
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)
|
||||
}
|
||||
|
||||
group, err := common.Gstore.Get(gid)
|
||||
group, err := common.Groups.Get(gid)
|
||||
if err == ErrNoRows {
|
||||
//log.Print("aaaaa monsters")
|
||||
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{"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.RunPreRenderHook("pre_render_panel_edit_group_perms", w, r, &user, &pi) {
|
||||
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)
|
||||
}
|
||||
|
||||
group, err := common.Gstore.Get(gid)
|
||||
group, err := common.Groups.Get(gid)
|
||||
if err == ErrNoRows {
|
||||
//log.Print("aaaaa monsters")
|
||||
return common.NotFound(w, r)
|
||||
|
@ -1360,6 +1307,7 @@ func routePanelGroupsEditSubmit(w http.ResponseWriter, r *http.Request, user com
|
|||
rank := r.FormValue("group-type")
|
||||
|
||||
var originalRank string
|
||||
// TODO: Use a switch for this
|
||||
if group.IsAdmin {
|
||||
originalRank = "Admin"
|
||||
} else if group.IsMod {
|
||||
|
@ -1407,7 +1355,7 @@ func routePanelGroupsEditSubmit(w http.ResponseWriter, r *http.Request, user com
|
|||
if err != nil {
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
group, err := common.Gstore.Get(gid)
|
||||
group, err := common.Groups.Get(gid)
|
||||
if err == ErrNoRows {
|
||||
//log.Print("aaaaa monsters o.o")
|
||||
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 {
|
||||
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.RunPreRenderHook("pre_render_panel_themes", w, r, &user, &pi) {
|
||||
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()})
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
|
@ -1672,12 +1620,7 @@ func routePanelLogsMod(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
return ferr
|
||||
}
|
||||
|
||||
var logCount int
|
||||
err := stmts.modlogCount.QueryRow().Scan(&logCount)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
||||
logCount := common.ModLogs.GlobalCount()
|
||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||
perPage := 10
|
||||
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()
|
||||
|
||||
// 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 action, elementType, ipaddress, doneAt string
|
||||
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)
|
||||
}
|
||||
|
||||
actor, err := common.Users.Get(actorID)
|
||||
if err != nil {
|
||||
actor = &common.User{Name: "Unknown", Link: common.BuildProfileURL("unknown", 0)}
|
||||
}
|
||||
actor := handleUnknownUser(common.Users.Get(actorID))
|
||||
|
||||
switch action {
|
||||
case "lock":
|
||||
topic, err := common.Topics.Get(elementID)
|
||||
if err != nil {
|
||||
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>"
|
||||
topic := handleUnknownTopic(common.Topics.Get(elementID))
|
||||
action = fmt.Sprintf("<a href='%s'>%s</a> was locked by <a href='%s'>%s</a>", topic.Link, topic.Title, actor.Link, actor.Name)
|
||||
case "unlock":
|
||||
topic, err := common.Topics.Get(elementID)
|
||||
if err != nil {
|
||||
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>"
|
||||
topic := handleUnknownTopic(common.Topics.Get(elementID))
|
||||
action = fmt.Sprintf("<a href='%s'>%s</a> was reopened by <a href='%s'>%s</a>", topic.Link, topic.Title, actor.Link, actor.Name)
|
||||
case "stick":
|
||||
topic, err := common.Topics.Get(elementID)
|
||||
if err != nil {
|
||||
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>"
|
||||
topic := handleUnknownTopic(common.Topics.Get(elementID))
|
||||
action = fmt.Sprintf("<a href='%s'>%s</a> was pinned by <a href='%s'>%s</a>", topic.Link, topic.Title, actor.Link, actor.Name)
|
||||
case "unstick":
|
||||
topic, err := common.Topics.Get(elementID)
|
||||
if err != nil {
|
||||
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>"
|
||||
topic := handleUnknownTopic(common.Topics.Get(elementID))
|
||||
action = fmt.Sprintf("<a href='%s'>%s</a> was unpinned by <a href='%s'>%s</a>", topic.Link, topic.Title, actor.Link, actor.Name)
|
||||
case "delete":
|
||||
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 {
|
||||
reply := common.BlankReply()
|
||||
reply.ID = elementID
|
||||
topic, err := reply.Topic()
|
||||
if err != nil {
|
||||
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>"
|
||||
topic := handleUnknownTopic(reply.Topic())
|
||||
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)
|
||||
}
|
||||
case "ban":
|
||||
targetUser, err := common.Users.Get(elementID)
|
||||
if err != nil {
|
||||
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>"
|
||||
targetUser := handleUnknownUser(common.Users.Get(elementID))
|
||||
action = fmt.Sprintf("<a href='%s'>%s</a> was banned by <a href='%s'>%s</a>", targetUser.Link, targetUser.Name, actor.Link, actor.Name)
|
||||
case "unban":
|
||||
targetUser, err := common.Users.Get(elementID)
|
||||
if err != nil {
|
||||
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>"
|
||||
targetUser := handleUnknownUser(common.Users.Get(elementID))
|
||||
action = fmt.Sprintf("<a href='%s'>%s</a> was unbanned by <a href='%s'>%s</a>", targetUser.Link, targetUser.Name, actor.Link, actor.Name)
|
||||
case "activate":
|
||||
targetUser, err := common.Users.Get(elementID)
|
||||
if err != nil {
|
||||
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>"
|
||||
targetUser := handleUnknownUser(common.Users.Get(elementID))
|
||||
action = fmt.Sprintf("<a href='%s'>%s</a> was activated by <a href='%s'>%s</a>", targetUser.Link, targetUser.Name, actor.Link, actor.Name)
|
||||
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})
|
||||
}
|
||||
|
@ -1768,7 +1699,7 @@ func routePanelLogsMod(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
}
|
||||
|
||||
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.RunPreRenderHook("pre_render_panel_mod_log", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -1792,7 +1723,7 @@ func routePanelDebug(w http.ResponseWriter, r *http.Request, user common.User) c
|
|||
openConnCount := dbStats.OpenConnections
|
||||
// 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)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
|
|
|
@ -33,20 +33,21 @@ func initGuilds() (err error) {
|
|||
router.HandleFunc("/guild/create/submit/", guilds.RouteCreateGuildSubmit)
|
||||
router.HandleFunc("/guild/members/", guilds.RouteMemberList)
|
||||
|
||||
guilds.Gstore, err = guilds.NewSQLGuildStore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
acc := qgen.Builder.Accumulator()
|
||||
|
||||
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.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.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.UnattachForumStmt = acc.Update("forums").Set("parentID = 0, parentType = ''").Where("fid = ?").Prepare()
|
||||
|
@ -72,8 +73,6 @@ func deactivateGuilds() {
|
|||
_ = guilds.MemberListStmt.Close()
|
||||
_ = guilds.MemberListJoinStmt.Close()
|
||||
_ = guilds.GetMemberStmt.Close()
|
||||
_ = guilds.GetGuildStmt.Close()
|
||||
_ = guilds.CreateGuildStmt.Close()
|
||||
_ = guilds.AttachForumStmt.Close()
|
||||
_ = guilds.UnattachForumStmt.Close()
|
||||
_ = guilds.AddMemberStmt.Close()
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
/* WIP Under Construction */
|
||||
package qgen
|
||||
|
||||
var Install *installer
|
||||
|
@ -13,11 +12,21 @@ type DB_Install_Instruction struct {
|
|||
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
|
||||
// TODO: Re-implement the query generation, query builder and installer adapters as layers on-top of a query text adapter
|
||||
type installer struct {
|
||||
adapter Adapter
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -26,8 +35,7 @@ func (install *installer) SetAdapter(name string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
install.adapter = adap
|
||||
install.instructions = []DB_Install_Instruction{}
|
||||
install.SetAdapterInstance(adap)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -36,49 +44,54 @@ func (install *installer) SetAdapterInstance(adapter Adapter) {
|
|||
install.instructions = []DB_Install_Instruction{}
|
||||
}
|
||||
|
||||
func (install *installer) RegisterPlugin(plugin QueryPlugin) {
|
||||
install.plugins = append(install.plugins, plugin)
|
||||
func (install *installer) AddPlugins(plugins ...QueryPlugin) {
|
||||
install.plugins = append(install.plugins, plugins...)
|
||||
}
|
||||
|
||||
func (install *installer) CreateTable(table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) error {
|
||||
for _, plugin := range install.plugins {
|
||||
err := plugin.Hook("CreateTableStart", table, charset, collation, columns, keys)
|
||||
tableStruct := &DB_Install_Table{table, charset, collation, columns, keys}
|
||||
err := install.RunHook("CreateTableStart", tableStruct)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
res, err := install.adapter.CreateTable("_installer", table, charset, collation, columns, keys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, plugin := range install.plugins {
|
||||
err := plugin.Hook("CreateTableAfter", table, charset, collation, columns, keys, res)
|
||||
err = install.RunHook("CreateTableAfter", tableStruct)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
install.instructions = append(install.instructions, DB_Install_Instruction{table, res, "create-table"})
|
||||
install.tables = append(install.tables, tableStruct)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Let plugins manipulate the parameters like in CreateTable
|
||||
func (install *installer) SimpleInsert(table string, columns string, fields string) error {
|
||||
for _, plugin := range install.plugins {
|
||||
err := plugin.Hook("SimpleInsertStart", table, columns, fields)
|
||||
err := install.RunHook("SimpleInsertStart", table, columns, fields)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
res, err := install.adapter.SimpleInsert("_installer", table, columns, fields)
|
||||
if err != nil {
|
||||
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 {
|
||||
err := plugin.Hook("SimpleInsertAfter", table, columns, fields, res)
|
||||
err := plugin.Hook(name, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
install.instructions = append(install.instructions, DB_Install_Instruction{table, res, "insert"})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ func main() {
|
|||
for _, adapter := range qgen.Registry {
|
||||
log.Printf("Building the queries for the %s adapter", adapter.GetName())
|
||||
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)
|
||||
if err != nil {
|
||||
|
@ -75,16 +75,6 @@ func writeStatements(adapter qgen.Adapter) error {
|
|||
return err
|
||||
}
|
||||
|
||||
/*err = writeReplaces(adapter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writeUpserts(adapter)
|
||||
if err != nil {
|
||||
return err
|
||||
}*/
|
||||
|
||||
err = writeUpdates(adapter)
|
||||
if err != nil {
|
||||
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", "'bigpost_min_words','250','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("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
|
||||
*/
|
||||
|
||||
// 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, 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("getSettings").Table("settings").Columns("name, content, type").Parse()
|
||||
|
||||
build.Select("isPluginActive").Table("plugins").Columns("active").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
|
||||
}
|
||||
|
||||
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 {
|
||||
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("updateSetting").Table("settings").Set("content = ?").Where("name = ?").Parse()
|
||||
|
||||
build.Update("updatePlugin").Table("plugins").Set("active = ?").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 {
|
||||
adapter.SimpleCount("reportExists", "topics", "data = ? AND data != '' AND parentID = 1", "")
|
||||
|
||||
adapter.SimpleCount("modlogCount", "moderation_logs", "", "")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@ func NewPrimaryKeySpitter() *PrimaryKeySpitter {
|
|||
func (spit *PrimaryKeySpitter) Hook(name string, args ...interface{}) error {
|
||||
if name == "CreateTableStart" {
|
||||
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" {
|
||||
expl := strings.Split(key.Columns, ",")
|
||||
if len(expl) > 1 {
|
||||
|
@ -23,7 +24,7 @@ func (spit *PrimaryKeySpitter) Hook(name string, args ...interface{}) error {
|
|||
found = key.Columns
|
||||
}
|
||||
if found != "" {
|
||||
table := args[0].(string)
|
||||
table := table.Name
|
||||
spit.keys[table] = found
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,8 +68,8 @@ func buildPanelRoutes() {
|
|||
Action("routePanelForumsEditPermsSubmit", "/panel/forums/edit/perms/submit/", "extra_data"),
|
||||
|
||||
View("routePanelSettings", "/panel/settings/"),
|
||||
View("routePanelSetting", "/panel/settings/edit/", "extra_data"),
|
||||
Action("routePanelSettingEdit", "/panel/settings/edit/submit/", "extra_data"),
|
||||
View("routePanelSettingEdit", "/panel/settings/edit/", "extra_data"),
|
||||
Action("routePanelSettingEditSubmit", "/panel/settings/edit/submit/", "extra_data"),
|
||||
|
||||
View("routePanelWordFilters", "/panel/settings/word-filters/"),
|
||||
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]
|
||||
if !ok {
|
||||
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)
|
||||
return
|
||||
|
@ -103,7 +103,7 @@ func routeOverview(w http.ResponseWriter, r *http.Request, user common.User) com
|
|||
}
|
||||
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.RunPreRenderHook("pre_render_overview", w, r, &user, &pi) {
|
||||
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)
|
||||
|
||||
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.RunPreRenderHook("pre_render_custom_page", w, r, &user, &pi) {
|
||||
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
|
||||
var qlist string
|
||||
group, err := common.Gstore.Get(user.Group)
|
||||
group, err := common.Groups.Get(user.Group)
|
||||
if err != nil {
|
||||
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)
|
||||
|
@ -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?
|
||||
var canSee []int
|
||||
if user.IsSuperAdmin {
|
||||
canSee, err = common.Fstore.GetAllVisibleIDs()
|
||||
canSee, err = common.Forums.GetAllVisibleIDs()
|
||||
if err != nil {
|
||||
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{}
|
||||
|
||||
for _, fid := range canSee {
|
||||
forum := common.Fstore.DirtyGet(fid)
|
||||
forum := common.Forums.DirtyGet(fid)
|
||||
if forum.Name != "" && forum.Active {
|
||||
if forum.ParentType == "" || forum.ParentType == "forum" {
|
||||
// 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)
|
||||
|
||||
forum := common.Fstore.DirtyGet(topicItem.ParentID)
|
||||
forum := common.Forums.DirtyGet(topicItem.ParentID)
|
||||
topicItem.ForumName = forum.Name
|
||||
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]
|
||||
}
|
||||
|
||||
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.RunPreRenderHook("pre_render_topic_list", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err = common.RunThemeTemplate(headerVars.ThemeName, "topics", pi, w)
|
||||
err = common.RunThemeTemplate(headerVars.Theme.Name, "topics", pi, w)
|
||||
if err != nil {
|
||||
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
|
||||
forum, err := common.Fstore.Get(fid)
|
||||
forum, err := common.Forums.Get(fid)
|
||||
if err == ErrNoRows {
|
||||
return common.NotFound(w, r)
|
||||
} else if err != nil {
|
||||
|
@ -408,7 +408,7 @@ func routeForum(w http.ResponseWriter, r *http.Request, user common.User, sfid s
|
|||
return nil
|
||||
}
|
||||
}
|
||||
err = common.RunThemeTemplate(headerVars.ThemeName, "forum", pi, w)
|
||||
err = common.RunThemeTemplate(headerVars.Theme.Name, "forum", pi, w)
|
||||
if err != nil {
|
||||
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 canSee []int
|
||||
if user.IsSuperAdmin {
|
||||
canSee, err = common.Fstore.GetAllVisibleIDs()
|
||||
canSee, err = common.Forums.GetAllVisibleIDs()
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
//log.Print("canSee ", canSee)
|
||||
} else {
|
||||
group, err := common.Gstore.Get(user.Group)
|
||||
group, err := common.Groups.Get(user.Group)
|
||||
if err != nil {
|
||||
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)
|
||||
|
@ -442,7 +441,7 @@ func routeForums(w http.ResponseWriter, r *http.Request, user common.User) commo
|
|||
|
||||
for _, fid := range canSee {
|
||||
// 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.LastTopicID != 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.RunPreRenderHook("pre_render_forum_list", w, r, &user, &pi) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
err = common.RunThemeTemplate(headerVars.ThemeName, "forums", pi, w)
|
||||
err = common.RunThemeTemplate(headerVars.Theme.Name, "forums", pi, w)
|
||||
if err != nil {
|
||||
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"))
|
||||
|
||||
// SEO URLs...
|
||||
// TODO: Make a shared function for this
|
||||
halves := strings.Split(r.URL.Path[len("/topic/"):], ".")
|
||||
if len(halves) < 2 {
|
||||
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
|
||||
}
|
||||
|
||||
postGroup, err := common.Gstore.Get(topic.Group)
|
||||
postGroup, err := common.Groups.Get(topic.Group)
|
||||
if err != nil {
|
||||
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.ContentLines = strings.Count(replyItem.Content, "\n")
|
||||
|
||||
postGroup, err = common.Gstore.Get(replyItem.Group)
|
||||
postGroup, err = common.Groups.Get(replyItem.Group)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -635,7 +635,7 @@ func routeTopicID(w http.ResponseWriter, r *http.Request, user common.User) comm
|
|||
return nil
|
||||
}
|
||||
}
|
||||
err = common.RunThemeTemplate(headerVars.ThemeName, "topic", tpage, w)
|
||||
err = common.RunThemeTemplate(headerVars.Theme.Name, "topic", tpage, w)
|
||||
if err != nil {
|
||||
return common.InternalError(err, w, r)
|
||||
}
|
||||
|
@ -671,6 +671,7 @@ func routeProfile(w http.ResponseWriter, r *http.Request, user common.User) comm
|
|||
puser = &user
|
||||
} else {
|
||||
// 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)
|
||||
if err == ErrNoRows {
|
||||
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)
|
||||
}
|
||||
|
||||
group, err := common.Gstore.Get(replyGroup)
|
||||
group, err := common.Groups.Get(replyGroup)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
// TODO: Add a phrase for this title
|
||||
ppage := common.ProfilePage{puser.Name + "'s Profile", user, headerVars, replyList, *puser}
|
||||
if common.PreRenderHooks["pre_render_profile"] != nil {
|
||||
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 {
|
||||
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 {
|
||||
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.RunPreRenderHook("pre_render_login", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -803,7 +805,7 @@ func routeLoginSubmit(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
|
||||
common.Auth.SetCookies(w, uid, session)
|
||||
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?
|
||||
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 {
|
||||
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.RunPreRenderHook("pre_render_register", w, r, &user, &pi) {
|
||||
return nil
|
||||
|
@ -937,10 +939,12 @@ func routeChangeTheme(w http.ResponseWriter, r *http.Request, user common.User)
|
|||
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"}]}`)
|
||||
|
||||
// TODO: Refactor this endpoint
|
||||
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")
|
||||
err := r.ParseForm()
|
||||
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]
|
||||
}
|
||||
_, _ = 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:
|
||||
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]) 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 ('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 [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');
|
||||
|
|
|
@ -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`) 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 ('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 `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');
|
||||
|
|
|
@ -27,3 +27,5 @@
|
|||
;
|
||||
;
|
||||
;
|
||||
;
|
||||
;
|
||||
|
|
|
@ -22,7 +22,7 @@ w.Write([]byte(tmpl_forum_vars.Title))
|
|||
w.Write(header_1)
|
||||
w.Write([]byte(tmpl_forum_vars.Header.Site.Name))
|
||||
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)
|
||||
if len(tmpl_forum_vars.Header.Stylesheets) != 0 {
|
||||
for _, item := range tmpl_forum_vars.Header.Stylesheets {
|
||||
|
@ -203,28 +203,37 @@ w.Write(forum_57)
|
|||
w.Write(forum_58)
|
||||
}
|
||||
w.Write(forum_59)
|
||||
if tmpl_forum_vars.Header.Theme.AboutSegment {
|
||||
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 {
|
||||
for _, item := range tmpl_forum_vars.Header.Themes {
|
||||
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([]byte(item.FriendlyName))
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(footer_5)
|
||||
}
|
||||
}
|
||||
}
|
||||
if tmpl_forum_vars.Header.Theme.Name == item.Name {
|
||||
w.Write(footer_6)
|
||||
if tmpl_forum_vars.Header.Widgets.RightSidebar != "" {
|
||||
}
|
||||
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_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
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ w.Write([]byte(tmpl_forums_vars.Title))
|
|||
w.Write(header_1)
|
||||
w.Write([]byte(tmpl_forums_vars.Header.Site.Name))
|
||||
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)
|
||||
if len(tmpl_forums_vars.Header.Stylesheets) != 0 {
|
||||
for _, item := range tmpl_forums_vars.Header.Stylesheets {
|
||||
|
@ -118,28 +118,37 @@ w.Write(forums_18)
|
|||
w.Write(forums_19)
|
||||
}
|
||||
w.Write(forums_20)
|
||||
if tmpl_forums_vars.Header.Theme.AboutSegment {
|
||||
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 {
|
||||
for _, item := range tmpl_forums_vars.Header.Themes {
|
||||
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([]byte(item.FriendlyName))
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(footer_5)
|
||||
}
|
||||
}
|
||||
}
|
||||
if tmpl_forums_vars.Header.Theme.Name == item.Name {
|
||||
w.Write(footer_6)
|
||||
if tmpl_forums_vars.Header.Widgets.RightSidebar != "" {
|
||||
}
|
||||
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_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
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ w.Write([]byte(tmpl_guilds_guild_list_vars.Title))
|
|||
w.Write(header_1)
|
||||
w.Write([]byte(tmpl_guilds_guild_list_vars.Header.Site.Name))
|
||||
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)
|
||||
if len(tmpl_guilds_guild_list_vars.Header.Stylesheets) != 0 {
|
||||
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_8)
|
||||
if tmpl_guilds_guild_list_vars.Header.Theme.AboutSegment {
|
||||
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 {
|
||||
for _, item := range tmpl_guilds_guild_list_vars.Header.Themes {
|
||||
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([]byte(item.FriendlyName))
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(footer_5)
|
||||
}
|
||||
}
|
||||
}
|
||||
if tmpl_guilds_guild_list_vars.Header.Theme.Name == item.Name {
|
||||
w.Write(footer_6)
|
||||
if tmpl_guilds_guild_list_vars.Header.Widgets.RightSidebar != "" {
|
||||
}
|
||||
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_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
|
||||
}
|
||||
|
|
|
@ -264,27 +264,36 @@ var topic_100 = []byte(`
|
|||
</main>
|
||||
|
||||
`)
|
||||
var footer_0 = []byte(`<div class="footer">
|
||||
<div id="poweredBy">Powered by Gosora - <span>Made with love by Azareal</span></div>
|
||||
var footer_0 = []byte(`<div class="about">
|
||||
<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">
|
||||
<div id="themeSelector" style="float: right;">
|
||||
<select id="themeSelectorSelect" name="themeSelector" aria-label="Change the site's appearance">
|
||||
`)
|
||||
var footer_1 = []byte(`<option val="`)
|
||||
var footer_2 = []byte(`"`)
|
||||
var footer_3 = []byte(` selected`)
|
||||
var footer_4 = []byte(`>`)
|
||||
var footer_5 = []byte(`</option>`)
|
||||
var footer_6 = []byte(`
|
||||
var footer_4 = []byte(`<option val="`)
|
||||
var footer_5 = []byte(`"`)
|
||||
var footer_6 = []byte(` selected`)
|
||||
var footer_7 = []byte(`>`)
|
||||
var footer_8 = []byte(`</option>`)
|
||||
var footer_9 = []byte(`
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
var footer_7 = []byte(`<aside class="sidebar">`)
|
||||
var footer_8 = []byte(`</aside>`)
|
||||
var footer_9 = []byte(`
|
||||
var footer_10 = []byte(`<aside class="sidebar">`)
|
||||
var footer_11 = []byte(`</aside>`)
|
||||
var footer_12 = []byte(`
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -498,7 +507,7 @@ var topic_alt_104 = []byte(`
|
|||
`)
|
||||
var profile_0 = []byte(`
|
||||
|
||||
<div id="profile_container">
|
||||
<div id="profile_container" class="colstack">
|
||||
|
||||
<div id="profile_left_lane" class="colstack_left">
|
||||
<!--<header class="colstack_item colstack_head rowhead">
|
||||
|
@ -882,10 +891,11 @@ var forum_7 = []byte(`">></a></div>`)
|
|||
var forum_8 = []byte(`
|
||||
|
||||
<main>
|
||||
<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`)
|
||||
var forum_9 = []byte(` has_opt`)
|
||||
var forum_10 = []byte(`"><h1>`)
|
||||
var forum_10 = []byte(`">
|
||||
<h1>`)
|
||||
var forum_11 = []byte(`</h1>
|
||||
</div>
|
||||
`)
|
||||
|
@ -904,10 +914,10 @@ var forum_16 = []byte(`
|
|||
<div style="clear: both;"></div>
|
||||
`)
|
||||
var forum_17 = []byte(`
|
||||
</div>
|
||||
`)
|
||||
</div>
|
||||
`)
|
||||
var forum_18 = []byte(`
|
||||
<div class="mod_floater auto_hide">
|
||||
<div class="mod_floater auto_hide">
|
||||
<form method="post">
|
||||
<div class="mod_floater_head">
|
||||
<span>What do you want to do with these 18 topics?</span>
|
||||
|
@ -920,11 +930,10 @@ var forum_18 = []byte(`
|
|||
<button>Run</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
`)
|
||||
</div>
|
||||
`)
|
||||
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">
|
||||
<form id="topic_create_form_form" enctype="multipart/form-data" action="/topic/create/submit/" method="post"></form>
|
||||
`)
|
||||
var forum_20 = []byte(`<img class="little_row_avatar" src="`)
|
||||
|
@ -958,10 +967,10 @@ var forum_25 = []byte(`
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
</div>
|
||||
`)
|
||||
var forum_26 = []byte(`
|
||||
<div id="forum_topic_list" class="rowblock topic_list">
|
||||
<div id="forum_topic_list" class="rowblock topic_list">
|
||||
`)
|
||||
var forum_27 = []byte(`<div class="topic_row" data-tid="`)
|
||||
var forum_28 = []byte(`">
|
||||
|
@ -1017,8 +1026,7 @@ var forum_56 = []byte(` <a href="/topics/create/`)
|
|||
var forum_57 = []byte(`">Start one?</a>`)
|
||||
var forum_58 = []byte(`</div>`)
|
||||
var forum_59 = []byte(`
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
`)
|
||||
var guilds_guild_list_0 = []byte(`
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
// 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. */
|
||||
package main
|
||||
import "net/http"
|
||||
import "./common"
|
||||
import "strconv"
|
||||
import "net/http"
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
|
@ -22,7 +22,7 @@ w.Write([]byte(tmpl_profile_vars.Title))
|
|||
w.Write(header_1)
|
||||
w.Write([]byte(tmpl_profile_vars.Header.Site.Name))
|
||||
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)
|
||||
if len(tmpl_profile_vars.Header.Stylesheets) != 0 {
|
||||
for _, item := range tmpl_profile_vars.Header.Stylesheets {
|
||||
|
@ -161,28 +161,37 @@ w.Write(profile_41)
|
|||
}
|
||||
w.Write(profile_42)
|
||||
w.Write(profile_43)
|
||||
if tmpl_profile_vars.Header.Theme.AboutSegment {
|
||||
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 {
|
||||
for _, item := range tmpl_profile_vars.Header.Themes {
|
||||
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([]byte(item.FriendlyName))
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(footer_5)
|
||||
}
|
||||
}
|
||||
}
|
||||
if tmpl_profile_vars.Header.Theme.Name == item.Name {
|
||||
w.Write(footer_6)
|
||||
if tmpl_profile_vars.Header.Widgets.RightSidebar != "" {
|
||||
}
|
||||
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_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
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
// 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. */
|
||||
package main
|
||||
import "strconv"
|
||||
import "net/http"
|
||||
import "./common"
|
||||
import "strconv"
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
|
@ -22,7 +22,7 @@ w.Write([]byte(tmpl_topic_vars.Title))
|
|||
w.Write(header_1)
|
||||
w.Write([]byte(tmpl_topic_vars.Header.Site.Name))
|
||||
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)
|
||||
if len(tmpl_topic_vars.Header.Stylesheets) != 0 {
|
||||
for _, item := range tmpl_topic_vars.Header.Stylesheets {
|
||||
|
@ -125,7 +125,7 @@ if tmpl_topic_vars.Topic.Avatar != "" {
|
|||
w.Write(topic_22)
|
||||
w.Write([]byte(tmpl_topic_vars.Topic.Avatar))
|
||||
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)
|
||||
if tmpl_topic_vars.Topic.ContentLines <= 5 {
|
||||
w.Write(topic_25)
|
||||
|
@ -223,7 +223,7 @@ if item.Avatar != "" {
|
|||
w.Write(topic_65)
|
||||
w.Write([]byte(item.Avatar))
|
||||
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)
|
||||
if item.ContentLines <= 5 {
|
||||
w.Write(topic_68)
|
||||
|
@ -296,28 +296,37 @@ w.Write(topic_98)
|
|||
w.Write(topic_99)
|
||||
}
|
||||
w.Write(topic_100)
|
||||
if tmpl_topic_vars.Header.Theme.AboutSegment {
|
||||
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 {
|
||||
for _, item := range tmpl_topic_vars.Header.Themes {
|
||||
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([]byte(item.FriendlyName))
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(footer_5)
|
||||
}
|
||||
}
|
||||
}
|
||||
if tmpl_topic_vars.Header.Theme.Name == item.Name {
|
||||
w.Write(footer_6)
|
||||
if tmpl_topic_vars.Header.Widgets.RightSidebar != "" {
|
||||
}
|
||||
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_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
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
// 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. */
|
||||
package main
|
||||
import "strconv"
|
||||
import "net/http"
|
||||
import "./common"
|
||||
import "strconv"
|
||||
|
||||
// nolint
|
||||
func init() {
|
||||
|
@ -22,7 +22,7 @@ w.Write([]byte(tmpl_topic_alt_vars.Title))
|
|||
w.Write(header_1)
|
||||
w.Write([]byte(tmpl_topic_alt_vars.Header.Site.Name))
|
||||
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)
|
||||
if len(tmpl_topic_alt_vars.Header.Stylesheets) != 0 {
|
||||
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_104)
|
||||
if tmpl_topic_alt_vars.Header.Theme.AboutSegment {
|
||||
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 {
|
||||
for _, item := range tmpl_topic_alt_vars.Header.Themes {
|
||||
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([]byte(item.FriendlyName))
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(footer_5)
|
||||
}
|
||||
}
|
||||
}
|
||||
if tmpl_topic_alt_vars.Header.Theme.Name == item.Name {
|
||||
w.Write(footer_6)
|
||||
if tmpl_topic_alt_vars.Header.Widgets.RightSidebar != "" {
|
||||
}
|
||||
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_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
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ w.Write([]byte(tmpl_topics_vars.Title))
|
|||
w.Write(header_1)
|
||||
w.Write([]byte(tmpl_topics_vars.Header.Site.Name))
|
||||
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)
|
||||
if len(tmpl_topics_vars.Header.Stylesheets) != 0 {
|
||||
for _, item := range tmpl_topics_vars.Header.Stylesheets {
|
||||
|
@ -199,28 +199,37 @@ w.Write(topics_55)
|
|||
w.Write(topics_56)
|
||||
}
|
||||
w.Write(topics_57)
|
||||
if tmpl_topics_vars.Header.Theme.AboutSegment {
|
||||
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 {
|
||||
for _, item := range tmpl_topics_vars.Header.Themes {
|
||||
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([]byte(item.FriendlyName))
|
||||
w.Write([]byte(item.Name))
|
||||
w.Write(footer_5)
|
||||
}
|
||||
}
|
||||
}
|
||||
if tmpl_topics_vars.Header.Theme.Name == item.Name {
|
||||
w.Write(footer_6)
|
||||
if tmpl_topics_vars.Header.Widgets.RightSidebar != "" {
|
||||
}
|
||||
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_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
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<nav class="colstack_left">
|
||||
<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 class="colstack_item rowmenu">
|
||||
<div class="rowitem passive"><a href="/user/edit/avatar/">Avatar</a></div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<main class="colstack account">
|
||||
<div class="colstack account">
|
||||
{{template "account-menu.html" . }}
|
||||
<div class="colstack_right">
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head rowhead">
|
||||
<div class="rowitem"><h1>Edit Avatar</h1></div>
|
||||
</div>
|
||||
|
@ -10,7 +10,7 @@
|
|||
<div class="rowitem"><img src="{{.CurrentUser.Avatar}}" height="128px" max-width="128px" /></div>
|
||||
</div>
|
||||
{{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">
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem formlabel"><a>Upload Avatar</a></div>
|
||||
|
@ -21,6 +21,6 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
<main class="colstack account">
|
||||
<div class="colstack account">
|
||||
{{template "account-menu.html" . }}
|
||||
<div class="colstack_right">
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head rowhead">
|
||||
<div class="rowitem"><h1>Emails</h1></div>
|
||||
</div>
|
||||
|
@ -17,6 +17,6 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{{template "header.html" . }}
|
||||
<main class="colstack account">
|
||||
<div class="colstack account">
|
||||
{{template "account-menu.html" . }}
|
||||
<div class="colstack_right">
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head rowhead">
|
||||
<div class="rowitem"><h1>Edit Username</h1></div>
|
||||
</div>
|
||||
<div class="colstack_item form_item">
|
||||
<div class="colstack_item the_form">
|
||||
<form action="/user/edit/username/submit/" method="post">
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem formlabel"><a>Current Username</a></div>
|
||||
|
@ -20,6 +20,6 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{{template "header.html" . }}
|
||||
<main class="colstack account">
|
||||
<div class="colstack account">
|
||||
{{template "account-menu.html" . }}
|
||||
<div class="colstack_right">
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head rowhead">
|
||||
<div class="rowitem"><h1>Edit Password</h1></div>
|
||||
</div>
|
||||
<div class="colstack_item form_item">
|
||||
<div class="colstack_item the_form">
|
||||
<form action="/user/edit/critical/submit/" method="post">
|
||||
<div class="formrow real_first_child">
|
||||
<div class="formitem formlabel"><a>Current Password</a></div>
|
||||
|
@ -24,6 +24,6 @@
|
|||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
</div>
|
||||
{{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 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">
|
||||
<div id="themeSelector" style="float: right;">
|
||||
<select id="themeSelectorSelect" name="themeSelector" aria-label="Change the site's appearance">
|
||||
{{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}}
|
||||
</select>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<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}}
|
||||
<link href="/static/{{.}}" rel="stylesheet" type="text/css">
|
||||
{{end}}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
<nav class="colstack_left" aria-label="The control panel menu">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a href="/panel/logs/mod/">Logs</a></div>
|
||||
|
@ -25,4 +26,5 @@
|
|||
{{end}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
|
@ -17,4 +18,5 @@
|
|||
{{end}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
{{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="rowitem"><a>Dashboard</a></div>
|
||||
</div>
|
||||
<div id="panel_dashboard" class="colstack_grid">
|
||||
{{range .GridItems}}
|
||||
<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}}
|
||||
</div>
|
||||
</main>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<main id="panel_dashboard_right" class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
|
@ -14,4 +15,5 @@
|
|||
<div class="grid_item grid_stat">{{.DBAdapter}}</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
{{template "header.html" . }}
|
||||
|
||||
<div class="colstack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<script>
|
||||
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}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
{{template "header.html" . }}
|
||||
|
||||
<div class="colstack">
|
||||
{{template "panel-menu.html" . }}
|
||||
<script>var form_vars = {
|
||||
'forum_active': ['Hide','Show'],
|
||||
|
@ -72,4 +74,5 @@
|
|||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
<nav class="colstack_left" aria-label="The control panel menu">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a href="/panel/groups/edit/{{.ID}}">Group Editor</a></div>
|
||||
|
@ -60,4 +61,5 @@
|
|||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
<nav class="colstack_left" aria-label="The control panel menu">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a href="/panel/groups/edit/{{.ID}}">Group Editor</a></div>
|
||||
|
@ -43,4 +44,5 @@
|
|||
</form>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
{{template "panel-menu.html" . }}
|
||||
<div class="colstack">
|
||||
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>Groups</h1></div>
|
||||
|
@ -57,4 +58,6 @@
|
|||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
|
||||
<nav class="colstack_left" aria-label="The control panel menu">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a href="/panel/logs/mod/">Logs</a></div>
|
||||
|
@ -37,4 +39,6 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
|
@ -21,4 +23,6 @@
|
|||
{{end}}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
|
@ -34,4 +36,6 @@
|
|||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
|
@ -13,4 +15,6 @@
|
|||
{{end}}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
|
||||
<nav class="colstack_left" aria-label="The control panel menu">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><a href="/panel/themes/">Theme Manager</a></div>
|
||||
|
@ -56,4 +58,6 @@
|
|||
{{end}}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
|
@ -33,4 +35,6 @@
|
|||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{{template "header.html" . }}
|
||||
{{template "panel-menu.html" . }}
|
||||
<div class="colstack">
|
||||
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
<div class="rowitem"><h1>Users</h1></div>
|
||||
|
@ -29,4 +30,6 @@
|
|||
</div>
|
||||
{{end}}
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
|
@ -1,4 +1,6 @@
|
|||
{{template "header.html" . }}
|
||||
<div class="colstack">
|
||||
|
||||
{{template "panel-menu.html" . }}
|
||||
<main class="colstack_right">
|
||||
<div class="colstack_item colstack_head">
|
||||
|
@ -42,4 +44,6 @@
|
|||
</form>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
{{template "footer.html" . }}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{template "header.html" . }}
|
||||
|
||||
<div id="profile_container">
|
||||
<div id="profile_container" class="colstack">
|
||||
|
||||
<div id="profile_left_lane" class="colstack_left">
|
||||
<!--<header class="colstack_item colstack_head rowhead">
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
|
||||
<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>
|
||||
<textarea name="topic_content" class="show_on_edit topic_content_input">{{.Topic.Content}}</textarea>
|
||||
|
||||
|
@ -57,7 +57,7 @@
|
|||
<span itemprop="text">{{.ActionType}}</span>
|
||||
</article>
|
||||
{{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 **/}}
|
||||
<p class="editable_block user_content" itemprop="text" style="margin:0;padding:0;">{{.ContentHtml}}</p>
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
:root {
|
||||
--header-border-color: hsl(0,0%,85%);
|
||||
--element-border-color: hsl(0,0%,90%);
|
||||
--header-border-color: hsl(0,0%,80%);
|
||||
--element-border-color: hsl(0,0%,85%);
|
||||
--element-background-color: white;
|
||||
--replies-lang-string: " replies";
|
||||
--topics-lang-string: " topics";
|
||||
|
@ -45,8 +45,10 @@ a {
|
|||
padding-top: 14px;
|
||||
display: flex;
|
||||
/*background-color: hsl(0,0%,97%);*/
|
||||
background-color: hsl(0,0%,98%);
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
#main {
|
||||
|
@ -163,14 +165,22 @@ ul {
|
|||
border-bottom: 2px solid var(--header-border-color);
|
||||
background-color: var(--element-background-color);
|
||||
margin-left: 12px;
|
||||
}
|
||||
.rowblock {
|
||||
margin-right: 12px;
|
||||
}
|
||||
.colstack_right {
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
.rowhead, .opthead, .colstack_head {
|
||||
padding: 13px;
|
||||
padding-top: 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 {
|
||||
font-size: 20px;
|
||||
font-weight: normal;
|
||||
|
@ -181,14 +191,18 @@ ul {
|
|||
margin-block-end: 0;
|
||||
display: inline-block;
|
||||
}
|
||||
.colstack_head a h1 {
|
||||
font-size: 16px;
|
||||
color: var(--primary-link-color);
|
||||
}
|
||||
|
||||
.colstack {
|
||||
display: flex;
|
||||
}
|
||||
#main .colstack_left {
|
||||
.colstack:not(#profile_container) .colstack_left {
|
||||
width: 300px;
|
||||
}
|
||||
#main .colstack_right {
|
||||
.colstack:not(#profile_container) .colstack_right {
|
||||
width: calc(90% - 300px);
|
||||
}
|
||||
|
||||
|
@ -412,8 +426,9 @@ label.uploadItem {
|
|||
padding-left: 33px;
|
||||
}
|
||||
|
||||
select, input, textarea {
|
||||
select, input, textarea, button {
|
||||
border: 1px solid var(--header-border-color);
|
||||
background: var(--element-background-color);
|
||||
padding: 5px;
|
||||
color: hsl(0,0%,30%);
|
||||
}
|
||||
|
@ -713,7 +728,7 @@ select, input, textarea {
|
|||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#profile_container, #profile_left_pane .topBlock {
|
||||
#profile_left_pane .topBlock {
|
||||
display: flex;
|
||||
}
|
||||
#profile_left_lane {
|
||||
|
@ -725,6 +740,7 @@ select, input, textarea {
|
|||
padding-bottom: 18px;
|
||||
border: 1px solid var(--element-border-color);
|
||||
border-bottom: 2px solid var(--element-border-color);
|
||||
background-color: var(--element-background-color);
|
||||
}
|
||||
#profile_left_pane .avatarRow {
|
||||
padding: 24px;
|
||||
|
@ -748,17 +764,21 @@ select, input, textarea {
|
|||
.rowmenu .passive {
|
||||
border: 1px solid var(--element-border-color);
|
||||
border-bottom: 2px solid var(--element-border-color);
|
||||
background-color: var(--element-background-color);
|
||||
margin-top: 6px;
|
||||
padding: 12px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.rowmenu {
|
||||
.colstack:not(#profile_container) .rowmenu {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.rowmenu .passive:hover {
|
||||
margin-left: 4px;
|
||||
.colstack:not(#profile_container) .rowmenu .passive {
|
||||
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 {
|
||||
padding-left: 12px;
|
||||
|
@ -771,50 +791,158 @@ select, input, textarea {
|
|||
#profile_right_lane .colstack_item, .colstack_right .colstack_item {
|
||||
border: 1px 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;
|
||||
}
|
||||
|
||||
.form_item {
|
||||
display: flex;
|
||||
#profile_right_lane .topic_reply_form {
|
||||
width: auto;
|
||||
}
|
||||
.form_item form, .colstack_item .formrow {
|
||||
display: contents;
|
||||
|
||||
.colstack_item .formrow {
|
||||
display: flex;
|
||||
}
|
||||
.colstack_right .formrow {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
padding-bottom: 4px;
|
||||
border-right: 1px solid var(--element-border-color);
|
||||
}
|
||||
.colstack_right .formrow:first-child {
|
||||
padding-top: 16px;
|
||||
}
|
||||
.colstack_right .formrow .formlabel {
|
||||
padding-top: 5px;
|
||||
}
|
||||
.colstack_right .formrow:last-child {
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.colstack_item:not(#profile_right_lane) .formrow .formlabel {
|
||||
width: min-content;
|
||||
width: 40%;
|
||||
margin-right: 12px;
|
||||
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);
|
||||
padding: 12px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
margin-top: 14px;
|
||||
margin-left: -8px;
|
||||
margin-right: -8px;
|
||||
background-color: var(--element-background-color);
|
||||
display: flex;
|
||||
}
|
||||
.footer #poweredBy {
|
||||
.about, #poweredBy {
|
||||
font-size: 17px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.footer #poweredBy span {
|
||||
.about {
|
||||
margin-top: 14px;
|
||||
}
|
||||
#poweredBy {
|
||||
margin-right: auto;
|
||||
}
|
||||
#poweredBy span {
|
||||
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) {
|
||||
.topic_inner_right {
|
||||
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",
|
||||
"Tag": "WIP",
|
||||
"Sidebars":"right",
|
||||
"AboutSegment":true,
|
||||
"Templates": [
|
||||
{
|
||||
"Name": "topic",
|
||||
|
|
|
@ -352,11 +352,12 @@ AdminStatLoop:
|
|||
} else {
|
||||
calcperc := int(cpuPerc[0]) / runtime.NumCPU()
|
||||
cpustr = strconv.Itoa(calcperc)
|
||||
if calcperc < 30 {
|
||||
switch {
|
||||
case calcperc < 30:
|
||||
cpuColour = "stat_green"
|
||||
} else if calcperc < 75 {
|
||||
case calcperc < 75:
|
||||
cpuColour = "stat_orange"
|
||||
} else {
|
||||
default:
|
||||
cpuColour = "stat_red"
|
||||
}
|
||||
}
|
||||
|
@ -409,20 +410,20 @@ AdminStatLoop:
|
|||
|
||||
// nolint
|
||||
if !noStatUpdates {
|
||||
w.Write([]byte("set #dash-totonline " + strconv.Itoa(totonline) + totunit + " online\r"))
|
||||
w.Write([]byte("set #dash-gonline " + strconv.Itoa(gonline) + gunit + " guests online\r"))
|
||||
w.Write([]byte("set #dash-uonline " + strconv.Itoa(uonline) + uunit + " users online\r"))
|
||||
w.Write([]byte("set #dash-totonline <span>" + strconv.Itoa(totonline) + totunit + " online</span>\r"))
|
||||
w.Write([]byte("set #dash-gonline <span>" + strconv.Itoa(gonline) + gunit + " guests online</span>\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-gonline grid_item grid_stat " + onlineGuestsColour + "\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"))
|
||||
|
||||
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"))
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue