Add a per-user theme switcher. The CSS might be slightly broken in the themes, that'll be fixed in a follow-up commit.
Added basic support for server sync. Re-added a few missing defers. Renamed TO-DO to TODO across the entire codebase. Renamed StaticForumStore to MemoryForumStore. The ForumStore is now built on a sync.Map with a view slice for generating /forums rather than a slice. Renamed many more functions and variables to satisfy the linter. increase_post_user_stats() and decrease_post_user_stats() are now methods on the User struct. We also fix a bug where they take the moderator's score rather than the target user's into account when recalculating their level after a post / topic is deleted. Transitioned the topic list to CSS Grid for Tempra Simple, with a float fallback. Cosmo and Cosmo Conflux are now hidden from the theme list. Fixed more data races. Added more debug data to the template compiler logs.
This commit is contained in:
parent
ba36814d8d
commit
91f70d2a4a
|
@ -127,6 +127,7 @@ func notifyWatchers(asid int64) {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
var uid int
|
var uid int
|
||||||
var uids []int
|
var uids []int
|
||||||
|
@ -143,7 +144,6 @@ func notifyWatchers(asid int64) {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_ = rows.Close()
|
|
||||||
|
|
||||||
var actorID, targetUserID, elementID int
|
var actorID, targetUserID, elementID int
|
||||||
var event, elementType string
|
var event, elementType string
|
||||||
|
|
1
auth.go
1
auth.go
|
@ -109,6 +109,7 @@ func (auth *DefaultAuth) Logout(w http.ResponseWriter, _ int) {
|
||||||
http.SetCookie(w, &cookie)
|
http.SetCookie(w, &cookie)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Set the cookie domain
|
||||||
func (auth *DefaultAuth) SetCookies(w http.ResponseWriter, uid int, session string) {
|
func (auth *DefaultAuth) SetCookies(w http.ResponseWriter, uid int, session string) {
|
||||||
cookie := http.Cookie{Name: "uid", Value: strconv.Itoa(uid), Path: "/", MaxAge: year}
|
cookie := http.Cookie{Name: "uid", Value: strconv.Itoa(uid), Path: "/", MaxAge: year}
|
||||||
http.SetCookie(w, &cookie)
|
http.SetCookie(w, &cookie)
|
||||||
|
|
2
cache.go
2
cache.go
|
@ -9,7 +9,7 @@ const CACHE_DYNAMIC int = 1
|
||||||
const CACHE_SQL int = 2
|
const CACHE_SQL int = 2
|
||||||
|
|
||||||
// ErrCacheDesync is thrown whenever a piece of data, for instance, a user is out of sync with the database. Currently unused.
|
// ErrCacheDesync is thrown whenever a piece of data, for instance, a user is out of sync with the database. Currently unused.
|
||||||
var ErrCacheDesync = errors.New("The cache is out of sync with the database.") // TO-DO: A cross-server synchronisation mechanism
|
var ErrCacheDesync = errors.New("The cache is out of sync with the database.") // TODO: A cross-server synchronisation mechanism
|
||||||
|
|
||||||
// ErrStoreCapacityOverflow is thrown whenever a datastore reaches it's maximum hard capacity. I'm not sure *if* this one is used, at the moment. Probably.
|
// ErrStoreCapacityOverflow is thrown whenever a datastore reaches it's maximum hard capacity. I'm not sure *if* this one is used, at the moment. Probably.
|
||||||
var ErrStoreCapacityOverflow = errors.New("This datastore has reached it's maximum capacity.")
|
var ErrStoreCapacityOverflow = errors.New("This datastore has reached it's maximum capacity.")
|
||||||
|
|
|
@ -72,7 +72,7 @@ func initDatabase() (err error) {
|
||||||
GuestPerms = groups[6].Perms
|
GuestPerms = groups[6].Perms
|
||||||
|
|
||||||
log.Print("Loading the forums.")
|
log.Print("Loading the forums.")
|
||||||
fstore = NewStaticForumStore()
|
fstore = NewMemoryForumStore()
|
||||||
err = fstore.LoadForums()
|
err = fstore.LoadForums()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -7,7 +7,7 @@ import "sync"
|
||||||
import "net/http"
|
import "net/http"
|
||||||
import "runtime/debug"
|
import "runtime/debug"
|
||||||
|
|
||||||
// TO-DO: Use the error_buffer variable to construct the system log in the Control Panel. Should we log errors caused by users too? Or just collect statistics on those or do nothing? Intercept recover()? Could we intercept the logger instead here? We might get too much information, if we intercept the logger, maybe make it part of the Debug page?
|
// TODO: Use the error_buffer variable to construct the system log in the Control Panel. Should we log errors caused by users too? Or just collect statistics on those or do nothing? Intercept recover()? Could we intercept the logger instead here? We might get too much information, if we intercept the logger, maybe make it part of the Debug page?
|
||||||
var errorBufferMutex sync.RWMutex
|
var errorBufferMutex sync.RWMutex
|
||||||
var errorBuffer []error
|
var errorBuffer []error
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ var plugins = make(map[string]*Plugin)
|
||||||
var hooks = map[string][]func(interface{}) interface{}{
|
var hooks = map[string][]func(interface{}) interface{}{
|
||||||
"forums_frow_assign": nil,
|
"forums_frow_assign": nil,
|
||||||
"topic_create_frow_assign": nil,
|
"topic_create_frow_assign": nil,
|
||||||
"rrow_assign": nil, // TO-DO: Rename this hook to topic_rrow_assign
|
"rrow_assign": nil, // TODO: Rename this hook to topic_rrow_assign
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hooks with a variable number of arguments
|
// Hooks with a variable number of arguments
|
||||||
|
|
2
files.go
2
files.go
|
@ -82,7 +82,7 @@ func addStaticFile(path string, prefix string) error {
|
||||||
func compressBytesGzip(in []byte) []byte {
|
func compressBytesGzip(in []byte) []byte {
|
||||||
var buff bytes.Buffer
|
var buff bytes.Buffer
|
||||||
gz := gzip.NewWriter(&buff)
|
gz := gzip.NewWriter(&buff)
|
||||||
_, _ = gz.Write(in) // TO-DO: What if this errors? What circumstances could it error under? Should we add a second return value?
|
_, _ = gz.Write(in) // TODO: What if this errors? What circumstances could it error under? Should we add a second return value?
|
||||||
_ = gz.Close()
|
_ = gz.Close()
|
||||||
return buff.Bytes()
|
return buff.Bytes()
|
||||||
}
|
}
|
||||||
|
|
2
forum.go
2
forum.go
|
@ -39,7 +39,7 @@ type ForumSimple struct {
|
||||||
Preset string
|
Preset string
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildForumUrl(slug string, fid int) string {
|
func buildForumURL(slug string, fid int) string {
|
||||||
if slug == "" {
|
if slug == "" {
|
||||||
return "/forum/" + strconv.Itoa(fid)
|
return "/forum/" + strconv.Itoa(fid)
|
||||||
}
|
}
|
||||||
|
|
319
forum_store.go
319
forum_store.go
|
@ -1,12 +1,14 @@
|
||||||
/* Work in progress. Check back later! */
|
/* Work in progress. Check back later! */
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "log"
|
import (
|
||||||
import "sync"
|
"database/sql"
|
||||||
|
"log"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
//import "sync/atomic"
|
"./query_gen/lib"
|
||||||
import "database/sql"
|
)
|
||||||
import "./query_gen/lib"
|
|
||||||
|
|
||||||
var forumUpdateMutex sync.Mutex
|
var forumUpdateMutex sync.Mutex
|
||||||
var forumCreateMutex sync.Mutex
|
var forumCreateMutex sync.Mutex
|
||||||
|
@ -24,7 +26,7 @@ type ForumStore interface {
|
||||||
Set(forum *Forum) error
|
Set(forum *Forum) error
|
||||||
//Update(Forum) error
|
//Update(Forum) error
|
||||||
//CascadeUpdate(Forum) error
|
//CascadeUpdate(Forum) error
|
||||||
Delete(id int) error
|
Delete(id int)
|
||||||
CascadeDelete(id int) error
|
CascadeDelete(id int) error
|
||||||
IncrementTopicCount(id int) error
|
IncrementTopicCount(id int) error
|
||||||
DecrementTopicCount(id int) error
|
DecrementTopicCount(id int) error
|
||||||
|
@ -32,6 +34,8 @@ type ForumStore interface {
|
||||||
Exists(id int) bool
|
Exists(id int) bool
|
||||||
GetAll() ([]*Forum, error)
|
GetAll() ([]*Forum, error)
|
||||||
GetAllIDs() ([]int, error)
|
GetAllIDs() ([]int, error)
|
||||||
|
GetAllVisible() ([]*Forum, error)
|
||||||
|
GetAllVisibleIDs() ([]int, error)
|
||||||
//GetChildren(parentID int, parentType string) ([]*Forum,error)
|
//GetChildren(parentID int, parentType string) ([]*Forum,error)
|
||||||
//GetFirstChild(parentID int, parentType string) (*Forum,error)
|
//GetFirstChild(parentID int, parentType string) (*Forum,error)
|
||||||
CreateForum(forumName string, forumDesc string, active bool, preset string) (int, error)
|
CreateForum(forumName string, forumDesc string, active bool, preset string) (int, error)
|
||||||
|
@ -39,17 +43,20 @@ type ForumStore interface {
|
||||||
GetGlobalCount() int
|
GetGlobalCount() int
|
||||||
}
|
}
|
||||||
|
|
||||||
type StaticForumStore struct {
|
type MemoryForumStore struct {
|
||||||
forums []*Forum // The IDs for a forum tend to be low and sequential for the most part, so we can get more performance out of using a slice instead of a map AND it has better concurrency
|
//forums map[int]*Forum
|
||||||
|
forums sync.Map
|
||||||
|
forumView atomic.Value // []*Forum
|
||||||
//fids []int
|
//fids []int
|
||||||
forumCapCount int
|
forumCount int
|
||||||
|
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
getAll *sql.Stmt
|
getAll *sql.Stmt
|
||||||
forumCount *sql.Stmt
|
delete *sql.Stmt
|
||||||
|
getForumCount *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStaticForumStore() *StaticForumStore {
|
func NewMemoryForumStore() *MemoryForumStore {
|
||||||
getStmt, err := qgen.Builder.SimpleSelect("forums", "name, desc, active, preset, parentID, parentType, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime", "fid = ?", "", "")
|
getStmt, err := qgen.Builder.SimpleSelect("forums", "name, desc, active, preset, parentID, parentType, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime", "fid = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -58,30 +65,44 @@ func NewStaticForumStore() *StaticForumStore {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
deleteStmt, err := qgen.Builder.SimpleUpdate("forums", "name= '', active = 0", "fid = ?")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
forumCountStmt, err := qgen.Builder.SimpleCount("forums", "name != ''", "")
|
forumCountStmt, err := qgen.Builder.SimpleCount("forums", "name != ''", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
return &StaticForumStore{
|
return &MemoryForumStore{
|
||||||
get: getStmt,
|
get: getStmt,
|
||||||
getAll: getAllStmt,
|
getAll: getAllStmt,
|
||||||
forumCount: forumCountStmt,
|
delete: deleteStmt,
|
||||||
|
getForumCount: forumCountStmt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) LoadForums() error {
|
func (mfs *MemoryForumStore) LoadForums() error {
|
||||||
log.Print("Adding the uncategorised forum")
|
log.Print("Adding the uncategorised forum")
|
||||||
var forums = []*Forum{
|
forumUpdateMutex.Lock()
|
||||||
&Forum{0, buildForumUrl(nameToSlug("Uncategorised"), 0), "Uncategorised", "", config.UncategorisedForumVisible, "all", 0, "", 0, "", "", 0, "", 0, ""},
|
defer forumUpdateMutex.Unlock()
|
||||||
|
|
||||||
|
var forumView []*Forum
|
||||||
|
addForum := func(forum *Forum) {
|
||||||
|
mfs.forums.Store(forum.ID, forum)
|
||||||
|
if forum.Active && forum.Name != "" {
|
||||||
|
forumView = append(forumView, forum)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addForum(&Forum{0, buildForumURL(nameToSlug("Uncategorised"), 0), "Uncategorised", "", config.UncategorisedForumVisible, "all", 0, "", 0, "", "", 0, "", 0, ""})
|
||||||
|
|
||||||
rows, err := get_forums_stmt.Query()
|
rows, err := get_forums_stmt.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var i = 1
|
var i = 0
|
||||||
for ; rows.Next(); i++ {
|
for ; rows.Next(); i++ {
|
||||||
forum := Forum{ID: 0, Active: true, Preset: "all"}
|
forum := Forum{ID: 0, Active: true, Preset: "all"}
|
||||||
err = rows.Scan(&forum.ID, &forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.ParentID, &forum.ParentType, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime)
|
err = rows.Scan(&forum.ID, &forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.ParentID, &forum.ParentType, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime)
|
||||||
|
@ -89,12 +110,6 @@ func (sfs *StaticForumStore) LoadForums() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ugh, you really shouldn't physically delete these items, it makes a big mess of things
|
|
||||||
if forum.ID != i {
|
|
||||||
log.Print("Stop physically deleting forums. You are messing up the IDs. Use the Forum Manager or delete_forum() instead x.x")
|
|
||||||
sfs.fillForumIDGap(i, forum.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if forum.Name == "" {
|
if forum.Name == "" {
|
||||||
if dev.DebugMode {
|
if dev.DebugMode {
|
||||||
log.Print("Adding a placeholder forum")
|
log.Print("Adding a placeholder forum")
|
||||||
|
@ -103,132 +118,164 @@ func (sfs *StaticForumStore) LoadForums() error {
|
||||||
log.Print("Adding the " + forum.Name + " forum")
|
log.Print("Adding the " + forum.Name + " forum")
|
||||||
}
|
}
|
||||||
|
|
||||||
forum.Link = buildForumUrl(nameToSlug(forum.Name), forum.ID)
|
forum.Link = buildForumURL(nameToSlug(forum.Name), forum.ID)
|
||||||
forum.LastTopicLink = buildTopicURL(nameToSlug(forum.LastTopic), forum.LastTopicID)
|
forum.LastTopicLink = buildTopicURL(nameToSlug(forum.LastTopic), forum.LastTopicID)
|
||||||
forums = append(forums, &forum)
|
addForum(&forum)
|
||||||
}
|
}
|
||||||
err = rows.Err()
|
mfs.forumCount = i
|
||||||
if err != nil {
|
mfs.forumView.Store(forumView)
|
||||||
return err
|
return rows.Err()
|
||||||
}
|
|
||||||
sfs.forums = forums
|
|
||||||
sfs.forumCapCount = i
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) DirtyGet(id int) *Forum {
|
// TODO: Hide social groups too
|
||||||
if !((id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "") {
|
func (mfs *MemoryForumStore) rebuildView() {
|
||||||
|
var forumView []*Forum
|
||||||
|
mfs.forums.Range(func(_ interface{}, value interface{}) bool {
|
||||||
|
forum := value.(*Forum)
|
||||||
|
if forum.Active && forum.Name != "" {
|
||||||
|
forumView = append(forumView, forum)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
mfs.forumView.Store(forumView)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mfs *MemoryForumStore) DirtyGet(id int) *Forum {
|
||||||
|
fint, ok := mfs.forums.Load(id)
|
||||||
|
forum := fint.(*Forum)
|
||||||
|
if !ok || forum.Name == "" {
|
||||||
return &Forum{ID: -1, Name: ""}
|
return &Forum{ID: -1, Name: ""}
|
||||||
}
|
}
|
||||||
return sfs.forums[id]
|
return forum
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) Get(id int) (*Forum, error) {
|
func (mfs *MemoryForumStore) Get(id int) (*Forum, error) {
|
||||||
if !((id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "") {
|
fint, ok := mfs.forums.Load(id)
|
||||||
|
forum := fint.(*Forum)
|
||||||
|
if !ok || forum.Name == "" {
|
||||||
return nil, ErrNoRows
|
return nil, ErrNoRows
|
||||||
}
|
}
|
||||||
return sfs.forums[id], nil
|
return forum, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) CascadeGet(id int) (*Forum, error) {
|
func (mfs *MemoryForumStore) CascadeGet(id int) (*Forum, error) {
|
||||||
if !((id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "") {
|
fint, ok := mfs.forums.Load(id)
|
||||||
return nil, ErrNoRows
|
forum := fint.(*Forum)
|
||||||
|
if !ok || forum.Name == "" {
|
||||||
|
err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime)
|
||||||
|
|
||||||
|
forum.Link = buildForumURL(nameToSlug(forum.Name), forum.ID)
|
||||||
|
forum.LastTopicLink = buildTopicURL(nameToSlug(forum.LastTopic), forum.LastTopicID)
|
||||||
|
return forum, err
|
||||||
}
|
}
|
||||||
return sfs.forums[id], nil
|
return forum, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) CascadeGetCopy(id int) (forum Forum, err error) {
|
func (mfs *MemoryForumStore) CascadeGetCopy(id int) (Forum, error) {
|
||||||
if !((id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != "") {
|
fint, ok := mfs.forums.Load(id)
|
||||||
return forum, ErrNoRows
|
forum := fint.(*Forum)
|
||||||
|
if !ok || forum.Name == "" {
|
||||||
|
err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime)
|
||||||
|
|
||||||
|
forum.Link = buildForumURL(nameToSlug(forum.Name), forum.ID)
|
||||||
|
forum.LastTopicLink = buildTopicURL(nameToSlug(forum.LastTopic), forum.LastTopicID)
|
||||||
|
return *forum, err
|
||||||
}
|
}
|
||||||
return *sfs.forums[id], nil
|
return *forum, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) BypassGet(id int) (*Forum, error) {
|
func (mfs *MemoryForumStore) BypassGet(id int) (*Forum, error) {
|
||||||
var forum = Forum{ID: id}
|
var forum = Forum{ID: id}
|
||||||
err := sfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime)
|
err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime)
|
||||||
|
|
||||||
|
forum.Link = buildForumURL(nameToSlug(forum.Name), forum.ID)
|
||||||
|
forum.LastTopicLink = buildTopicURL(nameToSlug(forum.LastTopic), forum.LastTopicID)
|
||||||
return &forum, err
|
return &forum, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) Load(id int) error {
|
func (mfs *MemoryForumStore) Load(id int) error {
|
||||||
var forum = Forum{ID: id}
|
var forum = Forum{ID: id}
|
||||||
err := sfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime)
|
err := mfs.get.QueryRow(id).Scan(&forum.Name, &forum.Desc, &forum.Active, &forum.Preset, &forum.TopicCount, &forum.LastTopic, &forum.LastTopicID, &forum.LastReplyer, &forum.LastReplyerID, &forum.LastTopicTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sfs.Set(&forum)
|
forum.Link = buildForumURL(nameToSlug(forum.Name), forum.ID)
|
||||||
|
forum.LastTopicLink = buildTopicURL(nameToSlug(forum.LastTopic), forum.LastTopicID)
|
||||||
|
|
||||||
|
mfs.Set(&forum)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Set should be able to add new indices not just replace existing ones for consistency with UserStore and TopicStore
|
func (mfs *MemoryForumStore) Set(forum *Forum) error {
|
||||||
func (sfs *StaticForumStore) Set(forum *Forum) error {
|
if !mfs.Exists(forum.ID) {
|
||||||
forumUpdateMutex.Lock()
|
|
||||||
if !sfs.Exists(forum.ID) {
|
|
||||||
forumUpdateMutex.Unlock()
|
|
||||||
return ErrNoRows
|
return ErrNoRows
|
||||||
}
|
}
|
||||||
sfs.forums[forum.ID] = forum
|
mfs.forums.Store(forum.ID, forum)
|
||||||
forumUpdateMutex.Unlock()
|
mfs.rebuildView()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) GetAll() ([]*Forum, error) {
|
func (mfs *MemoryForumStore) GetAll() (forumView []*Forum, err error) {
|
||||||
return sfs.forums, nil
|
mfs.forums.Range(func(_ interface{}, value interface{}) bool {
|
||||||
|
forumView = append(forumView, value.(*Forum))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return forumView, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement sub-forums.
|
func (mfs *MemoryForumStore) GetAllIDs() (ids []int, err error) {
|
||||||
/*func (sfs *StaticForumStore) GetChildren(parentID int, parentType string) ([]*Forum,error) {
|
mfs.forums.Range(func(_ interface{}, value interface{}) bool {
|
||||||
return nil, nil
|
ids = append(ids, value.(*Forum).ID)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return ids, nil
|
||||||
}
|
}
|
||||||
func (sfs *StaticForumStore) GetFirstChild(parentID int, parentType string) (*Forum,error) {
|
|
||||||
return nil, nil
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// We can cheat slightly, as the StaticForumStore has all the IDs under the cap ;)
|
func (mfs *MemoryForumStore) GetAllVisible() ([]*Forum, error) {
|
||||||
// Should we cache this? Well, it's only really used for superadmins right now.
|
return mfs.forumView.Load().([]*Forum), nil
|
||||||
func (sfs *StaticForumStore) GetAllIDs() ([]int, error) {
|
}
|
||||||
var max = sfs.forumCapCount
|
|
||||||
var ids = make([]int, max)
|
func (mfs *MemoryForumStore) GetAllVisibleIDs() ([]int, error) {
|
||||||
for i := 0; i < max; i++ {
|
forumView := mfs.forumView.Load().([]*Forum)
|
||||||
ids[i] = i
|
var ids = make([]int, len(forumView))
|
||||||
|
for i := 0; i < len(forumView); i++ {
|
||||||
|
ids[i] = forumView[i].ID
|
||||||
}
|
}
|
||||||
return ids, nil
|
return ids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) Exists(id int) bool {
|
// TODO: Implement sub-forums.
|
||||||
return (id <= sfs.forumCapCount) && (id >= 0) && sfs.forums[id].Name != ""
|
/*func (mfs *MemoryForumStore) GetChildren(parentID int, parentType string) ([]*Forum,error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (mfs *MemoryForumStore) GetFirstChild(parentID int, parentType string) (*Forum,error) {
|
||||||
|
return nil, nil
|
||||||
|
}*/
|
||||||
|
|
||||||
|
func (mfs *MemoryForumStore) Exists(id int) bool {
|
||||||
|
forum, ok := mfs.forums.Load(id)
|
||||||
|
return ok && forum.(*Forum).Name != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) Delete(id int) error {
|
// TODO: Batch deletions with name blanking? Is this necessary?
|
||||||
|
func (mfs *MemoryForumStore) Delete(id int) {
|
||||||
|
mfs.forums.Delete(id)
|
||||||
|
mfs.rebuildView()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mfs *MemoryForumStore) CascadeDelete(id int) error {
|
||||||
forumUpdateMutex.Lock()
|
forumUpdateMutex.Lock()
|
||||||
if !sfs.Exists(id) {
|
defer forumUpdateMutex.Unlock()
|
||||||
forumUpdateMutex.Unlock()
|
_, err := mfs.delete.Exec(id)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
sfs.forums[id].Name = ""
|
|
||||||
forumUpdateMutex.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sfs *StaticForumStore) CascadeDelete(id int) error {
|
|
||||||
forum, err := sfs.CascadeGet(id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
mfs.Delete(id)
|
||||||
forumUpdateMutex.Lock()
|
|
||||||
_, err = delete_forum_stmt.Exec(id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
forum.Name = ""
|
|
||||||
forumUpdateMutex.Unlock()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) IncrementTopicCount(id int) error {
|
func (mfs *MemoryForumStore) IncrementTopicCount(id int) error {
|
||||||
forum, err := sfs.CascadeGet(id)
|
forum, err := mfs.CascadeGet(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -240,8 +287,8 @@ func (sfs *StaticForumStore) IncrementTopicCount(id int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) DecrementTopicCount(id int) error {
|
func (mfs *MemoryForumStore) DecrementTopicCount(id int) error {
|
||||||
forum, err := sfs.CascadeGet(id)
|
forum, err := mfs.CascadeGet(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -253,19 +300,19 @@ func (sfs *StaticForumStore) DecrementTopicCount(id int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Have a pointer to the last topic rather than storing it on the forum itself
|
// TODO: Have a pointer to the last topic rather than storing it on the forum itself
|
||||||
func (sfs *StaticForumStore) UpdateLastTopic(topic_name string, tid int, username string, uid int, time string, fid int) error {
|
func (mfs *MemoryForumStore) UpdateLastTopic(topicName string, tid int, username string, uid int, time string, fid int) error {
|
||||||
forum, err := sfs.CascadeGet(fid)
|
forum, err := mfs.CascadeGet(fid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = update_forum_cache_stmt.Exec(topic_name, tid, username, uid, fid)
|
_, err = update_forum_cache_stmt.Exec(topicName, tid, username, uid, fid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
forum.LastTopic = topic_name
|
forum.LastTopic = topicName
|
||||||
forum.LastTopicID = tid
|
forum.LastTopicID = tid
|
||||||
forum.LastReplyer = username
|
forum.LastReplyer = username
|
||||||
forum.LastReplyerID = uid
|
forum.LastReplyerID = uid
|
||||||
|
@ -274,30 +321,7 @@ func (sfs *StaticForumStore) UpdateLastTopic(topic_name string, tid int, usernam
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) CreateForum(forumName string, forumDesc string, active bool, preset string) (int, error) {
|
func (mfs *MemoryForumStore) CreateForum(forumName string, forumDesc string, active bool, preset string) (int, error) {
|
||||||
var fid int
|
|
||||||
err := forum_entry_exists_stmt.QueryRow().Scan(&fid)
|
|
||||||
if err != nil && err != ErrNoRows {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if err != ErrNoRows {
|
|
||||||
forumUpdateMutex.Lock()
|
|
||||||
_, err = update_forum_stmt.Exec(forumName, forumDesc, active, preset, fid)
|
|
||||||
if err != nil {
|
|
||||||
return fid, err
|
|
||||||
}
|
|
||||||
forum, err := sfs.Get(fid)
|
|
||||||
if err != nil {
|
|
||||||
return 0, ErrCacheDesync
|
|
||||||
}
|
|
||||||
forum.Name = forumName
|
|
||||||
forum.Desc = forumDesc
|
|
||||||
forum.Active = active
|
|
||||||
forum.Preset = preset
|
|
||||||
forumUpdateMutex.Unlock()
|
|
||||||
return fid, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
forumCreateMutex.Lock()
|
forumCreateMutex.Lock()
|
||||||
res, err := create_forum_stmt.Exec(forumName, forumDesc, active, preset)
|
res, err := create_forum_stmt.Exec(forumName, forumDesc, active, preset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -308,34 +332,29 @@ func (sfs *StaticForumStore) CreateForum(forumName string, forumDesc string, act
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
fid = int(fid64)
|
fid := int(fid64)
|
||||||
|
|
||||||
sfs.forums = append(sfs.forums, &Forum{fid, buildForumUrl(nameToSlug(forumName), fid), forumName, forumDesc, active, preset, 0, "", 0, "", "", 0, "", 0, ""})
|
mfs.forums.Store(fid, &Forum{fid, buildForumURL(nameToSlug(forumName), fid), forumName, forumDesc, active, preset, 0, "", 0, "", "", 0, "", 0, ""})
|
||||||
sfs.forumCapCount++
|
mfs.forumCount++
|
||||||
|
|
||||||
// TO-DO: Add a GroupStore. How would it interact with the ForumStore?
|
// TODO: Add a GroupStore. How would it interact with the ForumStore?
|
||||||
permmapToQuery(presetToPermmap(preset), fid)
|
permmapToQuery(presetToPermmap(preset), fid)
|
||||||
forumCreateMutex.Unlock()
|
forumCreateMutex.Unlock()
|
||||||
|
|
||||||
|
if active {
|
||||||
|
mfs.rebuildView()
|
||||||
|
}
|
||||||
return fid, nil
|
return fid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sfs *StaticForumStore) fillForumIDGap(biggerID int, smallerID int) {
|
// TODO: Get the total count of forums in the forum store minus the blanked forums rather than doing a heavy query for this?
|
||||||
dummy := Forum{ID: 0, Name: "", Active: false, Preset: "all"}
|
|
||||||
for i := smallerID; i > biggerID; i++ {
|
|
||||||
sfs.forums = append(sfs.forums, &dummy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TO-DO: Get the total count of forums in the forum store minus the blanked forums rather than doing a heavy query for this?
|
|
||||||
// GetGlobalCount returns the total number of forums
|
// GetGlobalCount returns the total number of forums
|
||||||
func (sfs *StaticForumStore) GetGlobalCount() (fcount int) {
|
func (mfs *MemoryForumStore) GetGlobalCount() (fcount int) {
|
||||||
err := sfs.forumCount.QueryRow().Scan(&fcount)
|
err := mfs.getForumCount.QueryRow().Scan(&fcount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
return fcount
|
return fcount
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Work on MapForumStore
|
// TODO: Work on SqlForumStore
|
||||||
|
|
||||||
// TO-DO: Work on SqlForumStore
|
|
||||||
|
|
21
gen_mysql.go
21
gen_mysql.go
|
@ -43,6 +43,7 @@ var forum_entry_exists_stmt *sql.Stmt
|
||||||
var group_entry_exists_stmt *sql.Stmt
|
var group_entry_exists_stmt *sql.Stmt
|
||||||
var get_forum_topics_offset_stmt *sql.Stmt
|
var get_forum_topics_offset_stmt *sql.Stmt
|
||||||
var get_expired_scheduled_groups_stmt *sql.Stmt
|
var get_expired_scheduled_groups_stmt *sql.Stmt
|
||||||
|
var get_sync_stmt *sql.Stmt
|
||||||
var get_topic_replies_offset_stmt *sql.Stmt
|
var get_topic_replies_offset_stmt *sql.Stmt
|
||||||
var get_topic_list_stmt *sql.Stmt
|
var get_topic_list_stmt *sql.Stmt
|
||||||
var get_topic_user_stmt *sql.Stmt
|
var get_topic_user_stmt *sql.Stmt
|
||||||
|
@ -96,7 +97,6 @@ var increment_user_bigposts_stmt *sql.Stmt
|
||||||
var increment_user_megaposts_stmt *sql.Stmt
|
var increment_user_megaposts_stmt *sql.Stmt
|
||||||
var increment_user_topics_stmt *sql.Stmt
|
var increment_user_topics_stmt *sql.Stmt
|
||||||
var edit_profile_reply_stmt *sql.Stmt
|
var edit_profile_reply_stmt *sql.Stmt
|
||||||
var delete_forum_stmt *sql.Stmt
|
|
||||||
var update_forum_stmt *sql.Stmt
|
var update_forum_stmt *sql.Stmt
|
||||||
var update_setting_stmt *sql.Stmt
|
var update_setting_stmt *sql.Stmt
|
||||||
var update_plugin_stmt *sql.Stmt
|
var update_plugin_stmt *sql.Stmt
|
||||||
|
@ -110,6 +110,7 @@ var update_email_stmt *sql.Stmt
|
||||||
var verify_email_stmt *sql.Stmt
|
var verify_email_stmt *sql.Stmt
|
||||||
var set_temp_group_stmt *sql.Stmt
|
var set_temp_group_stmt *sql.Stmt
|
||||||
var update_word_filter_stmt *sql.Stmt
|
var update_word_filter_stmt *sql.Stmt
|
||||||
|
var bump_sync_stmt *sql.Stmt
|
||||||
var delete_reply_stmt *sql.Stmt
|
var delete_reply_stmt *sql.Stmt
|
||||||
var delete_topic_stmt *sql.Stmt
|
var delete_topic_stmt *sql.Stmt
|
||||||
var delete_profile_reply_stmt *sql.Stmt
|
var delete_profile_reply_stmt *sql.Stmt
|
||||||
|
@ -340,6 +341,12 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Print("Preparing get_sync statement.")
|
||||||
|
get_sync_stmt, err = db.Prepare("SELECT `last_update` FROM `sync`")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
log.Print("Preparing get_topic_replies_offset statement.")
|
log.Print("Preparing get_topic_replies_offset statement.")
|
||||||
get_topic_replies_offset_stmt, err = db.Prepare("SELECT `replies`.`rid`,`replies`.`content`,`replies`.`createdBy`,`replies`.`createdAt`,`replies`.`lastEdit`,`replies`.`lastEditBy`,`users`.`avatar`,`users`.`name`,`users`.`group`,`users`.`url_prefix`,`users`.`url_name`,`users`.`level`,`replies`.`ipaddress`,`replies`.`likeCount`,`replies`.`actionType` FROM `replies` LEFT JOIN `users` ON `replies`.`createdBy` = `users`.`uid` WHERE `tid` = ? LIMIT ?,?")
|
get_topic_replies_offset_stmt, err = db.Prepare("SELECT `replies`.`rid`,`replies`.`content`,`replies`.`createdBy`,`replies`.`createdAt`,`replies`.`lastEdit`,`replies`.`lastEditBy`,`users`.`avatar`,`users`.`name`,`users`.`group`,`users`.`url_prefix`,`users`.`url_name`,`users`.`level`,`replies`.`ipaddress`,`replies`.`likeCount`,`replies`.`actionType` FROM `replies` LEFT JOIN `users` ON `replies`.`createdBy` = `users`.`uid` WHERE `tid` = ? LIMIT ?,?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -658,12 +665,6 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing delete_forum statement.")
|
|
||||||
delete_forum_stmt, err = db.Prepare("UPDATE `forums` SET `name` = '',`active` = 0 WHERE `fid` = ?")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing update_forum statement.")
|
log.Print("Preparing update_forum statement.")
|
||||||
update_forum_stmt, err = db.Prepare("UPDATE `forums` SET `name` = ?,`desc` = ?,`active` = ?,`preset` = ? WHERE `fid` = ?")
|
update_forum_stmt, err = db.Prepare("UPDATE `forums` SET `name` = ?,`desc` = ?,`active` = ?,`preset` = ? WHERE `fid` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -742,6 +743,12 @@ func _gen_mysql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Print("Preparing bump_sync statement.")
|
||||||
|
bump_sync_stmt, err = db.Prepare("UPDATE `sync` SET `last_update` = UTC_TIMESTAMP()")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
log.Print("Preparing delete_reply statement.")
|
log.Print("Preparing delete_reply statement.")
|
||||||
delete_reply_stmt, err = db.Prepare("DELETE FROM `replies` WHERE `rid` = ?")
|
delete_reply_stmt, err = db.Prepare("DELETE FROM `replies` WHERE `rid` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
14
gen_pgsql.go
14
gen_pgsql.go
|
@ -32,7 +32,6 @@ var increment_user_bigposts_stmt *sql.Stmt
|
||||||
var increment_user_megaposts_stmt *sql.Stmt
|
var increment_user_megaposts_stmt *sql.Stmt
|
||||||
var increment_user_topics_stmt *sql.Stmt
|
var increment_user_topics_stmt *sql.Stmt
|
||||||
var edit_profile_reply_stmt *sql.Stmt
|
var edit_profile_reply_stmt *sql.Stmt
|
||||||
var delete_forum_stmt *sql.Stmt
|
|
||||||
var update_forum_stmt *sql.Stmt
|
var update_forum_stmt *sql.Stmt
|
||||||
var update_setting_stmt *sql.Stmt
|
var update_setting_stmt *sql.Stmt
|
||||||
var update_plugin_stmt *sql.Stmt
|
var update_plugin_stmt *sql.Stmt
|
||||||
|
@ -46,6 +45,7 @@ var update_email_stmt *sql.Stmt
|
||||||
var verify_email_stmt *sql.Stmt
|
var verify_email_stmt *sql.Stmt
|
||||||
var set_temp_group_stmt *sql.Stmt
|
var set_temp_group_stmt *sql.Stmt
|
||||||
var update_word_filter_stmt *sql.Stmt
|
var update_word_filter_stmt *sql.Stmt
|
||||||
|
var bump_sync_stmt *sql.Stmt
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
func _gen_pgsql() (err error) {
|
func _gen_pgsql() (err error) {
|
||||||
|
@ -203,12 +203,6 @@ func _gen_pgsql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Preparing delete_forum statement.")
|
|
||||||
delete_forum_stmt, err = db.Prepare("UPDATE `forums` SET `name` = '',`active` = 0 WHERE `fid` = ?")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Print("Preparing update_forum statement.")
|
log.Print("Preparing update_forum statement.")
|
||||||
update_forum_stmt, err = db.Prepare("UPDATE `forums` SET `name` = ?,`desc` = ?,`active` = ?,`preset` = ? WHERE `fid` = ?")
|
update_forum_stmt, err = db.Prepare("UPDATE `forums` SET `name` = ?,`desc` = ?,`active` = ?,`preset` = ? WHERE `fid` = ?")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -287,5 +281,11 @@ func _gen_pgsql() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Print("Preparing bump_sync statement.")
|
||||||
|
bump_sync_stmt, err = db.Prepare("UPDATE `sync` SET `last_update` = LOCALTIMESTAMP()")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,9 +65,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
if dev.SuperDebug {
|
if dev.SuperDebug {
|
||||||
log.Print("before route_static")
|
log.Print("before route_static")
|
||||||
log.Print("prefix:", prefix)
|
log.Print("prefix: ", prefix)
|
||||||
log.Print("req.URL.Path:", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("extra_data:", extra_data)
|
log.Print("extra_data: ", extra_data)
|
||||||
|
log.Print("req.Referer(): ", req.Referer())
|
||||||
}
|
}
|
||||||
|
|
||||||
if prefix == "/static" {
|
if prefix == "/static" {
|
||||||
|
@ -103,6 +104,9 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
case "/forum":
|
case "/forum":
|
||||||
route_forum(w,req,user,extra_data)
|
route_forum(w,req,user,extra_data)
|
||||||
return
|
return
|
||||||
|
case "/theme":
|
||||||
|
route_change_theme(w,req,user)
|
||||||
|
return
|
||||||
case "/report":
|
case "/report":
|
||||||
switch(req.URL.Path) {
|
switch(req.URL.Path) {
|
||||||
case "/report/submit/":
|
case "/report/submit/":
|
||||||
|
@ -230,7 +234,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
case "":
|
case "":
|
||||||
// Stop the favicons, robots.txt file, etc. resolving to the topics list
|
// Stop the favicons, robots.txt file, etc. resolving to the topics list
|
||||||
// TO-DO: Add support for favicons and robots.txt files
|
// TODO: Add support for favicons and robots.txt files
|
||||||
switch(extra_data) {
|
switch(extra_data) {
|
||||||
case "robots.txt":
|
case "robots.txt":
|
||||||
route_robots_txt(w,req)
|
route_robots_txt(w,req)
|
||||||
|
|
|
@ -56,8 +56,8 @@ func gloinit() error {
|
||||||
users = NewMemoryUserStore(config.UserCacheCapacity)
|
users = NewMemoryUserStore(config.UserCacheCapacity)
|
||||||
topics = NewMemoryTopicStore(config.TopicCacheCapacity)
|
topics = NewMemoryTopicStore(config.TopicCacheCapacity)
|
||||||
} else {
|
} else {
|
||||||
users = NewSqlUserStore()
|
users = NewSQLUserStore()
|
||||||
topics = NewSqlTopicStore()
|
topics = NewSQLTopicStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Loading the static files.")
|
log.Print("Loading the static files.")
|
||||||
|
@ -67,7 +67,7 @@ func gloinit() error {
|
||||||
}
|
}
|
||||||
auth = NewDefaultAuth()
|
auth = NewDefaultAuth()
|
||||||
|
|
||||||
err = initWordFilters()
|
err = LoadWordFilters()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Swap out LocalError for a panic for this?
|
// TODO: Swap out LocalError for a panic for this?
|
||||||
func BenchmarkTopicAdminRouteParallel(b *testing.B) {
|
func BenchmarkTopicAdminRouteParallel(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
if !gloinited {
|
if !gloinited {
|
||||||
|
@ -143,7 +143,7 @@ func BenchmarkTopicGuestRouteParallel(b *testing.B) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Make these routes compatible with the changes to the router
|
// TODO: Make these routes compatible with the changes to the router
|
||||||
/*
|
/*
|
||||||
func BenchmarkForumsAdminRouteParallel(b *testing.B) {
|
func BenchmarkForumsAdminRouteParallel(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
@ -1091,7 +1091,7 @@ func TestLevels(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Make this compatible with the changes to the router
|
// TODO: Make this compatible with the changes to the router
|
||||||
/*
|
/*
|
||||||
func TestStaticRoute(t *testing.T) {
|
func TestStaticRoute(t *testing.T) {
|
||||||
if !gloinited {
|
if !gloinited {
|
||||||
|
@ -1166,7 +1166,7 @@ func TestStaticRoute(t *testing.T) {
|
||||||
t.Print("No problems found in the topic-guest route!")
|
t.Print("No problems found in the topic-guest route!")
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// TO-DO: Make these routes compatible with the changes to the router
|
// TODO: Make these routes compatible with the changes to the router
|
||||||
/*
|
/*
|
||||||
func TestForumsAdminRoute(t *testing.T) {
|
func TestForumsAdminRoute(t *testing.T) {
|
||||||
if !gloinited {
|
if !gloinited {
|
||||||
|
|
|
@ -31,12 +31,12 @@ func _initPgsql() (err error) {
|
||||||
}
|
}
|
||||||
fmt.Println("Successfully connected to the database")
|
fmt.Println("Successfully connected to the database")
|
||||||
|
|
||||||
// TO-DO: Create the database, if it doesn't exist
|
// TODO: Create the database, if it doesn't exist
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func _pgEscapeBit(bit string) string {
|
func _pgEscapeBit(bit string) string {
|
||||||
// TO-DO: Write a custom parser, so that backslashes work properly in the sql.Open string. Do something similar for the database driver, if possible?
|
// TODO: Write a custom parser, so that backslashes work properly in the sql.Open string. Do something similar for the database driver, if possible?
|
||||||
return strings.Replace(bit, "'", "\\'", -1)
|
return strings.Replace(bit, "'", "\\'", -1)
|
||||||
}
|
}
|
||||||
|
|
48
main.go
48
main.go
|
@ -52,14 +52,14 @@ func init() {
|
||||||
wordFilterBox.Store(WordFilterBox(make(map[int]WordFilter)))
|
wordFilterBox.Store(WordFilterBox(make(map[int]WordFilter)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func initWordFilters() error {
|
func LoadWordFilters() error {
|
||||||
rows, err := get_word_filters_stmt.Query()
|
rows, err := get_word_filters_stmt.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
var wordFilters = wordFilterBox.Load().(WordFilterBox)
|
var wordFilters = WordFilterBox(make(map[int]WordFilter))
|
||||||
var wfid int
|
var wfid int
|
||||||
var find string
|
var find string
|
||||||
var replacement string
|
var replacement string
|
||||||
|
@ -92,8 +92,8 @@ func processConfig() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// TO-DO: Have a file for each run with the time/date the server started as the file name?
|
// TODO: Have a file for each run with the time/date the server started as the file name?
|
||||||
// TO-DO: Log panics with recover()
|
// TODO: Log panics with recover()
|
||||||
f, err := os.OpenFile("./operations.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
f, err := os.OpenFile("./operations.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -137,8 +137,8 @@ func main() {
|
||||||
users = NewMemoryUserStore(config.UserCacheCapacity)
|
users = NewMemoryUserStore(config.UserCacheCapacity)
|
||||||
topics = NewMemoryTopicStore(config.TopicCacheCapacity)
|
topics = NewMemoryTopicStore(config.TopicCacheCapacity)
|
||||||
} else {
|
} else {
|
||||||
users = NewSqlUserStore()
|
users = NewSQLUserStore()
|
||||||
topics = NewSqlTopicStore()
|
topics = NewSQLTopicStore()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Loading the static files.")
|
log.Print("Loading the static files.")
|
||||||
|
@ -156,7 +156,7 @@ func main() {
|
||||||
log.Print("Initialising the authentication system")
|
log.Print("Initialising the authentication system")
|
||||||
auth = NewDefaultAuth()
|
auth = NewDefaultAuth()
|
||||||
|
|
||||||
err = initWordFilters()
|
err = LoadWordFilters()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -174,16 +174,23 @@ func main() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
// TO-DO: Handle delayed moderation tasks
|
|
||||||
// TO-DO: Handle the daily clean-up. Move this to a 24 hour task?
|
// TODO: Handle delayed moderation tasks
|
||||||
// TO-DO: Sync with the database, if there are any changes
|
// TODO: Handle the daily clean-up. Move this to a 24 hour task?
|
||||||
// TO-DO: Manage the TopicStore, UserStore, and ForumStore
|
|
||||||
// TO-DO: Alert the admin, if CPU usage, RAM usage, or the number of posts in the past second are too high
|
// Sync with the database, if there are any changes
|
||||||
// TO-DO: Clean-up alerts with no unread matches which are over two weeks old. Move this to a 24 hour task?
|
err = handleServerSync()
|
||||||
|
if err != nil {
|
||||||
|
LogError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Manage the TopicStore, UserStore, and ForumStore
|
||||||
|
// TODO: Alert the admin, if CPU usage, RAM usage, or the number of posts in the past second are too high
|
||||||
|
// TODO: Clean-up alerts with no unread matches which are over two weeks old. Move this to a 24 hour task?
|
||||||
case <-fifteenMinuteTicker.C:
|
case <-fifteenMinuteTicker.C:
|
||||||
// TO-DO: Automatically lock topics, if they're really old, and the associated setting is enabled.
|
// TODO: Automatically lock topics, if they're really old, and the associated setting is enabled.
|
||||||
// TO-DO: Publish scheduled posts.
|
// TODO: Publish scheduled posts.
|
||||||
// TO-DO: Delete the empty users_groups_scheduler entries
|
// TODO: Delete the empty users_groups_scheduler entries
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -286,12 +293,13 @@ func main() {
|
||||||
// pprof.StopCPUProfile()
|
// pprof.StopCPUProfile()
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// TO-DO: Let users run *both* HTTP and HTTPS
|
// TODO: Let users run *both* HTTP and HTTPS
|
||||||
log.Print("Initialising the HTTP server")
|
log.Print("Initialising the HTTP server")
|
||||||
if !site.EnableSsl {
|
if !site.EnableSsl {
|
||||||
if site.Port == "" {
|
if site.Port == "" {
|
||||||
site.Port = "80"
|
site.Port = "80"
|
||||||
}
|
}
|
||||||
|
log.Print("Listening on port " + site.Port)
|
||||||
err = http.ListenAndServe(":"+site.Port, router)
|
err = http.ListenAndServe(":"+site.Port, router)
|
||||||
} else {
|
} else {
|
||||||
if site.Port == "" {
|
if site.Port == "" {
|
||||||
|
@ -299,14 +307,16 @@ func main() {
|
||||||
}
|
}
|
||||||
if site.Port == "80" || site.Port == "443" {
|
if site.Port == "80" || site.Port == "443" {
|
||||||
// We should also run the server on port 80
|
// We should also run the server on port 80
|
||||||
// TO-DO: Redirect to port 443
|
// TODO: Redirect to port 443
|
||||||
go func() {
|
go func() {
|
||||||
err = http.ListenAndServe(":80", &HttpsRedirect{})
|
log.Print("Listening on port 80")
|
||||||
|
err = http.ListenAndServe(":80", &HTTPSRedirect{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
log.Print("Listening on port " + site.Port)
|
||||||
err = http.ListenAndServeTLS(":"+site.Port, config.SslFullchain, config.SslPrivkey, router)
|
err = http.ListenAndServeTLS(":"+site.Port, config.SslFullchain, config.SslPrivkey, router)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
12
misc_test.go
12
misc_test.go
|
@ -3,7 +3,7 @@ package main
|
||||||
import "strconv"
|
import "strconv"
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
// TO-DO: Generate a test database to work with rather than a live one
|
// TODO: Generate a test database to work with rather than a live one
|
||||||
func TestUserStore(t *testing.T) {
|
func TestUserStore(t *testing.T) {
|
||||||
if !gloinited {
|
if !gloinited {
|
||||||
err := gloinit()
|
err := gloinit()
|
||||||
|
@ -18,14 +18,14 @@ func TestUserStore(t *testing.T) {
|
||||||
var user *User
|
var user *User
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
user, err = users.CascadeGet(-1)
|
_, err = users.CascadeGet(-1)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("UID #-1 shouldn't exist")
|
t.Error("UID #-1 shouldn't exist")
|
||||||
} else if err != ErrNoRows {
|
} else if err != ErrNoRows {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err = users.CascadeGet(0)
|
_, err = users.CascadeGet(0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("UID #0 shouldn't exist")
|
t.Error("UID #0 shouldn't exist")
|
||||||
} else if err != ErrNoRows {
|
} else if err != ErrNoRows {
|
||||||
|
@ -43,7 +43,7 @@ func TestUserStore(t *testing.T) {
|
||||||
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
t.Error("user.ID does not match the requested UID. Got '" + strconv.Itoa(user.ID) + "' instead.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Lock onto the specific error type. Is this even possible without sacrificing the detailed information in the error message?
|
// TODO: Lock onto the specific error type. Is this even possible without sacrificing the detailed information in the error message?
|
||||||
var userList map[int]*User
|
var userList map[int]*User
|
||||||
_, err = users.BulkCascadeGetMap([]int{-1})
|
_, err = users.BulkCascadeGetMap([]int{-1})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -90,7 +90,7 @@ func TestForumStore(t *testing.T) {
|
||||||
var forum *Forum
|
var forum *Forum
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
forum, err = fstore.CascadeGet(-1)
|
_, err = fstore.CascadeGet(-1)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("FID #-1 shouldn't exist")
|
t.Error("FID #-1 shouldn't exist")
|
||||||
} else if err != ErrNoRows {
|
} else if err != ErrNoRows {
|
||||||
|
@ -131,6 +131,8 @@ func TestForumStore(t *testing.T) {
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = forum
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSlugs(t *testing.T) {
|
func TestSlugs(t *testing.T) {
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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_socialgroups
|
||||||
func route_edit_topic(w http.ResponseWriter, r *http.Request, user User) {
|
func route_edit_topic(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
//log.Print("in route_edit_topic")
|
//log.Print("in route_edit_topic")
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
|
@ -35,7 +37,7 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add hooks to make use of headerLite
|
// TODO: Add hooks to make use of headerLite
|
||||||
_, ok := SimpleForumSessionCheck(w, r, &user, oldTopic.ParentID)
|
_, ok := SimpleForumSessionCheck(w, r, &user, oldTopic.ParentID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -108,6 +110,7 @@ func route_edit_topic(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Disable stat updates in posts handled by plugin_socialgroups
|
||||||
func route_delete_topic(w http.ResponseWriter, r *http.Request, user User) {
|
func route_delete_topic(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
tid, err := strconv.Atoi(r.URL.Path[len("/topic/delete/submit/"):])
|
tid, err := strconv.Atoi(r.URL.Path[len("/topic/delete/submit/"):])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -124,7 +127,7 @@ func route_delete_topic(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add hooks to make use of headerLite
|
// TODO: Add hooks to make use of headerLite
|
||||||
_, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID)
|
_, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -161,9 +164,15 @@ func route_delete_topic(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
//log.Print("Topic #" + strconv.Itoa(tid) + " was deleted by User #" + strconv.Itoa(user.ID))
|
//log.Print("Topic #" + strconv.Itoa(tid) + " was deleted by User #" + strconv.Itoa(user.ID))
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
|
||||||
wcount := wordCount(topic.Content)
|
topicCreator, err := users.CascadeGet(topic.CreatedBy)
|
||||||
err = decrease_post_user_stats(wcount, topic.CreatedBy, true, user)
|
if err == nil {
|
||||||
if err != nil {
|
wcount := wordCount(topic.Content)
|
||||||
|
err = topicCreator.decreasePostStats(wcount, true)
|
||||||
|
if err != nil {
|
||||||
|
InternalError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if err != ErrNoRows {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -192,7 +201,7 @@ func route_stick_topic(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add hooks to make use of headerLite
|
// TODO: Add hooks to make use of headerLite
|
||||||
_, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID)
|
_, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -248,7 +257,7 @@ func route_unstick_topic(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add hooks to make use of headerLite
|
// TODO: Add hooks to make use of headerLite
|
||||||
_, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID)
|
_, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -288,6 +297,8 @@ func route_unstick_topic(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Disable stat updates in posts handled by plugin_socialgroups
|
||||||
|
// TODO: Update the stats after edits so that we don't under or over decrement stats during deletes
|
||||||
func route_reply_edit_submit(w http.ResponseWriter, r *http.Request, user User) {
|
func route_reply_edit_submit(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -327,7 +338,7 @@ func route_reply_edit_submit(w http.ResponseWriter, r *http.Request, user User)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add hooks to make use of headerLite
|
// TODO: Add hooks to make use of headerLite
|
||||||
_, ok := SimpleForumSessionCheck(w, r, &user, fid)
|
_, ok := SimpleForumSessionCheck(w, r, &user, fid)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -344,6 +355,7 @@ func route_reply_edit_submit(w http.ResponseWriter, r *http.Request, user User)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Disable stat updates in posts handled by plugin_socialgroups
|
||||||
func route_reply_delete_submit(w http.ResponseWriter, r *http.Request, user User) {
|
func route_reply_delete_submit(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
err := r.ParseForm()
|
err := r.ParseForm()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -377,7 +389,7 @@ func route_reply_delete_submit(w http.ResponseWriter, r *http.Request, user User
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add hooks to make use of headerLite
|
// TODO: Add hooks to make use of headerLite
|
||||||
_, ok := SimpleForumSessionCheck(w, r, &user, fid)
|
_, ok := SimpleForumSessionCheck(w, r, &user, fid)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -399,9 +411,15 @@ func route_reply_delete_submit(w http.ResponseWriter, r *http.Request, user User
|
||||||
w.Write(successJSONBytes)
|
w.Write(successJSONBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
wcount := wordCount(reply.Content)
|
replyCreator, err := users.CascadeGet(reply.CreatedBy)
|
||||||
err = decrease_post_user_stats(wcount, reply.CreatedBy, false, user)
|
if err == nil {
|
||||||
if err != nil {
|
wcount := wordCount(reply.Content)
|
||||||
|
err = replyCreator.decreasePostStats(wcount, false)
|
||||||
|
if err != nil {
|
||||||
|
InternalErrorJSQ(err, w, r, isJs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if err != ErrNoRows {
|
||||||
InternalErrorJSQ(err, w, r, isJs)
|
InternalErrorJSQ(err, w, r, isJs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -524,7 +542,7 @@ func route_ips(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
|
|
||||||
ip := html.EscapeString(r.URL.Path[len("/users/ips/"):])
|
ip := html.EscapeString(r.URL.Path[len("/users/ips/"):])
|
||||||
var uid int
|
var uid int
|
||||||
var reqUserList map[int]bool = make(map[int]bool)
|
var reqUserList = make(map[int]bool)
|
||||||
|
|
||||||
rows, err := find_users_by_ip_users_stmt.Query(ip)
|
rows, err := find_users_by_ip_users_stmt.Query(ip)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -590,14 +608,14 @@ func route_ips(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the user ID map to a slice, then bulk load the users
|
// Convert the user ID map to a slice, then bulk load the users
|
||||||
var idSlice []int = make([]int, len(reqUserList))
|
var idSlice = make([]int, len(reqUserList))
|
||||||
var i int
|
var i int
|
||||||
for userID := range reqUserList {
|
for userID := range reqUserList {
|
||||||
idSlice[i] = userID
|
idSlice[i] = userID
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: What if a user is deleted via the Control Panel?
|
// TODO: What if a user is deleted via the Control Panel?
|
||||||
userList, err := users.BulkCascadeGetMap(idSlice)
|
userList, err := users.BulkCascadeGetMap(idSlice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
|
@ -616,7 +634,7 @@ func route_ips(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: This is being replaced with the new ban route system
|
// TODO: This is being replaced with the new ban route system
|
||||||
/*func route_ban(w http.ResponseWriter, r *http.Request, user User) {
|
/*func route_ban(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
headerVars, ok := SessionCheck(w,r,&user)
|
headerVars, ok := SessionCheck(w,r,&user)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
2
mysql.go
2
mysql.go
|
@ -66,7 +66,7 @@ func _initDatabase() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Is there a less noisy way of doing this for tests?
|
// TODO: Is there a less noisy way of doing this for tests?
|
||||||
log.Print("Preparing get_activity_feed_by_watcher statement.")
|
log.Print("Preparing get_activity_feed_by_watcher statement.")
|
||||||
get_activity_feed_by_watcher_stmt, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID FROM `activity_stream_matches` INNER JOIN `activity_stream` ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE `watcher` = ? ORDER BY activity_stream.asid ASC LIMIT 8")
|
get_activity_feed_by_watcher_stmt, err = db.Prepare("SELECT activity_stream_matches.asid, activity_stream.actor, activity_stream.targetUser, activity_stream.event, activity_stream.elementType, activity_stream.elementID FROM `activity_stream_matches` INNER JOIN `activity_stream` ON activity_stream_matches.asid = activity_stream.asid AND activity_stream_matches.watcher != activity_stream.actor WHERE `watcher` = ? ORDER BY activity_stream.asid ASC LIMIT 8")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -24,7 +24,7 @@ CREATE TABLE `forums`(
|
||||||
`active` tinyint DEFAULT 1 not null,
|
`active` tinyint DEFAULT 1 not null,
|
||||||
`topicCount` int DEFAULT 0 not null,
|
`topicCount` int DEFAULT 0 not null,
|
||||||
`preset` varchar(100) DEFAULT '' not null,
|
`preset` varchar(100) DEFAULT '' not null,
|
||||||
`parentID` int DEFAULT 0 not null, /* TO-DO: Add support for subforums */
|
`parentID` int DEFAULT 0 not null, /* TODO: Add support for subforums */
|
||||||
`parentType` varchar(50) DEFAULT '' not null,
|
`parentType` varchar(50) DEFAULT '' not null,
|
||||||
`lastTopic` varchar(100) DEFAULT '' not null,
|
`lastTopic` varchar(100) DEFAULT '' not null,
|
||||||
`lastTopicID` int DEFAULT 0 not null,
|
`lastTopicID` int DEFAULT 0 not null,
|
||||||
|
@ -181,16 +181,13 @@ CREATE TABLE `administration_logs`(
|
||||||
`doneAt` datetime not null
|
`doneAt` datetime not null
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE `sync`(
|
INSERT INTO sync(`last_update`) VALUES (UTC_TIMESTAMP());
|
||||||
`last_update` int not null,
|
|
||||||
`node_id` int not null
|
|
||||||
);
|
|
||||||
|
|
||||||
INSERT INTO settings(`name`,`content`,`type`) VALUES ('url_tags','1','bool');
|
INSERT INTO settings(`name`,`content`,`type`) VALUES ('url_tags','1','bool');
|
||||||
INSERT INTO settings(`name`,`content`,`type`,`constraints`) VALUES ('activation_type','1','list','1-3');
|
INSERT INTO settings(`name`,`content`,`type`,`constraints`) VALUES ('activation_type','1','list','1-3');
|
||||||
INSERT INTO settings(`name`,`content`,`type`) VALUES ('bigpost_min_words','250','int');
|
INSERT INTO settings(`name`,`content`,`type`) VALUES ('bigpost_min_words','250','int');
|
||||||
INSERT INTO settings(`name`,`content`,`type`) VALUES ('megapost_min_words','1000','int');
|
INSERT INTO settings(`name`,`content`,`type`) VALUES ('megapost_min_words','1000','int');
|
||||||
/* TO-DO: Implement the html-attribute setting type before deploying this */
|
/* TODO: Implement the html-attribute setting type before deploying this */
|
||||||
/*INSERT INTO settings(`name`,`content`,`type`) VALUES ('meta_desc','','html-attribute');*/
|
/*INSERT INTO settings(`name`,`content`,`type`) VALUES ('meta_desc','','html-attribute');*/
|
||||||
INSERT INTO themes(`uname`,`default`) VALUES ('tempra-simple',1);
|
INSERT INTO themes(`uname`,`default`) VALUES ('tempra-simple',1);
|
||||||
INSERT INTO emails(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,1);
|
INSERT INTO emails(`email`,`uid`,`validated`) VALUES ('admin@localhost',1,1);
|
||||||
|
|
|
@ -6,33 +6,33 @@ import "errors"
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
var wsHub WS_Hub
|
var wsHub WS_Hub
|
||||||
var wsNouser error = errors.New("This user isn't connected via WebSockets")
|
var errWsNouser = errors.New("This user isn't connected via WebSockets")
|
||||||
|
|
||||||
type WS_Hub struct {
|
type WS_Hub struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_ *WS_Hub) guest_count() int {
|
func (_ *WS_Hub) guestCount() int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_ *WS_Hub) user_count() int {
|
func (_ *WS_Hub) userCount() int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *WS_Hub) broadcast_message(_ string) error {
|
func (hub *WS_Hub) broadcastMessage(_ string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *WS_Hub) push_message(_ int, _ string) error {
|
func (hub *WS_Hub) pushMessage(_ int, _ string) error {
|
||||||
return wsNouser
|
return errWsNouser
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *WS_Hub) push_alert(_ int, _ int, _ string, _ string, _ int, _ int, _ int) error {
|
func (hub *WS_Hub) pushAlert(_ int, _ int, _ string, _ string, _ int, _ int, _ int) error {
|
||||||
return wsNouser
|
return errWsNouser
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *WS_Hub) push_alerts(_ []int, _ int, _ string, _ string, _ int, _ int, _ int) error {
|
func (hub *WS_Hub) pushAlerts(_ []int, _ int, _ string, _ string, _ int, _ int, _ int) error {
|
||||||
return wsNouser
|
return errWsNouser
|
||||||
}
|
}
|
||||||
|
|
||||||
func route_websockets(_ http.ResponseWriter, _ *http.Request, _ User) {
|
func route_websockets(_ http.ResponseWriter, _ *http.Request, _ User) {
|
||||||
|
|
13
pages.go
13
pages.go
|
@ -15,11 +15,12 @@ type HeaderVars struct {
|
||||||
Widgets PageWidgets
|
Widgets PageWidgets
|
||||||
Site *Site
|
Site *Site
|
||||||
Settings map[string]interface{}
|
Settings map[string]interface{}
|
||||||
|
Themes map[string]Theme // TODO: Use a slice containing every theme instead of the main map for speed
|
||||||
ThemeName string
|
ThemeName string
|
||||||
ExtData ExtData
|
ExtData ExtData
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add this to routes which don't use templates. E.g. Json APIs.
|
// TODO: Add this to routes which don't use templates. E.g. Json APIs.
|
||||||
type HeaderLite struct {
|
type HeaderLite struct {
|
||||||
Site *Site
|
Site *Site
|
||||||
Settings SettingBox
|
Settings SettingBox
|
||||||
|
@ -36,7 +37,7 @@ type PageWidgets struct {
|
||||||
items map[string]interface{} // Key: pluginname
|
items map[string]interface{} // Key: pluginname
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// TO-DO: Add a ExtDataHolder interface with methods for manipulating the contents?
|
// TODO: Add a ExtDataHolder interface with methods for manipulating the contents?
|
||||||
type ExtData struct {
|
type ExtData struct {
|
||||||
items map[string]interface{} // Key: pluginname
|
items map[string]interface{} // Key: pluginname
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
|
@ -531,7 +532,7 @@ func parseMessage(msg string /*, user User*/) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
outbytes = append(outbytes, urlOpen...)
|
outbytes = append(outbytes, urlOpen...)
|
||||||
var urlBit = []byte(buildForumUrl("", fid))
|
var urlBit = []byte(buildForumURL("", fid))
|
||||||
outbytes = append(outbytes, urlBit...)
|
outbytes = append(outbytes, urlBit...)
|
||||||
outbytes = append(outbytes, urlOpen2...)
|
outbytes = append(outbytes, urlOpen2...)
|
||||||
var fidBit = []byte("#fid-" + strconv.Itoa(fid))
|
var fidBit = []byte("#fid-" + strconv.Itoa(fid))
|
||||||
|
@ -539,7 +540,7 @@ func parseMessage(msg string /*, user User*/) string {
|
||||||
outbytes = append(outbytes, urlClose...)
|
outbytes = append(outbytes, urlClose...)
|
||||||
lastItem = i
|
lastItem = i
|
||||||
} else {
|
} else {
|
||||||
// TO-DO: Forum Shortcode Link
|
// TODO: Forum Shortcode Link
|
||||||
}
|
}
|
||||||
} else if msgbytes[i] == '@' {
|
} else if msgbytes[i] == '@' {
|
||||||
//log.Print("IN @")
|
//log.Print("IN @")
|
||||||
|
@ -798,7 +799,7 @@ func coerceIntBytes(data []byte) (res int, length int) {
|
||||||
return conv, i
|
return conv, i
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Write tests for this
|
// TODO: Write tests for this
|
||||||
func paginate(count int, perPage int, maxPages int) []int {
|
func paginate(count int, perPage int, maxPages int) []int {
|
||||||
if count < perPage {
|
if count < perPage {
|
||||||
return []int{1}
|
return []int{1}
|
||||||
|
@ -815,7 +816,7 @@ func paginate(count int, perPage int, maxPages int) []int {
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Write tests for this
|
// TODO: Write tests for this
|
||||||
func pageOffset(count int, page int, perPage int) (int, int, int) {
|
func pageOffset(count int, page int, perPage int) (int, int, int) {
|
||||||
var offset int
|
var offset int
|
||||||
lastPage := (count / perPage) + 1
|
lastPage := (count / perPage) + 1
|
||||||
|
|
|
@ -253,6 +253,7 @@ func route_panel_forums_create_submit(w http.ResponseWriter, r *http.Request, us
|
||||||
http.Redirect(w, r, "/panel/forums/", http.StatusSeeOther)
|
http.Redirect(w, r, "/panel/forums/", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Revamp this
|
||||||
func route_panel_forums_delete(w http.ResponseWriter, r *http.Request, user User, sfid string) {
|
func route_panel_forums_delete(w http.ResponseWriter, r *http.Request, user User, sfid string) {
|
||||||
headerVars, stats, ok := PanelSessionCheck(w, r, &user)
|
headerVars, stats, ok := PanelSessionCheck(w, r, &user)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -282,8 +283,8 @@ func route_panel_forums_delete(w http.ResponseWriter, r *http.Request, user User
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm_msg := "Are you sure you want to delete the '" + forum.Name + "' forum?"
|
confirmMsg := "Are you sure you want to delete the '" + forum.Name + "' forum?"
|
||||||
yousure := AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid), confirm_msg}
|
yousure := AreYouSure{"/panel/forums/delete/submit/" + strconv.Itoa(fid), confirmMsg}
|
||||||
|
|
||||||
pi := PanelPage{"Delete Forum", user, headerVars, stats, tList, yousure}
|
pi := PanelPage{"Delete Forum", user, headerVars, stats, tList, yousure}
|
||||||
if preRenderHooks["pre_render_panel_delete_forum"] != nil {
|
if preRenderHooks["pre_render_panel_delete_forum"] != nil {
|
||||||
|
@ -406,10 +407,10 @@ func route_panel_forums_edit_submit(w http.ResponseWriter, r *http.Request, user
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
forum_name := r.PostFormValue("forum_name")
|
forumName := r.PostFormValue("forum_name")
|
||||||
forum_desc := r.PostFormValue("forum_desc")
|
forumDesc := r.PostFormValue("forum_desc")
|
||||||
forum_preset := stripInvalidPreset(r.PostFormValue("forum_preset"))
|
forumPreset := stripInvalidPreset(r.PostFormValue("forum_preset"))
|
||||||
forum_active := r.PostFormValue("forum_active")
|
forumActive := r.PostFormValue("forum_active")
|
||||||
|
|
||||||
forum, err := fstore.CascadeGet(fid)
|
forum, err := fstore.CascadeGet(fid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
|
@ -420,40 +421,40 @@ func route_panel_forums_edit_submit(w http.ResponseWriter, r *http.Request, user
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if forum_name == "" {
|
if forumName == "" {
|
||||||
forum_name = forum.Name
|
forumName = forum.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
var active bool
|
var active bool
|
||||||
if forum_active == "" {
|
if forumActive == "" {
|
||||||
active = forum.Active
|
active = forum.Active
|
||||||
} else if forum_active == "1" || forum_active == "Show" {
|
} else if forumActive == "1" || forumActive == "Show" {
|
||||||
active = true
|
active = true
|
||||||
} else {
|
} else {
|
||||||
active = false
|
active = false
|
||||||
}
|
}
|
||||||
|
|
||||||
forumUpdateMutex.Lock()
|
forumUpdateMutex.Lock()
|
||||||
_, err = update_forum_stmt.Exec(forum_name, forum_desc, active, forum_preset, fid)
|
_, err = update_forum_stmt.Exec(forumName, forumDesc, active, forumPreset, fid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalErrorJSQ(err, w, r, isJs)
|
InternalErrorJSQ(err, w, r, isJs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if forum.Name != forum_name {
|
if forum.Name != forumName {
|
||||||
forum.Name = forum_name
|
forum.Name = forumName
|
||||||
}
|
}
|
||||||
if forum.Desc != forum_desc {
|
if forum.Desc != forumDesc {
|
||||||
forum.Desc = forum_desc
|
forum.Desc = forumDesc
|
||||||
}
|
}
|
||||||
if forum.Active != active {
|
if forum.Active != active {
|
||||||
forum.Active = active
|
forum.Active = active
|
||||||
}
|
}
|
||||||
if forum.Preset != forum_preset {
|
if forum.Preset != forumPreset {
|
||||||
forum.Preset = forum_preset
|
forum.Preset = forumPreset
|
||||||
}
|
}
|
||||||
forumUpdateMutex.Unlock()
|
forumUpdateMutex.Unlock()
|
||||||
|
|
||||||
permmapToQuery(presetToPermmap(forum_preset), fid)
|
permmapToQuery(presetToPermmap(forumPreset), fid)
|
||||||
|
|
||||||
if !isJs {
|
if !isJs {
|
||||||
http.Redirect(w, r, "/panel/forums/", http.StatusSeeOther)
|
http.Redirect(w, r, "/panel/forums/", http.StatusSeeOther)
|
||||||
|
@ -495,8 +496,8 @@ func route_panel_forums_edit_perms_submit(w http.ResponseWriter, r *http.Request
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
perm_preset := stripInvalidGroupForumPreset(r.PostFormValue("perm_preset"))
|
permPreset := stripInvalidGroupForumPreset(r.PostFormValue("perm_preset"))
|
||||||
fperms, changed := groupForumPresetToForumPerms(perm_preset)
|
fperms, changed := groupForumPresetToForumPerms(permPreset)
|
||||||
|
|
||||||
forum, err := fstore.CascadeGet(fid)
|
forum, err := fstore.CascadeGet(fid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
|
@ -519,7 +520,7 @@ func route_panel_forums_edit_perms_submit(w http.ResponseWriter, r *http.Request
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = add_forum_perms_to_group_stmt.Exec(gid, fid, perm_preset, perms)
|
_, err = add_forum_perms_to_group_stmt.Exec(gid, fid, permPreset, perms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalErrorJSQ(err, w, r, isJs)
|
InternalErrorJSQ(err, w, r, isJs)
|
||||||
return
|
return
|
||||||
|
@ -552,7 +553,7 @@ func route_panel_settings(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.Print("headerVars.Settings",headerVars.Settings)
|
//log.Print("headerVars.Settings",headerVars.Settings)
|
||||||
var settingList map[string]interface{} = make(map[string]interface{})
|
var settingList = make(map[string]interface{})
|
||||||
rows, err := get_settings_stmt.Query()
|
rows, err := get_settings_stmt.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
|
@ -560,6 +561,7 @@ func route_panel_settings(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
|
// nolint need the type so people viewing this file understand what it returns without visiting setting.go
|
||||||
var settingLabels map[string]string = GetAllSettingLabels()
|
var settingLabels map[string]string = GetAllSettingLabels()
|
||||||
var sname, scontent, stype string
|
var sname, scontent, stype string
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
|
@ -722,7 +724,7 @@ func route_panel_word_filters(w http.ResponseWriter, r *http.Request, user User)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var filterList WordFilterBox = wordFilterBox.Load().(WordFilterBox)
|
var filterList = wordFilterBox.Load().(WordFilterBox)
|
||||||
pi := PanelPage{"Word Filter Manager", user, headerVars, stats, tList, filterList}
|
pi := PanelPage{"Word Filter Manager", user, headerVars, stats, tList, filterList}
|
||||||
if preRenderHooks["pre_render_panel_word_filters"] != nil {
|
if preRenderHooks["pre_render_panel_word_filters"] != nil {
|
||||||
if runPreRenderHook("pre_render_panel_word_filters", w, r, &user, &pi) {
|
if runPreRenderHook("pre_render_panel_word_filters", w, r, &user, &pi) {
|
||||||
|
@ -766,14 +768,14 @@ func route_panel_word_filters_create(w http.ResponseWriter, r *http.Request, use
|
||||||
InternalErrorJSQ(err, w, r, isJs)
|
InternalErrorJSQ(err, w, r, isJs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
lastId, err := res.LastInsertId()
|
lastID, err := res.LastInsertId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalErrorJSQ(err, w, r, isJs)
|
InternalErrorJSQ(err, w, r, isJs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
addWordFilter(int(lastId), find, replacement)
|
addWordFilter(int(lastID), find, replacement)
|
||||||
http.Redirect(w, r, "/panel/settings/word-filters/", http.StatusSeeOther) // TO-DO: Return json for JS?
|
http.Redirect(w, r, "/panel/settings/word-filters/", http.StatusSeeOther) // TODO: Return json for JS?
|
||||||
}
|
}
|
||||||
|
|
||||||
func route_panel_word_filters_edit(w http.ResponseWriter, r *http.Request, user User, wfid string) {
|
func route_panel_word_filters_edit(w http.ResponseWriter, r *http.Request, user User, wfid string) {
|
||||||
|
@ -811,7 +813,7 @@ func route_panel_word_filters_edit_submit(w http.ResponseWriter, r *http.Request
|
||||||
PreError("Bad Form", w, r)
|
PreError("Bad Form", w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TO-DO: Either call it isJs or js rather than flip-flopping back and forth across the routes x.x
|
// TODO: Either call it isJs or js rather than flip-flopping back and forth across the routes x.x
|
||||||
isJs := (r.PostFormValue("isJs") == "1")
|
isJs := (r.PostFormValue("isJs") == "1")
|
||||||
if !user.Perms.EditSettings {
|
if !user.Perms.EditSettings {
|
||||||
NoPermissionsJSQ(w, r, user, isJs)
|
NoPermissionsJSQ(w, r, user, isJs)
|
||||||
|
@ -943,7 +945,7 @@ func route_panel_plugins_activate(w http.ResponseWriter, r *http.Request, user U
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var has_plugin bool = (err == nil)
|
var hasPlugin = (err == nil)
|
||||||
|
|
||||||
if plugins[uname].Activate != nil {
|
if plugins[uname].Activate != nil {
|
||||||
err = plugins[uname].Activate()
|
err = plugins[uname].Activate()
|
||||||
|
@ -955,7 +957,7 @@ func route_panel_plugins_activate(w http.ResponseWriter, r *http.Request, user U
|
||||||
|
|
||||||
//log.Print("err",err)
|
//log.Print("err",err)
|
||||||
//log.Print("active",active)
|
//log.Print("active",active)
|
||||||
if has_plugin {
|
if hasPlugin {
|
||||||
if active {
|
if active {
|
||||||
LocalError("The plugin is already active", w, r, user)
|
LocalError("The plugin is already active", w, r, user)
|
||||||
return
|
return
|
||||||
|
@ -1071,7 +1073,7 @@ func route_panel_plugins_install(w http.ResponseWriter, r *http.Request, user Us
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var has_plugin bool = (err == nil)
|
var hasPlugin = (err == nil)
|
||||||
|
|
||||||
if plugins[uname].Install != nil {
|
if plugins[uname].Install != nil {
|
||||||
err = plugins[uname].Install()
|
err = plugins[uname].Install()
|
||||||
|
@ -1089,7 +1091,7 @@ func route_panel_plugins_install(w http.ResponseWriter, r *http.Request, user Us
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_plugin {
|
if hasPlugin {
|
||||||
_, err = update_plugin_install_stmt.Exec(1, uname)
|
_, err = update_plugin_install_stmt.Exec(1, uname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
|
@ -1139,7 +1141,7 @@ func route_panel_users(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
// TO-DO: Add a UserStore method for iterating over global users and global user offsets
|
// TODO: Add a UserStore method for iterating over global users and global user offsets
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
puser := User{ID: 0}
|
puser := User{ID: 0}
|
||||||
err := rows.Scan(&puser.ID, &puser.Name, &puser.Group, &puser.Active, &puser.IsSuperAdmin, &puser.Avatar)
|
err := rows.Scan(&puser.ID, &puser.Name, &puser.Group, &puser.Active, &puser.IsSuperAdmin, &puser.Avatar)
|
||||||
|
@ -1485,7 +1487,7 @@ func route_panel_groups_edit_perms(w http.ResponseWriter, r *http.Request, user
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Load the phrases in bulk for efficiency?
|
// TODO: Load the phrases in bulk for efficiency?
|
||||||
var localPerms []NameLangToggle
|
var localPerms []NameLangToggle
|
||||||
localPerms = append(localPerms, NameLangToggle{"ViewTopic", GetLocalPermPhrase("ViewTopic"), group.Perms.ViewTopic})
|
localPerms = append(localPerms, NameLangToggle{"ViewTopic", GetLocalPermPhrase("ViewTopic"), group.Perms.ViewTopic})
|
||||||
localPerms = append(localPerms, NameLangToggle{"LikeItem", GetLocalPermPhrase("LikeItem"), group.Perms.LikeItem})
|
localPerms = append(localPerms, NameLangToggle{"LikeItem", GetLocalPermPhrase("LikeItem"), group.Perms.LikeItem})
|
||||||
|
@ -1877,7 +1879,9 @@ func route_panel_themes_set_default(w http.ResponseWriter, r *http.Request, user
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Make this less racey
|
// TODO: Make this less racey
|
||||||
|
changeDefaultThemeMutex.Lock()
|
||||||
|
defaultTheme := defaultThemeBox.Load().(string)
|
||||||
_, err = update_theme_stmt.Exec(0, defaultTheme)
|
_, err = update_theme_stmt.Exec(0, defaultTheme)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
|
@ -1896,9 +1900,10 @@ func route_panel_themes_set_default(w http.ResponseWriter, r *http.Request, user
|
||||||
dTheme.Active = false
|
dTheme.Active = false
|
||||||
themes[defaultTheme] = dTheme
|
themes[defaultTheme] = dTheme
|
||||||
|
|
||||||
defaultTheme = uname // TO-DO: Make this less racey
|
defaultThemeBox.Store(uname)
|
||||||
resetTemplateOverrides()
|
resetTemplateOverrides()
|
||||||
mapThemeTemplates(theme)
|
mapThemeTemplates(theme)
|
||||||
|
changeDefaultThemeMutex.Unlock()
|
||||||
|
|
||||||
http.Redirect(w, r, "/panel/themes/", http.StatusSeeOther)
|
http.Redirect(w, r, "/panel/themes/", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
|
@ -508,7 +508,7 @@ func stripInvalidPreset(preset string) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Move this into the phrase system?
|
// TODO: Move this into the phrase system?
|
||||||
func presetToLang(preset string) string {
|
func presetToLang(preset string) string {
|
||||||
switch preset {
|
switch preset {
|
||||||
case "all":
|
case "all":
|
||||||
|
@ -530,6 +530,7 @@ func presetToLang(preset string) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Is this racey?
|
||||||
func rebuildGroupPermissions(gid int) error {
|
func rebuildGroupPermissions(gid int) error {
|
||||||
var permstr []byte
|
var permstr []byte
|
||||||
log.Print("Reloading a group")
|
log.Print("Reloading a group")
|
||||||
|
@ -538,15 +539,15 @@ func rebuildGroupPermissions(gid int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp_perms := Perms{
|
tmpPerms := Perms{
|
||||||
//ExtData: make(map[string]bool),
|
//ExtData: make(map[string]bool),
|
||||||
}
|
}
|
||||||
err = json.Unmarshal(permstr, &tmp_perms)
|
err = json.Unmarshal(permstr, &tmpPerms)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
groups[gid].Perms = tmp_perms
|
groups[gid].Perms = tmpPerms
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,7 +559,7 @@ func overridePerms(perms *Perms, status bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: We need a better way of overriding forum perms rather than setting them one by one
|
// TODO: We need a better way of overriding forum perms rather than setting them one by one
|
||||||
func overrideForumPerms(perms *Perms, status bool) {
|
func overrideForumPerms(perms *Perms, status bool) {
|
||||||
perms.ViewTopic = status
|
perms.ViewTopic = status
|
||||||
perms.LikeItem = status
|
perms.LikeItem = status
|
||||||
|
|
6
pgsql.go
6
pgsql.go
|
@ -10,7 +10,7 @@ import "database/sql"
|
||||||
import _ "github.com/lib/pq"
|
import _ "github.com/lib/pq"
|
||||||
import "./query_gen/lib"
|
import "./query_gen/lib"
|
||||||
|
|
||||||
// TO-DO: Add support for SSL for all database drivers, not just pgsql
|
// TODO: Add support for SSL for all database drivers, not just pgsql
|
||||||
var db_sslmode = "disable" // verify-full
|
var db_sslmode = "disable" // verify-full
|
||||||
var get_activity_feed_by_watcher_stmt *sql.Stmt
|
var get_activity_feed_by_watcher_stmt *sql.Stmt
|
||||||
var get_activity_count_by_watcher_stmt *sql.Stmt
|
var get_activity_count_by_watcher_stmt *sql.Stmt
|
||||||
|
@ -24,7 +24,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func _init_database() (err error) {
|
func _init_database() (err error) {
|
||||||
// TO-DO: Investigate connect_timeout to see what it does exactly and whether it's relevant to us
|
// TODO: Investigate connect_timeout to see what it does exactly and whether it's relevant to us
|
||||||
var _dbpassword string
|
var _dbpassword string
|
||||||
if(dbpassword != ""){
|
if(dbpassword != ""){
|
||||||
_dbpassword = " password='" + _escape_bit(db_config.Password) + "'"
|
_dbpassword = " password='" + _escape_bit(db_config.Password) + "'"
|
||||||
|
@ -68,6 +68,6 @@ func _init_database() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func _escape_bit(bit string) string {
|
func _escape_bit(bit string) string {
|
||||||
// TO-DO: Write a custom parser, so that backslashes work properly in the sql.Open string. Do something similar for the database driver, if possible?
|
// TODO: Write a custom parser, so that backslashes work properly in the sql.Open string. Do something similar for the database driver, if possible?
|
||||||
return strings.Replace(bit,"'","\\'",-1)
|
return strings.Replace(bit,"'","\\'",-1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TO-DO: Let the admin edit phrases from inside the Control Panel? How should we persist these? Should we create a copy of the langpack or edit the primaries? Use the changeLangpack mutex for this?
|
// TODO: Let the admin edit phrases from inside the Control Panel? How should we persist these? Should we create a copy of the langpack or edit the primaries? Use the changeLangpack mutex for this?
|
||||||
|
// nolint Be quiet megacheck, this *is* used
|
||||||
var changeLangpackMutex sync.Mutex
|
var changeLangpackMutex sync.Mutex
|
||||||
var currentLanguage = "english"
|
var currentLanguage = "english"
|
||||||
var currentLangPack atomic.Value
|
var currentLangPack atomic.Value
|
||||||
|
@ -33,7 +34,8 @@ type LanguagePack struct {
|
||||||
SettingLabels map[string]string
|
SettingLabels map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Move the english language pack into it's own file and just keep the common logic here
|
// TODO: Add the ability to edit language JSON files from the Control Panel and automatically scan the files for changes
|
||||||
|
// TODO: Move the english language pack into a JSON file and load that on start-up
|
||||||
var langpacks = map[string]*LanguagePack{
|
var langpacks = map[string]*LanguagePack{
|
||||||
"english": &LanguagePack{
|
"english": &LanguagePack{
|
||||||
Name: "english",
|
Name: "english",
|
||||||
|
@ -135,7 +137,8 @@ func DeletePhrase() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Use atomics to store the pointer of the current active langpack?
|
// TODO: Use atomics to store the pointer of the current active langpack?
|
||||||
|
// nolint
|
||||||
func ChangeLanguagePack(name string) (exists bool) {
|
func ChangeLanguagePack(name string) (exists bool) {
|
||||||
changeLangpackMutex.Lock()
|
changeLangpackMutex.Lock()
|
||||||
pack, ok := langpacks[name]
|
pack, ok := langpacks[name]
|
||||||
|
|
|
@ -203,9 +203,8 @@ func bbcodeParseWithoutCode(msg string) string {
|
||||||
msg = bbcodeURLLabel.ReplaceAllString(msg, "<a href='$1$2//$3' rel='nofollow'>$4</i>")
|
msg = bbcodeURLLabel.ReplaceAllString(msg, "<a href='$1$2//$3' rel='nofollow'>$4</i>")
|
||||||
msg = bbcodeQuotes.ReplaceAllString(msg, "<span class='postQuote'>$1</span>")
|
msg = bbcodeQuotes.ReplaceAllString(msg, "<span class='postQuote'>$1</span>")
|
||||||
return bbcodeCode.ReplaceAllString(msg, "<span class='codequotes'>$1</span>")
|
return bbcodeCode.ReplaceAllString(msg, "<span class='codequotes'>$1</span>")
|
||||||
} else {
|
|
||||||
return string(msgbytes)
|
|
||||||
}
|
}
|
||||||
|
return string(msgbytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Does every type of BBCode
|
// Does every type of BBCode
|
||||||
|
@ -368,7 +367,7 @@ func bbcodeFullParse(msg string) string {
|
||||||
goto MainLoop
|
goto MainLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add support for negative numbers?
|
// TODO: Add support for negative numbers?
|
||||||
if number < 0 {
|
if number < 0 {
|
||||||
outbytes = append(outbytes, bbcodeNoNegative...)
|
outbytes = append(outbytes, bbcodeNoNegative...)
|
||||||
goto MainLoop
|
goto MainLoop
|
||||||
|
|
|
@ -4,7 +4,7 @@ package main
|
||||||
import "regexp"
|
import "regexp"
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
var markdownMaxDepth int = 25 // How deep the parser will go when parsing Markdown strings
|
var markdownMaxDepth = 25 // How deep the parser will go when parsing Markdown strings
|
||||||
var markdownUnclosedElement []byte
|
var markdownUnclosedElement []byte
|
||||||
|
|
||||||
var markdownBoldTagOpen, markdownBoldTagClose []byte
|
var markdownBoldTagOpen, markdownBoldTagClose []byte
|
||||||
|
@ -88,7 +88,7 @@ func _markdownParse(msg string, n int) string {
|
||||||
|
|
||||||
switch msg[index] {
|
switch msg[index] {
|
||||||
case '_':
|
case '_':
|
||||||
var startIndex int = index
|
var startIndex = index
|
||||||
if (index + 1) >= len(msg) {
|
if (index + 1) >= len(msg) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ func _markdownParse(msg string, n int) string {
|
||||||
lastElement = index
|
lastElement = index
|
||||||
index--
|
index--
|
||||||
case '~':
|
case '~':
|
||||||
var startIndex int = index
|
var startIndex = index
|
||||||
if (index + 1) >= len(msg) {
|
if (index + 1) >= len(msg) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -142,8 +142,8 @@ func _markdownParse(msg string, n int) string {
|
||||||
//log.Print("start string(msg[index])",string(msg[index]))
|
//log.Print("start string(msg[index])",string(msg[index]))
|
||||||
//log.Print("start []byte(msg[:index])",[]byte(msg[:index]))
|
//log.Print("start []byte(msg[:index])",[]byte(msg[:index]))
|
||||||
|
|
||||||
var startIndex int = index
|
var startIndex = index
|
||||||
var italic bool = true
|
var italic = true
|
||||||
var bold bool
|
var bold bool
|
||||||
if (index + 2) < len(msg) {
|
if (index + 2) < len(msg) {
|
||||||
//log.Print("start index + 1",index + 1)
|
//log.Print("start index + 1",index + 1)
|
||||||
|
@ -339,7 +339,7 @@ SwitchLoop:
|
||||||
// plugin_markdown doesn't support lists yet, but I want it to be easy to have nested lists when we do have them
|
// plugin_markdown doesn't support lists yet, but I want it to be easy to have nested lists when we do have them
|
||||||
func markdownSkipList(data string, index int) int {
|
func markdownSkipList(data string, index int) int {
|
||||||
var lastNewline int
|
var lastNewline int
|
||||||
var datalen int = len(data)
|
var datalen = len(data)
|
||||||
|
|
||||||
for ; index < datalen; index++ {
|
for ; index < datalen; index++ {
|
||||||
SkipListInnerLoop:
|
SkipListInnerLoop:
|
||||||
|
|
|
@ -25,7 +25,7 @@ var socialgroupsAttachForumStmt *sql.Stmt
|
||||||
var socialgroupsUnattachForumStmt *sql.Stmt
|
var socialgroupsUnattachForumStmt *sql.Stmt
|
||||||
var socialgroupsAddMemberStmt *sql.Stmt
|
var socialgroupsAddMemberStmt *sql.Stmt
|
||||||
|
|
||||||
// TO-DO: Add a better way of splitting up giant plugins like this
|
// TODO: Add a better way of splitting up giant plugins like this
|
||||||
|
|
||||||
// SocialGroup is a struct representing a social group
|
// SocialGroup is a struct representing a social group
|
||||||
type SocialGroup struct {
|
type SocialGroup struct {
|
||||||
|
@ -87,12 +87,12 @@ type SocialGroupMember struct {
|
||||||
RankString string /* Member, Mod, Admin, Owner */
|
RankString string /* Member, Mod, Admin, Owner */
|
||||||
PostCount int
|
PostCount int
|
||||||
JoinedAt string
|
JoinedAt string
|
||||||
Offline bool // TO-DO: Need to track the online states of members when WebSockets are enabled
|
Offline bool // TODO: Need to track the online states of members when WebSockets are enabled
|
||||||
|
|
||||||
User User
|
User User
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add a plugin interface instead of having a bunch of argument to AddPlugin?
|
// TODO: Add a plugin interface instead of having a bunch of argument to AddPlugin?
|
||||||
func init() {
|
func init() {
|
||||||
plugins["socialgroups"] = NewPlugin("socialgroups", "Social Groups", "Azareal", "http://github.com/Azareal", "", "", "", initSocialgroups, nil, deactivateSocialgroups, installSocialgroups, nil)
|
plugins["socialgroups"] = NewPlugin("socialgroups", "Social Groups", "Azareal", "http://github.com/Azareal", "", "", "", initSocialgroups, nil, deactivateSocialgroups, installSocialgroups, nil)
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ func initSocialgroups() (err error) {
|
||||||
plugins["socialgroups"].AddHook("pre_render_view_forum", socialgroupsPreRenderViewForum)
|
plugins["socialgroups"].AddHook("pre_render_view_forum", socialgroupsPreRenderViewForum)
|
||||||
plugins["socialgroups"].AddHook("simple_forum_check_pre_perms", socialgroupsForumCheck)
|
plugins["socialgroups"].AddHook("simple_forum_check_pre_perms", socialgroupsForumCheck)
|
||||||
plugins["socialgroups"].AddHook("forum_check_pre_perms", socialgroupsForumCheck)
|
plugins["socialgroups"].AddHook("forum_check_pre_perms", socialgroupsForumCheck)
|
||||||
// TO-DO: Auto-grant this perm to admins upon installation?
|
// TODO: Auto-grant this perm to admins upon installation?
|
||||||
registerPluginPerm("CreateSocialGroup")
|
registerPluginPerm("CreateSocialGroup")
|
||||||
router.HandleFunc("/groups/", socialgroupsGroupList)
|
router.HandleFunc("/groups/", socialgroupsGroupList)
|
||||||
router.HandleFunc("/group/", socialgroupsViewGroup)
|
router.HandleFunc("/group/", socialgroupsViewGroup)
|
||||||
|
@ -175,7 +175,7 @@ func deactivateSocialgroups() {
|
||||||
_ = socialgroupsAddMemberStmt.Close()
|
_ = socialgroupsAddMemberStmt.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Stop accessing the query builder directly and add a feature in Gosora which is more easily reversed, if an error comes up during the installation process
|
// TODO: Stop accessing the query builder directly and add a feature in Gosora which is more easily reversed, if an error comes up during the installation process
|
||||||
func installSocialgroups() error {
|
func installSocialgroups() error {
|
||||||
sgTableStmt, err := qgen.Builder.CreateTable("socialgroups", "utf8mb4", "utf8mb4_general_ci",
|
sgTableStmt, err := qgen.Builder.CreateTable("socialgroups", "utf8mb4", "utf8mb4_general_ci",
|
||||||
[]qgen.DB_Table_Column{
|
[]qgen.DB_Table_Column{
|
||||||
|
@ -229,9 +229,9 @@ func uninstallSocialgroups() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Do this properly via the widget system
|
// TODO: Do this properly via the widget system
|
||||||
func socialgroupsCommonAreaWidgets(headerVars *HeaderVars) {
|
func socialgroupsCommonAreaWidgets(headerVars *HeaderVars) {
|
||||||
// TO-DO: Hot Groups? Featured Groups? Official Groups?
|
// TODO: Hot Groups? Featured Groups? Official Groups?
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
var menu = WidgetMenu{"Social Groups", []WidgetMenuItem{
|
var menu = WidgetMenu{"Social Groups", []WidgetMenuItem{
|
||||||
WidgetMenuItem{"Create Group", "/group/create/", false},
|
WidgetMenuItem{"Create Group", "/group/create/", false},
|
||||||
|
@ -243,15 +243,15 @@ func socialgroupsCommonAreaWidgets(headerVars *HeaderVars) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if themes[defaultTheme].Sidebars == "left" {
|
if themes[headerVars.ThemeName].Sidebars == "left" {
|
||||||
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
||||||
} else if themes[defaultTheme].Sidebars == "right" || themes[defaultTheme].Sidebars == "both" {
|
} else if themes[headerVars.ThemeName].Sidebars == "right" || themes[headerVars.ThemeName].Sidebars == "both" {
|
||||||
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Do this properly via the widget system
|
// TODO: Do this properly via the widget system
|
||||||
// TO-DO: Make a better more customisable group widget system
|
// TODO: Make a better more customisable group widget system
|
||||||
func socialgroupsGroupWidgets(headerVars *HeaderVars, sgItem *SocialGroup) (success bool) {
|
func socialgroupsGroupWidgets(headerVars *HeaderVars, sgItem *SocialGroup) (success bool) {
|
||||||
return false // Disabled until the next commit
|
return false // Disabled until the next commit
|
||||||
|
|
||||||
|
@ -267,9 +267,9 @@ func socialgroupsGroupWidgets(headerVars *HeaderVars, sgItem *SocialGroup) (succ
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if themes[defaultTheme].Sidebars == "left" {
|
if themes[headerVars.ThemeName].Sidebars == "left" {
|
||||||
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
headerVars.Widgets.LeftSidebar = template.HTML(string(b.Bytes()))
|
||||||
} else if themes[defaultTheme].Sidebars == "right" || themes[defaultTheme].Sidebars == "both" {
|
} else if themes[headerVars.ThemeName].Sidebars == "right" || themes[headerVars.ThemeName].Sidebars == "both" {
|
||||||
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
headerVars.Widgets.RightSidebar = template.HTML(string(b.Bytes()))
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
@ -356,7 +356,7 @@ func socialgroupsCreateGroup(w http.ResponseWriter, r *http.Request, user User)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TO-DO: Add an approval queue mode for group creation
|
// TODO: Add an approval queue mode for group creation
|
||||||
if !user.Loggedin || !user.PluginPerms["CreateSocialGroup"] {
|
if !user.Loggedin || !user.PluginPerms["CreateSocialGroup"] {
|
||||||
NoPermissions(w, r, user)
|
NoPermissions(w, r, user)
|
||||||
return
|
return
|
||||||
|
@ -371,7 +371,7 @@ func socialgroupsCreateGroup(w http.ResponseWriter, r *http.Request, user User)
|
||||||
}
|
}
|
||||||
|
|
||||||
func socialgroupsCreateGroupSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
func socialgroupsCreateGroupSubmit(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
// TO-DO: Add an approval queue mode for group creation
|
// TODO: Add an approval queue mode for group creation
|
||||||
if !user.Loggedin || !user.PluginPerms["CreateSocialGroup"] {
|
if !user.Loggedin || !user.PluginPerms["CreateSocialGroup"] {
|
||||||
NoPermissions(w, r, user)
|
NoPermissions(w, r, user)
|
||||||
return
|
return
|
||||||
|
@ -561,7 +561,7 @@ func socialgroupsTrowAssign(args ...interface{}) interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: It would be nice, if you could select one of the boards in the group from that drop-down rather than just the one you got linked from
|
// TODO: It would be nice, if you could select one of the boards in the group from that drop-down rather than just the one you got linked from
|
||||||
func socialgroupsTopicCreatePreLoop(args ...interface{}) interface{} {
|
func socialgroupsTopicCreatePreLoop(args ...interface{}) interface{} {
|
||||||
var fid = args[2].(int)
|
var fid = args[2].(int)
|
||||||
if fstore.DirtyGet(fid).ParentType == "socialgroup" {
|
if fstore.DirtyGet(fid).ParentType == "socialgroup" {
|
||||||
|
@ -571,9 +571,9 @@ func socialgroupsTopicCreatePreLoop(args ...interface{}) interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add privacy options
|
// TODO: Add privacy options
|
||||||
// TO-DO: Add support for multiple boards and add per-board simplified permissions
|
// TODO: Add support for multiple boards and add per-board simplified permissions
|
||||||
// TO-DO: Take isJs into account for routes which expect JSON responses
|
// TODO: Take isJs into account for routes which expect JSON responses
|
||||||
func socialgroupsForumCheck(args ...interface{}) (skip interface{}) {
|
func socialgroupsForumCheck(args ...interface{}) (skip interface{}) {
|
||||||
var r = args[1].(*http.Request)
|
var r = args[1].(*http.Request)
|
||||||
var fid = args[3].(*int)
|
var fid = args[3].(*int)
|
||||||
|
@ -604,10 +604,10 @@ func socialgroupsForumCheck(args ...interface{}) (skip interface{}) {
|
||||||
var posts int
|
var posts int
|
||||||
var joinedAt string
|
var joinedAt string
|
||||||
|
|
||||||
// TO-DO: Group privacy settings. For now, groups are all globally visible
|
// TODO: Group privacy settings. For now, groups are all globally visible
|
||||||
|
|
||||||
// Clear the default group permissions
|
// Clear the default group permissions
|
||||||
// TO-DO: Do this more efficiently, doing it quick and dirty for now to get this out quickly
|
// TODO: Do this more efficiently, doing it quick and dirty for now to get this out quickly
|
||||||
overrideForumPerms(&user.Perms, false)
|
overrideForumPerms(&user.Perms, false)
|
||||||
user.Perms.ViewTopic = true
|
user.Perms.ViewTopic = true
|
||||||
|
|
||||||
|
@ -620,7 +620,7 @@ func socialgroupsForumCheck(args ...interface{}) (skip interface{}) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement bans properly by adding the Local Ban API in the next commit
|
// TODO: Implement bans properly by adding the Local Ban API in the next commit
|
||||||
if rank < 0 {
|
if rank < 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -641,7 +641,7 @@ func socialgroupsForumCheck(args ...interface{}) (skip interface{}) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Override redirects? I don't think this is needed quite yet
|
// TODO: Override redirects? I don't think this is needed quite yet
|
||||||
|
|
||||||
func socialgroupsWidgets(args ...interface{}) interface{} {
|
func socialgroupsWidgets(args ...interface{}) interface{} {
|
||||||
var zone = args[0].(string)
|
var zone = args[0].(string)
|
||||||
|
|
|
@ -3,7 +3,7 @@ package main
|
||||||
import "strconv"
|
import "strconv"
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
// TO-DO: Replace the soft tabs with hard ones
|
// TODO: Replace the soft tabs with hard ones
|
||||||
// go test -v
|
// go test -v
|
||||||
|
|
||||||
type ME_Pair struct {
|
type ME_Pair struct {
|
||||||
|
|
|
@ -19,7 +19,7 @@ function bind_to_alerts() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add the ability for users to dismiss alerts
|
// TODO: Add the ability for users to dismiss alerts
|
||||||
function load_alerts(menu_alerts)
|
function load_alerts(menu_alerts)
|
||||||
{
|
{
|
||||||
var alertListNode = menu_alerts.getElementsByClassName("alertList")[0];
|
var alertListNode = menu_alerts.getElementsByClassName("alertList")[0];
|
||||||
|
@ -119,7 +119,7 @@ $(document).ready(function(){
|
||||||
|
|
||||||
conn.onopen = function() {
|
conn.onopen = function() {
|
||||||
conn.send("page " + document.location.pathname + '\r');
|
conn.send("page " + document.location.pathname + '\r');
|
||||||
// TO-DO: Don't ask again, if it's denied. We could have a setting in the UCP which automatically requests this when someone flips desktop notifications on
|
// TODO: Don't ask again, if it's denied. We could have a setting in the UCP which automatically requests this when someone flips desktop notifications on
|
||||||
Notification.requestPermission();
|
Notification.requestPermission();
|
||||||
}
|
}
|
||||||
conn.onclose = function() {
|
conn.onclose = function() {
|
||||||
|
@ -148,15 +148,15 @@ $(document).ready(function(){
|
||||||
for (var i = 0; i < alertList.length; i++) alist += alertList[i];
|
for (var i = 0; i < alertList.length; i++) alist += alertList[i];
|
||||||
|
|
||||||
//console.log(alist);
|
//console.log(alist);
|
||||||
// TO-DO: Add support for other alert feeds like PM Alerts
|
// TODO: Add support for other alert feeds like PM Alerts
|
||||||
var general_alerts = document.getElementById("general_alerts");
|
var general_alerts = document.getElementById("general_alerts");
|
||||||
var alertListNode = general_alerts.getElementsByClassName("alertList")[0];
|
var alertListNode = general_alerts.getElementsByClassName("alertList")[0];
|
||||||
var alertCounterNode = general_alerts.getElementsByClassName("alert_counter")[0];
|
var alertCounterNode = general_alerts.getElementsByClassName("alert_counter")[0];
|
||||||
alertListNode.innerHTML = alist;
|
alertListNode.innerHTML = alist;
|
||||||
alertCounterNode.textContent = alertCount;
|
alertCounterNode.textContent = alertCount;
|
||||||
|
|
||||||
// TO-DO: Add some sort of notification queue to avoid flooding the end-user with notices?
|
// TODO: Add some sort of notification queue to avoid flooding the end-user with notices?
|
||||||
// TO-DO: Use the site name instead of "Something Happened"
|
// TODO: Use the site name instead of "Something Happened"
|
||||||
if(Notification.permission === "granted") {
|
if(Notification.permission === "granted") {
|
||||||
var n = new Notification("Something Happened",{
|
var n = new Notification("Something Happened",{
|
||||||
body: msg,
|
body: msg,
|
||||||
|
@ -354,7 +354,7 @@ $(document).ready(function(){
|
||||||
});
|
});
|
||||||
|
|
||||||
// This one's for Tempra Conflux
|
// This one's for Tempra Conflux
|
||||||
// TO-DO: We might want to use pure JS here
|
// TODO: We might want to use pure JS here
|
||||||
$(".ip_item").each(function(){
|
$(".ip_item").each(function(){
|
||||||
var ip = this.textContent;
|
var ip = this.textContent;
|
||||||
if(ip.length > 10){
|
if(ip.length > 10){
|
||||||
|
@ -396,6 +396,32 @@ $(document).ready(function(){
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$("#themeSelectorSelect").change(function(){
|
||||||
|
console.log("Changing the theme to " + this.options[this.selectedIndex].getAttribute("val"));
|
||||||
|
$.ajax({
|
||||||
|
url: this.form.getAttribute("action") + "?session=" + session,
|
||||||
|
type: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
data: { "newTheme": this.options[this.selectedIndex].getAttribute("val"), isJs: "1" },
|
||||||
|
success: function (data, status, xhr) {
|
||||||
|
console.log("Theme successfully switched");
|
||||||
|
console.log("data",data);
|
||||||
|
console.log("status",status);
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
// TODO: Use a standard error handler for the AJAX calls in here which throws up the response (if JSON) in a .notice? Might be difficult to trace errors in the console, if we reuse the same function every-time
|
||||||
|
error: function(xhr,status,errstr) {
|
||||||
|
console.log("The AJAX request failed");
|
||||||
|
console.log("xhr",xhr);
|
||||||
|
console.log("status",status);
|
||||||
|
console.log("errstr",errstr);
|
||||||
|
if(status=="parsererror") {
|
||||||
|
console.log("The server didn't respond with a valid JSON response");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
this.onkeyup = function(event) {
|
this.onkeyup = function(event) {
|
||||||
if(event.which == 37) this.querySelectorAll("#prevFloat a")[0].click();
|
if(event.which == 37) this.querySelectorAll("#prevFloat a")[0].click();
|
||||||
if(event.which == 39) this.querySelectorAll("#nextFloat a")[0].click();
|
if(event.which == 39) this.querySelectorAll("#nextFloat a")[0].click();
|
||||||
|
|
|
@ -14,7 +14,7 @@ type DB_Install_Instruction struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// A set of wrappers around the generator methods, so we can use this in the installer
|
// A set of wrappers around the generator methods, so we can use this in the installer
|
||||||
// TO-DO: Re-implement the query generation, query builder and installer adapters as layers on-top of a query text adapter
|
// TODO: Re-implement the query generation, query builder and installer adapters as layers on-top of a query text adapter
|
||||||
type installer struct {
|
type installer struct {
|
||||||
adapter DB_Adapter
|
adapter DB_Adapter
|
||||||
instructions []DB_Install_Instruction
|
instructions []DB_Install_Instruction
|
||||||
|
|
|
@ -54,7 +54,7 @@ func (adapter *Mysql_Adapter) CreateTable(name string, table string, charset str
|
||||||
}
|
}
|
||||||
|
|
||||||
var end string
|
var end string
|
||||||
// TO-DO: Exclude the other variants of text like mediumtext and longtext too
|
// TODO: Exclude the other variants of text like mediumtext and longtext too
|
||||||
if column.Default != "" && column.Type != "text" {
|
if column.Default != "" && column.Type != "text" {
|
||||||
end = " DEFAULT "
|
end = " DEFAULT "
|
||||||
if adapter.stringyType(column.Type) && column.Default != "''" {
|
if adapter.stringyType(column.Type) && column.Default != "''" {
|
||||||
|
@ -814,7 +814,7 @@ func (adapter *Mysql_Adapter) Write() error {
|
||||||
var stmts, body string
|
var stmts, body string
|
||||||
for _, name := range adapter.BufferOrder {
|
for _, name := range adapter.BufferOrder {
|
||||||
stmt := adapter.Buffer[name]
|
stmt := adapter.Buffer[name]
|
||||||
// TO-DO: Add support for create-table? Table creation might be a little complex for Go to do outside a SQL file :(
|
// TODO: Add support for create-table? Table creation might be a little complex for Go to do outside a SQL file :(
|
||||||
if stmt.Type != "create-table" {
|
if stmt.Type != "create-table" {
|
||||||
stmts += "var " + name + "_stmt *sql.Stmt\n"
|
stmts += "var " + name + "_stmt *sql.Stmt\n"
|
||||||
body += `
|
body += `
|
||||||
|
|
|
@ -29,7 +29,7 @@ func (adapter *Pgsql_Adapter) GetStmts() map[string]DB_Stmt {
|
||||||
return adapter.Buffer
|
return adapter.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement this
|
// TODO: Implement this
|
||||||
// We may need to change the CreateTable API to better suit PGSQL and the other database drivers which are coming up
|
// We may need to change the CreateTable API to better suit PGSQL and the other database drivers which are coming up
|
||||||
func (adapter *Pgsql_Adapter) CreateTable(name string, table string, charset string, collation string, columns []DB_Table_Column, keys []DB_Table_Key) (string, error) {
|
func (adapter *Pgsql_Adapter) CreateTable(name string, table string, charset string, collation string, columns []DB_Table_Column, keys []DB_Table_Key) (string, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
|
@ -93,7 +93,7 @@ func (adapter *Pgsql_Adapter) CreateTable(name string, table string, charset str
|
||||||
return querystr, nil
|
return querystr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement this
|
// TODO: Implement this
|
||||||
func (adapter *Pgsql_Adapter) SimpleInsert(name string, table string, columns string, fields string) (string, error) {
|
func (adapter *Pgsql_Adapter) SimpleInsert(name string, table string, columns string, fields string) (string, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return "", errors.New("You need a name for this statement")
|
return "", errors.New("You need a name for this statement")
|
||||||
|
@ -110,7 +110,7 @@ func (adapter *Pgsql_Adapter) SimpleInsert(name string, table string, columns st
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement this
|
// TODO: Implement this
|
||||||
func (adapter *Pgsql_Adapter) SimpleReplace(name string, table string, columns string, fields string) (string, error) {
|
func (adapter *Pgsql_Adapter) SimpleReplace(name string, table string, columns string, fields string) (string, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return "", errors.New("You need a name for this statement")
|
return "", errors.New("You need a name for this statement")
|
||||||
|
@ -127,7 +127,7 @@ func (adapter *Pgsql_Adapter) SimpleReplace(name string, table string, columns s
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implemented, but we need CreateTable and a better installer to *test* it
|
// TODO: Implemented, but we need CreateTable and a better installer to *test* it
|
||||||
func (adapter *Pgsql_Adapter) SimpleUpdate(name string, table string, set string, where string) (string, error) {
|
func (adapter *Pgsql_Adapter) SimpleUpdate(name string, table string, set string, where string) (string, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return "", errors.New("You need a name for this statement")
|
return "", errors.New("You need a name for this statement")
|
||||||
|
@ -144,7 +144,7 @@ func (adapter *Pgsql_Adapter) SimpleUpdate(name string, table string, set string
|
||||||
for _, token := range item.Expr {
|
for _, token := range item.Expr {
|
||||||
switch token.Type {
|
switch token.Type {
|
||||||
case "function":
|
case "function":
|
||||||
// TO-DO: Write a more sophisticated function parser on the utils side.
|
// TODO: Write a more sophisticated function parser on the utils side.
|
||||||
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
|
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
|
||||||
token.Contents = "LOCALTIMESTAMP()"
|
token.Contents = "LOCALTIMESTAMP()"
|
||||||
}
|
}
|
||||||
|
@ -170,7 +170,7 @@ func (adapter *Pgsql_Adapter) SimpleUpdate(name string, table string, set string
|
||||||
for _, token := range loc.Expr {
|
for _, token := range loc.Expr {
|
||||||
switch token.Type {
|
switch token.Type {
|
||||||
case "function":
|
case "function":
|
||||||
// TO-DO: Write a more sophisticated function parser on the utils side. What's the situation in regards to case sensitivity?
|
// TODO: Write a more sophisticated function parser on the utils side. What's the situation in regards to case sensitivity?
|
||||||
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
|
if strings.ToUpper(token.Contents) == "UTC_TIMESTAMP()" {
|
||||||
token.Contents = "LOCALTIMESTAMP()"
|
token.Contents = "LOCALTIMESTAMP()"
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,7 @@ func (adapter *Pgsql_Adapter) SimpleUpdate(name string, table string, set string
|
||||||
return querystr, nil
|
return querystr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement this
|
// TODO: Implement this
|
||||||
func (adapter *Pgsql_Adapter) SimpleDelete(name string, table string, where string) (string, error) {
|
func (adapter *Pgsql_Adapter) SimpleDelete(name string, table string, where string) (string, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return "", errors.New("You need a name for this statement")
|
return "", errors.New("You need a name for this statement")
|
||||||
|
@ -208,7 +208,7 @@ func (adapter *Pgsql_Adapter) SimpleDelete(name string, table string, where stri
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement this
|
// TODO: Implement this
|
||||||
// We don't want to accidentally wipe tables, so we'll have a seperate method for purging tables instead
|
// We don't want to accidentally wipe tables, so we'll have a seperate method for purging tables instead
|
||||||
func (adapter *Pgsql_Adapter) Purge(name string, table string) (string, error) {
|
func (adapter *Pgsql_Adapter) Purge(name string, table string) (string, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
|
@ -220,7 +220,7 @@ func (adapter *Pgsql_Adapter) Purge(name string, table string) (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement this
|
// TODO: Implement this
|
||||||
func (adapter *Pgsql_Adapter) SimpleSelect(name string, table string, columns string, where string, orderby string, limit string) (string, error) {
|
func (adapter *Pgsql_Adapter) SimpleSelect(name string, table string, columns string, where string, orderby string, limit string) (string, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return "", errors.New("You need a name for this statement")
|
return "", errors.New("You need a name for this statement")
|
||||||
|
@ -234,7 +234,7 @@ func (adapter *Pgsql_Adapter) SimpleSelect(name string, table string, columns st
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement this
|
// TODO: Implement this
|
||||||
func (adapter *Pgsql_Adapter) SimpleLeftJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) {
|
func (adapter *Pgsql_Adapter) SimpleLeftJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return "", errors.New("You need a name for this statement")
|
return "", errors.New("You need a name for this statement")
|
||||||
|
@ -254,7 +254,7 @@ func (adapter *Pgsql_Adapter) SimpleLeftJoin(name string, table1 string, table2
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement this
|
// TODO: Implement this
|
||||||
func (adapter *Pgsql_Adapter) SimpleInnerJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) {
|
func (adapter *Pgsql_Adapter) SimpleInnerJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return "", errors.New("You need a name for this statement")
|
return "", errors.New("You need a name for this statement")
|
||||||
|
@ -274,22 +274,22 @@ func (adapter *Pgsql_Adapter) SimpleInnerJoin(name string, table1 string, table2
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement this
|
// TODO: Implement this
|
||||||
func (adapter *Pgsql_Adapter) SimpleInsertSelect(name string, ins DB_Insert, sel DB_Select) (string, error) {
|
func (adapter *Pgsql_Adapter) SimpleInsertSelect(name string, ins DB_Insert, sel DB_Select) (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement this
|
// TODO: Implement this
|
||||||
func (adapter *Pgsql_Adapter) SimpleInsertLeftJoin(name string, ins DB_Insert, sel DB_Join) (string, error) {
|
func (adapter *Pgsql_Adapter) SimpleInsertLeftJoin(name string, ins DB_Insert, sel DB_Join) (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement this
|
// TODO: Implement this
|
||||||
func (adapter *Pgsql_Adapter) SimpleInsertInnerJoin(name string, ins DB_Insert, sel DB_Join) (string, error) {
|
func (adapter *Pgsql_Adapter) SimpleInsertInnerJoin(name string, ins DB_Insert, sel DB_Join) (string, error) {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Implement this
|
// TODO: Implement this
|
||||||
func (adapter *Pgsql_Adapter) SimpleCount(name string, table string, where string, limit string) (string, error) {
|
func (adapter *Pgsql_Adapter) SimpleCount(name string, table string, where string, limit string) (string, error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return "", errors.New("You need a name for this statement")
|
return "", errors.New("You need a name for this statement")
|
||||||
|
@ -304,7 +304,7 @@ func (adapter *Pgsql_Adapter) Write() error {
|
||||||
var stmts, body string
|
var stmts, body string
|
||||||
for _, name := range adapter.BufferOrder {
|
for _, name := range adapter.BufferOrder {
|
||||||
stmt := adapter.Buffer[name]
|
stmt := adapter.Buffer[name]
|
||||||
// TO-DO: Add support for create-table? Table creation might be a little complex for Go to do outside a SQL file :(
|
// TODO: Add support for create-table? Table creation might be a little complex for Go to do outside a SQL file :(
|
||||||
if stmt.Type != "create-table" {
|
if stmt.Type != "create-table" {
|
||||||
stmts += "var " + name + "_stmt *sql.Stmt\n"
|
stmts += "var " + name + "_stmt *sql.Stmt\n"
|
||||||
body += `
|
body += `
|
||||||
|
|
|
@ -121,7 +121,7 @@ type DB_Adapter interface {
|
||||||
SimpleCount(string,string,string,string) (string, error)
|
SimpleCount(string,string,string,string) (string, error)
|
||||||
Write() error
|
Write() error
|
||||||
|
|
||||||
// TO-DO: Add a simple query builder
|
// TODO: Add a simple query builder
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAdapter(name string) (adap DB_Adapter, err error) {
|
func GetAdapter(name string) (adap DB_Adapter, err error) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func write_statements(adapter qgen.DB_Adapter) error {
|
func write_statements(adapter qgen.DB_Adapter) error {
|
||||||
err := create_tables(adapter)
|
err := create_tables(adapter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -84,46 +85,47 @@ func write_statements(adapter qgen.DB_Adapter) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func create_tables(adapter qgen.DB_Adapter) error {
|
func create_tables(adapter qgen.DB_Adapter) error {
|
||||||
qgen.Install.CreateTable("users","utf8mb4","utf8mb4_general_ci",
|
qgen.Install.CreateTable("users", "utf8mb4", "utf8mb4_general_ci",
|
||||||
[]qgen.DB_Table_Column{
|
[]qgen.DB_Table_Column{
|
||||||
qgen.DB_Table_Column{"uid","int",0,false,true,""},
|
qgen.DB_Table_Column{"uid", "int", 0, false, true, ""},
|
||||||
qgen.DB_Table_Column{"name","varchar",100,false,false,""},
|
qgen.DB_Table_Column{"name", "varchar", 100, false, false, ""},
|
||||||
qgen.DB_Table_Column{"password","varchar",100,false,false,""},
|
qgen.DB_Table_Column{"password", "varchar", 100, false, false, ""},
|
||||||
qgen.DB_Table_Column{"salt","varchar",80,false,false,"''"},
|
qgen.DB_Table_Column{"salt", "varchar", 80, false, false, "''"},
|
||||||
qgen.DB_Table_Column{"group","int",0,false,false,""},
|
qgen.DB_Table_Column{"group", "int", 0, false, false, ""},
|
||||||
qgen.DB_Table_Column{"active","boolean",0,false,false,"0"},
|
qgen.DB_Table_Column{"active", "boolean", 0, false, false, "0"},
|
||||||
qgen.DB_Table_Column{"is_super_admin","boolean",0,false,false,"0"},
|
qgen.DB_Table_Column{"is_super_admin", "boolean", 0, false, false, "0"},
|
||||||
qgen.DB_Table_Column{"createdAt","createdAt",0,false,false,""},
|
qgen.DB_Table_Column{"createdAt", "createdAt", 0, false, false, ""},
|
||||||
qgen.DB_Table_Column{"lastActiveAt","datetime",0,false,false,""},
|
qgen.DB_Table_Column{"lastActiveAt", "datetime", 0, false, false, ""},
|
||||||
qgen.DB_Table_Column{"session","varchar",200,false,false,"''"},
|
qgen.DB_Table_Column{"session", "varchar", 200, false, false, "''"},
|
||||||
qgen.DB_Table_Column{"last_ip","varchar",200,false,false,"0.0.0.0.0"},
|
qgen.DB_Table_Column{"last_ip", "varchar", 200, false, false, "0.0.0.0.0"},
|
||||||
qgen.DB_Table_Column{"email","varchar",200,false,false,"''"},
|
qgen.DB_Table_Column{"email", "varchar", 200, false, false, "''"},
|
||||||
qgen.DB_Table_Column{"avatar","varchar",100,false,false,"''"},
|
qgen.DB_Table_Column{"avatar", "varchar", 100, false, false, "''"},
|
||||||
qgen.DB_Table_Column{"message","text",0,false,false,"''"},
|
qgen.DB_Table_Column{"message", "text", 0, false, false, "''"},
|
||||||
qgen.DB_Table_Column{"url_prefix","varchar",20,false,false,"''"},
|
qgen.DB_Table_Column{"url_prefix", "varchar", 20, false, false, "''"},
|
||||||
qgen.DB_Table_Column{"url_name","varchar",100,false,false,"''"},
|
qgen.DB_Table_Column{"url_name", "varchar", 100, false, false, "''"},
|
||||||
qgen.DB_Table_Column{"level","smallint",0,false,false,"0"},
|
qgen.DB_Table_Column{"level", "smallint", 0, false, false, "0"},
|
||||||
qgen.DB_Table_Column{"score","int",0,false,false,"0"},
|
qgen.DB_Table_Column{"score", "int", 0, false, false, "0"},
|
||||||
qgen.DB_Table_Column{"posts","int",0,false,false,"0"},
|
qgen.DB_Table_Column{"posts", "int", 0, false, false, "0"},
|
||||||
qgen.DB_Table_Column{"bigposts","int",0,false,false,"0"},
|
qgen.DB_Table_Column{"bigposts", "int", 0, false, false, "0"},
|
||||||
qgen.DB_Table_Column{"megaposts","int",0,false,false,"0"},
|
qgen.DB_Table_Column{"megaposts", "int", 0, false, false, "0"},
|
||||||
qgen.DB_Table_Column{"topics","int",0,false,false,"0"},
|
qgen.DB_Table_Column{"topics", "int", 0, false, false, "0"},
|
||||||
//qgen.DB_Table_Column{"penalty_count","int",0,false,false,"0"},
|
//qgen.DB_Table_Column{"penalty_count","int",0,false,false,"0"},
|
||||||
qgen.DB_Table_Column{"temp_group","int",0,false,false,"0"}, // For temporary groups, set this to zero when a temporary group isn't in effect
|
qgen.DB_Table_Column{"temp_group", "int", 0, false, false, "0"}, // For temporary groups, set this to zero when a temporary group isn't in effect
|
||||||
},
|
},
|
||||||
[]qgen.DB_Table_Key{
|
[]qgen.DB_Table_Key{
|
||||||
qgen.DB_Table_Key{"uid","primary"},
|
qgen.DB_Table_Key{"uid", "primary"},
|
||||||
qgen.DB_Table_Key{"name","unique"},
|
qgen.DB_Table_Key{"name", "unique"},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// What should we do about global penalties? Put them on the users table for speed? Or keep them here?
|
// What should we do about global penalties? Put them on the users table for speed? Or keep them here?
|
||||||
// Should we add IP Penalties? No, that's a stupid idea, just implement IP Bans properly. What about shadowbans?
|
// Should we add IP Penalties? No, that's a stupid idea, just implement IP Bans properly. What about shadowbans?
|
||||||
// TO-DO: Perm overrides
|
// TODO: Perm overrides
|
||||||
// TO-DO: Add a mod-queue and other basic auto-mod features. This is needed for awaiting activation and the mod_queue penalty flag
|
// TODO: Add a mod-queue and other basic auto-mod features. This is needed for awaiting activation and the mod_queue penalty flag
|
||||||
// TO-DO: Add a penalty type where a user is stopped from creating plugin_socialgroups social groups
|
// TODO: Add a penalty type where a user is stopped from creating plugin_socialgroups social groups
|
||||||
// TO-DO: Shadow bans. We will probably have a CanShadowBan permission for this, as we *really* don't want people using this lightly.
|
// TODO: Shadow bans. We will probably have a CanShadowBan permission for this, as we *really* don't want people using this lightly.
|
||||||
/*qgen.Install.CreateTable("users_penalties","","",
|
/*qgen.Install.CreateTable("users_penalties","","",
|
||||||
[]qgen.DB_Table_Column{
|
[]qgen.DB_Table_Column{
|
||||||
qgen.DB_Table_Column{"uid","int",0,false,false,""},
|
qgen.DB_Table_Column{"uid","int",0,false,false,""},
|
||||||
|
@ -149,330 +151,347 @@ func create_tables(adapter qgen.DB_Adapter) error {
|
||||||
[]qgen.DB_Table_Key{},
|
[]qgen.DB_Table_Key{},
|
||||||
)*/
|
)*/
|
||||||
|
|
||||||
qgen.Install.CreateTable("users_groups_scheduler","","",
|
qgen.Install.CreateTable("users_groups_scheduler", "", "",
|
||||||
[]qgen.DB_Table_Column{
|
[]qgen.DB_Table_Column{
|
||||||
qgen.DB_Table_Column{"uid","int",0,false,false,""},
|
qgen.DB_Table_Column{"uid", "int", 0, false, false, ""},
|
||||||
qgen.DB_Table_Column{"set_group","int",0,false,false,""},
|
qgen.DB_Table_Column{"set_group", "int", 0, false, false, ""},
|
||||||
|
|
||||||
qgen.DB_Table_Column{"issued_by","int",0,false,false,""},
|
qgen.DB_Table_Column{"issued_by", "int", 0, false, false, ""},
|
||||||
qgen.DB_Table_Column{"issued_at","createdAt",0,false,false,""},
|
qgen.DB_Table_Column{"issued_at", "createdAt", 0, false, false, ""},
|
||||||
qgen.DB_Table_Column{"revert_at","datetime",0,false,false,""},
|
qgen.DB_Table_Column{"revert_at", "datetime", 0, false, false, ""},
|
||||||
qgen.DB_Table_Column{"temporary","boolean",0,false,false,""}, // special case for permanent bans to do the necessary bookkeeping, might be removed in the future
|
qgen.DB_Table_Column{"temporary", "boolean", 0, false, false, ""}, // special case for permanent bans to do the necessary bookkeeping, might be removed in the future
|
||||||
},
|
},
|
||||||
[]qgen.DB_Table_Key{
|
[]qgen.DB_Table_Key{
|
||||||
qgen.DB_Table_Key{"uid","primary"},
|
qgen.DB_Table_Key{"uid", "primary"},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
qgen.Install.CreateTable("word_filters","","",
|
qgen.Install.CreateTable("word_filters", "", "",
|
||||||
[]qgen.DB_Table_Column{
|
[]qgen.DB_Table_Column{
|
||||||
qgen.DB_Table_Column{"wfid","int",0,false,true,""},
|
qgen.DB_Table_Column{"wfid", "int", 0, false, true, ""},
|
||||||
qgen.DB_Table_Column{"find","varchar",200,false,false,""},
|
qgen.DB_Table_Column{"find", "varchar", 200, false, false, ""},
|
||||||
qgen.DB_Table_Column{"replacement","varchar",200,false,false,""},
|
qgen.DB_Table_Column{"replacement", "varchar", 200, false, false, ""},
|
||||||
},
|
},
|
||||||
[]qgen.DB_Table_Key{
|
[]qgen.DB_Table_Key{
|
||||||
qgen.DB_Table_Key{"wfid","primary"},
|
qgen.DB_Table_Key{"wfid", "primary"},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
qgen.Install.CreateTable("sync", "", "",
|
||||||
|
[]qgen.DB_Table_Column{
|
||||||
|
qgen.DB_Table_Column{"last_update", "datetime", 0, false, false, ""},
|
||||||
|
},
|
||||||
|
[]qgen.DB_Table_Key{},
|
||||||
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func seed_tables(adapter qgen.DB_Adapter) error {
|
func seed_tables(adapter qgen.DB_Adapter) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func write_selects(adapter qgen.DB_Adapter) error {
|
func write_selects(adapter qgen.DB_Adapter) error {
|
||||||
// url_prefix and url_name will be removed from this query in a later commit
|
// url_prefix and url_name will be removed from this query in a later commit
|
||||||
adapter.SimpleSelect("get_user","users","name, group, is_super_admin, avatar, message, url_prefix, url_name, level","uid = ?","","")
|
adapter.SimpleSelect("get_user", "users", "name, group, is_super_admin, avatar, message, url_prefix, url_name, level", "uid = ?", "", "")
|
||||||
|
|
||||||
// Looking for get_topic? Your statement is in another castle
|
// Looking for get_topic? Your statement is in another castle
|
||||||
|
|
||||||
adapter.SimpleSelect("get_reply","replies","tid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount","rid = ?","","")
|
adapter.SimpleSelect("get_reply", "replies", "tid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount", "rid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_user_reply","users_replies","uid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress","rid = ?","","")
|
adapter.SimpleSelect("get_user_reply", "users_replies", "uid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress", "rid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_password","users","password,salt","uid = ?","","")
|
adapter.SimpleSelect("get_password", "users", "password,salt", "uid = ?", "", "")
|
||||||
|
|
||||||
|
adapter.SimpleSelect("get_settings", "settings", "name, content, type", "", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_settings","settings","name, content, type","","","")
|
adapter.SimpleSelect("get_setting", "settings", "content, type", "name = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_setting","settings","content, type","name = ?","","")
|
adapter.SimpleSelect("get_full_setting", "settings", "name, type, constraints", "name = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_full_setting","settings","name, type, constraints","name = ?","","")
|
adapter.SimpleSelect("get_full_settings", "settings", "name, content, type, constraints", "", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_full_settings","settings","name, content, type, constraints","","","")
|
adapter.SimpleSelect("get_groups", "users_groups", "gid, name, permissions, plugin_perms, is_mod, is_admin, is_banned, tag", "", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_groups","users_groups","gid, name, permissions, plugin_perms, is_mod, is_admin, is_banned, tag","","","")
|
adapter.SimpleSelect("get_forums", "forums", "fid, name, desc, active, preset, parentID, parentType, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime", "", "fid ASC", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_forums","forums","fid, name, desc, active, preset, parentID, parentType, topicCount, lastTopic, lastTopicID, lastReplyer, lastReplyerID, lastTopicTime","","fid ASC","")
|
adapter.SimpleSelect("get_forums_permissions", "forums_permissions", "gid, fid, permissions", "", "gid ASC, fid ASC", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_forums_permissions","forums_permissions","gid, fid, permissions","","gid ASC, fid ASC","")
|
adapter.SimpleSelect("get_plugins", "plugins", "uname, active, installed", "", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_plugins","plugins","uname, active, installed","","","")
|
adapter.SimpleSelect("get_themes", "themes", "uname, default", "", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_themes","themes","uname, default","","","")
|
adapter.SimpleSelect("get_widgets", "widgets", "position, side, type, active, location, data", "", "position ASC", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_widgets","widgets","position, side, type, active, location, data","","position ASC","")
|
adapter.SimpleSelect("is_plugin_active", "plugins", "active", "uname = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("is_plugin_active","plugins","active","uname = ?","","")
|
|
||||||
|
|
||||||
//adapter.SimpleSelect("is_plugin_installed","plugins","installed","uname = ?","","")
|
//adapter.SimpleSelect("is_plugin_installed","plugins","installed","uname = ?","","")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_users","users","uid, name, group, active, is_super_admin, avatar","","","")
|
adapter.SimpleSelect("get_users", "users", "uid, name, group, active, is_super_admin, avatar", "", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_users_offset","users","uid, name, group, active, is_super_admin, avatar","","","?,?")
|
adapter.SimpleSelect("get_users_offset", "users", "uid, name, group, active, is_super_admin, avatar", "", "", "?,?")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_word_filters","word_filters","wfid, find, replacement","","","")
|
adapter.SimpleSelect("get_word_filters", "word_filters", "wfid, find, replacement", "", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("is_theme_default","themes","default","uname = ?","","")
|
adapter.SimpleSelect("is_theme_default", "themes", "default", "uname = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_modlogs","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","","","")
|
adapter.SimpleSelect("get_modlogs", "moderation_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_modlogs_offset","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","","","?,?")
|
adapter.SimpleSelect("get_modlogs_offset", "moderation_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "", "", "?,?")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_reply_tid","replies","tid","rid = ?","","")
|
adapter.SimpleSelect("get_reply_tid", "replies", "tid", "rid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_topic_fid","topics","parentID","tid = ?","","")
|
adapter.SimpleSelect("get_topic_fid", "topics", "parentID", "tid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_user_reply_uid","users_replies","uid","rid = ?","","")
|
adapter.SimpleSelect("get_user_reply_uid", "users_replies", "uid", "rid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("has_liked_topic","likes","targetItem","sentBy = ? and targetItem = ? and targetType = 'topics'","","")
|
adapter.SimpleSelect("has_liked_topic", "likes", "targetItem", "sentBy = ? and targetItem = ? and targetType = 'topics'", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("has_liked_reply","likes","targetItem","sentBy = ? and targetItem = ? and targetType = 'replies'","","")
|
adapter.SimpleSelect("has_liked_reply", "likes", "targetItem", "sentBy = ? and targetItem = ? and targetType = 'replies'", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_user_name","users","name","uid = ?","","")
|
adapter.SimpleSelect("get_user_name", "users", "name", "uid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_user_active","users","active","uid = ?","","")
|
adapter.SimpleSelect("get_user_active", "users", "active", "uid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_emails_by_user","emails","email, validated, token","uid = ?","","")
|
adapter.SimpleSelect("get_emails_by_user", "emails", "email, validated, token", "uid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_topic_basic","topics","title, content","tid = ?","","")
|
adapter.SimpleSelect("get_topic_basic", "topics", "title, content", "tid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_activity_entry","activity_stream","actor, targetUser, event, elementType, elementID","asid = ?","","")
|
adapter.SimpleSelect("get_activity_entry", "activity_stream", "actor, targetUser, event, elementType, elementID", "asid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleSelect("forum_entry_exists","forums","fid","name = ''","fid ASC","0,1")
|
adapter.SimpleSelect("forum_entry_exists", "forums", "fid", "name = ''", "fid ASC", "0,1")
|
||||||
|
|
||||||
adapter.SimpleSelect("group_entry_exists","users_groups","gid","name = ''","gid ASC","0,1")
|
adapter.SimpleSelect("group_entry_exists", "users_groups", "gid", "name = ''", "gid ASC", "0,1")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_forum_topics_offset","topics","tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, postCount, likeCount","parentID = ?","sticky DESC, lastReplyAt DESC, createdBy DESC","?,?")
|
adapter.SimpleSelect("get_forum_topics_offset", "topics", "tid, title, content, createdBy, is_closed, sticky, createdAt, lastReplyAt, lastReplyBy, parentID, postCount, likeCount", "parentID = ?", "sticky DESC, lastReplyAt DESC, createdBy DESC", "?,?")
|
||||||
|
|
||||||
adapter.SimpleSelect("get_expired_scheduled_groups","users_groups_scheduler","uid","UTC_TIMESTAMP() > revert_at AND temporary = 1","","")
|
adapter.SimpleSelect("get_expired_scheduled_groups", "users_groups_scheduler", "uid", "UTC_TIMESTAMP() > revert_at AND temporary = 1", "", "")
|
||||||
|
|
||||||
|
adapter.SimpleSelect("get_sync", "sync", "last_update", "", "", "")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func write_left_joins(adapter qgen.DB_Adapter) error {
|
func write_left_joins(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleLeftJoin("get_topic_replies_offset","replies","users","replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress, replies.likeCount, replies.actionType","replies.createdBy = users.uid","tid = ?","","?,?")
|
adapter.SimpleLeftJoin("get_topic_replies_offset", "replies", "users", "replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress, replies.likeCount, replies.actionType", "replies.createdBy = users.uid", "tid = ?", "", "?,?")
|
||||||
|
|
||||||
adapter.SimpleLeftJoin("get_topic_list","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar","topics.createdBy = users.uid","","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC","")
|
adapter.SimpleLeftJoin("get_topic_list", "topics", "users", "topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.parentID, users.name, users.avatar", "topics.createdBy = users.uid", "", "topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy DESC", "")
|
||||||
|
|
||||||
adapter.SimpleLeftJoin("get_topic_user","topics","users","topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level","topics.createdBy = users.uid","tid = ?","","")
|
adapter.SimpleLeftJoin("get_topic_user", "topics", "users", "topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.url_prefix, users.url_name, users.level", "topics.createdBy = users.uid", "tid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleLeftJoin("get_topic_by_reply","replies","topics","topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, topics.data","replies.tid = topics.tid","rid = ?","","")
|
adapter.SimpleLeftJoin("get_topic_by_reply", "replies", "topics", "topics.tid, topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, topics.data", "replies.tid = topics.tid", "rid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleLeftJoin("get_topic_replies","replies","users","replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress","replies.createdBy = users.uid","tid = ?","","")
|
adapter.SimpleLeftJoin("get_topic_replies", "replies", "users", "replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.group, users.url_prefix, users.url_name, users.level, replies.ipaddress", "replies.createdBy = users.uid", "tid = ?", "", "")
|
||||||
|
|
||||||
adapter.SimpleLeftJoin("get_forum_topics","topics","users","topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar","topics.createdBy = users.uid","topics.parentID = ?","topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc","")
|
adapter.SimpleLeftJoin("get_forum_topics", "topics", "users", "topics.tid, topics.title, topics.content, topics.createdBy, topics.is_closed, topics.sticky, topics.createdAt, topics.lastReplyAt, topics.parentID, users.name, users.avatar", "topics.createdBy = users.uid", "topics.parentID = ?", "topics.sticky DESC, topics.lastReplyAt DESC, topics.createdBy desc", "")
|
||||||
|
|
||||||
adapter.SimpleLeftJoin("get_profile_replies","users_replies","users","users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group","users_replies.createdBy = users.uid","users_replies.uid = ?","","")
|
adapter.SimpleLeftJoin("get_profile_replies", "users_replies", "users", "users_replies.rid, users_replies.content, users_replies.createdBy, users_replies.createdAt, users_replies.lastEdit, users_replies.lastEditBy, users.avatar, users.name, users.group", "users_replies.createdBy = users.uid", "users_replies.uid = ?", "", "")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func write_inner_joins(adapter qgen.DB_Adapter) error {
|
func write_inner_joins(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleInnerJoin("get_watchers","activity_stream","activity_subscriptions","activity_subscriptions.user","activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor","asid = ?","","")
|
adapter.SimpleInnerJoin("get_watchers", "activity_stream", "activity_subscriptions", "activity_subscriptions.user", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", "")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func write_inserts(adapter qgen.DB_Adapter) error {
|
func write_inserts(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleInsert("create_topic","topics","parentID,title,content,parsed_content,createdAt,lastReplyAt,lastReplyBy,ipaddress,words,createdBy","?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?")
|
adapter.SimpleInsert("create_topic", "topics", "parentID,title,content,parsed_content,createdAt,lastReplyAt,lastReplyBy,ipaddress,words,createdBy", "?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("create_report","topics","title,content,parsed_content,createdAt,lastReplyAt,createdBy,data,parentID,css_class","?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,1,'report'")
|
adapter.SimpleInsert("create_report", "topics", "title,content,parsed_content,createdAt,lastReplyAt,createdBy,data,parentID,css_class", "?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,1,'report'")
|
||||||
|
|
||||||
adapter.SimpleInsert("create_reply","replies","tid,content,parsed_content,createdAt,ipaddress,words,createdBy","?,?,?,UTC_TIMESTAMP(),?,?,?")
|
adapter.SimpleInsert("create_reply", "replies", "tid,content,parsed_content,createdAt,ipaddress,words,createdBy", "?,?,?,UTC_TIMESTAMP(),?,?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("create_action_reply","replies","tid,actionType,ipaddress,createdBy","?,?,?,?")
|
adapter.SimpleInsert("create_action_reply", "replies", "tid,actionType,ipaddress,createdBy", "?,?,?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("create_like","likes","weight, targetItem, targetType, sentBy","?,?,?,?")
|
adapter.SimpleInsert("create_like", "likes", "weight, targetItem, targetType, sentBy", "?,?,?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("add_activity","activity_stream","actor,targetUser,event,elementType,elementID","?,?,?,?,?")
|
adapter.SimpleInsert("add_activity", "activity_stream", "actor,targetUser,event,elementType,elementID", "?,?,?,?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("notify_one","activity_stream_matches","watcher,asid","?,?")
|
adapter.SimpleInsert("notify_one", "activity_stream_matches", "watcher,asid", "?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("add_email","emails","email, uid, validated, token","?,?,?,?")
|
adapter.SimpleInsert("add_email", "emails", "email, uid, validated, token", "?,?,?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("create_profile_reply","users_replies","uid, content, parsed_content, createdAt, createdBy, ipaddress","?,?,?,UTC_TIMESTAMP(),?,?")
|
adapter.SimpleInsert("create_profile_reply", "users_replies", "uid, content, parsed_content, createdAt, createdBy, ipaddress", "?,?,?,UTC_TIMESTAMP(),?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("add_subscription","activity_subscriptions","user,targetID,targetType,level","?,?,?,2")
|
adapter.SimpleInsert("add_subscription", "activity_subscriptions", "user,targetID,targetType,level", "?,?,?,2")
|
||||||
|
|
||||||
adapter.SimpleInsert("create_forum","forums","name, desc, active, preset","?,?,?,?")
|
adapter.SimpleInsert("create_forum", "forums", "name, desc, active, preset", "?,?,?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("add_forum_perms_to_forum","forums_permissions","gid,fid,preset,permissions","?,?,?,?")
|
adapter.SimpleInsert("add_forum_perms_to_forum", "forums_permissions", "gid,fid,preset,permissions", "?,?,?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("add_plugin","plugins","uname, active, installed","?,?,?")
|
adapter.SimpleInsert("add_plugin", "plugins", "uname, active, installed", "?,?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("add_theme","themes","uname,default","?,?")
|
adapter.SimpleInsert("add_theme", "themes", "uname,default", "?,?")
|
||||||
|
|
||||||
|
adapter.SimpleInsert("create_group", "users_groups", "name, tag, is_admin, is_mod, is_banned, permissions", "?,?,?,?,?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("create_group","users_groups","name, tag, is_admin, is_mod, is_banned, permissions","?,?,?,?,?,?")
|
adapter.SimpleInsert("add_modlog_entry", "moderation_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "?,?,?,?,?,UTC_TIMESTAMP()")
|
||||||
|
|
||||||
adapter.SimpleInsert("add_modlog_entry","moderation_logs","action, elementID, elementType, ipaddress, actorID, doneAt","?,?,?,?,?,UTC_TIMESTAMP()")
|
adapter.SimpleInsert("add_adminlog_entry", "administration_logs", "action, elementID, elementType, ipaddress, actorID, doneAt", "?,?,?,?,?,UTC_TIMESTAMP()")
|
||||||
|
|
||||||
adapter.SimpleInsert("add_adminlog_entry","administration_logs","action, elementID, elementType, ipaddress, actorID, doneAt","?,?,?,?,?,UTC_TIMESTAMP()")
|
adapter.SimpleInsert("create_word_filter", "word_filters", "find, replacement", "?,?")
|
||||||
|
|
||||||
adapter.SimpleInsert("create_word_filter","word_filters","find, replacement","?,?")
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func write_replaces(adapter qgen.DB_Adapter) error {
|
func write_replaces(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleReplace("add_forum_perms_to_group","forums_permissions","gid,fid,preset,permissions","?,?,?,?")
|
adapter.SimpleReplace("add_forum_perms_to_group", "forums_permissions", "gid,fid,preset,permissions", "?,?,?,?")
|
||||||
|
|
||||||
adapter.SimpleReplace("replace_schedule_group","users_groups_scheduler","uid, set_group, issued_by, issued_at, revert_at, temporary","?,?,?,UTC_TIMESTAMP(),?,?")
|
adapter.SimpleReplace("replace_schedule_group", "users_groups_scheduler", "uid, set_group, issued_by, issued_at, revert_at, temporary", "?,?,?,UTC_TIMESTAMP(),?,?")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func write_updates(adapter qgen.DB_Adapter) error {
|
func write_updates(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleUpdate("add_replies_to_topic","topics","postCount = postCount + ?, lastReplyBy = ?, lastReplyAt = UTC_TIMESTAMP()","tid = ?")
|
adapter.SimpleUpdate("add_replies_to_topic", "topics", "postCount = postCount + ?, lastReplyBy = ?, lastReplyAt = UTC_TIMESTAMP()", "tid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("remove_replies_from_topic","topics","postCount = postCount - ?","tid = ?")
|
adapter.SimpleUpdate("remove_replies_from_topic", "topics", "postCount = postCount - ?", "tid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("add_topics_to_forum","forums","topicCount = topicCount + ?","fid = ?")
|
adapter.SimpleUpdate("add_topics_to_forum", "forums", "topicCount = topicCount + ?", "fid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("remove_topics_from_forum","forums","topicCount = topicCount - ?","fid = ?")
|
adapter.SimpleUpdate("remove_topics_from_forum", "forums", "topicCount = topicCount - ?", "fid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_forum_cache","forums","lastTopic = ?, lastTopicID = ?, lastReplyer = ?, lastReplyerID = ?, lastTopicTime = UTC_TIMESTAMP()","fid = ?")
|
adapter.SimpleUpdate("update_forum_cache", "forums", "lastTopic = ?, lastTopicID = ?, lastReplyer = ?, lastReplyerID = ?, lastTopicTime = UTC_TIMESTAMP()", "fid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("add_likes_to_topic","topics","likeCount = likeCount + ?","tid = ?")
|
adapter.SimpleUpdate("add_likes_to_topic", "topics", "likeCount = likeCount + ?", "tid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("add_likes_to_reply","replies","likeCount = likeCount + ?","rid = ?")
|
adapter.SimpleUpdate("add_likes_to_reply", "replies", "likeCount = likeCount + ?", "rid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("edit_topic","topics","title = ?, content = ?, parsed_content = ?, is_closed = ?","tid = ?")
|
adapter.SimpleUpdate("edit_topic", "topics", "title = ?, content = ?, parsed_content = ?, is_closed = ?", "tid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("edit_reply","replies","content = ?, parsed_content = ?","rid = ?")
|
adapter.SimpleUpdate("edit_reply", "replies", "content = ?, parsed_content = ?", "rid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("stick_topic","topics","sticky = 1","tid = ?")
|
adapter.SimpleUpdate("stick_topic", "topics", "sticky = 1", "tid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("unstick_topic","topics","sticky = 0","tid = ?")
|
adapter.SimpleUpdate("unstick_topic", "topics", "sticky = 0", "tid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_last_ip","users","last_ip = ?","uid = ?")
|
adapter.SimpleUpdate("update_last_ip", "users", "last_ip = ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_session","users","session = ?","uid = ?")
|
adapter.SimpleUpdate("update_session", "users", "session = ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("set_password","users","password = ?, salt = ?","uid = ?")
|
adapter.SimpleUpdate("set_password", "users", "password = ?, salt = ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("set_avatar","users","avatar = ?","uid = ?")
|
adapter.SimpleUpdate("set_avatar", "users", "avatar = ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("set_username","users","name = ?","uid = ?")
|
adapter.SimpleUpdate("set_username", "users", "name = ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("change_group","users","group = ?","uid = ?")
|
adapter.SimpleUpdate("change_group", "users", "group = ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("activate_user","users","active = 1","uid = ?")
|
adapter.SimpleUpdate("activate_user", "users", "active = 1", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_user_level","users","level = ?","uid = ?")
|
adapter.SimpleUpdate("update_user_level", "users", "level = ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("increment_user_score","users","score = score + ?","uid = ?")
|
adapter.SimpleUpdate("increment_user_score", "users", "score = score + ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("increment_user_posts","users","posts = posts + ?","uid = ?")
|
adapter.SimpleUpdate("increment_user_posts", "users", "posts = posts + ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("increment_user_bigposts","users","posts = posts + ?, bigposts = bigposts + ?","uid = ?")
|
adapter.SimpleUpdate("increment_user_bigposts", "users", "posts = posts + ?, bigposts = bigposts + ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("increment_user_megaposts","users","posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?","uid = ?")
|
adapter.SimpleUpdate("increment_user_megaposts", "users", "posts = posts + ?, bigposts = bigposts + ?, megaposts = megaposts + ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("increment_user_topics","users","topics = topics + ?","uid = ?")
|
adapter.SimpleUpdate("increment_user_topics", "users", "topics = topics + ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("edit_profile_reply","users_replies","content = ?, parsed_content = ?","rid = ?")
|
adapter.SimpleUpdate("edit_profile_reply", "users_replies", "content = ?, parsed_content = ?", "rid = ?")
|
||||||
|
|
||||||
//delete_forum_stmt, err = db.Prepare("delete from forums where fid = ?")
|
adapter.SimpleUpdate("update_forum", "forums", "name = ?, desc = ?, active = ?, preset = ?", "fid = ?")
|
||||||
adapter.SimpleUpdate("delete_forum","forums","name= '', active = 0","fid = ?")
|
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_forum","forums","name = ?, desc = ?, active = ?, preset = ?","fid = ?")
|
adapter.SimpleUpdate("update_setting", "settings", "content = ?", "name = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_setting","settings","content = ?","name = ?")
|
adapter.SimpleUpdate("update_plugin", "plugins", "active = ?", "uname = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_plugin","plugins","active = ?","uname = ?")
|
adapter.SimpleUpdate("update_plugin_install", "plugins", "installed = ?", "uname = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_plugin_install","plugins","installed = ?","uname = ?")
|
adapter.SimpleUpdate("update_theme", "themes", "default = ?", "uname = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_theme","themes","default = ?","uname = ?")
|
adapter.SimpleUpdate("update_user", "users", "name = ?, email = ?, group = ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_user","users","name = ?, email = ?, group = ?","uid = ?")
|
adapter.SimpleUpdate("update_group_perms", "users_groups", "permissions = ?", "gid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_group_perms","users_groups","permissions = ?","gid = ?")
|
adapter.SimpleUpdate("update_group_rank", "users_groups", "is_admin = ?, is_mod = ?, is_banned = ?", "gid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_group_rank","users_groups","is_admin = ?, is_mod = ?, is_banned = ?","gid = ?")
|
adapter.SimpleUpdate("update_group", "users_groups", "name = ?, tag = ?", "gid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_group","users_groups","name = ?, tag = ?","gid = ?")
|
adapter.SimpleUpdate("update_email", "emails", "email = ?, uid = ?, validated = ?, token = ?", "email = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_email","emails","email = ?, uid = ?, validated = ?, token = ?","email = ?")
|
adapter.SimpleUpdate("verify_email", "emails", "validated = 1, token = ''", "email = ?") // Need to fix this: Empty string isn't working, it gets set to 1 instead x.x -- Has this been fixed?
|
||||||
|
|
||||||
adapter.SimpleUpdate("verify_email","emails","validated = 1, token = ''","email = ?") // Need to fix this: Empty string isn't working, it gets set to 1 instead x.x -- Has this been fixed?
|
adapter.SimpleUpdate("set_temp_group", "users", "temp_group = ?", "uid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("set_temp_group","users","temp_group = ?","uid = ?")
|
adapter.SimpleUpdate("update_word_filter", "word_filters", "find = ?, replacement = ?", "wfid = ?")
|
||||||
|
|
||||||
adapter.SimpleUpdate("update_word_filter","word_filters","find = ?, replacement = ?","wfid = ?")
|
adapter.SimpleUpdate("bump_sync", "sync", "last_update = UTC_TIMESTAMP()", "")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func write_deletes(adapter qgen.DB_Adapter) error {
|
func write_deletes(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleDelete("delete_reply","replies","rid = ?")
|
adapter.SimpleDelete("delete_reply", "replies", "rid = ?")
|
||||||
|
|
||||||
adapter.SimpleDelete("delete_topic","topics","tid = ?")
|
adapter.SimpleDelete("delete_topic", "topics", "tid = ?")
|
||||||
|
|
||||||
|
adapter.SimpleDelete("delete_profile_reply", "users_replies", "rid = ?")
|
||||||
|
|
||||||
adapter.SimpleDelete("delete_profile_reply","users_replies","rid = ?")
|
adapter.SimpleDelete("delete_forum_perms_by_forum", "forums_permissions", "fid = ?")
|
||||||
|
|
||||||
adapter.SimpleDelete("delete_forum_perms_by_forum","forums_permissions","fid = ?")
|
adapter.SimpleDelete("delete_activity_stream_match", "activity_stream_matches", "watcher = ? AND asid = ?")
|
||||||
|
|
||||||
adapter.SimpleDelete("delete_activity_stream_match","activity_stream_matches","watcher = ? AND asid = ?")
|
|
||||||
//adapter.SimpleDelete("delete_activity_stream_matches_by_watcher","activity_stream_matches","watcher = ?")
|
//adapter.SimpleDelete("delete_activity_stream_matches_by_watcher","activity_stream_matches","watcher = ?")
|
||||||
|
|
||||||
adapter.SimpleDelete("delete_word_filter","word_filters","wfid = ?")
|
adapter.SimpleDelete("delete_word_filter", "word_filters", "wfid = ?")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func write_simple_counts(adapter qgen.DB_Adapter) error {
|
func write_simple_counts(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleCount("report_exists","topics","data = ? AND data != '' AND parentID = 1","")
|
adapter.SimpleCount("report_exists", "topics", "data = ? AND data != '' AND parentID = 1", "")
|
||||||
|
|
||||||
adapter.SimpleCount("group_count","users_groups","","")
|
adapter.SimpleCount("group_count", "users_groups", "", "")
|
||||||
|
|
||||||
adapter.SimpleCount("modlog_count","moderation_logs","","")
|
adapter.SimpleCount("modlog_count", "moderation_logs", "", "")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func write_insert_selects(adapter qgen.DB_Adapter) error {
|
func write_insert_selects(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleInsertSelect("add_forum_perms_to_forum_admins",
|
adapter.SimpleInsertSelect("add_forum_perms_to_forum_admins",
|
||||||
qgen.DB_Insert{"forums_permissions","gid,fid,preset,permissions",""},
|
qgen.DB_Insert{"forums_permissions", "gid,fid,preset,permissions", ""},
|
||||||
qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 1","",""},
|
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 1", "", ""},
|
||||||
)
|
)
|
||||||
|
|
||||||
adapter.SimpleInsertSelect("add_forum_perms_to_forum_staff",
|
adapter.SimpleInsertSelect("add_forum_perms_to_forum_staff",
|
||||||
qgen.DB_Insert{"forums_permissions","gid,fid,preset,permissions",""},
|
qgen.DB_Insert{"forums_permissions", "gid,fid,preset,permissions", ""},
|
||||||
qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 0 AND is_mod = 1","",""},
|
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 1", "", ""},
|
||||||
)
|
)
|
||||||
|
|
||||||
adapter.SimpleInsertSelect("add_forum_perms_to_forum_members",
|
adapter.SimpleInsertSelect("add_forum_perms_to_forum_members",
|
||||||
qgen.DB_Insert{"forums_permissions","gid,fid,preset,permissions",""},
|
qgen.DB_Insert{"forums_permissions", "gid,fid,preset,permissions", ""},
|
||||||
qgen.DB_Select{"users_groups","gid, ? AS fid, ? AS preset, ? AS permissions","is_admin = 0 AND is_mod = 0 AND is_banned = 0","",""},
|
qgen.DB_Select{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 0 AND is_banned = 0", "", ""},
|
||||||
)
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func write_insert_left_joins(adapter qgen.DB_Adapter) error {
|
func write_insert_left_joins(adapter qgen.DB_Adapter) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func write_insert_inner_joins(adapter qgen.DB_Adapter) error {
|
func write_insert_inner_joins(adapter qgen.DB_Adapter) error {
|
||||||
adapter.SimpleInsertInnerJoin("notify_watchers",
|
adapter.SimpleInsertInnerJoin("notify_watchers",
|
||||||
qgen.DB_Insert{"activity_stream_matches","watcher, asid",""},
|
qgen.DB_Insert{"activity_stream_matches", "watcher, asid", ""},
|
||||||
qgen.DB_Join{"activity_stream","activity_subscriptions","activity_subscriptions.user, activity_stream.asid","activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor","asid = ?","",""},
|
qgen.DB_Join{"activity_stream", "activity_subscriptions", "activity_subscriptions.user, activity_stream.asid", "activity_subscriptions.targetType = activity_stream.elementType AND activity_subscriptions.targetID = activity_stream.elementID AND activity_subscriptions.user != activity_stream.actor", "asid = ?", "", ""},
|
||||||
)
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
19
router.go
19
router.go
|
@ -6,30 +6,35 @@ import "strings"
|
||||||
import "sync"
|
import "sync"
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
// TO-DO: Support the new handler signatures created by our efforts to move the PreRoute middleware into the generated router
|
// TODO: Support the new handler signatures created by our efforts to move the PreRoute middleware into the generated router
|
||||||
|
// nolint Stop linting the uselessness of this file, we never know when we might need this file again
|
||||||
type Router struct {
|
type Router struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
routes map[string]func(http.ResponseWriter, *http.Request)
|
routes map[string]func(http.ResponseWriter, *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func NewRouter() *Router {
|
func NewRouter() *Router {
|
||||||
return &Router{
|
return &Router{
|
||||||
routes: make(map[string]func(http.ResponseWriter, *http.Request)),
|
routes: make(map[string]func(http.ResponseWriter, *http.Request)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func (router *Router) Handle(pattern string, handle http.Handler) {
|
func (router *Router) Handle(pattern string, handle http.Handler) {
|
||||||
router.Lock()
|
router.Lock()
|
||||||
router.routes[pattern] = handle.ServeHTTP
|
router.routes[pattern] = handle.ServeHTTP
|
||||||
router.Unlock()
|
router.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func (router *Router) HandleFunc(pattern string, handle func(http.ResponseWriter, *http.Request)) {
|
func (router *Router) HandleFunc(pattern string, handle func(http.ResponseWriter, *http.Request)) {
|
||||||
router.Lock()
|
router.Lock()
|
||||||
router.routes[pattern] = handle
|
router.routes[pattern] = handle
|
||||||
router.Unlock()
|
router.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' {
|
if len(req.URL.Path) == 0 || req.URL.Path[0] != '/' {
|
||||||
w.WriteHeader(405)
|
w.WriteHeader(405)
|
||||||
|
@ -37,10 +42,10 @@ func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var /*extra_data, */prefix string
|
var /*extraData, */ prefix string
|
||||||
if req.URL.Path[len(req.URL.Path) - 1] != '/' {
|
if req.URL.Path[len(req.URL.Path)-1] != '/' {
|
||||||
//extra_data = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:]
|
//extraData = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:]
|
||||||
prefix = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1]
|
prefix = req.URL.Path[:strings.LastIndexByte(req.URL.Path, '/')+1]
|
||||||
} else {
|
} else {
|
||||||
prefix = req.URL.Path
|
prefix = req.URL.Path
|
||||||
}
|
}
|
||||||
|
@ -50,9 +55,9 @@ func (router *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
router.RUnlock()
|
router.RUnlock()
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
handle(w,req)
|
handle(w, req)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//log.Print("req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/')]",req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/')])
|
//log.Print("req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/')]",req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/')])
|
||||||
NotFound(w,req)
|
NotFound(w, req)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "log"
|
import "log"
|
||||||
|
|
||||||
//import "strings"
|
//import "strings"
|
||||||
import "os"
|
import "os"
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ func main() {
|
||||||
routes()
|
routes()
|
||||||
|
|
||||||
var out string
|
var out string
|
||||||
var fdata string = "// Code generated by. DO NOT EDIT.\n/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */\n"
|
var fdata = "// Code generated by. DO NOT EDIT.\n/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */\n"
|
||||||
|
|
||||||
for _, route := range route_list {
|
for _, route := range route_list {
|
||||||
var end int
|
var end int
|
||||||
|
@ -142,9 +143,10 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
|
|
||||||
if dev.SuperDebug {
|
if dev.SuperDebug {
|
||||||
log.Print("before route_static")
|
log.Print("before route_static")
|
||||||
log.Print("prefix:", prefix)
|
log.Print("prefix: ", prefix)
|
||||||
log.Print("req.URL.Path:", req.URL.Path)
|
log.Print("req.URL.Path: ", req.URL.Path)
|
||||||
log.Print("extra_data:", extra_data)
|
log.Print("extra_data: ", extra_data)
|
||||||
|
log.Print("req.Referer(): ", req.Referer())
|
||||||
}
|
}
|
||||||
|
|
||||||
if prefix == "/static" {
|
if prefix == "/static" {
|
||||||
|
@ -178,7 +180,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
case "":
|
case "":
|
||||||
// Stop the favicons, robots.txt file, etc. resolving to the topics list
|
// Stop the favicons, robots.txt file, etc. resolving to the topics list
|
||||||
// TO-DO: Add support for favicons and robots.txt files
|
// TODO: Add support for favicons and robots.txt files
|
||||||
switch(extra_data) {
|
switch(extra_data) {
|
||||||
case "robots.txt":
|
case "robots.txt":
|
||||||
route_robots_txt(w,req)
|
route_robots_txt(w,req)
|
||||||
|
@ -207,7 +209,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
NotFound(w,req)
|
NotFound(w,req)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
write_file("./gen_router.go",fdata)
|
write_file("./gen_router.go", fdata)
|
||||||
log.Println("Successfully generated the router")
|
log.Println("Successfully generated the router")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ func routes() {
|
||||||
addRoute("route_forum", "/forum/", "", "extra_data")
|
addRoute("route_forum", "/forum/", "", "extra_data")
|
||||||
//addRoute("route_topic_create","/topics/create/","","extra_data")
|
//addRoute("route_topic_create","/topics/create/","","extra_data")
|
||||||
//addRoute("route_topics","/topics/",""/*,"&groups","&forums"*/)
|
//addRoute("route_topics","/topics/",""/*,"&groups","&forums"*/)
|
||||||
|
addRoute("route_change_theme", "/theme/", "")
|
||||||
|
|
||||||
addRouteGroup("/report/",
|
addRouteGroup("/report/",
|
||||||
Route{"route_report_submit", "/report/submit/", "", []string{"extra_data"}},
|
Route{"route_report_submit", "/report/submit/", "", []string{"extra_data"}},
|
||||||
|
|
178
routes.go
178
routes.go
|
@ -35,10 +35,10 @@ func init() {
|
||||||
hvars = &HeaderVars{Site: site}
|
hvars = &HeaderVars{Site: site}
|
||||||
}
|
}
|
||||||
|
|
||||||
type HttpsRedirect struct {
|
type HTTPSRedirect struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (red *HttpsRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
dest := "https://" + req.Host + req.URL.Path
|
dest := "https://" + req.Host + req.URL.Path
|
||||||
if len(req.URL.RawQuery) > 0 {
|
if len(req.URL.RawQuery) > 0 {
|
||||||
dest += "?" + req.URL.RawQuery
|
dest += "?" + req.URL.RawQuery
|
||||||
|
@ -54,13 +54,14 @@ func route_static(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
h := w.Header()
|
||||||
|
|
||||||
// Surely, there's a more efficient way of doing this?
|
// Surely, there's a more efficient way of doing this?
|
||||||
if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && file.Info.ModTime().Before(t.Add(1*time.Second)) {
|
t, err := time.Parse(http.TimeFormat, h.Get("If-Modified-Since"))
|
||||||
|
if err == nil && file.Info.ModTime().Before(t.Add(1*time.Second)) {
|
||||||
w.WriteHeader(http.StatusNotModified)
|
w.WriteHeader(http.StatusNotModified)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
h := w.Header()
|
|
||||||
h.Set("Last-Modified", file.FormattedModTime)
|
h.Set("Last-Modified", file.FormattedModTime)
|
||||||
h.Set("Content-Type", file.Mimetype)
|
h.Set("Content-Type", file.Mimetype)
|
||||||
//Cache-Control: max-age=31536000
|
//Cache-Control: max-age=31536000
|
||||||
|
@ -68,7 +69,7 @@ func route_static(w http.ResponseWriter, r *http.Request) {
|
||||||
h.Set("Vary", "Accept-Encoding")
|
h.Set("Vary", "Accept-Encoding")
|
||||||
//http.ServeContent(w,r,r.URL.Path,file.Info.ModTime(),file)
|
//http.ServeContent(w,r,r.URL.Path,file.Info.ModTime(),file)
|
||||||
//w.Write(file.Data)
|
//w.Write(file.Data)
|
||||||
if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
|
if strings.Contains(h.Get("Accept-Encoding"), "gzip") {
|
||||||
h.Set("Content-Encoding", "gzip")
|
h.Set("Content-Encoding", "gzip")
|
||||||
h.Set("Content-Length", strconv.FormatInt(file.GzipLength, 10))
|
h.Set("Content-Length", strconv.FormatInt(file.GzipLength, 10))
|
||||||
io.Copy(w, bytes.NewReader(file.GzipData)) // Use w.Write instead?
|
io.Copy(w, bytes.NewReader(file.GzipData)) // Use w.Write instead?
|
||||||
|
@ -90,9 +91,9 @@ func route_fstatic(w http.ResponseWriter, r *http.Request){
|
||||||
http.ServeFile(w,r,r.URL.Path)
|
http.ServeFile(w,r,r.URL.Path)
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// TO-DO: Make this a static file somehow? Is it possible for us to put this file somewhere else?
|
// TODO: Make this a static file somehow? Is it possible for us to put this file somewhere else?
|
||||||
// TO-DO: Add a sitemap
|
// TODO: Add a sitemap
|
||||||
// TO-DO: Add an API so that plugins can register disallowed areas. E.g. /groups/join for plugin_socialgroups
|
// TODO: Add an API so that plugins can register disallowed areas. E.g. /groups/join for plugin_socialgroups
|
||||||
func route_robots_txt(w http.ResponseWriter, r *http.Request) {
|
func route_robots_txt(w http.ResponseWriter, r *http.Request) {
|
||||||
_, _ = w.Write([]byte(`User-agent: *
|
_, _ = w.Write([]byte(`User-agent: *
|
||||||
Disallow: /panel/
|
Disallow: /panel/
|
||||||
|
@ -148,7 +149,7 @@ func route_custom_page(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Paginate this
|
// TODO: Paginate this
|
||||||
func route_topics(w http.ResponseWriter, r *http.Request, user User) {
|
func route_topics(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
headerVars, ok := SessionCheck(w, r, &user)
|
headerVars, ok := SessionCheck(w, r, &user)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -232,7 +233,7 @@ func route_topics(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: What if a user is deleted via the Control Panel?
|
// TODO: What if a user is deleted via the Control Panel?
|
||||||
userList, err := users.BulkCascadeGetMap(idSlice)
|
userList, err := users.BulkCascadeGetMap(idSlice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
|
@ -240,7 +241,7 @@ func route_topics(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second pass to the add the user data
|
// Second pass to the add the user data
|
||||||
// TO-DO: Use a pointer to TopicsRow instead of TopicsRow itself?
|
// TODO: Use a pointer to TopicsRow instead of TopicsRow itself?
|
||||||
for _, topicItem := range topicList {
|
for _, topicItem := range topicList {
|
||||||
topicItem.Creator = userList[topicItem.CreatedBy]
|
topicItem.Creator = userList[topicItem.CreatedBy]
|
||||||
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
||||||
|
@ -279,7 +280,7 @@ func route_forum(w http.ResponseWriter, r *http.Request, user User, sfid string)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Fix this double-check
|
// TODO: Fix this double-check
|
||||||
forum, err := fstore.CascadeGet(fid)
|
forum, err := fstore.CascadeGet(fid)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
NotFound(w, r)
|
NotFound(w, r)
|
||||||
|
@ -309,7 +310,7 @@ func route_forum(w http.ResponseWriter, r *http.Request, user User, sfid string)
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
// TO-DO: Use something other than TopicsRow as we don't need to store the forum name and link on each and every topic item?
|
// TODO: Use something other than TopicsRow as we don't need to store the forum name and link on each and every topic item?
|
||||||
var topicList []*TopicsRow
|
var topicList []*TopicsRow
|
||||||
var reqUserList = make(map[int]bool)
|
var reqUserList = make(map[int]bool)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
|
@ -347,7 +348,7 @@ func route_forum(w http.ResponseWriter, r *http.Request, user User, sfid string)
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: What if a user is deleted via the Control Panel?
|
// TODO: What if a user is deleted via the Control Panel?
|
||||||
userList, err := users.BulkCascadeGetMap(idSlice)
|
userList, err := users.BulkCascadeGetMap(idSlice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
|
@ -355,7 +356,7 @@ func route_forum(w http.ResponseWriter, r *http.Request, user User, sfid string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second pass to the add the user data
|
// Second pass to the add the user data
|
||||||
// TO-DO: Use a pointer to TopicsRow instead of TopicsRow itself?
|
// TODO: Use a pointer to TopicsRow instead of TopicsRow itself?
|
||||||
for _, topicItem := range topicList {
|
for _, topicItem := range topicList {
|
||||||
topicItem.Creator = userList[topicItem.CreatedBy]
|
topicItem.Creator = userList[topicItem.CreatedBy]
|
||||||
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
topicItem.LastUser = userList[topicItem.LastReplyBy]
|
||||||
|
@ -381,7 +382,7 @@ func route_forums(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
var forumList []Forum
|
var forumList []Forum
|
||||||
var canSee []int
|
var canSee []int
|
||||||
if user.IsSuperAdmin {
|
if user.IsSuperAdmin {
|
||||||
canSee, err = fstore.GetAllIDs()
|
canSee, err = fstore.GetAllVisibleIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
return
|
return
|
||||||
|
@ -396,7 +397,7 @@ func route_forums(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
for _, fid := range canSee {
|
for _, fid := range canSee {
|
||||||
//log.Print(forums[fid])
|
//log.Print(forums[fid])
|
||||||
var forum = *fstore.DirtyGet(fid)
|
var forum = *fstore.DirtyGet(fid)
|
||||||
if forum.Active && forum.Name != "" && forum.ParentID == 0 {
|
if forum.ParentID == 0 {
|
||||||
if forum.LastTopicID != 0 {
|
if forum.LastTopicID != 0 {
|
||||||
forum.LastTopicTime, err = relativeTime(forum.LastTopicTime)
|
forum.LastTopicTime, err = relativeTime(forum.LastTopicTime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -582,7 +583,7 @@ func route_topic_id(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
}
|
}
|
||||||
replyItem.Liked = false
|
replyItem.Liked = false
|
||||||
|
|
||||||
// TO-DO: Rename this to topic_rrow_assign
|
// TODO: Rename this to topic_rrow_assign
|
||||||
if hooks["rrow_assign"] != nil {
|
if hooks["rrow_assign"] != nil {
|
||||||
runHook("rrow_assign", &replyItem)
|
runHook("rrow_assign", &replyItem)
|
||||||
}
|
}
|
||||||
|
@ -682,7 +683,7 @@ func route_profile(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
replyLiked := false
|
replyLiked := false
|
||||||
replyLikeCount := 0
|
replyLikeCount := 0
|
||||||
|
|
||||||
// TO-DO: Add a hook here
|
// TODO: Add a hook here
|
||||||
|
|
||||||
replyList = append(replyList, Reply{rid, puser.ID, replyContent, parseMessage(replyContent), replyCreatedBy, buildProfileURL(nameToSlug(replyCreatedByName), replyCreatedBy), replyCreatedByName, replyGroup, replyCreatedAt, replyLastEdit, replyLastEditBy, replyAvatar, replyClassName, replyLines, replyTag, "", "", "", 0, "", replyLiked, replyLikeCount, "", ""})
|
replyList = append(replyList, Reply{rid, puser.ID, replyContent, parseMessage(replyContent), replyCreatedBy, buildProfileURL(nameToSlug(replyCreatedByName), replyCreatedBy), replyCreatedByName, replyGroup, replyCreatedAt, replyLastEdit, replyLastEditBy, replyAvatar, replyClassName, replyLines, replyTag, "", "", "", 0, "", replyLiked, replyLikeCount, "", ""})
|
||||||
}
|
}
|
||||||
|
@ -731,10 +732,11 @@ func route_topic_create(w http.ResponseWriter, r *http.Request, user User, sfid
|
||||||
runVhook("topic_create_pre_loop", w, r, fid, &headerVars, &user, &strictmode)
|
runVhook("topic_create_pre_loop", w, r, fid, &headerVars, &user, &strictmode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Re-add support for plugin_socialgroups
|
||||||
var forumList []Forum
|
var forumList []Forum
|
||||||
var canSee []int
|
var canSee []int
|
||||||
if user.IsSuperAdmin {
|
if user.IsSuperAdmin {
|
||||||
canSee, err = fstore.GetAllIDs()
|
canSee, err = fstore.GetAllVisibleIDs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
return
|
return
|
||||||
|
@ -744,25 +746,23 @@ func route_topic_create(w http.ResponseWriter, r *http.Request, user User, sfid
|
||||||
canSee = group.CanSee
|
canSee = group.CanSee
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: plugin_superadmin needs to be able to override this loop. Skip flag on topic_create_pre_loop?
|
// TODO: plugin_superadmin needs to be able to override this loop. Skip flag on topic_create_pre_loop?
|
||||||
for _, ffid := range canSee {
|
for _, ffid := range canSee {
|
||||||
// TO-DO: Surely, there's a better way of doing this. I've added it in for now to support plugin_socialgroups, but we really need to clean this up
|
// TODO: Surely, there's a better way of doing this. I've added it in for now to support plugin_socialgroups, but we really need to clean this up
|
||||||
if strictmode && ffid != fid {
|
if strictmode && ffid != fid {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do a bulk forum fetch, just in case it's the SqlForumStore?
|
// Do a bulk forum fetch, just in case it's the SqlForumStore?
|
||||||
forum := fstore.DirtyGet(ffid)
|
forum := fstore.DirtyGet(ffid)
|
||||||
if forum.Active && forum.Name != "" {
|
fcopy := *forum
|
||||||
fcopy := *forum
|
if hooks["topic_create_frow_assign"] != nil {
|
||||||
if hooks["topic_create_frow_assign"] != nil {
|
// TODO: Add the skip feature to all the other row based hooks?
|
||||||
// TO-DO: Add the skip feature to all the other row based hooks?
|
if runHook("topic_create_frow_assign", &fcopy).(bool) {
|
||||||
if runHook("topic_create_frow_assign", &fcopy).(bool) {
|
continue
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
forumList = append(forumList, fcopy)
|
|
||||||
}
|
}
|
||||||
|
forumList = append(forumList, fcopy)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctpage := CreateTopicPage{"Create Topic", user, headerVars, forumList, fid}
|
ctpage := CreateTopicPage{"Create Topic", user, headerVars, forumList, fid}
|
||||||
|
@ -789,7 +789,7 @@ func route_topic_create_submit(w http.ResponseWriter, r *http.Request, user User
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add hooks to make use of headerLite
|
// TODO: Add hooks to make use of headerLite
|
||||||
_, ok := SimpleForumSessionCheck(w, r, &user, fid)
|
_, ok := SimpleForumSessionCheck(w, r, &user, fid)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -799,7 +799,7 @@ func route_topic_create_submit(w http.ResponseWriter, r *http.Request, user User
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
topic_name := html.EscapeString(r.PostFormValue("topic-name"))
|
topicName := html.EscapeString(r.PostFormValue("topic-name"))
|
||||||
content := html.EscapeString(preparseMessage(r.PostFormValue("topic-content")))
|
content := html.EscapeString(preparseMessage(r.PostFormValue("topic-content")))
|
||||||
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
ipaddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -808,7 +808,7 @@ func route_topic_create_submit(w http.ResponseWriter, r *http.Request, user User
|
||||||
}
|
}
|
||||||
|
|
||||||
wcount := wordCount(content)
|
wcount := wordCount(content)
|
||||||
res, err := create_topic_stmt.Exec(fid, topic_name, content, parseMessage(content), user.ID, ipaddress, wcount, user.ID)
|
res, err := create_topic_stmt.Exec(fid, topicName, content, parseMessage(content), user.ID, ipaddress, wcount, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
return
|
return
|
||||||
|
@ -832,13 +832,13 @@ func route_topic_create_submit(w http.ResponseWriter, r *http.Request, user User
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "/topic/"+strconv.FormatInt(lastID, 10), http.StatusSeeOther)
|
http.Redirect(w, r, "/topic/"+strconv.FormatInt(lastID, 10), http.StatusSeeOther)
|
||||||
err = increase_post_user_stats(wcount, user.ID, true, user)
|
err = user.increasePostStats(wcount, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = fstore.UpdateLastTopic(topic_name, int(lastID), user.Name, user.ID, time.Now().Format("2006-01-02 15:04:05"), fid)
|
err = fstore.UpdateLastTopic(topicName, int(lastID), user.Name, user.ID, time.Now().Format("2006-01-02 15:04:05"), fid)
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
}
|
}
|
||||||
|
@ -865,7 +865,7 @@ func route_create_reply(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add hooks to make use of headerLite
|
// TODO: Add hooks to make use of headerLite
|
||||||
_, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID)
|
_, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -933,7 +933,7 @@ func route_create_reply(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
||||||
err = increase_post_user_stats(wcount, user.ID, false, user)
|
err = user.increasePostStats(wcount, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
return
|
return
|
||||||
|
@ -962,7 +962,7 @@ func route_like_topic(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add hooks to make use of headerLite
|
// TODO: Add hooks to make use of headerLite
|
||||||
_, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID)
|
_, ok := SimpleForumSessionCheck(w, r, &user, topic.ParentID)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -1073,7 +1073,7 @@ func route_reply_like_submit(w http.ResponseWriter, r *http.Request, user User)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add hooks to make use of headerLite
|
// TODO: Add hooks to make use of headerLite
|
||||||
_, ok := SimpleForumSessionCheck(w, r, &user, fid)
|
_, ok := SimpleForumSessionCheck(w, r, &user, fid)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
|
@ -1171,8 +1171,8 @@ func route_profile_reply_create(w http.ResponseWriter, r *http.Request, user Use
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var user_name string
|
var userName string
|
||||||
err = get_user_name_stmt.QueryRow(uid).Scan(&user_name)
|
err = get_user_name_stmt.QueryRow(uid).Scan(&userName)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
LocalError("The profile you're trying to post on doesn't exist.", w, r, user)
|
LocalError("The profile you're trying to post on doesn't exist.", w, r, user)
|
||||||
return
|
return
|
||||||
|
@ -1184,7 +1184,7 @@ func route_profile_reply_create(w http.ResponseWriter, r *http.Request, user Use
|
||||||
http.Redirect(w, r, "/user/"+strconv.Itoa(uid), http.StatusSeeOther)
|
http.Redirect(w, r, "/user/"+strconv.Itoa(uid), http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
func route_report_submit(w http.ResponseWriter, r *http.Request, user User, sitem_id string) {
|
func route_report_submit(w http.ResponseWriter, r *http.Request, user User, sitemID string) {
|
||||||
if !user.Loggedin {
|
if !user.Loggedin {
|
||||||
LoginRequired(w, r, user)
|
LoginRequired(w, r, user)
|
||||||
return
|
return
|
||||||
|
@ -1204,18 +1204,18 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, user User, site
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
item_id, err := strconv.Atoi(sitem_id)
|
itemID, err := strconv.Atoi(sitemID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LocalError("Bad ID", w, r, user)
|
LocalError("Bad ID", w, r, user)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
item_type := r.FormValue("type")
|
itemType := r.FormValue("type")
|
||||||
|
|
||||||
var fid int = 1
|
var fid = 1
|
||||||
var title, content string
|
var title, content string
|
||||||
if item_type == "reply" {
|
if itemType == "reply" {
|
||||||
reply, err := getReply(item_id)
|
reply, err := getReply(itemID)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
LocalError("We were unable to find the reported post", w, r, user)
|
LocalError("We were unable to find the reported post", w, r, user)
|
||||||
return
|
return
|
||||||
|
@ -1234,9 +1234,9 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, user User, site
|
||||||
}
|
}
|
||||||
|
|
||||||
title = "Reply: " + topic.Title
|
title = "Reply: " + topic.Title
|
||||||
content = reply.Content + "\n\nOriginal Post: #rid-" + strconv.Itoa(item_id)
|
content = reply.Content + "\n\nOriginal Post: #rid-" + strconv.Itoa(itemID)
|
||||||
} else if item_type == "user-reply" {
|
} else if itemType == "user-reply" {
|
||||||
userReply, err := getUserReply(item_id)
|
userReply, err := getUserReply(itemID)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
LocalError("We weren't able to find the reported post", w, r, user)
|
LocalError("We weren't able to find the reported post", w, r, user)
|
||||||
return
|
return
|
||||||
|
@ -1255,8 +1255,8 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, user User, site
|
||||||
}
|
}
|
||||||
title = "Profile: " + title
|
title = "Profile: " + title
|
||||||
content = userReply.Content + "\n\nOriginal Post: @" + strconv.Itoa(userReply.ParentID)
|
content = userReply.Content + "\n\nOriginal Post: @" + strconv.Itoa(userReply.ParentID)
|
||||||
} else if item_type == "topic" {
|
} else if itemType == "topic" {
|
||||||
err = get_topic_basic_stmt.QueryRow(item_id).Scan(&title, &content)
|
err = get_topic_basic_stmt.QueryRow(itemID).Scan(&title, &content)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
NotFound(w, r)
|
NotFound(w, r)
|
||||||
return
|
return
|
||||||
|
@ -1265,10 +1265,10 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, user User, site
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
title = "Topic: " + title
|
title = "Topic: " + title
|
||||||
content = content + "\n\nOriginal Post: #tid-" + strconv.Itoa(item_id)
|
content = content + "\n\nOriginal Post: #tid-" + strconv.Itoa(itemID)
|
||||||
} else {
|
} else {
|
||||||
if vhooks["report_preassign"] != nil {
|
if vhooks["report_preassign"] != nil {
|
||||||
runVhookNoreturn("report_preassign", &item_id, &item_type)
|
runVhookNoreturn("report_preassign", &itemID, &itemType)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Don't try to guess the type
|
// Don't try to guess the type
|
||||||
|
@ -1277,7 +1277,7 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, user User, site
|
||||||
}
|
}
|
||||||
|
|
||||||
var count int
|
var count int
|
||||||
rows, err := report_exists_stmt.Query(item_type + "_" + strconv.Itoa(item_id))
|
rows, err := report_exists_stmt.Query(itemType + "_" + strconv.Itoa(itemID))
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
return
|
return
|
||||||
|
@ -1295,7 +1295,7 @@ func route_report_submit(w http.ResponseWriter, r *http.Request, user User, site
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := create_report_stmt.Exec(title, content, parseMessage(content), user.ID, item_type+"_"+strconv.Itoa(item_id))
|
res, err := create_report_stmt.Exec(title, content, parseMessage(content), user.ID, itemType+"_"+strconv.Itoa(itemID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
return
|
return
|
||||||
|
@ -1546,14 +1546,15 @@ func route_account_own_edit_username_submit(w http.ResponseWriter, r *http.Reque
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
new_username := html.EscapeString(r.PostFormValue("account-new-username"))
|
newUsername := html.EscapeString(r.PostFormValue("account-new-username"))
|
||||||
_, err = set_username_stmt.Exec(new_username, strconv.Itoa(user.ID))
|
_, err = set_username_stmt.Exec(newUsername, strconv.Itoa(user.ID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LocalError("Unable to change the username. Does someone else already have this name?", w, r, user)
|
LocalError("Unable to change the username. Does someone else already have this name?", w, r, user)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Name = new_username
|
// TODO: Use the reloaded data instead for the name?
|
||||||
|
user.Name = newUsername
|
||||||
err = users.Load(user.ID)
|
err = users.Load(user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LocalError("Your account doesn't exist!", w, r, user)
|
LocalError("Your account doesn't exist!", w, r, user)
|
||||||
|
@ -1704,6 +1705,7 @@ func route_account_own_edit_email_token_submit(w http.ResponseWriter, r *http.Re
|
||||||
templates.ExecuteTemplate(w, "account-own-edit-email.html", pi)
|
templates.ExecuteTemplate(w, "account-own-edit-email.html", pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move this into member_routes.go
|
||||||
func route_logout(w http.ResponseWriter, r *http.Request, user User) {
|
func route_logout(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
if !user.Loggedin {
|
if !user.Loggedin {
|
||||||
LocalError("You can't logout without logging in first.", w, r, user)
|
LocalError("You can't logout without logging in first.", w, r, user)
|
||||||
|
@ -1731,9 +1733,9 @@ func route_login(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
templates.ExecuteTemplate(w, "login.html", pi)
|
templates.ExecuteTemplate(w, "login.html", pi)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Log failed attempted logins?
|
// TODO: Log failed attempted logins?
|
||||||
// TO-DO: Lock IPS out if they have too many failed attempts?
|
// TODO: Lock IPS out if they have too many failed attempts?
|
||||||
// TO-DO: Log unusual countries in comparison to the country a user usually logs in from? Alert the user about this?
|
// TODO: Log unusual countries in comparison to the country a user usually logs in from? Alert the user about this?
|
||||||
func route_login_submit(w http.ResponseWriter, r *http.Request, user User) {
|
func route_login_submit(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
if user.Loggedin {
|
if user.Loggedin {
|
||||||
LocalError("You're already logged in.", w, r, user)
|
LocalError("You're already logged in.", w, r, user)
|
||||||
|
@ -1842,11 +1844,11 @@ func route_register_submit(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm_password := r.PostFormValue("confirm_password")
|
confirmPassword := r.PostFormValue("confirm_password")
|
||||||
log.Print("Registration Attempt! Username: " + username)
|
log.Print("Registration Attempt! Username: " + username) // TODO: Add controls over what is logged when?
|
||||||
|
|
||||||
// Do the two inputted passwords match..?
|
// Do the two inputted passwords match..?
|
||||||
if password != confirm_password {
|
if password != confirmPassword {
|
||||||
LocalError("The two passwords don't match.", w, r, user)
|
LocalError("The two passwords don't match.", w, r, user)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1898,8 +1900,48 @@ func route_register_submit(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: We don't need support XML here to support sitemaps, we could handle those elsewhere
|
// TODO: Set the cookie domain
|
||||||
var phrase_login_alerts = []byte(`{"msgs":[{"msg":"Login to see your alerts","path":"/accounts/login"}]}`)
|
func route_change_theme(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
|
//headerLite, _ := SimpleSessionCheck(w, r, &user)
|
||||||
|
err := r.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
PreError("Bad Form", w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Rename isJs to something else, just in case we rewrite the JS side in WebAssembly?
|
||||||
|
isJs := (r.PostFormValue("isJs") == "1")
|
||||||
|
|
||||||
|
newTheme := html.EscapeString(r.PostFormValue("newTheme"))
|
||||||
|
|
||||||
|
theme, ok := themes[newTheme]
|
||||||
|
if !ok || theme.HideFromThemes {
|
||||||
|
log.Print("Bad Theme: ", newTheme)
|
||||||
|
LocalErrorJSQ("That theme doesn't exist", w, r, user, isJs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Store the current theme in the user's account?
|
||||||
|
/*if user.Loggedin {
|
||||||
|
_, err = change_theme_stmt.Exec(newTheme, user.ID)
|
||||||
|
if err != nil {
|
||||||
|
InternalError(err, w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
cookie := http.Cookie{Name: "current_theme", Value: newTheme, Path: "/", MaxAge: year}
|
||||||
|
http.SetCookie(w, &cookie)
|
||||||
|
|
||||||
|
if !isJs {
|
||||||
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
} else {
|
||||||
|
_, _ = w.Write(successJSONBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We don't need support XML here to support sitemaps, we could handle those elsewhere
|
||||||
|
var phraseLoginAlerts = []byte(`{"msgs":[{"msg":"Login to see your alerts","path":"/accounts/login"}]}`)
|
||||||
|
|
||||||
func route_api(w http.ResponseWriter, r *http.Request, user User) {
|
func route_api(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
@ -1931,7 +1973,7 @@ func route_api(w http.ResponseWriter, r *http.Request, user User) {
|
||||||
}
|
}
|
||||||
case "alerts": // A feed of events tailored for a specific user
|
case "alerts": // A feed of events tailored for a specific user
|
||||||
if !user.Loggedin {
|
if !user.Loggedin {
|
||||||
w.Write(phrase_login_alerts)
|
w.Write(phraseLoginAlerts)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
// TODO: Move PreRoute and it's friends here in the next commit, I'm avoiding doing it in this one as I don't want the diffs to be lost
|
|
@ -0,0 +1,3 @@
|
||||||
|
CREATE TABLE `sync` (
|
||||||
|
`last_update` datetime not null
|
||||||
|
);
|
|
@ -0,0 +1,3 @@
|
||||||
|
CREATE TABLE `sync` (
|
||||||
|
`last_update` timestamp not null
|
||||||
|
);
|
|
@ -24,7 +24,6 @@ type Setting struct {
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
settingBox.Store(SettingBox(make(map[string]interface{})))
|
settingBox.Store(SettingBox(make(map[string]interface{})))
|
||||||
//settingBox.Store(make(map[string]interface{}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadSettings() error {
|
func LoadSettings() error {
|
||||||
|
@ -34,8 +33,7 @@ func LoadSettings() error {
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
sBox := settingBox.Load().(SettingBox)
|
var sBox = SettingBox(make(map[string]interface{}))
|
||||||
//sBox := settingBox.Load().(map[string]interface{})
|
|
||||||
var sname, scontent, stype, sconstraints string
|
var sname, scontent, stype, sconstraints string
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
err = rows.Scan(&sname, &scontent, &stype, &sconstraints)
|
err = rows.Scan(&sname, &scontent, &stype, &sconstraints)
|
||||||
|
@ -56,10 +54,10 @@ func LoadSettings() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions.
|
// TODO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions.
|
||||||
func (sBox SettingBox) ParseSetting(sname string, scontent string, stype string, constraint string) string {
|
func (sBox SettingBox) ParseSetting(sname string, scontent string, stype string, constraint string) string {
|
||||||
var err error
|
var err error
|
||||||
var ssBox map[string]interface{} = map[string]interface{}(sBox)
|
var ssBox = map[string]interface{}(sBox)
|
||||||
if stype == "bool" {
|
if stype == "bool" {
|
||||||
ssBox[sname] = (scontent == "1")
|
ssBox[sname] = (scontent == "1")
|
||||||
} else if stype == "int" {
|
} else if stype == "int" {
|
||||||
|
|
64
site.go
64
site.go
|
@ -2,61 +2,57 @@ package main
|
||||||
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
var site *Site = &Site{Name:"Magical Fairy Land"}
|
var site = &Site{Name: "Magical Fairy Land"}
|
||||||
var db_config DB_Config = DB_Config{Host:"localhost"}
|
var db_config = DB_Config{Host: "localhost"}
|
||||||
var config Config
|
var config Config
|
||||||
var dev DevConfig
|
var dev DevConfig
|
||||||
|
|
||||||
type Site struct
|
type Site struct {
|
||||||
{
|
Name string
|
||||||
Name string
|
Email string
|
||||||
Email string
|
Url string
|
||||||
Url string
|
Port string
|
||||||
Port string
|
EnableSsl bool
|
||||||
EnableSsl bool
|
|
||||||
EnableEmails bool
|
EnableEmails bool
|
||||||
HasProxy bool
|
HasProxy bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type DB_Config struct
|
type DB_Config struct {
|
||||||
{
|
Host string
|
||||||
Host string
|
|
||||||
Username string
|
Username string
|
||||||
Password string
|
Password string
|
||||||
Dbname string
|
Dbname string
|
||||||
Port string
|
Port string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct
|
type Config struct {
|
||||||
{
|
SslPrivkey string
|
||||||
SslPrivkey string
|
|
||||||
SslFullchain string
|
SslFullchain string
|
||||||
|
|
||||||
MaxRequestSize int
|
MaxRequestSize int
|
||||||
CacheTopicUser int
|
CacheTopicUser int
|
||||||
UserCacheCapacity int
|
UserCacheCapacity int
|
||||||
TopicCacheCapacity int
|
TopicCacheCapacity int
|
||||||
|
|
||||||
SmtpServer string
|
SmtpServer string
|
||||||
SmtpUsername string
|
SmtpUsername string
|
||||||
SmtpPassword string
|
SmtpPassword string
|
||||||
SmtpPort string
|
SmtpPort string
|
||||||
|
|
||||||
DefaultRoute func(http.ResponseWriter, *http.Request, User)
|
DefaultRoute func(http.ResponseWriter, *http.Request, User)
|
||||||
DefaultGroup int
|
DefaultGroup int
|
||||||
ActivationGroup int
|
ActivationGroup int
|
||||||
StaffCss string
|
StaffCss string
|
||||||
UncategorisedForumVisible bool
|
UncategorisedForumVisible bool
|
||||||
MinifyTemplates bool
|
MinifyTemplates bool
|
||||||
MultiServer bool
|
MultiServer bool
|
||||||
|
|
||||||
Noavatar string
|
Noavatar string
|
||||||
ItemsPerPage int
|
ItemsPerPage int
|
||||||
}
|
}
|
||||||
|
|
||||||
type DevConfig struct
|
type DevConfig struct {
|
||||||
{
|
DebugMode bool
|
||||||
DebugMode bool
|
|
||||||
SuperDebug bool
|
SuperDebug bool
|
||||||
Profiling bool
|
Profiling bool
|
||||||
}
|
}
|
||||||
|
|
40
tasks.go
40
tasks.go
|
@ -2,6 +2,12 @@ package main
|
||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
|
var lastSync time.Time
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
lastSync = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
func handleExpiredScheduledGroups() error {
|
func handleExpiredScheduledGroups() error {
|
||||||
rows, err := get_expired_scheduled_groups_stmt.Query()
|
rows, err := get_expired_scheduled_groups_stmt.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -27,3 +33,37 @@ func handleExpiredScheduledGroups() error {
|
||||||
}
|
}
|
||||||
return rows.Err()
|
return rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleServerSync() error {
|
||||||
|
var lastUpdate time.Time
|
||||||
|
var lastUpdateStr string
|
||||||
|
err := get_sync_stmt.QueryRow().Scan(&lastUpdateStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
layout := "2006-01-02 15:04:05"
|
||||||
|
lastUpdate, err = time.Parse(layout, lastUpdateStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if lastUpdate.After(lastSync) {
|
||||||
|
// TODO: A more granular sync
|
||||||
|
err = fstore.LoadForums()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO: Resync the groups
|
||||||
|
// TODO: Resync the permissions
|
||||||
|
err = LoadSettings()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = LoadWordFilters()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -166,10 +166,26 @@ w.Write(forum_42)
|
||||||
}
|
}
|
||||||
w.Write(forum_43)
|
w.Write(forum_43)
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
if tmpl_forum_vars.Header.Widgets.RightSidebar != "" {
|
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(footer_1)
|
||||||
w.Write([]byte(string(tmpl_forum_vars.Header.Widgets.RightSidebar)))
|
w.Write([]byte(item.Name))
|
||||||
w.Write(footer_2)
|
w.Write(footer_2)
|
||||||
}
|
if tmpl_forum_vars.Header.ThemeName == item.Name {
|
||||||
w.Write(footer_3)
|
w.Write(footer_3)
|
||||||
}
|
}
|
||||||
|
w.Write(footer_4)
|
||||||
|
w.Write([]byte(item.FriendlyName))
|
||||||
|
w.Write(footer_5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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(footer_8)
|
||||||
|
}
|
||||||
|
w.Write(footer_9)
|
||||||
|
}
|
||||||
|
|
|
@ -109,10 +109,26 @@ w.Write(forums_17)
|
||||||
}
|
}
|
||||||
w.Write(forums_18)
|
w.Write(forums_18)
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
if tmpl_forums_vars.Header.Widgets.RightSidebar != "" {
|
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(footer_1)
|
||||||
w.Write([]byte(string(tmpl_forums_vars.Header.Widgets.RightSidebar)))
|
w.Write([]byte(item.Name))
|
||||||
w.Write(footer_2)
|
w.Write(footer_2)
|
||||||
}
|
if tmpl_forums_vars.Header.ThemeName == item.Name {
|
||||||
w.Write(footer_3)
|
w.Write(footer_3)
|
||||||
}
|
}
|
||||||
|
w.Write(footer_4)
|
||||||
|
w.Write([]byte(item.FriendlyName))
|
||||||
|
w.Write(footer_5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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(footer_8)
|
||||||
|
}
|
||||||
|
w.Write(footer_9)
|
||||||
|
}
|
||||||
|
|
109
template_init.go
109
template_init.go
|
@ -6,8 +6,9 @@ import "net/http"
|
||||||
|
|
||||||
var templates = template.New("")
|
var templates = template.New("")
|
||||||
|
|
||||||
|
// nolint
|
||||||
func interpreted_topic_template(pi TopicPage, w http.ResponseWriter) {
|
func interpreted_topic_template(pi TopicPage, w http.ResponseWriter) {
|
||||||
mapping, ok := themes[defaultTheme].TemplatesMap["topic"]
|
mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap["topic"]
|
||||||
if !ok {
|
if !ok {
|
||||||
mapping = "topic"
|
mapping = "topic"
|
||||||
}
|
}
|
||||||
|
@ -17,11 +18,13 @@ func interpreted_topic_template(pi TopicPage, w http.ResponseWriter) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
var template_topic_handle func(TopicPage, http.ResponseWriter) = interpreted_topic_template
|
var template_topic_handle func(TopicPage, http.ResponseWriter) = interpreted_topic_template
|
||||||
var template_topic_alt_handle func(TopicPage, http.ResponseWriter) = interpreted_topic_template
|
var template_topic_alt_handle func(TopicPage, http.ResponseWriter) = interpreted_topic_template
|
||||||
|
|
||||||
|
// nolint
|
||||||
var template_topics_handle func(TopicsPage, http.ResponseWriter) = func(pi TopicsPage, w http.ResponseWriter) {
|
var template_topics_handle func(TopicsPage, http.ResponseWriter) = func(pi TopicsPage, w http.ResponseWriter) {
|
||||||
mapping, ok := themes[defaultTheme].TemplatesMap["topics"]
|
mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap["topics"]
|
||||||
if !ok {
|
if !ok {
|
||||||
mapping = "topics"
|
mapping = "topics"
|
||||||
}
|
}
|
||||||
|
@ -31,8 +34,9 @@ var template_topics_handle func(TopicsPage, http.ResponseWriter) = func(pi Topic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
var template_forum_handle func(ForumPage, http.ResponseWriter) = func(pi ForumPage, w http.ResponseWriter) {
|
var template_forum_handle func(ForumPage, http.ResponseWriter) = func(pi ForumPage, w http.ResponseWriter) {
|
||||||
mapping, ok := themes[defaultTheme].TemplatesMap["forum"]
|
mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap["forum"]
|
||||||
if !ok {
|
if !ok {
|
||||||
mapping = "forum"
|
mapping = "forum"
|
||||||
}
|
}
|
||||||
|
@ -42,8 +46,9 @@ var template_forum_handle func(ForumPage, http.ResponseWriter) = func(pi ForumPa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
var template_forums_handle func(ForumsPage, http.ResponseWriter) = func(pi ForumsPage, w http.ResponseWriter) {
|
var template_forums_handle func(ForumsPage, http.ResponseWriter) = func(pi ForumsPage, w http.ResponseWriter) {
|
||||||
mapping, ok := themes[defaultTheme].TemplatesMap["forums"]
|
mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap["forums"]
|
||||||
if !ok {
|
if !ok {
|
||||||
mapping = "forums"
|
mapping = "forums"
|
||||||
}
|
}
|
||||||
|
@ -53,8 +58,9 @@ var template_forums_handle func(ForumsPage, http.ResponseWriter) = func(pi Forum
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
var template_profile_handle func(ProfilePage, http.ResponseWriter) = func(pi ProfilePage, w http.ResponseWriter) {
|
var template_profile_handle func(ProfilePage, http.ResponseWriter) = func(pi ProfilePage, w http.ResponseWriter) {
|
||||||
mapping, ok := themes[defaultTheme].TemplatesMap["profile"]
|
mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap["profile"]
|
||||||
if !ok {
|
if !ok {
|
||||||
mapping = "profile"
|
mapping = "profile"
|
||||||
}
|
}
|
||||||
|
@ -64,8 +70,9 @@ var template_profile_handle func(ProfilePage, http.ResponseWriter) = func(pi Pro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nolint
|
||||||
var template_create_topic_handle func(CreateTopicPage, http.ResponseWriter) = func(pi CreateTopicPage, w http.ResponseWriter) {
|
var template_create_topic_handle func(CreateTopicPage, http.ResponseWriter) = func(pi CreateTopicPage, w http.ResponseWriter) {
|
||||||
mapping, ok := themes[defaultTheme].TemplatesMap["create-topic"]
|
mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap["create-topic"]
|
||||||
if !ok {
|
if !ok {
|
||||||
mapping = "create-topic"
|
mapping = "create-topic"
|
||||||
}
|
}
|
||||||
|
@ -77,12 +84,18 @@ var template_create_topic_handle func(CreateTopicPage, http.ResponseWriter) = fu
|
||||||
|
|
||||||
func compileTemplates() error {
|
func compileTemplates() error {
|
||||||
var c CTemplateSet
|
var c CTemplateSet
|
||||||
|
|
||||||
|
// Schemas to train the template compiler on what to expect
|
||||||
|
// TODO: Add support for interface{}s
|
||||||
user := User{62, buildProfileURL("fake-user", 62), "Fake User", "compiler@localhost", 0, false, false, false, false, false, false, GuestPerms, make(map[string]bool), "", false, "", "", "", "", "", 0, 0, "0.0.0.0.0", 0}
|
user := User{62, buildProfileURL("fake-user", 62), "Fake User", "compiler@localhost", 0, false, false, false, false, false, false, GuestPerms, make(map[string]bool), "", false, "", "", "", "", "", 0, 0, "0.0.0.0.0", 0}
|
||||||
// TO-DO: Do a more accurate level calculation for this?
|
// TODO: Do a more accurate level calculation for this?
|
||||||
user2 := User{1, buildProfileURL("admin-alice", 1), "Admin Alice", "alice@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", "", "", "", "", 58, 1000, "127.0.0.1", 0}
|
user2 := User{1, buildProfileURL("admin-alice", 1), "Admin Alice", "alice@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", "", "", "", "", 58, 1000, "127.0.0.1", 0}
|
||||||
user3 := User{2, buildProfileURL("admin-fred", 62), "Admin Fred", "fred@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", "", "", "", "", 42, 900, "::1", 0}
|
user3 := User{2, buildProfileURL("admin-fred", 62), "Admin Fred", "fred@localhost", 1, true, true, true, true, false, false, AllPerms, make(map[string]bool), "", true, "", "", "", "", "", 42, 900, "::1", 0}
|
||||||
headerVars := &HeaderVars{
|
headerVars := &HeaderVars{
|
||||||
Site: site,
|
Site: site,
|
||||||
|
Settings: settingBox.Load().(SettingBox),
|
||||||
|
Themes: themes,
|
||||||
|
ThemeName: defaultThemeBox.Load().(string),
|
||||||
NoticeList: []string{"test"},
|
NoticeList: []string{"test"},
|
||||||
Stylesheets: []string{"panel"},
|
Stylesheets: []string{"panel"},
|
||||||
Scripts: []string{"whatever"},
|
Scripts: []string{"whatever"},
|
||||||
|
@ -97,66 +110,64 @@ func compileTemplates() error {
|
||||||
var replyList []Reply
|
var replyList []Reply
|
||||||
replyList = append(replyList, Reply{0, 0, "Yo!", "Yo!", 0, "alice", "Alice", config.DefaultGroup, "", 0, 0, "", "", 0, "", "", "", "", 0, "127.0.0.1", false, 1, "", ""})
|
replyList = append(replyList, Reply{0, 0, "Yo!", "Yo!", 0, "alice", "Alice", config.DefaultGroup, "", 0, 0, "", "", 0, "", "", "", "", 0, "127.0.0.1", false, 1, "", ""})
|
||||||
|
|
||||||
var varList map[string]VarItem = make(map[string]VarItem)
|
var varList = make(map[string]VarItem)
|
||||||
tpage := TopicPage{"Title", user, headerVars, replyList, topic, 1, 1}
|
tpage := TopicPage{"Title", user, headerVars, replyList, topic, 1, 1}
|
||||||
topic_id_tmpl, err := c.compileTemplate("topic.html", "templates/", "TopicPage", tpage, varList)
|
topicIDTmpl, err := c.compileTemplate("topic.html", "templates/", "TopicPage", tpage, varList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
topic_id_alt_tmpl, err := c.compileTemplate("topic_alt.html", "templates/", "TopicPage", tpage, varList)
|
topicIDAltTmpl, err := c.compileTemplate("topic_alt.html", "templates/", "TopicPage", tpage, varList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
varList = make(map[string]VarItem)
|
varList = make(map[string]VarItem)
|
||||||
ppage := ProfilePage{"User 526", user, headerVars, replyList, user}
|
ppage := ProfilePage{"User 526", user, headerVars, replyList, user}
|
||||||
profile_tmpl, err := c.compileTemplate("profile.html", "templates/", "ProfilePage", ppage, varList)
|
profileTmpl, err := c.compileTemplate("profile.html", "templates/", "ProfilePage", ppage, varList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var forumList []Forum
|
var forumList []Forum
|
||||||
forums, err := fstore.GetAll()
|
forums, err := fstore.GetAllVisible()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, forum := range forums {
|
for _, forum := range forums {
|
||||||
if forum.Active {
|
forumList = append(forumList, *forum)
|
||||||
forumList = append(forumList, *forum)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
varList = make(map[string]VarItem)
|
varList = make(map[string]VarItem)
|
||||||
forums_page := ForumsPage{"Forum List", user, headerVars, forumList}
|
forumsPage := ForumsPage{"Forum List", user, headerVars, forumList}
|
||||||
forums_tmpl, err := c.compileTemplate("forums.html", "templates/", "ForumsPage", forums_page, varList)
|
forumsTmpl, err := c.compileTemplate("forums.html", "templates/", "ForumsPage", forumsPage, varList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var topicsList []*TopicsRow
|
var topicsList []*TopicsRow
|
||||||
topicsList = append(topicsList, &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, "Date", "Date", user3.ID, 1, "", "127.0.0.1", 0, 1, "classname", "", &user2, "", 0, &user3, "General", "/forum/general.2"})
|
topicsList = append(topicsList, &TopicsRow{1, "topic-title", "Topic Title", "The topic content.", 1, false, false, "Date", "Date", user3.ID, 1, "", "127.0.0.1", 0, 1, "classname", "", &user2, "", 0, &user3, "General", "/forum/general.2"})
|
||||||
topics_page := TopicsPage{"Topic List", user, headerVars, topicsList}
|
topicsPage := TopicsPage{"Topic List", user, headerVars, topicsList}
|
||||||
topics_tmpl, err := c.compileTemplate("topics.html", "templates/", "TopicsPage", topics_page, varList)
|
topicsTmpl, err := c.compileTemplate("topics.html", "templates/", "TopicsPage", topicsPage, varList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
//var topicList []TopicUser
|
//var topicList []TopicUser
|
||||||
//topicList = append(topicList,TopicUser{1,"topic-title","Topic Title","The topic content.",1,false,false,"Date","Date",1,"","127.0.0.1",0,1,"classname","","admin-fred","Admin Fred",config.DefaultGroup,"",0,"","","","",58,false})
|
//topicList = append(topicList,TopicUser{1,"topic-title","Topic Title","The topic content.",1,false,false,"Date","Date",1,"","127.0.0.1",0,1,"classname","","admin-fred","Admin Fred",config.DefaultGroup,"",0,"","","","",58,false})
|
||||||
forum_item := Forum{1, "general", "General Forum", "Where the general stuff happens", true, "all", 0, "", 0, "", "", 0, "", 0, ""}
|
forumItem := Forum{1, "general", "General Forum", "Where the general stuff happens", true, "all", 0, "", 0, "", "", 0, "", 0, ""}
|
||||||
forum_page := ForumPage{"General Forum", user, headerVars, topicsList, forum_item, 1, 1}
|
forumPage := ForumPage{"General Forum", user, headerVars, topicsList, forumItem, 1, 1}
|
||||||
forum_tmpl, err := c.compileTemplate("forum.html", "templates/", "ForumPage", forum_page, varList)
|
forumTmpl, err := c.compileTemplate("forum.html", "templates/", "ForumPage", forumPage, varList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Writing the templates")
|
log.Print("Writing the templates")
|
||||||
go writeTemplate("topic", topic_id_tmpl)
|
go writeTemplate("topic", topicIDTmpl)
|
||||||
go writeTemplate("topic_alt", topic_id_alt_tmpl)
|
go writeTemplate("topic_alt", topicIDAltTmpl)
|
||||||
go writeTemplate("profile", profile_tmpl)
|
go writeTemplate("profile", profileTmpl)
|
||||||
go writeTemplate("forums", forums_tmpl)
|
go writeTemplate("forums", forumsTmpl)
|
||||||
go writeTemplate("topics", topics_tmpl)
|
go writeTemplate("topics", topicsTmpl)
|
||||||
go writeTemplate("forum", forum_tmpl)
|
go writeTemplate("forum", forumTmpl)
|
||||||
go func() {
|
go func() {
|
||||||
err := writeFile("./template_list.go", "package main\n\n// nolint\n"+c.FragOut)
|
err := writeFile("./template_list.go", "package main\n\n// nolint\n"+c.FragOut)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -180,66 +191,62 @@ func initTemplates() {
|
||||||
}
|
}
|
||||||
compileTemplates()
|
compileTemplates()
|
||||||
|
|
||||||
// TO-DO: Add support for 64-bit integers
|
// TODO: Add support for 64-bit integers
|
||||||
// TO-DO: Add support for floats
|
// TODO: Add support for floats
|
||||||
fmap := make(map[string]interface{})
|
fmap := make(map[string]interface{})
|
||||||
fmap["add"] = func(left interface{}, right interface{}) interface{} {
|
fmap["add"] = func(left interface{}, right interface{}) interface{} {
|
||||||
var left_int int
|
var leftInt, rightInt int
|
||||||
var right_int int
|
|
||||||
switch left := left.(type) {
|
switch left := left.(type) {
|
||||||
case uint, uint8, uint16, int, int32:
|
case uint, uint8, uint16, int, int32:
|
||||||
left_int = left.(int)
|
leftInt = left.(int)
|
||||||
}
|
}
|
||||||
switch right := right.(type) {
|
switch right := right.(type) {
|
||||||
case uint, uint8, uint16, int, int32:
|
case uint, uint8, uint16, int, int32:
|
||||||
right_int = right.(int)
|
rightInt = right.(int)
|
||||||
}
|
}
|
||||||
return left_int + right_int
|
return leftInt + rightInt
|
||||||
}
|
}
|
||||||
|
|
||||||
fmap["subtract"] = func(left interface{}, right interface{}) interface{} {
|
fmap["subtract"] = func(left interface{}, right interface{}) interface{} {
|
||||||
var left_int int
|
var leftInt, rightInt int
|
||||||
var right_int int
|
|
||||||
switch left := left.(type) {
|
switch left := left.(type) {
|
||||||
case uint, uint8, uint16, int, int32:
|
case uint, uint8, uint16, int, int32:
|
||||||
left_int = left.(int)
|
leftInt = left.(int)
|
||||||
}
|
}
|
||||||
switch right := right.(type) {
|
switch right := right.(type) {
|
||||||
case uint, uint8, uint16, int, int32:
|
case uint, uint8, uint16, int, int32:
|
||||||
right_int = right.(int)
|
rightInt = right.(int)
|
||||||
}
|
}
|
||||||
return left_int - right_int
|
return leftInt - rightInt
|
||||||
}
|
}
|
||||||
|
|
||||||
fmap["multiply"] = func(left interface{}, right interface{}) interface{} {
|
fmap["multiply"] = func(left interface{}, right interface{}) interface{} {
|
||||||
var left_int int
|
var leftInt, rightInt int
|
||||||
var right_int int
|
|
||||||
switch left := left.(type) {
|
switch left := left.(type) {
|
||||||
case uint, uint8, uint16, int, int32:
|
case uint, uint8, uint16, int, int32:
|
||||||
left_int = left.(int)
|
leftInt = left.(int)
|
||||||
}
|
}
|
||||||
switch right := right.(type) {
|
switch right := right.(type) {
|
||||||
case uint, uint8, uint16, int, int32:
|
case uint, uint8, uint16, int, int32:
|
||||||
right_int = right.(int)
|
rightInt = right.(int)
|
||||||
}
|
}
|
||||||
return left_int * right_int
|
return leftInt * rightInt
|
||||||
}
|
}
|
||||||
|
|
||||||
fmap["divide"] = func(left interface{}, right interface{}) interface{} {
|
fmap["divide"] = func(left interface{}, right interface{}) interface{} {
|
||||||
var left_int int
|
var leftInt, rightInt int
|
||||||
var right_int int
|
|
||||||
switch left := left.(type) {
|
switch left := left.(type) {
|
||||||
case uint, uint8, uint16, int, int32:
|
case uint, uint8, uint16, int, int32:
|
||||||
left_int = left.(int)
|
leftInt = left.(int)
|
||||||
}
|
}
|
||||||
switch right := right.(type) {
|
switch right := right.(type) {
|
||||||
case uint, uint8, uint16, int, int32:
|
case uint, uint8, uint16, int, int32:
|
||||||
right_int = right.(int)
|
rightInt = right.(int)
|
||||||
}
|
}
|
||||||
if left_int == 0 || right_int == 0 {
|
if leftInt == 0 || rightInt == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return left_int / right_int
|
return leftInt / rightInt
|
||||||
}
|
}
|
||||||
|
|
||||||
// The interpreted templates...
|
// The interpreted templates...
|
||||||
|
|
|
@ -245,11 +245,27 @@ var topic_91 = []byte(`
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
`)
|
`)
|
||||||
var footer_0 = []byte(` </div>
|
var footer_0 = []byte(`<div class="footer">
|
||||||
|
<div id="poweredBy" style="float: left;margin-top: 4px;">Powered by Gosora - <span style="font-size: 12px;">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(`<aside class="sidebar">`)
|
var footer_1 = []byte(`<option val="`)
|
||||||
var footer_2 = []byte(`</aside>`)
|
var footer_2 = []byte(`"`)
|
||||||
var footer_3 = []byte(`
|
var footer_3 = []byte(` selected`)
|
||||||
|
var footer_4 = []byte(`>`)
|
||||||
|
var footer_5 = []byte(`</option>`)
|
||||||
|
var footer_6 = []byte(`
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
var footer_7 = []byte(`<aside class="sidebar">`)
|
||||||
|
var footer_8 = []byte(`</aside>`)
|
||||||
|
var footer_9 = []byte(`
|
||||||
<div style="clear: both;"></div>
|
<div style="clear: both;"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -471,7 +487,7 @@ var profile_14 = []byte(`&type=user" class="profile_menu_item report_item">Repor
|
||||||
<div id="profile_right_lane" class="colstack_right">
|
<div id="profile_right_lane" class="colstack_right">
|
||||||
`)
|
`)
|
||||||
var profile_15 = []byte(`
|
var profile_15 = []byte(`
|
||||||
<!-- TO-DO: Inline the display: none; CSS -->
|
<!-- TODO: Inline the display: none; CSS -->
|
||||||
<div id="ban_user_head" class="colstack_item colstack_head hash_hide ban_user_hash" style="display: none;">
|
<div id="ban_user_head" class="colstack_item colstack_head hash_hide ban_user_hash" style="display: none;">
|
||||||
<div class="rowitem"><h1>Ban User</h1></div>
|
<div class="rowitem"><h1>Ban User</h1></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -675,23 +691,26 @@ var topics_19 = []byte(`<span class="rowsmall topic_status_e topic_status_sticky
|
||||||
var topics_20 = []byte(`
|
var topics_20 = []byte(`
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="rowitem topic_right passive datarow" style="`)
|
<div class="rowitem topic_right passive datarow `)
|
||||||
var topics_21 = []byte(`background-image: url(`)
|
var topics_21 = []byte(`topic_sticky`)
|
||||||
var topics_22 = []byte(`);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`)
|
var topics_22 = []byte(`topic_closed`)
|
||||||
var topics_23 = []byte(`">
|
var topics_23 = []byte(`" style="`)
|
||||||
|
var topics_24 = []byte(`background-image: url(`)
|
||||||
|
var topics_25 = []byte(`);background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;`)
|
||||||
|
var topics_26 = []byte(`">
|
||||||
<span>
|
<span>
|
||||||
<a href="`)
|
<a href="`)
|
||||||
var topics_24 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
var topics_27 = []byte(`" class="lastName" style="font-size: 14px;">`)
|
||||||
var topics_25 = []byte(`</a><br>
|
var topics_28 = []byte(`</a><br>
|
||||||
<span class="rowsmall lastReplyAt">Last: `)
|
<span class="rowsmall lastReplyAt">Last: `)
|
||||||
var topics_26 = []byte(`</span>
|
var topics_29 = []byte(`</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
`)
|
`)
|
||||||
var topics_27 = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
|
var topics_30 = []byte(`<div class="rowitem passive">There aren't any topics yet.`)
|
||||||
var topics_28 = []byte(` <a href="/topics/create/">Start one?</a>`)
|
var topics_31 = []byte(` <a href="/topics/create/">Start one?</a>`)
|
||||||
var topics_29 = []byte(`</div>`)
|
var topics_32 = []byte(`</div>`)
|
||||||
var topics_30 = []byte(`
|
var topics_33 = []byte(`
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -158,10 +158,26 @@ w.Write(profile_41)
|
||||||
w.Write(profile_42)
|
w.Write(profile_42)
|
||||||
w.Write(profile_43)
|
w.Write(profile_43)
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
if tmpl_profile_vars.Header.Widgets.RightSidebar != "" {
|
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(footer_1)
|
||||||
w.Write([]byte(string(tmpl_profile_vars.Header.Widgets.RightSidebar)))
|
w.Write([]byte(item.Name))
|
||||||
w.Write(footer_2)
|
w.Write(footer_2)
|
||||||
}
|
if tmpl_profile_vars.Header.ThemeName == item.Name {
|
||||||
w.Write(footer_3)
|
w.Write(footer_3)
|
||||||
}
|
}
|
||||||
|
w.Write(footer_4)
|
||||||
|
w.Write([]byte(item.FriendlyName))
|
||||||
|
w.Write(footer_5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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(footer_8)
|
||||||
|
}
|
||||||
|
w.Write(footer_9)
|
||||||
|
}
|
||||||
|
|
|
@ -273,10 +273,26 @@ w.Write(topic_90)
|
||||||
}
|
}
|
||||||
w.Write(topic_91)
|
w.Write(topic_91)
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
if tmpl_topic_vars.Header.Widgets.RightSidebar != "" {
|
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(footer_1)
|
||||||
w.Write([]byte(string(tmpl_topic_vars.Header.Widgets.RightSidebar)))
|
w.Write([]byte(item.Name))
|
||||||
w.Write(footer_2)
|
w.Write(footer_2)
|
||||||
}
|
if tmpl_topic_vars.Header.ThemeName == item.Name {
|
||||||
w.Write(footer_3)
|
w.Write(footer_3)
|
||||||
}
|
}
|
||||||
|
w.Write(footer_4)
|
||||||
|
w.Write([]byte(item.FriendlyName))
|
||||||
|
w.Write(footer_5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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(footer_8)
|
||||||
|
}
|
||||||
|
w.Write(footer_9)
|
||||||
|
}
|
||||||
|
|
|
@ -266,10 +266,26 @@ w.Write(topic_alt_87)
|
||||||
}
|
}
|
||||||
w.Write(topic_alt_88)
|
w.Write(topic_alt_88)
|
||||||
w.Write(footer_0)
|
w.Write(footer_0)
|
||||||
if tmpl_topic_alt_vars.Header.Widgets.RightSidebar != "" {
|
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(footer_1)
|
||||||
w.Write([]byte(string(tmpl_topic_alt_vars.Header.Widgets.RightSidebar)))
|
w.Write([]byte(item.Name))
|
||||||
w.Write(footer_2)
|
w.Write(footer_2)
|
||||||
}
|
if tmpl_topic_alt_vars.Header.ThemeName == item.Name {
|
||||||
w.Write(footer_3)
|
w.Write(footer_3)
|
||||||
}
|
}
|
||||||
|
w.Write(footer_4)
|
||||||
|
w.Write([]byte(item.FriendlyName))
|
||||||
|
w.Write(footer_5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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(footer_8)
|
||||||
|
}
|
||||||
|
w.Write(footer_9)
|
||||||
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
// Code generated by Gosora. More below:
|
// Code generated by Gosora. More below:
|
||||||
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
/* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */
|
||||||
package main
|
package main
|
||||||
import "strconv"
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -115,32 +115,56 @@ if item.Sticky {
|
||||||
w.Write(topics_19)
|
w.Write(topics_19)
|
||||||
}
|
}
|
||||||
w.Write(topics_20)
|
w.Write(topics_20)
|
||||||
if item.LastUser.Avatar != "" {
|
if item.Sticky {
|
||||||
w.Write(topics_21)
|
w.Write(topics_21)
|
||||||
w.Write([]byte(item.LastUser.Avatar))
|
} else {
|
||||||
|
if item.IsClosed {
|
||||||
w.Write(topics_22)
|
w.Write(topics_22)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
w.Write(topics_23)
|
w.Write(topics_23)
|
||||||
w.Write([]byte(item.LastUser.Link))
|
if item.LastUser.Avatar != "" {
|
||||||
w.Write(topics_24)
|
w.Write(topics_24)
|
||||||
w.Write([]byte(item.LastUser.Name))
|
w.Write([]byte(item.LastUser.Avatar))
|
||||||
w.Write(topics_25)
|
w.Write(topics_25)
|
||||||
w.Write([]byte(item.LastReplyAt))
|
}
|
||||||
w.Write(topics_26)
|
w.Write(topics_26)
|
||||||
}
|
w.Write([]byte(item.LastUser.Link))
|
||||||
} else {
|
|
||||||
w.Write(topics_27)
|
w.Write(topics_27)
|
||||||
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
|
w.Write([]byte(item.LastUser.Name))
|
||||||
w.Write(topics_28)
|
w.Write(topics_28)
|
||||||
}
|
w.Write([]byte(item.LastReplyAt))
|
||||||
w.Write(topics_29)
|
w.Write(topics_29)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
w.Write(topics_30)
|
w.Write(topics_30)
|
||||||
w.Write(footer_0)
|
if tmpl_topics_vars.CurrentUser.Perms.CreateTopic {
|
||||||
if tmpl_topics_vars.Header.Widgets.RightSidebar != "" {
|
w.Write(topics_31)
|
||||||
w.Write(footer_1)
|
|
||||||
w.Write([]byte(string(tmpl_topics_vars.Header.Widgets.RightSidebar)))
|
|
||||||
w.Write(footer_2)
|
|
||||||
}
|
}
|
||||||
|
w.Write(topics_32)
|
||||||
|
}
|
||||||
|
w.Write(topics_33)
|
||||||
|
w.Write(footer_0)
|
||||||
|
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_3)
|
||||||
}
|
}
|
||||||
|
w.Write(footer_4)
|
||||||
|
w.Write([]byte(item.FriendlyName))
|
||||||
|
w.Write(footer_5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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(footer_8)
|
||||||
|
}
|
||||||
|
w.Write(footer_9)
|
||||||
|
}
|
||||||
|
|
45
templates.go
45
templates.go
|
@ -13,7 +13,7 @@ import (
|
||||||
"text/template/parse"
|
"text/template/parse"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TO-DO: Turn this file into a library
|
// TODO: Turn this file into a library
|
||||||
var ctemplates []string
|
var ctemplates []string
|
||||||
var tmplPtrMap = make(map[string]interface{})
|
var tmplPtrMap = make(map[string]interface{})
|
||||||
var textOverlapList = make(map[string]int)
|
var textOverlapList = make(map[string]int)
|
||||||
|
@ -263,6 +263,11 @@ func (c *CTemplateSet) compileSwitch(varholder string, holdreflect reflect.Value
|
||||||
for _, key := range outVal.MapKeys() {
|
for _, key := range outVal.MapKeys() {
|
||||||
item = outVal.MapIndex(key)
|
item = outVal.MapIndex(key)
|
||||||
}
|
}
|
||||||
|
fmt.Println("Range item:", item)
|
||||||
|
|
||||||
|
if !item.IsValid() {
|
||||||
|
panic("item" + "^\n" + "Invalid map. Maybe, it doesn't have any entries for the template engine to analyse?")
|
||||||
|
}
|
||||||
|
|
||||||
if node.ElseList != nil {
|
if node.ElseList != nil {
|
||||||
out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compileSwitch("item", item, templateName, node.List) + "}\n} else {\n" + c.compileSwitch("item", item, templateName, node.ElseList) + "}\n"
|
out = "if len(" + out + ") != 0 {\nfor _, item := range " + out + " {\n" + c.compileSwitch("item", item, templateName, node.List) + "}\n} else {\n" + c.compileSwitch("item", item, templateName, node.ElseList) + "}\n"
|
||||||
|
@ -352,13 +357,23 @@ func (c *CTemplateSet) compileSubswitch(varholder string, holdreflect reflect.Va
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cur.IsValid() {
|
if !cur.IsValid() {
|
||||||
|
if dev.DebugMode {
|
||||||
|
fmt.Println("Debug Data:")
|
||||||
|
fmt.Println("Holdreflect:", holdreflect)
|
||||||
|
fmt.Println("Holdreflect.Kind()", holdreflect.Kind())
|
||||||
|
if !dev.SuperDebug {
|
||||||
|
fmt.Println("cur.Kind():", cur.Kind().String())
|
||||||
|
}
|
||||||
|
fmt.Println("")
|
||||||
|
}
|
||||||
|
|
||||||
panic(varholder + varbit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
panic(varholder + varbit + "^\n" + "Invalid value. Maybe, it doesn't exist?")
|
||||||
}
|
}
|
||||||
|
|
||||||
cur = cur.FieldByName(id)
|
cur = cur.FieldByName(id)
|
||||||
if cur.Kind() == reflect.Interface {
|
if cur.Kind() == reflect.Interface {
|
||||||
cur = cur.Elem()
|
cur = cur.Elem()
|
||||||
// TO-DO: Surely, there's a better way of detecting this?
|
// TODO: Surely, there's a better way of detecting this?
|
||||||
/*if cur.Kind() == reflect.String && cur.Type().Name() != "string" {
|
/*if cur.Kind() == reflect.String && cur.Type().Name() != "string" {
|
||||||
varbit = "string(" + varbit + "." + id + ")"*/
|
varbit = "string(" + varbit + "." + id + ")"*/
|
||||||
//if cur.Kind() == reflect.String && cur.Type().Name() != "string" {
|
//if cur.Kind() == reflect.String && cur.Type().Name() != "string" {
|
||||||
|
@ -795,7 +810,7 @@ func (c *CTemplateSet) compileIfVarsub(varname string, varholder string, templat
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Fix this up so that it works for regular pointers and not just struct pointers. Ditto for the other cur.Kind() == reflect.Ptr we have in this file
|
// TODO: Fix this up so that it works for regular pointers and not just struct pointers. Ditto for the other cur.Kind() == reflect.Ptr we have in this file
|
||||||
if cur.Kind() == reflect.Ptr {
|
if cur.Kind() == reflect.Ptr {
|
||||||
if dev.SuperDebug {
|
if dev.SuperDebug {
|
||||||
fmt.Println("Looping over pointer")
|
fmt.Println("Looping over pointer")
|
||||||
|
@ -950,7 +965,7 @@ func (c *CTemplateSet) compileSubtemplate(pvarholder string, pholdreflect reflec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Cascade errors back up the tree to the caller?
|
// TODO: Cascade errors back up the tree to the caller?
|
||||||
res, err := ioutil.ReadFile(c.dir + node.Name)
|
res, err := ioutil.ReadFile(c.dir + node.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -998,7 +1013,7 @@ func (c *CTemplateSet) compileCommand(*parse.CommandNode) (out string) {
|
||||||
panic("Uh oh! Something went wrong!")
|
panic("Uh oh! Something went wrong!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Write unit tests for this
|
// TODO: Write unit tests for this
|
||||||
func minify(data string) string {
|
func minify(data string) string {
|
||||||
data = strings.Replace(data, "\t", "", -1)
|
data = strings.Replace(data, "\t", "", -1)
|
||||||
data = strings.Replace(data, "\v", "", -1)
|
data = strings.Replace(data, "\v", "", -1)
|
||||||
|
@ -1008,30 +1023,30 @@ func minify(data string) string {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Strip comments
|
// TODO: Strip comments
|
||||||
// TO-DO: Handle CSS nested in <style> tags?
|
// TODO: Handle CSS nested in <style> tags?
|
||||||
// TO-DO: Write unit tests for this
|
// TODO: Write unit tests for this
|
||||||
func minifyHTML(data string) string {
|
func minifyHTML(data string) string {
|
||||||
return minify(data)
|
return minify(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Have static files use this
|
// TODO: Have static files use this
|
||||||
// TO-DO: Strip comments
|
// TODO: Strip comments
|
||||||
// TO-DO: Convert the rgb()s to hex codes?
|
// TODO: Convert the rgb()s to hex codes?
|
||||||
// TO-DO: Write unit tests for this
|
// TODO: Write unit tests for this
|
||||||
func minifyCSS(data string) string {
|
func minifyCSS(data string) string {
|
||||||
return minify(data)
|
return minify(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Convert this to three character hex strings whenever possible?
|
// TODO: Convert this to three character hex strings whenever possible?
|
||||||
// TO-DO: Write unit tests for this
|
// TODO: Write unit tests for this
|
||||||
// nolint
|
// nolint
|
||||||
func rgbToHexstr(red int, green int, blue int) string {
|
func rgbToHexstr(red int, green int, blue int) string {
|
||||||
return strconv.FormatInt(int64(red), 16) + strconv.FormatInt(int64(green), 16) + strconv.FormatInt(int64(blue), 16)
|
return strconv.FormatInt(int64(red), 16) + strconv.FormatInt(int64(green), 16) + strconv.FormatInt(int64(blue), 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// TO-DO: Write unit tests for this
|
// TODO: Write unit tests for this
|
||||||
func hexstrToRgb(hexstr string) (red int, blue int, green int, err error) {
|
func hexstrToRgb(hexstr string) (red int, blue int, green int, err error) {
|
||||||
// Strip the # at the start
|
// Strip the # at the start
|
||||||
if hexstr[0] == '#' {
|
if hexstr[0] == '#' {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<div class="rowitem passive"><a href="/user/edit/critical/">Password</a></div>
|
<div class="rowitem passive"><a href="/user/edit/critical/">Password</a></div>
|
||||||
<div class="rowitem passive"><a href="/user/edit/email/">Email</a></div>
|
<div class="rowitem passive"><a href="/user/edit/email/">Email</a></div>
|
||||||
<div class="rowitem passive"><a href="/user/edit/notifications">Notifications</a></div>
|
<div class="rowitem passive"><a href="/user/edit/notifications">Notifications</a></div>
|
||||||
{{/** TO-DO: Add an alerts page with pagination to go through alerts which either don't fit in the alerts drop-down or which have already been dismissed. Bear in mind though that dismissed alerts older than two weeks might be purged to save space and to speed up the database **/}}
|
{{/** TODO: Add an alerts page with pagination to go through alerts which either don't fit in the alerts drop-down or which have already been dismissed. Bear in mind though that dismissed alerts older than two weeks might be purged to save space and to speed up the database **/}}
|
||||||
<div class="rowitem passive"><a>Coming Soon</a></div>
|
<div class="rowitem passive"><a>Coming Soon</a></div>
|
||||||
<div class="rowitem passive"><a>Coming Soon</a></div>
|
<div class="rowitem passive"><a>Coming Soon</a></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<div class="rowitem"><h1>Emails</h1></div>
|
<div class="rowitem"><h1>Emails</h1></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="colstack_item">
|
<div class="colstack_item">
|
||||||
<!-- TO-DO: Do we need this inline CSS? -->
|
<!-- TODO: Do we need this inline CSS? -->
|
||||||
{{range .ItemList}}
|
{{range .ItemList}}
|
||||||
<div class="rowitem" style="font-weight: normal;">
|
<div class="rowitem" style="font-weight: normal;">
|
||||||
<a style="text-transform: none;">{{.Email}}</a>
|
<a style="text-transform: none;">{{.Email}}</a>
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
|
<div class="footer">
|
||||||
|
<div id="poweredBy" style="float: left;margin-top: 4px;">Powered by Gosora - <span style="font-size: 12px;">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}}
|
||||||
|
{{end}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{if .Header.Widgets.RightSidebar}}<aside class="sidebar">{{.Header.Widgets.RightSidebar}}</aside>{{end}}
|
{{if .Header.Widgets.RightSidebar}}<aside class="sidebar">{{.Header.Widgets.RightSidebar}}</aside>{{end}}
|
||||||
<div style="clear: both;"></div>
|
<div style="clear: both;"></div>
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<span>
|
<span>
|
||||||
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a>
|
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a>
|
||||||
<br /><a class="rowsmall" href="{{.Creator.Link}}">Starter: {{.Creator.Name}}</a>
|
<br /><a class="rowsmall" href="{{.Creator.Link}}">Starter: {{.Creator.Name}}</a>
|
||||||
{{/** TO-DO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}}
|
{{/** TODO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}}
|
||||||
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>{{end}}
|
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>{{end}}
|
||||||
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span style="float: left;">
|
<span style="float: left;">
|
||||||
{{/** TO-DO: Make sure the forum_active_name class is set and unset when the activity status of this forum is changed **/}}
|
{{/** TODO: Make sure the forum_active_name class is set and unset when the activity status of this forum is changed **/}}
|
||||||
<a data-field="forum_name" data-type="text" class="editable_block forum_name{{if not .Active}} forum_active_name{{end}}">{{.Name}}</a>
|
<a data-field="forum_name" data-type="text" class="editable_block forum_name{{if not .Active}} forum_active_name{{end}}">{{.Name}}</a>
|
||||||
</span>
|
</span>
|
||||||
<br /><span data-field="forum_desc" data-type="text" class="editable_block forum_desc rowsmall">{{.Desc}}</span>
|
<br /><span data-field="forum_desc" data-type="text" class="editable_block forum_desc rowsmall">{{.Desc}}</span>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
{{if .Settings}}<a href="/panel/settings/" class="panel_tag">Settings</a>{{end}}
|
{{if .Settings}}<a href="/panel/settings/" class="panel_tag">Settings</a>{{end}}
|
||||||
{{if .Active}}<a href="/panel/plugins/deactivate/{{.UName}}?session={{$.CurrentUser.Session}}" class="panel_tag">Deactivate</a>
|
{{if .Active}}<a href="/panel/plugins/deactivate/{{.UName}}?session={{$.CurrentUser.Session}}" class="panel_tag">Deactivate</a>
|
||||||
{{else if .Installable}}
|
{{else if .Installable}}
|
||||||
{{/** TO-DO: Write a custom template interpreter to fix this nonsense **/}}
|
{{/** TODO: Write a custom template interpreter to fix this nonsense **/}}
|
||||||
{{if .Installed}}<a href="/panel/plugins/activate/{{.UName}}?session={{$.CurrentUser.Session}}" class="panel_tag">Activate</a>{{else}}<a href="/panel/plugins/install/{{.UName}}?session={{$.CurrentUser.Session}}" class="panel_tag">Install</a>{{end}}
|
{{if .Installed}}<a href="/panel/plugins/activate/{{.UName}}?session={{$.CurrentUser.Session}}" class="panel_tag">Activate</a>{{else}}<a href="/panel/plugins/install/{{.UName}}?session={{$.CurrentUser.Session}}" class="panel_tag">Install</a>{{end}}
|
||||||
{{else}}<a href="/panel/plugins/activate/{{.UName}}?session={{$.CurrentUser.Session}}" class="panel_tag">Activate</a>{{end}}
|
{{else}}<a href="/panel/plugins/activate/{{.UName}}?session={{$.CurrentUser.Session}}" class="panel_tag">Activate</a>{{end}}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -26,12 +26,12 @@
|
||||||
|
|
||||||
<div id="profile_right_lane" class="colstack_right">
|
<div id="profile_right_lane" class="colstack_right">
|
||||||
{{if .CurrentUser.Perms.BanUsers}}
|
{{if .CurrentUser.Perms.BanUsers}}
|
||||||
<!-- TO-DO: Inline the display: none; CSS -->
|
<!-- TODO: Inline the display: none; CSS -->
|
||||||
<div id="ban_user_head" class="colstack_item colstack_head hash_hide ban_user_hash" style="display: none;">
|
<div id="ban_user_head" class="colstack_item colstack_head hash_hide ban_user_hash" style="display: none;">
|
||||||
<div class="rowitem"><h1>Ban User</h1></div>
|
<div class="rowitem"><h1>Ban User</h1></div>
|
||||||
</div>
|
</div>
|
||||||
<form id="ban_user_form" class="hash_hide ban_user_hash" action="/users/ban/submit/{{.ProfileOwner.ID}}?session={{.CurrentUser.Session}}" method="post" style="display: none;">
|
<form id="ban_user_form" class="hash_hide ban_user_hash" action="/users/ban/submit/{{.ProfileOwner.ID}}?session={{.CurrentUser.Session}}" method="post" style="display: none;">
|
||||||
{{/** TO-DO: Put a JS duration calculator here instead of this text? **/}}
|
{{/** TODO: Put a JS duration calculator here instead of this text? **/}}
|
||||||
<div class="colline">If all the fields are left blank, the ban will be permanent.</div>
|
<div class="colline">If all the fields are left blank, the ban will be permanent.</div>
|
||||||
<div class="colstack_item">
|
<div class="colstack_item">
|
||||||
<div class="formrow real_first_child">
|
<div class="formrow real_first_child">
|
||||||
|
@ -100,7 +100,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{/** Quick subpage switcher **/}}
|
{{/** Quick subpage switcher **/}}
|
||||||
{{/** TO-DO: Stop inlining this **/}}
|
{{/** TODO: Stop inlining this **/}}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function handle_profile_hashbit() {
|
function handle_profile_hashbit() {
|
||||||
var hash_class = ""
|
var hash_class = ""
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
{{/** TO-DO: Move this into a CSS file **/}}
|
{{/** TODO: Move this into a CSS file **/}}
|
||||||
{{template "socialgroups_css.html" . }}
|
{{template "socialgroups_css.html" . }}
|
||||||
|
|
||||||
{{/** TO-DO: Port the page template functions to the template interpreter **/}}
|
{{/** TODO: Port the page template functions to the template interpreter **/}}
|
||||||
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" href="/group/members/{{.SocialGroup.ID}}?page={{subtract .Page 1}}"><</a></div>{{end}}
|
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" href="/group/members/{{.SocialGroup.ID}}?page={{subtract .Page 1}}"><</a></div>{{end}}
|
||||||
{{if ne .LastPage .Page}}<link rel="prerender" href="/group/members/{{.SocialGroup.ID}}?page={{add .Page 1}}" />
|
{{if ne .LastPage .Page}}<link rel="prerender" href="/group/members/{{.SocialGroup.ID}}?page={{add .Page 1}}" />
|
||||||
<div id="nextFloat" class="next_button"><a class="next_link" href="/group/members/{{.SocialGroup.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
<div id="nextFloat" class="next_button"><a class="next_link" href="/group/members/{{.SocialGroup.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{{template "header.html" . }}
|
{{template "header.html" . }}
|
||||||
{{/** TO-DO: Move this into a CSS file **/}}
|
{{/** TODO: Move this into a CSS file **/}}
|
||||||
{{template "socialgroups_css.html" . }}
|
{{template "socialgroups_css.html" . }}
|
||||||
|
|
||||||
{{/** TO-DO: Port the page template functions to the template interpreter **/}}
|
{{/** TODO: Port the page template functions to the template interpreter **/}}
|
||||||
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" href="/group/{{.SocialGroup.ID}}?page={{subtract .Page 1}}"><</a></div>{{end}}
|
{{if gt .Page 1}}<div id="prevFloat" class="prev_button"><a class="prev_link" href="/group/{{.SocialGroup.ID}}?page={{subtract .Page 1}}"><</a></div>{{end}}
|
||||||
{{if ne .LastPage .Page}}<link rel="prerender" href="/group/{{.SocialGroup.ID}}?page={{add .Page 1}}" />
|
{{if ne .LastPage .Page}}<link rel="prerender" href="/group/{{.SocialGroup.ID}}?page={{add .Page 1}}" />
|
||||||
<div id="nextFloat" class="next_button"><a class="next_link" href="/group/{{.SocialGroup.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
<div id="nextFloat" class="next_button"><a class="next_link" href="/group/{{.SocialGroup.ID}}?page={{add .Page 1}}">></a></div>{{end}}
|
||||||
|
|
|
@ -13,12 +13,12 @@
|
||||||
<span>
|
<span>
|
||||||
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a> {{if .ForumName}}<a class="rowsmall" href="{{.ForumLink}}">{{.ForumName}}</a>{{end}}
|
<a class="rowtopic" href="{{.Link}}">{{.Title}}</a> {{if .ForumName}}<a class="rowsmall" href="{{.ForumLink}}">{{.ForumName}}</a>{{end}}
|
||||||
<br /><a class="rowsmall" href="{{.Creator.Link}}">Starter: {{.Creator.Name}}</a>
|
<br /><a class="rowsmall" href="{{.Creator.Link}}">Starter: {{.Creator.Name}}</a>
|
||||||
{{/** TO-DO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}}
|
{{/** TODO: Avoid the double '|' when both .IsClosed and .Sticky are set to true. We could probably do this with CSS **/}}
|
||||||
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>{{end}}
|
{{if .IsClosed}}<span class="rowsmall topic_status_e topic_status_closed" title="Status: Closed"> | 🔒︎</span>{{end}}
|
||||||
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
{{if .Sticky}}<span class="rowsmall topic_status_e topic_status_sticky" title="Status: Pinned"> | 📍︎</span>{{end}}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="rowitem topic_right passive datarow" style="{{if .LastUser.Avatar}}background-image: url({{.LastUser.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
<div class="rowitem topic_right passive datarow {{if .Sticky}}topic_sticky{{else if .IsClosed}}topic_closed{{end}}" style="{{if .LastUser.Avatar}}background-image: url({{.LastUser.Avatar}});background-position: left;background-repeat: no-repeat;background-size: 64px;padding-left: 72px;{{end}}">
|
||||||
<span>
|
<span>
|
||||||
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br>
|
<a href="{{.LastUser.Link}}" class="lastName" style="font-size: 14px;">{{.LastUser.Name}}</a><br>
|
||||||
<span class="rowsmall lastReplyAt">Last: {{.LastReplyAt}}</span>
|
<span class="rowsmall lastReplyAt">Last: {{.LastReplyAt}}</span>
|
||||||
|
|
27
themes.go
27
themes.go
|
@ -14,11 +14,14 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"text/template"
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultTheme string
|
|
||||||
var themes = make(map[string]Theme)
|
var themes = make(map[string]Theme)
|
||||||
|
var defaultThemeBox atomic.Value
|
||||||
|
var changeDefaultThemeMutex sync.Mutex
|
||||||
|
|
||||||
//var overridenTemplates map[string]interface{} = make(map[string]interface{})
|
//var overridenTemplates map[string]interface{} = make(map[string]interface{})
|
||||||
var overridenTemplates = make(map[string]bool)
|
var overridenTemplates = make(map[string]bool)
|
||||||
|
@ -65,6 +68,7 @@ type ThemeResource struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadThemes() error {
|
func LoadThemes() error {
|
||||||
|
changeDefaultThemeMutex.Lock()
|
||||||
rows, err := get_themes_stmt.Query()
|
rows, err := get_themes_stmt.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -100,7 +104,7 @@ func LoadThemes() error {
|
||||||
if defaultThemeSwitch {
|
if defaultThemeSwitch {
|
||||||
log.Print("Loading the theme '" + theme.Name + "'")
|
log.Print("Loading the theme '" + theme.Name + "'")
|
||||||
theme.Active = true
|
theme.Active = true
|
||||||
defaultTheme = uname
|
defaultThemeBox.Store(uname)
|
||||||
mapThemeTemplates(theme)
|
mapThemeTemplates(theme)
|
||||||
} else {
|
} else {
|
||||||
theme.Active = false
|
theme.Active = false
|
||||||
|
@ -113,6 +117,7 @@ func LoadThemes() error {
|
||||||
}
|
}
|
||||||
themes[uname] = theme
|
themes[uname] = theme
|
||||||
}
|
}
|
||||||
|
changeDefaultThemeMutex.Unlock()
|
||||||
return rows.Err()
|
return rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +163,7 @@ func initThemes() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func addThemeStaticFiles(theme Theme) error {
|
func addThemeStaticFiles(theme Theme) error {
|
||||||
// TO-DO: Use a function instead of a closure to make this more testable? What about a function call inside the closure to take the theme variable into account?
|
// TODO: Use a function instead of a closure to make this more testable? What about a function call inside the closure to take the theme variable into account?
|
||||||
return filepath.Walk("./themes/"+theme.Name+"/public", func(path string, f os.FileInfo, err error) error {
|
return filepath.Walk("./themes/"+theme.Name+"/public", func(path string, f os.FileInfo, err error) error {
|
||||||
if dev.DebugMode {
|
if dev.DebugMode {
|
||||||
log.Print("Attempting to add static file '" + path + "' for default theme '" + theme.Name + "'")
|
log.Print("Attempting to add static file '" + path + "' for default theme '" + theme.Name + "'")
|
||||||
|
@ -375,8 +380,8 @@ func resetTemplateOverrides() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW method of doing theme templates to allow one user to have a different theme to another. Under construction.
|
// NEW method of doing theme templates to allow one user to have a different theme to another. Under construction.
|
||||||
// TO-DO: Generate the type switch instead of writing it by hand
|
// TODO: Generate the type switch instead of writing it by hand
|
||||||
// TO-DO: Cut the number of types in half
|
// TODO: Cut the number of types in half
|
||||||
func RunThemeTemplate(theme string, template string, pi interface{}, w http.ResponseWriter) {
|
func RunThemeTemplate(theme string, template string, pi interface{}, w http.ResponseWriter) {
|
||||||
switch tmplO := GetThemeTemplate(theme, template).(type) {
|
switch tmplO := GetThemeTemplate(theme, template).(type) {
|
||||||
case *func(TopicPage, http.ResponseWriter):
|
case *func(TopicPage, http.ResponseWriter):
|
||||||
|
@ -415,7 +420,7 @@ func RunThemeTemplate(theme string, template string, pi interface{}, w http.Resp
|
||||||
case func(Page, http.ResponseWriter):
|
case func(Page, http.ResponseWriter):
|
||||||
tmplO(pi.(Page), w)
|
tmplO(pi.(Page), w)
|
||||||
case string:
|
case string:
|
||||||
mapping, ok := themes[defaultTheme].TemplatesMap[template]
|
mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap[template]
|
||||||
if !ok {
|
if !ok {
|
||||||
mapping = template
|
mapping = template
|
||||||
}
|
}
|
||||||
|
@ -458,7 +463,7 @@ func GetThemeTemplate(theme string, template string) interface{} {
|
||||||
// CreateThemeTemplate creates a theme template on the current default theme
|
// CreateThemeTemplate creates a theme template on the current default theme
|
||||||
func CreateThemeTemplate(theme string, name string) {
|
func CreateThemeTemplate(theme string, name string) {
|
||||||
themes[theme].TmplPtr[name] = func(pi Page, w http.ResponseWriter) {
|
themes[theme].TmplPtr[name] = func(pi Page, w http.ResponseWriter) {
|
||||||
mapping, ok := themes[defaultTheme].TemplatesMap[name]
|
mapping, ok := themes[defaultThemeBox.Load().(string)].TemplatesMap[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
mapping = name
|
mapping = name
|
||||||
}
|
}
|
||||||
|
@ -468,3 +473,11 @@ func CreateThemeTemplate(theme string, name string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetDefaultThemeName() string {
|
||||||
|
return defaultThemeBox.Load().(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetDefaultThemeName(name string) {
|
||||||
|
defaultThemeBox.Store(name)
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
"FullImage": "cosmo-conflux.png",
|
"FullImage": "cosmo-conflux.png",
|
||||||
"ForkOf": "cosmo",
|
"ForkOf": "cosmo",
|
||||||
"MobileFriendly": true,
|
"MobileFriendly": true,
|
||||||
|
"HideFromThemes": true,
|
||||||
"Tag": "🏗️",
|
"Tag": "🏗️",
|
||||||
"URL": "github.com/Azareal/Gosora",
|
"URL": "github.com/Azareal/Gosora",
|
||||||
"Templates": [
|
"Templates": [
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
"Creator": "Azareal",
|
"Creator": "Azareal",
|
||||||
"FullImage": "cosmo.png",
|
"FullImage": "cosmo.png",
|
||||||
"MobileFriendly": true,
|
"MobileFriendly": true,
|
||||||
|
"HideFromThemes": true,
|
||||||
"Tag": "🏗️",
|
"Tag": "🏗️",
|
||||||
"URL": "github.com/Azareal/Gosora",
|
"URL": "github.com/Azareal/Gosora",
|
||||||
"Templates": [
|
"Templates": [
|
||||||
|
|
|
@ -183,11 +183,11 @@ a {
|
||||||
|
|
||||||
/* Topic View */
|
/* Topic View */
|
||||||
|
|
||||||
/* TO-DO: How should we handle the sticky headers? */
|
/* TODO: How should we handle the sticky headers? */
|
||||||
.topic_sticky_head {
|
.topic_sticky_head {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TO-DO: Rewrite the closed topic header so that it looks more consistent with the rest of the theme */
|
/* TODO: Rewrite the closed topic header so that it looks more consistent with the rest of the theme */
|
||||||
.topic_closed_head .topic_status_closed {
|
.topic_closed_head .topic_status_closed {
|
||||||
margin-bottom: -10px;
|
margin-bottom: -10px;
|
||||||
font-size: 19px;
|
font-size: 19px;
|
||||||
|
@ -443,6 +443,24 @@ input, select, textarea {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
background-color: rgb(61,61,61);
|
||||||
|
margin-top: 5px;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
padding-left: 13px;
|
||||||
|
padding-right: 13px;
|
||||||
|
clear: left;
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
.footer select {
|
||||||
|
background-color: #444444;
|
||||||
|
border: 1px solid #555555;
|
||||||
|
color: #999999;
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Forum View */
|
/* Forum View */
|
||||||
.rowhead {
|
.rowhead {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -536,7 +554,7 @@ input, select, textarea {
|
||||||
.simple .user_tag {
|
.simple .user_tag {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
/* TO-DO: Have a has_avatar class for profile comments and topic replies to allow posts without avatars? Won't that look inconsistent next to everything else for just about every theme though? */
|
/* TODO: Have a has_avatar class for profile comments and topic replies to allow posts without avatars? Won't that look inconsistent next to everything else for just about every theme though? */
|
||||||
#profile_comments .rowitem {
|
#profile_comments .rowitem {
|
||||||
background-repeat: no-repeat, repeat-y;
|
background-repeat: no-repeat, repeat-y;
|
||||||
background-size: 128px;
|
background-size: 128px;
|
||||||
|
|
|
@ -165,7 +165,7 @@ li a {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.colblock_left {
|
/*.colblock_left {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
padding-top: 0px;
|
padding-top: 0px;
|
||||||
|
@ -181,7 +181,7 @@ li a {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
.colblock_left:empty, .colblock_right:empty { display: none; }
|
.colblock_left:empty, .colblock_right:empty { display: none; }*/
|
||||||
|
|
||||||
/* The new method of doing columns layouts, colblock is now deprecated :( */
|
/* The new method of doing columns layouts, colblock is now deprecated :( */
|
||||||
.colstack_left {
|
.colstack_left {
|
||||||
|
@ -366,6 +366,36 @@ button {
|
||||||
|
|
||||||
/* Topics */
|
/* Topics */
|
||||||
|
|
||||||
|
.topic_list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: calc(100% - 204px) 204px;
|
||||||
|
}
|
||||||
|
.topic_list .topic_inner_right {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.topic_list .rowitem:last-child {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.topic_list .lastReplyAt {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@supports not (display: grid) {
|
||||||
|
.topic_list .rowitem {
|
||||||
|
float: left;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.topic_list .topic_left {
|
||||||
|
width: calc(100% - 204px);
|
||||||
|
}
|
||||||
|
.topic_list .topic_right {
|
||||||
|
width: 204px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.topic_status_sticky {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.topic_sticky {
|
.topic_sticky {
|
||||||
background-color: rgb(255,255,234);
|
background-color: rgb(255,255,234);
|
||||||
}
|
}
|
||||||
|
@ -373,10 +403,10 @@ button {
|
||||||
background-color: rgb(248,248,248);
|
background-color: rgb(248,248,248);
|
||||||
}
|
}
|
||||||
.topic_sticky_head {
|
.topic_sticky_head {
|
||||||
background-color:#FFFFEA;
|
background-color: #FFFFEA;
|
||||||
}
|
}
|
||||||
.topic_closed_head {
|
.topic_closed_head {
|
||||||
background-color:#eaeaea;
|
background-color: #eaeaea;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topic_status {
|
.topic_status {
|
||||||
|
@ -390,7 +420,9 @@ button {
|
||||||
color: #505050; /* 80,80,80 */
|
color: #505050; /* 80,80,80 */
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
.topic_status:empty { display: none; }
|
.topic_status:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.username, .panel_tag {
|
.username, .panel_tag {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
|
@ -434,7 +466,9 @@ button.username {
|
||||||
color: #202020;
|
color: #202020;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
.post_item > .mod_button > button:hover { opacity: 0.9; }
|
.post_item > .mod_button > button:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
.user_tag {
|
.user_tag {
|
||||||
float: right;
|
float: right;
|
||||||
|
@ -449,8 +483,12 @@ button.username {
|
||||||
background-color: #ffeaff;
|
background-color: #ffeaff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mod_button { margin-right: 4px; }
|
.mod_button {
|
||||||
.like_label:before, .like_count_label:before { content: "😀"; }
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
.like_label:before, .like_count_label:before {
|
||||||
|
content: "😀";
|
||||||
|
}
|
||||||
.like_count_label {
|
.like_count_label {
|
||||||
color: #505050;
|
color: #505050;
|
||||||
float: right;
|
float: right;
|
||||||
|
@ -565,6 +603,7 @@ button.username {
|
||||||
background-size: 128px;
|
background-size: 128px;
|
||||||
padding-left: 136px;
|
padding-left: 136px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Profiles */
|
/* Profiles */
|
||||||
#profile_left_lane {
|
#profile_left_lane {
|
||||||
width: 220px;
|
width: 220px;
|
||||||
|
|
|
@ -18,14 +18,12 @@
|
||||||
.panel_compactrow .panel_tag {
|
.panel_compactrow .panel_tag {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel_compactrow {
|
.panel_compactrow {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel_compacttext {
|
.panel_compacttext {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +37,9 @@
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
position: static;
|
position: static;
|
||||||
}
|
}
|
||||||
.panel_upshift:visited { color: black; }
|
.panel_upshift:visited {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
/*.panel_tag_upshift {
|
/*.panel_tag_upshift {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -47,31 +47,85 @@
|
||||||
color: #505050;
|
color: #505050;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
.panel_floater { float: right; }
|
.panel_floater {
|
||||||
.panel_rank_tag_admin:before { content: "👑"; }
|
float: right;
|
||||||
.panel_rank_tag_mod:before { content: "👮"; }
|
}
|
||||||
.panel_rank_tag_banned:before { content: "⛓️"; }
|
.panel_rank_tag_admin:before {
|
||||||
.panel_rank_tag_guest:before { content: "👽"; }
|
content: "👑";
|
||||||
.panel_rank_tag_member:before { content: "👪"; }
|
}
|
||||||
|
.panel_rank_tag_mod:before {
|
||||||
|
content: "👮";
|
||||||
|
}
|
||||||
|
.panel_rank_tag_banned:before {
|
||||||
|
content: "⛓️";
|
||||||
|
}
|
||||||
|
.panel_rank_tag_guest:before {
|
||||||
|
content: "👽";
|
||||||
|
}
|
||||||
|
.panel_rank_tag_member:before {
|
||||||
|
content: "👪";
|
||||||
|
}
|
||||||
|
|
||||||
.forum_preset_announce:before { content: "📣"; }
|
.forum_preset_announce:before {
|
||||||
.forum_preset_members:before { content: "👪"; }
|
content: "📣";
|
||||||
.forum_preset_staff:before { content: "👮"; }
|
}
|
||||||
.forum_preset_admins:before { content: "👑"; }
|
.forum_preset_members:before {
|
||||||
.forum_preset_archive:before { content: "☠️"; }
|
content: "👪";
|
||||||
.forum_preset_all, .forum_preset_custom, .forum_preset_ { display: none !important; }
|
}
|
||||||
.forum_active_Hide:before { content: "🕵️"; }
|
.forum_preset_staff:before {
|
||||||
.forum_active_Show { display: none !important; }
|
content: "👮";
|
||||||
.forum_active_name { color: #707070; }
|
}
|
||||||
.builtin_forum_divider { border-bottom-style: solid; }
|
.forum_preset_admins:before {
|
||||||
|
content: "👑";
|
||||||
|
}
|
||||||
|
.forum_preset_archive:before {
|
||||||
|
content: "☠️";
|
||||||
|
}
|
||||||
|
.forum_preset_all, .forum_preset_custom, .forum_preset_ {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.forum_active_Hide:before {
|
||||||
|
content: "🕵️";
|
||||||
|
}
|
||||||
|
.forum_active_Show {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
.forum_active_name {
|
||||||
|
color: #707070;
|
||||||
|
}
|
||||||
|
.builtin_forum_divider {
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
.perm_preset_no_access:before { content: "No Access"; color: maroon; }
|
.perm_preset_no_access:before {
|
||||||
.perm_preset_read_only:before { content: "Read Only"; color: green; }
|
content: "No Access";
|
||||||
.perm_preset_can_post:before { content: "Can Post"; color: green; }
|
color: maroon;
|
||||||
.perm_preset_can_moderate:before { content: "Can Moderate"; color: darkblue; }
|
}
|
||||||
.perm_preset_custom:before { content: "Custom"; color: black; }
|
.perm_preset_read_only:before {
|
||||||
.perm_preset_default:before { content: "Default"; }
|
content: "Read Only";
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
.perm_preset_can_post:before {
|
||||||
|
content: "Can Post";
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
.perm_preset_can_moderate:before {
|
||||||
|
content: "Can Moderate";
|
||||||
|
color: darkblue;
|
||||||
|
}
|
||||||
|
.perm_preset_custom:before {
|
||||||
|
content: "Custom";
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.perm_preset_default:before {
|
||||||
|
content: "Default";
|
||||||
|
}
|
||||||
|
|
||||||
#panel_dashboard_right .colstack_head {
|
#panel_dashboard_right .colstack_head {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: Figure out how to handle this on the Control Panel */
|
||||||
|
.footer {
|
||||||
|
display: none;
|
||||||
|
}
|
|
@ -5,7 +5,7 @@ import "sync"
|
||||||
import "database/sql"
|
import "database/sql"
|
||||||
import "./query_gen/lib"
|
import "./query_gen/lib"
|
||||||
|
|
||||||
// TO-DO: Add the watchdog goroutine
|
// TODO: Add the watchdog goroutine
|
||||||
var topics TopicStore
|
var topics TopicStore
|
||||||
|
|
||||||
type TopicStore interface {
|
type TopicStore interface {
|
||||||
|
@ -165,47 +165,47 @@ func (sts *MemoryTopicStore) GetCapacity() int {
|
||||||
return sts.capacity
|
return sts.capacity
|
||||||
}
|
}
|
||||||
|
|
||||||
type SqlTopicStore struct {
|
type SQLTopicStore struct {
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSqlTopicStore() *SqlTopicStore {
|
func NewSQLTopicStore() *SQLTopicStore {
|
||||||
stmt, err := qgen.Builder.SimpleSelect("topics", "title, content, createdBy, createdAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data", "tid = ?", "", "")
|
stmt, err := qgen.Builder.SimpleSelect("topics", "title, content, createdBy, createdAt, is_closed, sticky, parentID, ipaddress, postCount, likeCount, data", "tid = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
return &SqlTopicStore{stmt}
|
return &SQLTopicStore{stmt}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sts *SqlTopicStore) Get(id int) (*Topic, error) {
|
func (sts *SQLTopicStore) Get(id int) (*Topic, error) {
|
||||||
topic := Topic{ID: id}
|
topic := Topic{ID: id}
|
||||||
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
||||||
topic.Link = buildTopicURL(nameToSlug(topic.Title), id)
|
topic.Link = buildTopicURL(nameToSlug(topic.Title), id)
|
||||||
return &topic, err
|
return &topic, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sts *SqlTopicStore) GetUnsafe(id int) (*Topic, error) {
|
func (sts *SQLTopicStore) GetUnsafe(id int) (*Topic, error) {
|
||||||
topic := Topic{ID: id}
|
topic := Topic{ID: id}
|
||||||
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
||||||
topic.Link = buildTopicURL(nameToSlug(topic.Title), id)
|
topic.Link = buildTopicURL(nameToSlug(topic.Title), id)
|
||||||
return &topic, err
|
return &topic, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sts *SqlTopicStore) CascadeGet(id int) (*Topic, error) {
|
func (sts *SQLTopicStore) CascadeGet(id int) (*Topic, error) {
|
||||||
topic := Topic{ID: id}
|
topic := Topic{ID: id}
|
||||||
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
||||||
topic.Link = buildTopicURL(nameToSlug(topic.Title), id)
|
topic.Link = buildTopicURL(nameToSlug(topic.Title), id)
|
||||||
return &topic, err
|
return &topic, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sts *SqlTopicStore) BypassGet(id int) (*Topic, error) {
|
func (sts *SQLTopicStore) BypassGet(id int) (*Topic, error) {
|
||||||
topic := &Topic{ID: id}
|
topic := &Topic{ID: id}
|
||||||
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
||||||
topic.Link = buildTopicURL(nameToSlug(topic.Title), id)
|
topic.Link = buildTopicURL(nameToSlug(topic.Title), id)
|
||||||
return topic, err
|
return topic, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sts *SqlTopicStore) Load(id int) error {
|
func (sts *SQLTopicStore) Load(id int) error {
|
||||||
topic := Topic{ID: id}
|
topic := Topic{ID: id}
|
||||||
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
err := sts.get.QueryRow(id).Scan(&topic.Title, &topic.Content, &topic.CreatedBy, &topic.CreatedAt, &topic.IsClosed, &topic.Sticky, &topic.ParentID, &topic.IPAddress, &topic.PostCount, &topic.LikeCount, &topic.Data)
|
||||||
topic.Link = buildTopicURL(nameToSlug(topic.Title), id)
|
topic.Link = buildTopicURL(nameToSlug(topic.Title), id)
|
||||||
|
@ -213,29 +213,29 @@ func (sts *SqlTopicStore) Load(id int) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholder methods, the actual queries are done elsewhere
|
// Placeholder methods, the actual queries are done elsewhere
|
||||||
func (sts *SqlTopicStore) Set(item *Topic) error {
|
func (sts *SQLTopicStore) Set(item *Topic) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (sts *SqlTopicStore) Add(item *Topic) error {
|
func (sts *SQLTopicStore) Add(item *Topic) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (sts *SqlTopicStore) AddUnsafe(item *Topic) error {
|
func (sts *SQLTopicStore) AddUnsafe(item *Topic) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (sts *SqlTopicStore) Remove(id int) error {
|
func (sts *SQLTopicStore) Remove(id int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (sts *SqlTopicStore) RemoveUnsafe(id int) error {
|
func (sts *SQLTopicStore) RemoveUnsafe(id int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (sts *SqlTopicStore) AddLastTopic(item *Topic, fid int) error {
|
func (sts *SQLTopicStore) AddLastTopic(item *Topic, fid int) error {
|
||||||
// Coming Soon...
|
// Coming Soon...
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (sts *SqlTopicStore) GetCapacity() int {
|
func (sts *SQLTopicStore) GetCapacity() int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sts *SqlTopicStore) GetLength() int {
|
func (sts *SQLTopicStore) GetLength() int {
|
||||||
return 0 // Return the total number of topics on the forums?
|
return 0 // Return the total number of topics on the forums?
|
||||||
}
|
}
|
||||||
|
|
125
user.go
125
user.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
//"log"
|
//"log"
|
||||||
//"fmt"
|
//"fmt"
|
||||||
|
"html"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -15,9 +16,11 @@ import (
|
||||||
|
|
||||||
var guestUser = User{ID: 0, Link: "#", Group: 6, Perms: GuestPerms}
|
var guestUser = User{ID: 0, Link: "#", Group: 6, Perms: GuestPerms}
|
||||||
|
|
||||||
var PreRoute func(http.ResponseWriter, *http.Request) (User, bool) = _pre_route
|
// nolint
|
||||||
|
var PreRoute func(http.ResponseWriter, *http.Request) (User, bool) = preRoute
|
||||||
|
|
||||||
// TO-DO: Are these even session checks anymore? We might need to rethink these names
|
// TODO: Are these even session checks anymore? We might need to rethink these names
|
||||||
|
// nolint We need these types so people can tell what they are without scrolling to the bottom of the file
|
||||||
var PanelSessionCheck func(http.ResponseWriter, *http.Request, *User) (*HeaderVars, PanelStats, bool) = _panel_session_check
|
var PanelSessionCheck func(http.ResponseWriter, *http.Request, *User) (*HeaderVars, PanelStats, bool) = _panel_session_check
|
||||||
var SimplePanelSessionCheck func(http.ResponseWriter, *http.Request, *User) (*HeaderLite, bool) = _simple_panel_session_check
|
var SimplePanelSessionCheck func(http.ResponseWriter, *http.Request, *User) (*HeaderLite, bool) = _simple_panel_session_check
|
||||||
var SimpleForumSessionCheck func(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerLite *HeaderLite, success bool) = _simple_forum_session_check
|
var SimpleForumSessionCheck func(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerLite *HeaderLite, success bool) = _simple_forum_session_check
|
||||||
|
@ -79,7 +82,7 @@ func (user *User) Unban() error {
|
||||||
return users.Load(user.ID)
|
return users.Load(user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Use a transaction to avoid race conditions
|
// TODO: Use a transaction to avoid race conditions
|
||||||
// Make this more stateless?
|
// Make this more stateless?
|
||||||
func (user *User) ScheduleGroupUpdate(gid int, issuedBy int, duration time.Duration) error {
|
func (user *User) ScheduleGroupUpdate(gid int, issuedBy int, duration time.Duration) error {
|
||||||
var temporary bool
|
var temporary bool
|
||||||
|
@ -99,7 +102,7 @@ func (user *User) ScheduleGroupUpdate(gid int, issuedBy int, duration time.Durat
|
||||||
return users.Load(user.ID)
|
return users.Load(user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Use a transaction to avoid race conditions
|
// TODO: Use a transaction to avoid race conditions
|
||||||
func (user *User) RevertGroupUpdate() error {
|
func (user *User) RevertGroupUpdate() error {
|
||||||
_, err := replace_schedule_group_stmt.Exec(user.ID, 0, 0, time.Now(), false)
|
_, err := replace_schedule_group_stmt.Exec(user.ID, 0, 0, time.Now(), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -117,18 +120,18 @@ func BcryptCheckPassword(realPassword string, password string, salt string) (err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Investigate. Do we need the extra salt?
|
// Investigate. Do we need the extra salt?
|
||||||
func BcryptGeneratePassword(password string) (hashed_password string, salt string, err error) {
|
func BcryptGeneratePassword(password string) (hashedPassword string, salt string, err error) {
|
||||||
salt, err = GenerateSafeString(saltLength)
|
salt, err = GenerateSafeString(saltLength)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
password = password + salt
|
password = password + salt
|
||||||
hashed_password, err = BcryptGeneratePasswordNoSalt(password)
|
hashedPassword, err = BcryptGeneratePasswordNoSalt(password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
return hashed_password, salt, nil
|
return hashedPassword, salt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func BcryptGeneratePasswordNoSalt(password string) (hash string, err error) {
|
func BcryptGeneratePasswordNoSalt(password string) (hash string, err error) {
|
||||||
|
@ -149,17 +152,18 @@ func SetPassword(uid int, password string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendValidationEmail(username string, email string, token string) bool {
|
func SendValidationEmail(username string, email string, token string) bool {
|
||||||
var schema string = "http"
|
var schema = "http"
|
||||||
if site.EnableSsl {
|
if site.EnableSsl {
|
||||||
schema += "s"
|
schema += "s"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Move these to the phrase system
|
||||||
subject := "Validate Your Email @ " + site.Name
|
subject := "Validate Your Email @ " + site.Name
|
||||||
msg := "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 + "://" + site.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."
|
msg := "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 + "://" + site.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."
|
||||||
return SendEmail(email, subject, msg)
|
return SendEmail(email, subject, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Support for left sidebars and sidebars on both sides
|
// TODO: Support for left sidebars and sidebars on both sides
|
||||||
// http.Request is for context.Context middleware. Mostly for plugin_socialgroups right now
|
// http.Request is for context.Context middleware. Mostly for plugin_socialgroups right now
|
||||||
func BuildWidgets(zone string, data interface{}, headerVars *HeaderVars, r *http.Request) {
|
func BuildWidgets(zone string, data interface{}, headerVars *HeaderVars, r *http.Request) {
|
||||||
if vhooks["intercept_build_widgets"] != nil {
|
if vhooks["intercept_build_widgets"] != nil {
|
||||||
|
@ -168,8 +172,8 @@ func BuildWidgets(zone string, data interface{}, headerVars *HeaderVars, r *http
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.Print("themes[defaultTheme].Sidebars",themes[defaultTheme].Sidebars)
|
//log.Print("themes[headerVars.ThemeName].Sidebars",themes[headerVars.ThemeName].Sidebars)
|
||||||
if themes[defaultTheme].Sidebars == "right" {
|
if themes[headerVars.ThemeName].Sidebars == "right" {
|
||||||
if len(docks.RightSidebar) != 0 {
|
if len(docks.RightSidebar) != 0 {
|
||||||
var sbody string
|
var sbody string
|
||||||
for _, widget := range docks.RightSidebar {
|
for _, widget := range docks.RightSidebar {
|
||||||
|
@ -258,13 +262,26 @@ func _forum_session_check(w http.ResponseWriter, r *http.Request, user *User, fi
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even if they have the right permissions, the control panel is only open to supermods+. There are many areas without subpermissions which assume that the current user is a supermod+ and admins are extremely unlikely to give these permissions to someone who isn't at-least a supermod to begin with
|
// Even if they have the right permissions, the control panel is only open to supermods+. There are many areas without subpermissions which assume that the current user is a supermod+ and admins are extremely unlikely to give these permissions to someone who isn't at-least a supermod to begin with
|
||||||
|
// TODO: Do a panel specific theme?
|
||||||
func _panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, stats PanelStats, success bool) {
|
func _panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, stats PanelStats, success bool) {
|
||||||
|
var themeName = defaultThemeBox.Load().(string)
|
||||||
|
|
||||||
|
cookie, err := r.Cookie("current_theme")
|
||||||
|
if err == nil {
|
||||||
|
cookie := html.EscapeString(cookie.Value)
|
||||||
|
theme, ok := themes[cookie]
|
||||||
|
if ok && !theme.HideFromThemes {
|
||||||
|
themeName = cookie
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
headerVars = &HeaderVars{
|
headerVars = &HeaderVars{
|
||||||
Site: site,
|
Site: site,
|
||||||
Settings: settingBox.Load().(SettingBox),
|
Settings: settingBox.Load().(SettingBox),
|
||||||
ThemeName: defaultTheme, // TO-DO: Is this racey?
|
Themes: themes,
|
||||||
|
ThemeName: themeName,
|
||||||
}
|
}
|
||||||
// TO-DO: We should probably initialise headerVars.ExtData
|
// TODO: We should probably initialise headerVars.ExtData
|
||||||
|
|
||||||
if !user.IsSuperMod {
|
if !user.IsSuperMod {
|
||||||
NoPermissions(w, r, *user)
|
NoPermissions(w, r, *user)
|
||||||
|
@ -272,8 +289,8 @@ func _panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (h
|
||||||
}
|
}
|
||||||
|
|
||||||
headerVars.Stylesheets = append(headerVars.Stylesheets, headerVars.ThemeName+"/panel.css")
|
headerVars.Stylesheets = append(headerVars.Stylesheets, headerVars.ThemeName+"/panel.css")
|
||||||
if len(themes[defaultTheme].Resources) != 0 {
|
if len(themes[headerVars.ThemeName].Resources) != 0 {
|
||||||
rlist := themes[defaultTheme].Resources
|
rlist := themes[headerVars.ThemeName].Resources
|
||||||
for _, resource := range rlist {
|
for _, resource := range rlist {
|
||||||
if resource.Location == "global" || resource.Location == "panel" {
|
if resource.Location == "global" || resource.Location == "panel" {
|
||||||
halves := strings.Split(resource.Name, ".")
|
halves := strings.Split(resource.Name, ".")
|
||||||
|
@ -289,18 +306,18 @@ func _panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (h
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err := group_count_stmt.QueryRow().Scan(&stats.Groups)
|
err = group_count_stmt.QueryRow().Scan(&stats.Groups)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
InternalError(err, w)
|
InternalError(err, w)
|
||||||
return headerVars, stats, false
|
return headerVars, stats, false
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.Users = users.GetGlobalCount()
|
stats.Users = users.GetGlobalCount()
|
||||||
stats.Forums = fstore.GetGlobalCount() // TO-DO: Stop it from showing the blanked forums
|
stats.Forums = fstore.GetGlobalCount() // TODO: Stop it from showing the blanked forums
|
||||||
stats.Settings = len(headerVars.Settings)
|
stats.Settings = len(headerVars.Settings)
|
||||||
stats.WordFilters = len(wordFilterBox.Load().(WordFilterBox))
|
stats.WordFilters = len(wordFilterBox.Load().(WordFilterBox))
|
||||||
stats.Themes = len(themes)
|
stats.Themes = len(themes)
|
||||||
stats.Reports = 0 // TO-DO: Do the report count. Only show open threads?
|
stats.Reports = 0 // TODO: Do the report count. Only show open threads?
|
||||||
|
|
||||||
pusher, ok := w.(http.Pusher)
|
pusher, ok := w.(http.Pusher)
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -308,9 +325,9 @@ func _panel_session_check(w http.ResponseWriter, r *http.Request, user *User) (h
|
||||||
pusher.Push("/static/"+headerVars.ThemeName+"/panel.css", nil)
|
pusher.Push("/static/"+headerVars.ThemeName+"/panel.css", nil)
|
||||||
pusher.Push("/static/global.js", nil)
|
pusher.Push("/static/global.js", nil)
|
||||||
pusher.Push("/static/jquery-3.1.1.min.js", nil)
|
pusher.Push("/static/jquery-3.1.1.min.js", nil)
|
||||||
// TO-DO: Push the theme CSS files
|
// TODO: Push the theme CSS files
|
||||||
// TO-DO: Push the theme scripts
|
// TODO: Push the theme scripts
|
||||||
// TO-DO: Push avatars?
|
// TODO: Push avatars?
|
||||||
}
|
}
|
||||||
|
|
||||||
return headerVars, stats, true
|
return headerVars, stats, true
|
||||||
|
@ -337,19 +354,32 @@ func _simple_session_check(w http.ResponseWriter, r *http.Request, user *User) (
|
||||||
return headerLite, true
|
return headerLite, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add the ability for admins to restrict certain themes to certain groups?
|
||||||
func _session_check(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, success bool) {
|
func _session_check(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, success bool) {
|
||||||
|
var themeName = defaultThemeBox.Load().(string)
|
||||||
|
|
||||||
|
cookie, err := r.Cookie("current_theme")
|
||||||
|
if err == nil {
|
||||||
|
cookie := html.EscapeString(cookie.Value)
|
||||||
|
theme, ok := themes[cookie]
|
||||||
|
if ok && !theme.HideFromThemes {
|
||||||
|
themeName = cookie
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
headerVars = &HeaderVars{
|
headerVars = &HeaderVars{
|
||||||
Site: site,
|
Site: site,
|
||||||
Settings: settingBox.Load().(SettingBox),
|
Settings: settingBox.Load().(SettingBox),
|
||||||
ThemeName: defaultTheme, // TO-DO: Is this racey?
|
Themes: themes,
|
||||||
|
ThemeName: themeName,
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.IsBanned {
|
if user.IsBanned {
|
||||||
headerVars.NoticeList = append(headerVars.NoticeList, "Your account has been suspended. Some of your permissions may have been revoked.")
|
headerVars.NoticeList = append(headerVars.NoticeList, "Your account has been suspended. Some of your permissions may have been revoked.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(themes[defaultTheme].Resources) != 0 {
|
if len(themes[headerVars.ThemeName].Resources) != 0 {
|
||||||
rlist := themes[defaultTheme].Resources
|
rlist := themes[headerVars.ThemeName].Resources
|
||||||
for _, resource := range rlist {
|
for _, resource := range rlist {
|
||||||
if resource.Location == "global" || resource.Location == "frontend" {
|
if resource.Location == "global" || resource.Location == "frontend" {
|
||||||
halves := strings.Split(resource.Name, ".")
|
halves := strings.Split(resource.Name, ".")
|
||||||
|
@ -370,15 +400,15 @@ func _session_check(w http.ResponseWriter, r *http.Request, user *User) (headerV
|
||||||
pusher.Push("/static/"+headerVars.ThemeName+"/main.css", nil)
|
pusher.Push("/static/"+headerVars.ThemeName+"/main.css", nil)
|
||||||
pusher.Push("/static/global.js", nil)
|
pusher.Push("/static/global.js", nil)
|
||||||
pusher.Push("/static/jquery-3.1.1.min.js", nil)
|
pusher.Push("/static/jquery-3.1.1.min.js", nil)
|
||||||
// TO-DO: Push the theme CSS files
|
// TODO: Push the theme CSS files
|
||||||
// TO-DO: Push the theme scripts
|
// TODO: Push the theme scripts
|
||||||
// TO-DO: Push avatars?
|
// TODO: Push avatars?
|
||||||
}
|
}
|
||||||
|
|
||||||
return headerVars, true
|
return headerVars, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func _pre_route(w http.ResponseWriter, r *http.Request) (User, bool) {
|
func preRoute(w http.ResponseWriter, r *http.Request) (User, bool) {
|
||||||
user, halt := auth.SessionCheck(w, r)
|
user, halt := auth.SessionCheck(w, r)
|
||||||
if halt {
|
if halt {
|
||||||
return *user, false
|
return *user, false
|
||||||
|
@ -401,11 +431,14 @@ func _pre_route(w http.ResponseWriter, r *http.Request) (User, bool) {
|
||||||
user.LastIP = host
|
user.LastIP = host
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Set the X-Frame-Options header
|
h := w.Header()
|
||||||
|
h.Set("X-Frame-Options", "deny")
|
||||||
|
//h.Set("X-XSS-Protection", "1")
|
||||||
|
// TODO: Set the content policy header
|
||||||
return *user, true
|
return *user, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func words_to_score(wcount int, topic bool) (score int) {
|
func wordsToScore(wcount int, topic bool) (score int) {
|
||||||
if topic {
|
if topic {
|
||||||
score = 2
|
score = 2
|
||||||
} else {
|
} else {
|
||||||
|
@ -421,11 +454,12 @@ func words_to_score(wcount int, topic bool) (score int) {
|
||||||
return score
|
return score
|
||||||
}
|
}
|
||||||
|
|
||||||
func increase_post_user_stats(wcount int, uid int, topic bool, user User) error {
|
// TODO: Move this to where the other User methods are
|
||||||
|
func (user *User) increasePostStats(wcount int, topic bool) error {
|
||||||
var mod int
|
var mod int
|
||||||
baseScore := 1
|
baseScore := 1
|
||||||
if topic {
|
if topic {
|
||||||
_, err := increment_user_topics_stmt.Exec(1, uid)
|
_, err := increment_user_topics_stmt.Exec(1, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -434,38 +468,40 @@ func increase_post_user_stats(wcount int, uid int, topic bool, user User) error
|
||||||
|
|
||||||
settings := settingBox.Load().(SettingBox)
|
settings := settingBox.Load().(SettingBox)
|
||||||
if wcount >= settings["megapost_min_words"].(int) {
|
if wcount >= settings["megapost_min_words"].(int) {
|
||||||
_, err := increment_user_megaposts_stmt.Exec(1, 1, 1, uid)
|
_, err := increment_user_megaposts_stmt.Exec(1, 1, 1, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mod = 4
|
mod = 4
|
||||||
} else if wcount >= settings["bigpost_min_words"].(int) {
|
} else if wcount >= settings["bigpost_min_words"].(int) {
|
||||||
_, err := increment_user_bigposts_stmt.Exec(1, 1, uid)
|
_, err := increment_user_bigposts_stmt.Exec(1, 1, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mod = 1
|
mod = 1
|
||||||
} else {
|
} else {
|
||||||
_, err := increment_user_posts_stmt.Exec(1, uid)
|
_, err := increment_user_posts_stmt.Exec(1, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err := increment_user_score_stmt.Exec(baseScore+mod, uid)
|
_, err := increment_user_score_stmt.Exec(baseScore+mod, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
//log.Print(user.Score + base_score + mod)
|
//log.Print(user.Score + base_score + mod)
|
||||||
//log.Print(getLevel(user.Score + base_score + mod))
|
//log.Print(getLevel(user.Score + base_score + mod))
|
||||||
_, err = update_user_level_stmt.Exec(getLevel(user.Score+baseScore+mod), uid)
|
// TODO: Use a transaction to prevent level desyncs?
|
||||||
|
_, err = update_user_level_stmt.Exec(getLevel(user.Score+baseScore+mod), user.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func decrease_post_user_stats(wcount int, uid int, topic bool, user User) error {
|
// TODO: Move this to where the other User methods are
|
||||||
|
func (user *User) decreasePostStats(wcount int, topic bool) error {
|
||||||
var mod int
|
var mod int
|
||||||
baseScore := -1
|
baseScore := -1
|
||||||
if topic {
|
if topic {
|
||||||
_, err := increment_user_topics_stmt.Exec(-1, uid)
|
_, err := increment_user_topics_stmt.Exec(-1, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -474,28 +510,29 @@ func decrease_post_user_stats(wcount int, uid int, topic bool, user User) error
|
||||||
|
|
||||||
settings := settingBox.Load().(SettingBox)
|
settings := settingBox.Load().(SettingBox)
|
||||||
if wcount >= settings["megapost_min_words"].(int) {
|
if wcount >= settings["megapost_min_words"].(int) {
|
||||||
_, err := increment_user_megaposts_stmt.Exec(-1, -1, -1, uid)
|
_, err := increment_user_megaposts_stmt.Exec(-1, -1, -1, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mod = 4
|
mod = 4
|
||||||
} else if wcount >= settings["bigpost_min_words"].(int) {
|
} else if wcount >= settings["bigpost_min_words"].(int) {
|
||||||
_, err := increment_user_bigposts_stmt.Exec(-1, -1, uid)
|
_, err := increment_user_bigposts_stmt.Exec(-1, -1, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
mod = 1
|
mod = 1
|
||||||
} else {
|
} else {
|
||||||
_, err := increment_user_posts_stmt.Exec(-1, uid)
|
_, err := increment_user_posts_stmt.Exec(-1, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, err := increment_user_score_stmt.Exec(baseScore-mod, uid)
|
_, err := increment_user_score_stmt.Exec(baseScore-mod, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = update_user_level_stmt.Exec(getLevel(user.Score-baseScore-mod), uid)
|
// TODO: Use a transaction to prevent level desyncs?
|
||||||
|
_, err = update_user_level_stmt.Exec(getLevel(user.Score-baseScore-mod), user.ID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TO-DO: Add the watchdog goroutine
|
// TODO: Add the watchdog goroutine
|
||||||
var users UserStore
|
var users UserStore
|
||||||
var errAccountExists = errors.New("this username is already in use")
|
var errAccountExists = errors.New("this username is already in use")
|
||||||
|
|
||||||
|
@ -36,46 +36,46 @@ type UserStore interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MemoryUserStore struct {
|
type MemoryUserStore struct {
|
||||||
items map[int]*User
|
items map[int]*User
|
||||||
length int
|
length int
|
||||||
capacity int
|
capacity int
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
register *sql.Stmt
|
register *sql.Stmt
|
||||||
username_exists *sql.Stmt
|
usernameExists *sql.Stmt
|
||||||
user_count *sql.Stmt
|
userCount *sql.Stmt
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMemoryUserStore(capacity int) *MemoryUserStore {
|
func NewMemoryUserStore(capacity int) *MemoryUserStore {
|
||||||
get_stmt, err := qgen.Builder.SimpleSelect("users", "name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group", "uid = ?", "", "")
|
getStmt, err := qgen.Builder.SimpleSelect("users", "name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group", "uid = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an admin version of register_stmt with more flexibility?
|
// Add an admin version of register_stmt with more flexibility?
|
||||||
// create_account_stmt, err = db.Prepare("INSERT INTO
|
// create_account_stmt, err = db.Prepare("INSERT INTO
|
||||||
register_stmt, err := qgen.Builder.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message", "?,?,?,?,?,0,'',?,''")
|
registerStmt, err := qgen.Builder.SimpleInsert("users", "name, email, password, salt, group, is_super_admin, session, active, message", "?,?,?,?,?,0,'',?,''")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
username_exists_stmt, err := qgen.Builder.SimpleSelect("users", "name", "name = ?", "", "")
|
usernameExistsStmt, err := qgen.Builder.SimpleSelect("users", "name", "name = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
user_count_stmt, err := qgen.Builder.SimpleCount("users", "", "")
|
userCountStmt, err := qgen.Builder.SimpleCount("users", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &MemoryUserStore{
|
return &MemoryUserStore{
|
||||||
items: make(map[int]*User),
|
items: make(map[int]*User),
|
||||||
capacity: capacity,
|
capacity: capacity,
|
||||||
get: get_stmt,
|
get: getStmt,
|
||||||
register: register_stmt,
|
register: registerStmt,
|
||||||
username_exists: username_exists_stmt,
|
usernameExists: usernameExistsStmt,
|
||||||
user_count: user_count_stmt,
|
userCount: userCountStmt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,10 +135,10 @@ func (sus *MemoryUserStore) bulkGet(ids []int) (list []*User) {
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
||||||
// TO-DO: ID of 0 should always error?
|
// TODO: ID of 0 should always error?
|
||||||
func (sus *MemoryUserStore) BulkCascadeGetMap(ids []int) (list map[int]*User, err error) {
|
func (sus *MemoryUserStore) BulkCascadeGetMap(ids []int) (list map[int]*User, err error) {
|
||||||
var idCount int = len(ids)
|
var idCount = len(ids)
|
||||||
list = make(map[int]*User)
|
list = make(map[int]*User)
|
||||||
if idCount == 0 {
|
if idCount == 0 {
|
||||||
return list, nil
|
return list, nil
|
||||||
|
@ -324,7 +324,7 @@ func (sus *MemoryUserStore) RemoveUnsafe(id int) error {
|
||||||
|
|
||||||
func (sus *MemoryUserStore) CreateUser(username string, password string, email string, group int, active int) (int, error) {
|
func (sus *MemoryUserStore) CreateUser(username string, password string, email string, group int, active int) (int, error) {
|
||||||
// Is this username already taken..?
|
// Is this username already taken..?
|
||||||
err := sus.username_exists.QueryRow(username).Scan(&username)
|
err := sus.usernameExists.QueryRow(username).Scan(&username)
|
||||||
if err != ErrNoRows {
|
if err != ErrNoRows {
|
||||||
return 0, errAccountExists
|
return 0, errAccountExists
|
||||||
}
|
}
|
||||||
|
@ -363,21 +363,21 @@ func (sus *MemoryUserStore) GetCapacity() int {
|
||||||
// Return the total number of users registered on the forums
|
// Return the total number of users registered on the forums
|
||||||
func (sus *MemoryUserStore) GetGlobalCount() int {
|
func (sus *MemoryUserStore) GetGlobalCount() int {
|
||||||
var ucount int
|
var ucount int
|
||||||
err := sus.user_count.QueryRow().Scan(&ucount)
|
err := sus.userCount.QueryRow().Scan(&ucount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
return ucount
|
return ucount
|
||||||
}
|
}
|
||||||
|
|
||||||
type SqlUserStore struct {
|
type SQLUserStore struct {
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
register *sql.Stmt
|
register *sql.Stmt
|
||||||
usernameExists *sql.Stmt
|
usernameExists *sql.Stmt
|
||||||
userCount *sql.Stmt
|
userCount *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSqlUserStore() *SqlUserStore {
|
func NewSQLUserStore() *SQLUserStore {
|
||||||
getStmt, err := qgen.Builder.SimpleSelect("users", "name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group", "uid = ?", "", "")
|
getStmt, err := qgen.Builder.SimpleSelect("users", "name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group", "uid = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -400,7 +400,7 @@ func NewSqlUserStore() *SqlUserStore {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SqlUserStore{
|
return &SQLUserStore{
|
||||||
get: getStmt,
|
get: getStmt,
|
||||||
register: registerStmt,
|
register: registerStmt,
|
||||||
usernameExists: usernameExistsStmt,
|
usernameExists: usernameExistsStmt,
|
||||||
|
@ -408,7 +408,7 @@ func NewSqlUserStore() *SqlUserStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sus *SqlUserStore) Get(id int) (*User, error) {
|
func (sus *SQLUserStore) Get(id int) (*User, error) {
|
||||||
user := User{ID: id, Loggedin: true}
|
user := User{ID: id, Loggedin: true}
|
||||||
err := sus.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 := sus.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)
|
||||||
|
|
||||||
|
@ -425,7 +425,7 @@ func (sus *SqlUserStore) Get(id int) (*User, error) {
|
||||||
return &user, err
|
return &user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sus *SqlUserStore) GetUnsafe(id int) (*User, error) {
|
func (sus *SQLUserStore) GetUnsafe(id int) (*User, error) {
|
||||||
user := User{ID: id, Loggedin: true}
|
user := User{ID: id, Loggedin: true}
|
||||||
err := sus.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 := sus.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)
|
||||||
|
|
||||||
|
@ -442,7 +442,7 @@ func (sus *SqlUserStore) GetUnsafe(id int) (*User, error) {
|
||||||
return &user, err
|
return &user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sus *SqlUserStore) CascadeGet(id int) (*User, error) {
|
func (sus *SQLUserStore) CascadeGet(id int) (*User, error) {
|
||||||
user := User{ID: id, Loggedin: true}
|
user := User{ID: id, Loggedin: true}
|
||||||
err := sus.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 := sus.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)
|
||||||
|
|
||||||
|
@ -459,8 +459,8 @@ func (sus *SqlUserStore) CascadeGet(id int) (*User, error) {
|
||||||
return &user, err
|
return &user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
||||||
func (sus *SqlUserStore) BulkCascadeGetMap(ids []int) (list map[int]*User, err error) {
|
func (sus *SQLUserStore) BulkCascadeGetMap(ids []int) (list map[int]*User, err error) {
|
||||||
var qlist string
|
var qlist string
|
||||||
var uidList []interface{}
|
var uidList []interface{}
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
|
@ -506,7 +506,7 @@ func (sus *SqlUserStore) BulkCascadeGetMap(ids []int) (list map[int]*User, err e
|
||||||
return list, nil
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sus *SqlUserStore) BypassGet(id int) (*User, error) {
|
func (sus *SQLUserStore) BypassGet(id int) (*User, error) {
|
||||||
user := User{ID: id, Loggedin: true}
|
user := User{ID: id, Loggedin: true}
|
||||||
err := sus.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 := sus.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)
|
||||||
|
|
||||||
|
@ -523,13 +523,13 @@ func (sus *SqlUserStore) BypassGet(id int) (*User, error) {
|
||||||
return &user, err
|
return &user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sus *SqlUserStore) Load(id int) error {
|
func (sus *SQLUserStore) Load(id int) error {
|
||||||
user := &User{ID: id}
|
user := &User{ID: id}
|
||||||
// Simplify this into a quick check to see whether the user exists. Add an Exists method to facilitate this?
|
// Simplify this into a quick check to see whether the user exists. Add an Exists method to facilitate this?
|
||||||
return sus.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)
|
return sus.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)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sus *SqlUserStore) CreateUser(username string, password string, email string, group int, active int) (int, error) {
|
func (sus *SQLUserStore) CreateUser(username string, password string, email string, group int, active int) (int, error) {
|
||||||
// Is this username already taken..?
|
// Is this username already taken..?
|
||||||
err := sus.usernameExists.QueryRow(username).Scan(&username)
|
err := sus.usernameExists.QueryRow(username).Scan(&username)
|
||||||
if err != ErrNoRows {
|
if err != ErrNoRows {
|
||||||
|
@ -556,27 +556,27 @@ func (sus *SqlUserStore) CreateUser(username string, password string, email stri
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholder methods, as we're not don't need to do any cache management with this implementation ofr the UserStore
|
// Placeholder methods, as we're not don't need to do any cache management with this implementation ofr the UserStore
|
||||||
func (sus *SqlUserStore) Set(item *User) error {
|
func (sus *SQLUserStore) Set(item *User) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (sus *SqlUserStore) Add(item *User) error {
|
func (sus *SQLUserStore) Add(item *User) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (sus *SqlUserStore) AddUnsafe(item *User) error {
|
func (sus *SQLUserStore) AddUnsafe(item *User) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (sus *SqlUserStore) Remove(id int) error {
|
func (sus *SQLUserStore) Remove(id int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (sus *SqlUserStore) RemoveUnsafe(id int) error {
|
func (sus *SQLUserStore) RemoveUnsafe(id int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (sus *SqlUserStore) GetCapacity() int {
|
func (sus *SQLUserStore) GetCapacity() int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the total number of users registered on the forums
|
// Return the total number of users registered on the forums
|
||||||
func (sus *SqlUserStore) GetLength() int {
|
func (sus *SQLUserStore) GetLength() int {
|
||||||
var ucount int
|
var ucount int
|
||||||
err := sus.userCount.QueryRow().Scan(&ucount)
|
err := sus.userCount.QueryRow().Scan(&ucount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -584,7 +584,7 @@ func (sus *SqlUserStore) GetLength() int {
|
||||||
}
|
}
|
||||||
return ucount
|
return ucount
|
||||||
}
|
}
|
||||||
func (sus *SqlUserStore) GetGlobalCount() int {
|
func (sus *SQLUserStore) GetGlobalCount() int {
|
||||||
var ucount int
|
var ucount int
|
||||||
err := sus.userCount.QueryRow().Scan(&ucount)
|
err := sus.userCount.QueryRow().Scan(&ucount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
48
utils.go
48
utils.go
|
@ -205,16 +205,16 @@ func SendEmail(email string, subject string, msg string) (res bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
email_data, err := con.Data()
|
emailData, err := con.Data()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, err = fmt.Fprintf(email_data, body)
|
_, err = fmt.Fprintf(emailData, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = email_data.Close()
|
err = emailData.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -229,7 +229,7 @@ func weakPassword(password string) error {
|
||||||
if len(password) < 8 {
|
if len(password) < 8 {
|
||||||
return errors.New("your password needs to be at-least eight characters long")
|
return errors.New("your password needs to be at-least eight characters long")
|
||||||
}
|
}
|
||||||
var charMap map[rune]int = make(map[rune]int)
|
var charMap = make(map[rune]int)
|
||||||
var numbers /*letters, */, symbols, upper, lower int
|
var numbers /*letters, */, symbols, upper, lower int
|
||||||
for _, char := range password {
|
for _, char := range password {
|
||||||
charItem, ok := charMap[char]
|
charItem, ok := charMap[char]
|
||||||
|
@ -254,7 +254,7 @@ func weakPassword(password string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO-DO: Disable the linter on these and fix up the grammar
|
// TODO: Disable the linter on these and fix up the grammar
|
||||||
if numbers == 0 {
|
if numbers == 0 {
|
||||||
return errors.New("you don't have any numbers in your password")
|
return errors.New("you don't have any numbers in your password")
|
||||||
}
|
}
|
||||||
|
@ -306,15 +306,15 @@ func wordCount(input string) (count int) {
|
||||||
if input == "" {
|
if input == "" {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
in_space := false
|
var inSpace bool
|
||||||
for _, value := range input {
|
for _, value := range input {
|
||||||
if unicode.IsSpace(value) {
|
if unicode.IsSpace(value) {
|
||||||
if !in_space {
|
if !inSpace {
|
||||||
in_space = true
|
inSpace = true
|
||||||
}
|
}
|
||||||
} else if in_space {
|
} else if inSpace {
|
||||||
count++
|
count++
|
||||||
in_space = false
|
inSpace = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count + 1
|
return count + 1
|
||||||
|
@ -323,14 +323,14 @@ func wordCount(input string) (count int) {
|
||||||
func getLevel(score int) (level int) {
|
func getLevel(score int) (level int) {
|
||||||
var base float64 = 25
|
var base float64 = 25
|
||||||
var current, prev float64
|
var current, prev float64
|
||||||
exp_factor := 2.8
|
expFactor := 2.8
|
||||||
|
|
||||||
for i := 1; ; i++ {
|
for i := 1; ; i++ {
|
||||||
_, bit := math.Modf(float64(i) / 10)
|
_, bit := math.Modf(float64(i) / 10)
|
||||||
if bit == 0 {
|
if bit == 0 {
|
||||||
exp_factor += 0.1
|
expFactor += 0.1
|
||||||
}
|
}
|
||||||
current = base + math.Pow(float64(i), exp_factor) + (prev / 3)
|
current = base + math.Pow(float64(i), expFactor) + (prev / 3)
|
||||||
prev = current
|
prev = current
|
||||||
if float64(score) < current {
|
if float64(score) < current {
|
||||||
break
|
break
|
||||||
|
@ -344,14 +344,14 @@ func getLevelScore(getLevel int) (score int) {
|
||||||
var base float64 = 25
|
var base float64 = 25
|
||||||
var current, prev float64
|
var current, prev float64
|
||||||
var level int
|
var level int
|
||||||
exp_factor := 2.8
|
expFactor := 2.8
|
||||||
|
|
||||||
for i := 1; ; i++ {
|
for i := 1; ; i++ {
|
||||||
_, bit := math.Modf(float64(i) / 10)
|
_, bit := math.Modf(float64(i) / 10)
|
||||||
if bit == 0 {
|
if bit == 0 {
|
||||||
exp_factor += 0.1
|
expFactor += 0.1
|
||||||
}
|
}
|
||||||
current = base + math.Pow(float64(i), exp_factor) + (prev / 3)
|
current = base + math.Pow(float64(i), expFactor) + (prev / 3)
|
||||||
prev = current
|
prev = current
|
||||||
level++
|
level++
|
||||||
if level <= getLevel {
|
if level <= getLevel {
|
||||||
|
@ -364,16 +364,16 @@ func getLevelScore(getLevel int) (score int) {
|
||||||
func getLevels(maxLevel int) []float64 {
|
func getLevels(maxLevel int) []float64 {
|
||||||
var base float64 = 25
|
var base float64 = 25
|
||||||
var current, prev float64 // = 0
|
var current, prev float64 // = 0
|
||||||
exp_factor := 2.8
|
expFactor := 2.8
|
||||||
var out []float64
|
var out []float64
|
||||||
out = append(out, 0)
|
out = append(out, 0)
|
||||||
|
|
||||||
for i := 1; i <= maxLevel; i++ {
|
for i := 1; i <= maxLevel; i++ {
|
||||||
_, bit := math.Modf(float64(i) / 10)
|
_, bit := math.Modf(float64(i) / 10)
|
||||||
if bit == 0 {
|
if bit == 0 {
|
||||||
exp_factor += 0.1
|
expFactor += 0.1
|
||||||
}
|
}
|
||||||
current = base + math.Pow(float64(i), exp_factor) + (prev / 3)
|
current = base + math.Pow(float64(i), expFactor) + (prev / 3)
|
||||||
prev = current
|
prev = current
|
||||||
out = append(out, current)
|
out = append(out, current)
|
||||||
}
|
}
|
||||||
|
@ -396,16 +396,10 @@ func buildSlug(slug string, id int) string {
|
||||||
|
|
||||||
func addModLog(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) {
|
func addModLog(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) {
|
||||||
_, err = add_modlog_entry_stmt.Exec(action, elementID, elementType, ipaddress, actorID)
|
_, err = add_modlog_entry_stmt.Exec(action, elementID, elementType, ipaddress, actorID)
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func addAdminLog(action string, elementID string, elementType int, ipaddress string, actorID int) (err error) {
|
func addAdminLog(action string, elementID string, elementType int, ipaddress string, actorID int) (err error) {
|
||||||
_, err = add_adminlog_entry_stmt.Exec(action, elementID, elementType, ipaddress, actorID)
|
_, err = add_adminlog_entry_stmt.Exec(action, elementID, elementType, ipaddress, actorID)
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (hub *WS_Hub) broadcastMessage(msg string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
w.Write([]byte(msg))
|
_, _ = w.Write([]byte(msg))
|
||||||
}
|
}
|
||||||
hub.users.RUnlock()
|
hub.users.RUnlock()
|
||||||
return nil
|
return nil
|
||||||
|
@ -85,8 +85,8 @@ func (hub *WS_Hub) pushMessage(targetUser int, msg string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hub *WS_Hub) pushAlert(targetUser int, asid int, event string, elementType string, actorID int, targetUser_id int, elementID int) error {
|
func (hub *WS_Hub) pushAlert(targetUser int, asid int, event string, elementType string, actorID int, targetUserID int, elementID int) error {
|
||||||
//log.Print("In push_alert")
|
//log.Print("In pushAlert")
|
||||||
hub.users.RLock()
|
hub.users.RLock()
|
||||||
wsUser, ok := hub.onlineUsers[targetUser]
|
wsUser, ok := hub.onlineUsers[targetUser]
|
||||||
hub.users.RUnlock()
|
hub.users.RUnlock()
|
||||||
|
@ -95,7 +95,7 @@ func (hub *WS_Hub) pushAlert(targetUser int, asid int, event string, elementType
|
||||||
}
|
}
|
||||||
|
|
||||||
//log.Print("Building alert")
|
//log.Print("Building alert")
|
||||||
alert, err := buildAlert(asid, event, elementType, actorID, targetUser_id, elementID, *wsUser.User)
|
alert, err := buildAlert(asid, event, elementType, actorID, targetUserID, elementID, *wsUser.User)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ func (hub *WS_Hub) pushAlert(targetUser int, asid int, event string, elementType
|
||||||
}
|
}
|
||||||
|
|
||||||
w.Write([]byte(alert))
|
w.Write([]byte(alert))
|
||||||
w.Close()
|
_ = w.Close()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,45 +270,45 @@ var adminStatsMutex sync.RWMutex
|
||||||
func adminStatsTicker() {
|
func adminStatsTicker() {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
var last_uonline int = -1
|
var lastUonline = -1
|
||||||
var last_gonline int = -1
|
var lastGonline = -1
|
||||||
var last_totonline int = -1
|
var lastTotonline = -1
|
||||||
var last_cpu_perc int = -1
|
var lastCPUPerc = -1
|
||||||
var last_available_ram int64 = -1
|
var lastAvailableRAM int64 = -1
|
||||||
var no_stat_updates bool = false
|
var noStatUpdates bool
|
||||||
var no_ram_updates bool = false
|
var noRAMUpdates bool
|
||||||
|
|
||||||
var onlineColour, onlineGuestsColour, onlineUsersColour, cpustr, cpuColour, ramstr, ramColour string
|
var onlineColour, onlineGuestsColour, onlineUsersColour, cpustr, cpuColour, ramstr, ramColour string
|
||||||
var cpuerr, ramerr error
|
var cpuerr, ramerr error
|
||||||
var memres *mem.VirtualMemoryStat
|
var memres *mem.VirtualMemoryStat
|
||||||
var cpu_perc []float64
|
var cpuPerc []float64
|
||||||
|
|
||||||
var totunit, uunit, gunit string
|
var totunit, uunit, gunit string
|
||||||
|
|
||||||
AdminStatLoop:
|
AdminStatLoop:
|
||||||
for {
|
for {
|
||||||
adminStatsMutex.RLock()
|
adminStatsMutex.RLock()
|
||||||
watch_count := len(adminStatsWatchers)
|
watchCount := len(adminStatsWatchers)
|
||||||
adminStatsMutex.RUnlock()
|
adminStatsMutex.RUnlock()
|
||||||
if watch_count == 0 {
|
if watchCount == 0 {
|
||||||
break AdminStatLoop
|
break AdminStatLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu_perc, cpuerr = cpu.Percent(time.Duration(time.Second), true)
|
cpuPerc, cpuerr = cpu.Percent(time.Second, true)
|
||||||
memres, ramerr = mem.VirtualMemory()
|
memres, ramerr = mem.VirtualMemory()
|
||||||
uonline := wsHub.userCount()
|
uonline := wsHub.userCount()
|
||||||
gonline := wsHub.guestCount()
|
gonline := wsHub.guestCount()
|
||||||
totonline := uonline + gonline
|
totonline := uonline + gonline
|
||||||
|
|
||||||
// It's far more likely that the CPU Usage will change than the other stats, so we'll optimise them seperately...
|
// It's far more likely that the CPU Usage will change than the other stats, so we'll optimise them seperately...
|
||||||
no_stat_updates = (uonline == last_uonline && gonline == last_gonline && totonline == last_totonline)
|
noStatUpdates = (uonline == lastUonline && gonline == lastGonline && totonline == lastTotonline)
|
||||||
no_ram_updates = (last_available_ram == int64(memres.Available))
|
noRAMUpdates = (lastAvailableRAM == int64(memres.Available))
|
||||||
if int(cpu_perc[0]) == last_cpu_perc && no_stat_updates && no_ram_updates {
|
if int(cpuPerc[0]) == lastCPUPerc && noStatUpdates && noRAMUpdates {
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !no_stat_updates {
|
if !noStatUpdates {
|
||||||
if totonline > 10 {
|
if totonline > 10 {
|
||||||
onlineColour = "stat_green"
|
onlineColour = "stat_green"
|
||||||
} else if totonline > 3 {
|
} else if totonline > 3 {
|
||||||
|
@ -341,7 +341,7 @@ AdminStatLoop:
|
||||||
if cpuerr != nil {
|
if cpuerr != nil {
|
||||||
cpustr = "Unknown"
|
cpustr = "Unknown"
|
||||||
} else {
|
} else {
|
||||||
calcperc := int(cpu_perc[0]) / runtime.NumCPU()
|
calcperc := int(cpuPerc[0]) / runtime.NumCPU()
|
||||||
cpustr = strconv.Itoa(calcperc)
|
cpustr = strconv.Itoa(calcperc)
|
||||||
if calcperc < 30 {
|
if calcperc < 30 {
|
||||||
cpuColour = "stat_green"
|
cpuColour = "stat_green"
|
||||||
|
@ -352,26 +352,26 @@ AdminStatLoop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !no_ram_updates {
|
if !noRAMUpdates {
|
||||||
if ramerr != nil {
|
if ramerr != nil {
|
||||||
ramstr = "Unknown"
|
ramstr = "Unknown"
|
||||||
} else {
|
} else {
|
||||||
total_count, total_unit := convertByteUnit(float64(memres.Total))
|
totalCount, totalUnit := convertByteUnit(float64(memres.Total))
|
||||||
used_count := convertByteInUnit(float64(memres.Total-memres.Available), total_unit)
|
usedCount := convertByteInUnit(float64(memres.Total-memres.Available), totalUnit)
|
||||||
|
|
||||||
// Round totals with .9s up, it's how most people see it anyway. Floats are notoriously imprecise, so do it off 0.85
|
// Round totals with .9s up, it's how most people see it anyway. Floats are notoriously imprecise, so do it off 0.85
|
||||||
var totstr string
|
var totstr string
|
||||||
if (total_count - float64(int(total_count))) > 0.85 {
|
if (totalCount - float64(int(totalCount))) > 0.85 {
|
||||||
used_count += 1.0 - (total_count - float64(int(total_count)))
|
usedCount += 1.0 - (totalCount - float64(int(totalCount)))
|
||||||
totstr = strconv.Itoa(int(total_count) + 1)
|
totstr = strconv.Itoa(int(totalCount) + 1)
|
||||||
} else {
|
} else {
|
||||||
totstr = fmt.Sprintf("%.1f", total_count)
|
totstr = fmt.Sprintf("%.1f", totalCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
if used_count > total_count {
|
if usedCount > totalCount {
|
||||||
used_count = total_count
|
usedCount = totalCount
|
||||||
}
|
}
|
||||||
ramstr = fmt.Sprintf("%.1f", used_count) + " / " + totstr + total_unit
|
ramstr = fmt.Sprintf("%.1f", usedCount) + " / " + totstr + totalUnit
|
||||||
|
|
||||||
ramperc := ((memres.Total - memres.Available) * 100) / memres.Total
|
ramperc := ((memres.Total - memres.Available) * 100) / memres.Total
|
||||||
if ramperc < 50 {
|
if ramperc < 50 {
|
||||||
|
@ -388,7 +388,7 @@ AdminStatLoop:
|
||||||
watchers := adminStatsWatchers
|
watchers := adminStatsWatchers
|
||||||
adminStatsMutex.RUnlock()
|
adminStatsMutex.RUnlock()
|
||||||
|
|
||||||
for watcher, _ := range watchers {
|
for watcher := range watchers {
|
||||||
w, err := watcher.conn.NextWriter(websocket.TextMessage)
|
w, err := watcher.conn.NextWriter(websocket.TextMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//log.Print(err.Error())
|
//log.Print(err.Error())
|
||||||
|
@ -398,7 +398,8 @@ AdminStatLoop:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !no_stat_updates {
|
// nolint
|
||||||
|
if !noStatUpdates {
|
||||||
w.Write([]byte("set #dash-totonline " + strconv.Itoa(totonline) + totunit + " online\r"))
|
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-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-uonline " + strconv.Itoa(uonline) + uunit + " users online\r"))
|
||||||
|
@ -411,7 +412,7 @@ AdminStatLoop:
|
||||||
w.Write([]byte("set #dash-cpu CPU: " + cpustr + "%\r"))
|
w.Write([]byte("set #dash-cpu CPU: " + cpustr + "%\r"))
|
||||||
w.Write([]byte("set-class #dash-cpu grid_item grid_istat " + cpuColour + "\r"))
|
w.Write([]byte("set-class #dash-cpu grid_item grid_istat " + cpuColour + "\r"))
|
||||||
|
|
||||||
if !no_ram_updates {
|
if !noRAMUpdates {
|
||||||
w.Write([]byte("set #dash-ram RAM: " + ramstr + "\r"))
|
w.Write([]byte("set #dash-ram RAM: " + ramstr + "\r"))
|
||||||
w.Write([]byte("set-class #dash-ram grid_item grid_istat " + ramColour + "\r"))
|
w.Write([]byte("set-class #dash-ram grid_item grid_istat " + ramColour + "\r"))
|
||||||
}
|
}
|
||||||
|
@ -419,11 +420,11 @@ AdminStatLoop:
|
||||||
w.Close()
|
w.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
last_uonline = uonline
|
lastUonline = uonline
|
||||||
last_gonline = gonline
|
lastGonline = gonline
|
||||||
last_totonline = totonline
|
lastTotonline = totonline
|
||||||
last_cpu_perc = int(cpu_perc[0])
|
lastCPUPerc = int(cpuPerc[0])
|
||||||
last_available_ram = int64(memres.Available)
|
lastAvailableRAM = int64(memres.Available)
|
||||||
|
|
||||||
//time.Sleep(time.Second)
|
//time.Sleep(time.Second)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue