diff --git a/common/activity_stream.go b/common/activity_stream.go new file mode 100644 index 00000000..dcf30f2f --- /dev/null +++ b/common/activity_stream.go @@ -0,0 +1,52 @@ +package common + +import "database/sql" +import "github.com/Azareal/Gosora/query_gen" + +var Activity ActivityStream + +type ActivityStream interface { + Add(alert Alert) (int, error) + Get(id int) (Alert, error) + Count() (count int) +} + +type DefaultActivityStream struct { + add *sql.Stmt + get *sql.Stmt + count *sql.Stmt +} + +func NewDefaultActivityStream(acc *qgen.Accumulator) (*DefaultActivityStream, error) { + return &DefaultActivityStream{ + add: acc.Insert("activity_stream").Columns("actor, targetUser, event, elementType, elementID, createdAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(), + get: acc.Select("activity_stream").Columns("actor, targetUser, event, elementType, elementID, createdAt").Where("asid = ?").Prepare(), + count: acc.Count("activity_stream").Prepare(), + }, acc.FirstError() +} + +func (s *DefaultActivityStream) Add(alert Alert) (int, error) { + res, err := s.add.Exec(alert.ActorID, alert.TargetUserID, alert.Event, alert.ElementType, alert.ElementID) + if err != nil { + return 0, err + } + + lastID, err := res.LastInsertId() + return int(lastID), err +} + +func (s *DefaultActivityStream) Get(id int) (Alert, error) { + var a = Alert{ASID: id} + err := s.get.QueryRow(id).Scan(&a.ActorID, &a.TargetUserID, &a.Event, &a.ElementType, &a.ElementID, &a.CreatedAt) + return a, err +} + +// TODO: Write a test for this +// Count returns the total number of activity stream items +func (s *DefaultActivityStream) Count() (count int) { + err := s.count.QueryRow().Scan(&count) + if err != nil { + LogError(err) + } + return count +} \ No newline at end of file diff --git a/common/alerts.go b/common/alerts.go index 11e9cdaf..ab40b82e 100644 --- a/common/alerts.go +++ b/common/alerts.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" "time" + //"fmt" "github.com/Azareal/Gosora/common/phrases" "github.com/Azareal/Gosora/query_gen" @@ -30,11 +31,9 @@ type Alert struct { } type AlertStmts struct { - addActivity *sql.Stmt notifyWatchers *sql.Stmt notifyOne *sql.Stmt getWatchers *sql.Stmt - getActivityEntry *sql.Stmt } var alertStmts AlertStmts @@ -44,14 +43,12 @@ var alertStmts AlertStmts func init() { DbInits.Add(func(acc *qgen.Accumulator) error { alertStmts = AlertStmts{ - addActivity: acc.Insert("activity_stream").Columns("actor, targetUser, event, elementType, elementID, createdAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(), notifyWatchers: acc.SimpleInsertInnerJoin( qgen.DBInsert{"activity_stream_matches", "watcher, asid", ""}, qgen.DBJoin{"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 = ?", "", ""}, ), notifyOne: acc.Insert("activity_stream_matches").Columns("watcher, asid").Fields("?,?").Prepare(), getWatchers: acc.SimpleInnerJoin("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 = ?", "", ""), - getActivityEntry: acc.Select("activity_stream").Columns("actor, targetUser, event, elementType, elementID, createdAt").Where("asid = ?").Prepare(), } return acc.FirstError() }) @@ -79,7 +76,6 @@ func BuildAlert(alert Alert, user User /* The current user */) (out string, err return } }*/ - if alert.Event == "friend_invite" { return buildAlertString(".new_friend_invite", []string{alert.Actor.Name}, alert.Actor.Link, alert.Actor.Avatar, alert.ASID), nil } @@ -163,45 +159,41 @@ func buildAlertString(msg string, sub []string, path string, avatar string, asid } func AddActivityAndNotifyAll(actor int, targetUser int, event string, elementType string, elementID int) error { - res, err := alertStmts.addActivity.Exec(actor, targetUser, event, elementType, elementID) + id, err := Activity.Add(Alert{ActorID: actor, TargetUserID: targetUser, Event: event, ElementType: elementType, ElementID: elementID}) if err != nil { return err } - lastID, err := res.LastInsertId() - if err != nil { - return err - } - return NotifyWatchers(lastID) + return NotifyWatchers(id) } func AddActivityAndNotifyTarget(alert Alert) error { - res, err := alertStmts.addActivity.Exec(alert.ActorID, alert.TargetUserID, alert.Event, alert.ElementType, alert.ElementID) + id, err := Activity.Add(alert) if err != nil { return err } - lastID, err := res.LastInsertId() + err = NotifyOne(alert.TargetUserID, id) if err != nil { return err } - - err = NotifyOne(alert.TargetUserID, lastID) - if err != nil { - return err - } - alert.ASID = int(lastID) + alert.ASID = id // Live alerts, if the target is online and WebSockets is enabled - _ = WsHub.pushAlert(alert.TargetUserID, alert) + if EnableWebsockets { + go func() { + _ = WsHub.pushAlert(alert.TargetUserID, alert) + //fmt.Println("err:",err) + }() + } return nil } -func NotifyOne(watcher int, asid int64) error { +func NotifyOne(watcher int, asid int) error { _, err := alertStmts.notifyOne.Exec(watcher, asid) return err } -func NotifyWatchers(asid int64) error { +func NotifyWatchers(asid int) error { _, err := alertStmts.notifyWatchers.Exec(asid) if err != nil { return err @@ -214,7 +206,7 @@ func NotifyWatchers(asid int64) error { return nil } -func notifyWatchers(asid int64) { +func notifyWatchers(asid int) { rows, err := alertStmts.getWatchers.Query(asid) if err != nil && err != ErrNoRows { LogError(err) @@ -238,12 +230,10 @@ func notifyWatchers(asid int64) { return } - var alert = Alert{ASID: int(asid)} - err = alertStmts.getActivityEntry.QueryRow(asid).Scan(&alert.ActorID, &alert.TargetUserID, &alert.Event, &alert.ElementType, &alert.ElementID, &alert.CreatedAt) + alert, err := Activity.Get(asid) if err != nil && err != ErrNoRows { LogError(err) return } - _ = WsHub.pushAlerts(uids, alert) } diff --git a/common/attachments.go b/common/attachments.go index f3ee2afb..49b93322 100644 --- a/common/attachments.go +++ b/common/attachments.go @@ -29,7 +29,7 @@ type AttachmentStore interface { Add(sectionID int, sectionTable string, originID int, originTable string, uploadedBy int, path string, extra string) (int, error) MoveTo(sectionID int, originID int, originTable string) error MoveToByExtra(sectionID int, originTable string, extra string) error - GlobalCount() int + Count() int CountIn(originTable string, oid int) int CountInPath(path string) int Delete(aid int) error @@ -149,18 +149,18 @@ func (store *DefaultAttachmentStore) Add(sectionID int, sectionTable string, ori return int(lid), err } -func (store *DefaultAttachmentStore) MoveTo(sectionID int, originID int, originTable string) error { - _, err := store.move.Exec(sectionID, originID, originTable) +func (s *DefaultAttachmentStore) MoveTo(sectionID int, originID int, originTable string) error { + _, err := s.move.Exec(sectionID, originID, originTable) return err } -func (store *DefaultAttachmentStore) MoveToByExtra(sectionID int, originTable string, extra string) error { - _, err := store.moveByExtra.Exec(sectionID, originTable, extra) +func (s *DefaultAttachmentStore) MoveToByExtra(sectionID int, originTable string, extra string) error { + _, err := s.moveByExtra.Exec(sectionID, originTable, extra) return err } -func (store *DefaultAttachmentStore) GlobalCount() (count int) { - err := store.count.QueryRow().Scan(&count) +func (s *DefaultAttachmentStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) if err != nil { LogError(err) } diff --git a/common/audit_logs.go b/common/audit_logs.go index dda02669..125f4785 100644 --- a/common/audit_logs.go +++ b/common/audit_logs.go @@ -21,7 +21,7 @@ type LogItem struct { type LogStore interface { Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) - GlobalCount() int + Count() int GetOffset(offset int, perPage int) (logs []LogItem, err error) } @@ -40,17 +40,17 @@ func NewModLogStore(acc *qgen.Accumulator) (*SQLModLogStore, error) { } // TODO: Make a store for this? -func (store *SQLModLogStore) Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) { - _, err = store.create.Exec(action, elementID, elementType, ipaddress, actorID) +func (s *SQLModLogStore) Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) { + _, err = s.create.Exec(action, elementID, elementType, ipaddress, actorID) return err } -func (store *SQLModLogStore) GlobalCount() (logCount int) { - err := store.count.QueryRow().Scan(&logCount) +func (s *SQLModLogStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) if err != nil { LogError(err) } - return logCount + return count } func buildLogList(rows *sql.Rows) (logs []LogItem, err error) { @@ -91,21 +91,21 @@ func NewAdminLogStore(acc *qgen.Accumulator) (*SQLAdminLogStore, error) { } // TODO: Make a store for this? -func (store *SQLAdminLogStore) Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) { - _, err = store.create.Exec(action, elementID, elementType, ipaddress, actorID) +func (s *SQLAdminLogStore) Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) { + _, err = s.create.Exec(action, elementID, elementType, ipaddress, actorID) return err } -func (store *SQLAdminLogStore) GlobalCount() (logCount int) { - err := store.count.QueryRow().Scan(&logCount) +func (s *SQLAdminLogStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) if err != nil { LogError(err) } - return logCount + return count } -func (store *SQLAdminLogStore) GetOffset(offset int, perPage int) (logs []LogItem, err error) { - rows, err := store.getOffset.Query(offset, perPage) +func (s *SQLAdminLogStore) GetOffset(offset int, perPage int) (logs []LogItem, err error) { + rows, err := s.getOffset.Query(offset, perPage) if err != nil { return logs, err } diff --git a/common/cache.go b/common/cache.go index ea048c78..0fc6dbdd 100644 --- a/common/cache.go +++ b/common/cache.go @@ -14,7 +14,7 @@ type DataStore interface { DirtyGet(id int) interface{} Get(id int) (interface{}, error) BypassGet(id int) (interface{}, error) - //GlobalCount() + //Count() int } // nolint diff --git a/common/forum_store.go b/common/forum_store.go index 9fe0663f..49cb52e1 100644 --- a/common/forum_store.go +++ b/common/forum_store.go @@ -47,7 +47,7 @@ type ForumStore interface { Create(forumName string, forumDesc string, active bool, preset string) (int, error) UpdateOrder(updateMap map[int]int) error - GlobalCount() int + Count() int } type ForumCache interface { @@ -379,13 +379,13 @@ func (s *MemoryForumStore) Length() (length int) { } // TODO: Get the total count of forums in the forum store rather than doing a heavy query for this? -// GlobalCount returns the total number of forums -func (s *MemoryForumStore) GlobalCount() (fcount int) { - err := s.count.QueryRow().Scan(&fcount) +// Count returns the total number of forums +func (s *MemoryForumStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) if err != nil { LogError(err) } - return fcount + return count } // TODO: Work on SqlForumStore diff --git a/common/group_store.go b/common/group_store.go index edcadff0..c95f65e8 100644 --- a/common/group_store.go +++ b/common/group_store.go @@ -25,7 +25,7 @@ type GroupStore interface { GetAll() ([]*Group, error) GetRange(lower int, higher int) ([]*Group, error) Reload(id int) error // ? - Should we move this to GroupCache? It might require us to do some unnecessary casting though - GlobalCount() int + Count() int } type GroupCache interface { @@ -336,14 +336,14 @@ func (mgs *MemoryGroupStore) GetRange(lower int, higher int) (groups []*Group, e return groups, nil } -func (mgs *MemoryGroupStore) Length() int { - mgs.RLock() - defer mgs.RUnlock() - return mgs.groupCount +func (s *MemoryGroupStore) Length() int { + s.RLock() + defer s.RUnlock() + return s.groupCount } -func (mgs *MemoryGroupStore) GlobalCount() (count int) { - err := mgs.count.QueryRow().Scan(&count) +func (s *MemoryGroupStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) if err != nil { LogError(err) } diff --git a/common/misc_logs.go b/common/misc_logs.go index ea67ceb9..6230caae 100644 --- a/common/misc_logs.go +++ b/common/misc_logs.go @@ -55,7 +55,7 @@ func (log *RegLogItem) Create() (id int, err error) { } type RegLogStore interface { - GlobalCount() (logCount int) + Count() (count int) GetOffset(offset int, perPage int) (logs []RegLogItem, err error) } @@ -71,12 +71,12 @@ func NewRegLogStore(acc *qgen.Accumulator) (*SQLRegLogStore, error) { }, acc.FirstError() } -func (store *SQLRegLogStore) GlobalCount() (logCount int) { - err := store.count.QueryRow().Scan(&logCount) +func (s *SQLRegLogStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) if err != nil { LogError(err) } - return logCount + return count } func (store *SQLRegLogStore) GetOffset(offset int, perPage int) (logs []RegLogItem, err error) { @@ -142,8 +142,8 @@ func (log *LoginLogItem) Create() (id int, err error) { } type LoginLogStore interface { - GlobalCount() (logCount int) - Count(uid int) (logCount int) + Count() (count int) + CountUser(uid int) (count int) GetOffset(uid int, offset int, perPage int) (logs []LoginLogItem, err error) } @@ -161,20 +161,20 @@ func NewLoginLogStore(acc *qgen.Accumulator) (*SQLLoginLogStore, error) { }, acc.FirstError() } -func (store *SQLLoginLogStore) GlobalCount() (logCount int) { - err := store.count.QueryRow().Scan(&logCount) +func (s *SQLLoginLogStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) if err != nil { LogError(err) } - return logCount + return count } -func (store *SQLLoginLogStore) Count(uid int) (logCount int) { - err := store.countForUser.QueryRow(uid).Scan(&logCount) +func (s *SQLLoginLogStore) CountUser(uid int) (count int) { + err := s.countForUser.QueryRow(uid).Scan(&count) if err != nil { LogError(err) } - return logCount + return count } func (store *SQLLoginLogStore) GetOffset(uid int, offset int, perPage int) (logs []LoginLogItem, err error) { diff --git a/common/page_store.go b/common/page_store.go index 5965b3a4..d7a8db5e 100644 --- a/common/page_store.go +++ b/common/page_store.go @@ -72,7 +72,7 @@ var Pages PageStore // Holds the custom pages, but doesn't include the template pages in /pages/ which are a lot more flexible yet harder to use and which are too risky security-wise to make editable in the Control Panel type PageStore interface { - GlobalCount() (pageCount int) + Count() (count int) Get(id int) (*CustomPage, error) GetByName(name string) (*CustomPage, error) GetOffset(offset int, perPage int) (pages []*CustomPage, err error) @@ -99,15 +99,15 @@ func NewDefaultPageStore(acc *qgen.Accumulator) (*DefaultPageStore, error) { }, acc.FirstError() } -func (store *DefaultPageStore) GlobalCount() (pageCount int) { - err := store.count.QueryRow().Scan(&pageCount) +func (s *DefaultPageStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) if err != nil { LogError(err) } - return pageCount + return count } -func (store *DefaultPageStore) parseAllowedGroups(raw string, page *CustomPage) error { +func (s *DefaultPageStore) parseAllowedGroups(raw string, page *CustomPage) error { if raw == "" { return nil } diff --git a/common/pages.go b/common/pages.go index 37c1a7ce..94261c59 100644 --- a/common/pages.go +++ b/common/pages.go @@ -594,6 +594,34 @@ type PanelRegLogsPage struct { Paginator } +type DebugPageCache struct { + Topics int + Users int + Replies int + + TCap int + UCap int + RCap int + + TopicListThaw bool +} + +type DebugPageDatabase struct { + Topics int + Users int + Replies int + ProfileReplies int + ActivityStream int +} + +type DebugPageDisk struct { + Static int + Attachments int + Avatars int + Logs int + Backups int +} + type PanelDebugPage struct { *BasePanelPage GoVersion string @@ -607,15 +635,9 @@ type PanelDebugPage struct { CPUs int MemStats runtime.MemStats - TCache int - UCache int - RCache int - - TCap int - UCap int - RCap int - - TopicListThaw bool + Cache DebugPageCache + Database DebugPageDatabase + Disk DebugPageDisk } type PageSimple struct { diff --git a/common/poll_store.go b/common/poll_store.go index 146a7ecc..cc23b69f 100644 --- a/common/poll_store.go +++ b/common/poll_store.go @@ -51,7 +51,7 @@ type PollStore interface { Create(parent Pollable, pollType int, pollOptions map[int]string) (int, error) CastVote(optionIndex int, pollID int, uid int, ipaddress string) error Reload(id int) error - //GlobalCount() int + //Count() int SetCache(cache PollCache) GetCache() PollCache @@ -68,7 +68,7 @@ type DefaultPollStore struct { incrementVoteCount *sql.Stmt incrementVoteCountForOption *sql.Stmt delete *sql.Stmt - //pollCount *sql.Stmt + //count *sql.Stmt } func NewDefaultPollStore(cache PollCache) (*DefaultPollStore, error) { @@ -86,7 +86,7 @@ func NewDefaultPollStore(cache PollCache) (*DefaultPollStore, error) { addVote: acc.Insert("polls_votes").Columns("pollID, uid, option, castAt, ipaddress").Fields("?,?,?,UTC_TIMESTAMP(),?").Prepare(), incrementVoteCount: acc.Update("polls").Set("votes = votes + 1").Where("pollID = ?").Prepare(), incrementVoteCountForOption: acc.Update("polls_options").Set("votes = votes + 1").Where("option = ? AND pollID = ?").Prepare(), - //pollCount: acc.SimpleCount("polls", "", ""), + //count: acc.SimpleCount("polls", "", ""), }, acc.FirstError() } diff --git a/common/profile_reply_store.go b/common/profile_reply_store.go index 9b2c99f5..3483a262 100644 --- a/common/profile_reply_store.go +++ b/common/profile_reply_store.go @@ -3,7 +3,7 @@ package common import ( "database/sql" - "github.com/Azareal/Gosora/query_gen" + qgen "github.com/Azareal/Gosora/query_gen" ) var Prstore ProfileReplyStore @@ -11,6 +11,7 @@ var Prstore ProfileReplyStore type ProfileReplyStore interface { Get(id int) (*ProfileReply, error) Create(profileID int, content string, createdBy int, ipaddress string) (id int, err error) + Count() (count int) } // TODO: Refactor this to stop using the global stmt store @@ -18,23 +19,25 @@ type ProfileReplyStore interface { type SQLProfileReplyStore struct { get *sql.Stmt create *sql.Stmt + count *sql.Stmt } func NewSQLProfileReplyStore(acc *qgen.Accumulator) (*SQLProfileReplyStore, error) { return &SQLProfileReplyStore{ get: acc.Select("users_replies").Columns("uid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress").Where("rid = ?").Prepare(), create: acc.Insert("users_replies").Columns("uid, content, parsed_content, createdAt, createdBy, ipaddress").Fields("?,?,?,UTC_TIMESTAMP(),?,?").Prepare(), + count: acc.Count("users_replies").Prepare(), }, acc.FirstError() } -func (store *SQLProfileReplyStore) Get(id int) (*ProfileReply, error) { - reply := ProfileReply{ID: id} - err := store.get.QueryRow(id).Scan(&reply.ParentID, &reply.Content, &reply.CreatedBy, &reply.CreatedAt, &reply.LastEdit, &reply.LastEditBy, &reply.IPAddress) - return &reply, err +func (s *SQLProfileReplyStore) Get(id int) (*ProfileReply, error) { + r := ProfileReply{ID: id} + err := s.get.QueryRow(id).Scan(&r.ParentID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.IPAddress) + return &r, err } -func (store *SQLProfileReplyStore) Create(profileID int, content string, createdBy int, ipaddress string) (id int, err error) { - res, err := store.create.Exec(profileID, content, ParseMessage(content, 0, ""), createdBy, ipaddress) +func (s *SQLProfileReplyStore) Create(profileID int, content string, createdBy int, ipaddress string) (id int, err error) { + res, err := s.create.Exec(profileID, content, ParseMessage(content, 0, ""), createdBy, ipaddress) if err != nil { return 0, err } @@ -42,7 +45,16 @@ func (store *SQLProfileReplyStore) Create(profileID int, content string, created if err != nil { return 0, err } - // Should we reload the user? return int(lastID), err } + +// TODO: Write a test for this +// Count returns the total number of topic replies on these forums +func (s *SQLProfileReplyStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) + if err != nil { + LogError(err) + } + return count +} diff --git a/common/reply_store.go b/common/reply_store.go index e0c08af4..d73a7b67 100644 --- a/common/reply_store.go +++ b/common/reply_store.go @@ -9,6 +9,7 @@ var Rstore ReplyStore type ReplyStore interface { Get(id int) (*Reply, error) Create(topic *Topic, content string, ipaddress string, uid int) (id int, err error) + Count() (count int) SetCache(cache ReplyCache) GetCache() ReplyCache @@ -19,6 +20,7 @@ type SQLReplyStore struct { get *sql.Stmt create *sql.Stmt + count *sql.Stmt } func NewSQLReplyStore(acc *qgen.Accumulator, cache ReplyCache) (*SQLReplyStore, error) { @@ -29,22 +31,22 @@ func NewSQLReplyStore(acc *qgen.Accumulator, cache ReplyCache) (*SQLReplyStore, cache: cache, get: acc.Select("replies").Columns("tid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount, attachCount, actionType").Where("rid = ?").Prepare(), create: acc.Insert("replies").Columns("tid, content, parsed_content, createdAt, lastUpdated, ipaddress, words, createdBy").Fields("?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?").Prepare(), + count: acc.Count("replies").Prepare(), }, acc.FirstError() } func (s *SQLReplyStore) Get(id int) (*Reply, error) { - //log.Print("SQLReplyStore.Get") - reply, err := s.cache.Get(id) + r, err := s.cache.Get(id) if err == nil { - return reply, nil + return r, nil } - reply = &Reply{ID: id} - err = s.get.QueryRow(id).Scan(&reply.ParentID, &reply.Content, &reply.CreatedBy, &reply.CreatedAt, &reply.LastEdit, &reply.LastEditBy, &reply.IPAddress, &reply.LikeCount, &reply.AttachCount, &reply.ActionType) + r = &Reply{ID: id} + err = s.get.QueryRow(id).Scan(&r.ParentID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.IPAddress, &r.LikeCount, &r.AttachCount, &r.ActionType) if err == nil { - _ = s.cache.Set(reply) + _ = s.cache.Set(r) } - return reply, err + return r, err } // TODO: Write a test for this @@ -62,6 +64,16 @@ func (s *SQLReplyStore) Create(topic *Topic, content string, ipaddress string, u return int(lastID), topic.AddReply(int(lastID), uid) } +// TODO: Write a test for this +// Count returns the total number of topic replies on these forums +func (s *SQLReplyStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) + if err != nil { + LogError(err) + } + return count +} + func (s *SQLReplyStore) SetCache(cache ReplyCache) { s.cache = cache } diff --git a/common/routes_common.go b/common/routes_common.go index 522105e0..a74e234f 100644 --- a/common/routes_common.go +++ b/common/routes_common.go @@ -138,10 +138,10 @@ func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (header //h.Set("Content-Security-Policy", "default-src 'self'") // TODO: GDPR. Add a global control panel notice warning the admins of staff members who don't have 2FA enabled - stats.Users = Users.GlobalCount() - stats.Groups = Groups.GlobalCount() - stats.Forums = Forums.GlobalCount() - stats.Pages = Pages.GlobalCount() + stats.Users = Users.Count() + stats.Groups = Groups.Count() + stats.Forums = Forums.Count() + stats.Pages = Pages.Count() stats.Settings = len(header.Settings) stats.WordFilters = WordFilters.EstCount() stats.Themes = len(Themes) diff --git a/common/template_init.go b/common/template_init.go index f4108a40..fe37aa39 100644 --- a/common/template_init.go +++ b/common/template_init.go @@ -745,6 +745,8 @@ func initDefaultTmplFuncMap() { byteFloat, unit = ConvertByteUnit(float64(bytes)) case uint64: byteFloat, unit = ConvertByteUnit(float64(bytes)) + case float64: + byteFloat, unit = ConvertByteUnit(bytes) default: panic("bytes is not an int, int64 or uint64") } diff --git a/common/theme.go b/common/theme.go index 4e72b436..21efe8a5 100644 --- a/common/theme.go +++ b/common/theme.go @@ -23,6 +23,7 @@ import ( ) var ErrNoDefaultTheme = errors.New("The default theme isn't registered in the system") +var ErrBadDefaultTemplate = errors.New("The template you tried to load doesn't exist in the interpreted pool.") type Theme struct { Path string // Redirect this file to another folder @@ -312,6 +313,9 @@ func (theme *Theme) RunTmpl(template string, pi interface{}, w io.Writer) error if !ok { mapping = template } + if theme.IntTmplHandle.Lookup(mapping+".html") == nil { + return ErrBadDefaultTemplate + } return theme.IntTmplHandle.ExecuteTemplate(w, mapping+".html", pi) default: log.Print("theme ", theme) diff --git a/common/topic_store.go b/common/topic_store.go index 1ee350f1..41824fda 100644 --- a/common/topic_store.go +++ b/common/topic_store.go @@ -36,7 +36,7 @@ type TopicStore interface { // TODO: Implement these two methods //Replies(tid int) ([]*Reply, error) //RepliesRange(tid int, lower int, higher int) ([]*Reply, error) - GlobalCount() int + Count() int SetCache(cache TopicCache) GetCache() TopicCache @@ -47,7 +47,7 @@ type DefaultTopicStore struct { get *sql.Stmt exists *sql.Stmt - topicCount *sql.Stmt + count *sql.Stmt create *sql.Stmt } @@ -61,7 +61,7 @@ func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) { cache: cache, get: acc.Select("topics").Columns("title, content, createdBy, createdAt, lastReplyBy, lastReplyAt, lastReplyID, is_closed, sticky, parentID, ipaddress, views, postCount, likeCount, attachCount, poll, data").Where("tid = ?").Prepare(), exists: acc.Select("topics").Columns("tid").Where("tid = ?").Prepare(), - topicCount: acc.Count("topics").Prepare(), + count: acc.Count("topics").Prepare(), create: acc.Insert("topics").Columns("parentID, title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, ipaddress, words, createdBy").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?").Prepare(), }, acc.FirstError() } @@ -239,13 +239,13 @@ func (mts *DefaultTopicStore) AddLastTopic(item *Topic, fid int) error { return nil } -// GlobalCount returns the total number of topics on these forums -func (mts *DefaultTopicStore) GlobalCount() (tcount int) { - err := mts.topicCount.QueryRow().Scan(&tcount) +// Count returns the total number of topics on these forums +func (s *DefaultTopicStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) if err != nil { LogError(err) } - return tcount + return count } func (mts *DefaultTopicStore) SetCache(cache TopicCache) { diff --git a/common/user_store.go b/common/user_store.go index 58e53b26..7a15620e 100644 --- a/common/user_store.go +++ b/common/user_store.go @@ -26,7 +26,7 @@ type UserStore interface { BypassGet(id int) (*User, error) Create(username string, password string, email string, group int, active bool) (int, error) Reload(id int) error - GlobalCount() int + Count() int SetCache(cache UserCache) GetCache() UserCache @@ -41,7 +41,7 @@ type DefaultUserStore struct { exists *sql.Stmt register *sql.Stmt usernameExists *sql.Stmt - userCount *sql.Stmt + count *sql.Stmt } // NewDefaultUserStore gives you a new instance of DefaultUserStore @@ -59,7 +59,7 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) { exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""), register: acc.Insert("users").Columns("name, email, password, salt, group, is_super_admin, session, active, message, createdAt, lastActiveAt, lastLiked, oldestItemLikedCreatedAt").Fields("?,?,?,?,?,0,'',?,'',UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(), // TODO: Implement user_count on users_groups here usernameExists: acc.SimpleSelect("users", "name", "name = ?", "", ""), - userCount: acc.Count("users").Prepare(), + count: acc.Count("users").Prepare(), }, acc.FirstError() } @@ -282,24 +282,24 @@ func (mus *DefaultUserStore) Create(username string, password string, email stri return int(lastID), err } -// GlobalCount returns the total number of users registered on the forums -func (mus *DefaultUserStore) GlobalCount() (ucount int) { - err := mus.userCount.QueryRow().Scan(&ucount) +// Count returns the total number of users registered on the forums +func (s *DefaultUserStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) if err != nil { LogError(err) } - return ucount + return count } -func (mus *DefaultUserStore) SetCache(cache UserCache) { - mus.cache = cache +func (s *DefaultUserStore) SetCache(cache UserCache) { + s.cache = cache } // TODO: We're temporarily doing this so that you can do ucache != nil in getTopicUser. Refactor it. -func (mus *DefaultUserStore) GetCache() UserCache { - _, ok := mus.cache.(*NullUserCache) +func (s *DefaultUserStore) GetCache() UserCache { + _, ok := s.cache.(*NullUserCache) if ok { return nil } - return mus.cache + return s.cache } diff --git a/common/utils.go b/common/utils.go index 397fc532..b24bfa4b 100644 --- a/common/utils.go +++ b/common/utils.go @@ -10,6 +10,7 @@ import ( "crypto/rand" "encoding/base32" "encoding/base64" + "path/filepath" "errors" "fmt" "html" @@ -509,3 +510,17 @@ func BuildSlug(slug string, id int) string { } return slug + "." + strconv.Itoa(id) } + +func DirSize(path string) (int, error) { + var size int64 + err := filepath.Walk(path, func(_ string, file os.FileInfo, err error) error { + if err != nil { + return err + } + if !file.IsDir() { + size += file.Size() + } + return err + }) + return int(size), err +} \ No newline at end of file diff --git a/common/widget.go b/common/widget.go index 6c332df5..e721b66a 100644 --- a/common/widget.go +++ b/common/widget.go @@ -4,6 +4,7 @@ import ( "database/sql" "encoding/json" "strings" + "strconv" "sync/atomic" "github.com/Azareal/Gosora/query_gen" @@ -76,11 +77,22 @@ func (widget *Widget) Copy() (owidget *Widget) { // TODO: Test this // TODO: Add support for zone:id. Perhaps, carry a ZoneID property around in *Header? It might allow some weirdness like frontend[5] which matches any zone with an ID of 5 but it would be a tad faster than verifying each zone, although it might be problematic if users end up relying on this behaviour for areas which don't pass IDs to the widgets system but *probably* should -func (widget *Widget) Allowed(zone string) bool { +// TODO: Add a selector which also matches topics inside a specific forum? +func (widget *Widget) Allowed(zone string, zoneid int) bool { for _, loc := range strings.Split(widget.Location, "|") { + if len(loc) == 0 { + continue + } + sloc := strings.Split(":",loc) + if len(sloc) > 1 { + iloc, _ := strconv.Atoi(sloc[1]) + if zoneid != 0 && iloc != zoneid { + continue + } + } if loc == "global" || loc == zone { return true - } else if len(loc) > 0 && loc[0] == '!' { + } else if loc[0] == '!' { loc = loc[1:] if loc != "global" && loc != zone { return true diff --git a/common/widgets.go b/common/widgets.go index fa9314bc..f8378ba0 100644 --- a/common/widgets.go +++ b/common/widgets.go @@ -166,7 +166,7 @@ func HasWidgets(dock string, header *Header) bool { if !widget.Enabled { continue } - if widget.Allowed(header.Zone) { + if widget.Allowed(header.Zone,header.ZoneID) { wcount++ } } @@ -210,7 +210,7 @@ func BuildWidget(dock string, header *Header) (sbody string) { if !widget.Enabled { continue } - if widget.Allowed(header.Zone) { + if widget.Allowed(header.Zone,header.ZoneID) { item, err := widget.Build(header) if err != nil { LogError(err) diff --git a/common/word_filters.go b/common/word_filters.go index e22d7ec9..cb68ee7d 100644 --- a/common/word_filters.go +++ b/common/word_filters.go @@ -24,7 +24,7 @@ type WordFilterStore interface { Update(id int, find string, replacement string) error Length() int EstCount() int - GlobalCount() (count int) + Count() (count int) } type DefaultWordFilterStore struct { @@ -53,9 +53,9 @@ func NewDefaultWordFilterStore(acc *qgen.Accumulator) (*DefaultWordFilterStore, } // ReloadAll drops all the items in the memory cache and replaces them with fresh copies from the database -func (store *DefaultWordFilterStore) ReloadAll() error { +func (s *DefaultWordFilterStore) ReloadAll() error { var wordFilters = make(map[int]*WordFilter) - filters, err := store.bypassGetAll() + filters, err := s.bypassGetAll() if err != nil { return err } @@ -64,13 +64,13 @@ func (store *DefaultWordFilterStore) ReloadAll() error { wordFilters[filter.ID] = filter } - store.box.Store(wordFilters) + s.box.Store(wordFilters) return nil } // ? - Return pointers to word filters intead to save memory? -- A map is a pointer. -func (store *DefaultWordFilterStore) bypassGetAll() (filters []*WordFilter, err error) { - rows, err := store.getAll.Query() +func (s *DefaultWordFilterStore) bypassGetAll() (filters []*WordFilter, err error) { + rows, err := s.getAll.Query() if err != nil { return nil, err } @@ -88,49 +88,49 @@ func (store *DefaultWordFilterStore) bypassGetAll() (filters []*WordFilter, err } // GetAll returns all of the word filters in a map. Do note mutate this map (or maps returned from any store not explicitly noted as copies) as multiple threads may be accessing it at once -func (store *DefaultWordFilterStore) GetAll() (filters map[int]*WordFilter, err error) { - return store.box.Load().(map[int]*WordFilter), nil +func (s *DefaultWordFilterStore) GetAll() (filters map[int]*WordFilter, err error) { + return s.box.Load().(map[int]*WordFilter), nil } // Create adds a new word filter to the database and refreshes the memory cache -func (store *DefaultWordFilterStore) Create(find string, replacement string) error { - _, err := store.create.Exec(find, replacement) +func (s *DefaultWordFilterStore) Create(find string, replacement string) error { + _, err := s.create.Exec(find, replacement) if err != nil { return err } - return store.ReloadAll() + return s.ReloadAll() } // Delete removes a word filter from the database and refreshes the memory cache -func (store *DefaultWordFilterStore) Delete(id int) error { - _, err := store.delete.Exec(id) +func (s *DefaultWordFilterStore) Delete(id int) error { + _, err := s.delete.Exec(id) if err != nil { return err } - return store.ReloadAll() + return s.ReloadAll() } -func (store *DefaultWordFilterStore) Update(id int, find string, replacement string) error { - _, err := store.update.Exec(find, replacement, id) +func (s *DefaultWordFilterStore) Update(id int, find string, replacement string) error { + _, err := s.update.Exec(find, replacement, id) if err != nil { return err } - return store.ReloadAll() + return s.ReloadAll() } // Length gets the number of word filters currently in memory, for the DefaultWordFilterStore, this should be all of them -func (store *DefaultWordFilterStore) Length() int { - return len(store.box.Load().(map[int]*WordFilter)) +func (s *DefaultWordFilterStore) Length() int { + return len(s.box.Load().(map[int]*WordFilter)) } // EstCount provides the same result as Length(), intended for alternate implementations of WordFilterStore, so that Length is the number of items in cache, if only a subset is held there and EstCount is the total count -func (store *DefaultWordFilterStore) EstCount() int { - return len(store.box.Load().(map[int]*WordFilter)) +func (s *DefaultWordFilterStore) EstCount() int { + return len(s.box.Load().(map[int]*WordFilter)) } -// GlobalCount gets the total number of word filters directly from the database -func (store *DefaultWordFilterStore) GlobalCount() (count int) { - err := store.count.QueryRow().Scan(&count) +// Count gets the total number of word filters directly from the database +func (s *DefaultWordFilterStore) Count() (count int) { + err := s.count.QueryRow().Scan(&count) if err != nil { LogError(err) } diff --git a/main.go b/main.go index 69a99f63..1de9775e 100644 --- a/main.go +++ b/main.go @@ -243,6 +243,10 @@ func storeInit() (err error) { if err != nil { return errors.WithStack(err) } + c.Activity, err = c.NewDefaultActivityStream(acc) + if err != nil { + return errors.WithStack(err) + } // TODO: Let the admin choose other thumbnailers, maybe ones defined in plugins c.Thumbnailer = c.NewCaireThumbnailer() @@ -486,7 +490,7 @@ func main() { // TODO: Add a LastRequested field to cached User structs to avoid evicting the same things which wind up getting loaded again anyway? if ucache != nil { ucap := ucache.GetCapacity() - if ucache.Length() <= ucap || c.Users.GlobalCount() <= ucap { + if ucache.Length() <= ucap || c.Users.Count() <= ucap { couldNotDealloc = false continue } diff --git a/misc_test.go b/misc_test.go index 20bf03bd..85ef51f5 100644 --- a/misc_test.go +++ b/misc_test.go @@ -164,7 +164,7 @@ func userStoreTest(t *testing.T, newUserID int) { expect(t, !c.Users.Exists(newUserID), fmt.Sprintf("UID #%d shouldn't exist", newUserID)) expect(t, isCacheLengthZero(ucache), fmt.Sprintf("User cache length should be 0, not %d", cacheLength(ucache))) - expectIntToBeX(t, c.Users.GlobalCount(), 1, "The number of users should be one, not %d") + expectIntToBeX(t, c.Users.Count(), 1, "The number of users should be one, not %d") var awaitingActivation = 5 // TODO: Write tests for the registration validators @@ -452,7 +452,7 @@ func topicStoreTest(t *testing.T, newID int) { ok = c.Topics.Exists(1) expect(t, ok, "TID #1 should exist") - count := c.Topics.GlobalCount() + count := c.Topics.Count() expect(t, count == 1, fmt.Sprintf("Global count for topics should be 1, not %d", count)) //Create(fid int, topicName string, content string, uid int, ipaddress string) (tid int, err error) @@ -461,7 +461,7 @@ func topicStoreTest(t *testing.T, newID int) { expect(t, tid == newID, fmt.Sprintf("TID for the new topic should be %d, not %d", newID, tid)) expect(t, c.Topics.Exists(newID), fmt.Sprintf("TID #%d should exist", newID)) - count = c.Topics.GlobalCount() + count = c.Topics.Count() expect(t, count == 2, fmt.Sprintf("Global count for topics should be 2, not %d", count)) var iFrag = func(cond bool) string { @@ -541,7 +541,7 @@ func TestForumStore(t *testing.T) { fcache, ok := c.Forums.(c.ForumCache) expect(t, ok, "Unable to cast ForumStore to ForumCache") - expect(t, c.Forums.GlobalCount() == 2, "The forumstore global count should be 2") + expect(t, c.Forums.Count() == 2, "The forumstore global count should be 2") expect(t, fcache.Length() == 2, "The forum cache length should be 2") _, err := c.Forums.Get(-1) @@ -601,7 +601,7 @@ func TestForumStore(t *testing.T) { expect(t, fid == 3, "The first forum we create should have an ID of 3") expect(t, c.Forums.Exists(3), "FID #2 should exist") - expect(t, c.Forums.GlobalCount() == 3, "The forumstore global count should be 3") + expect(t, c.Forums.Count() == 3, "The forumstore global count should be 3") expect(t, fcache.Length() == 3, "The forum cache length should be 3") forum, err = c.Forums.Get(3) @@ -618,7 +618,7 @@ func TestForumStore(t *testing.T) { expectNilErr(t, c.Forums.Delete(3)) expect(t, forum.ID == 3, fmt.Sprintf("forum pointer shenanigans")) - expect(t, c.Forums.GlobalCount() == 2, "The forumstore global count should be 2") + expect(t, c.Forums.Count() == 2, "The forumstore global count should be 2") expect(t, fcache.Length() == 2, "The forum cache length should be 2") expect(t, !c.Forums.Exists(3), "FID #3 shouldn't exist after being deleted") _, err = c.Forums.Get(3) @@ -938,10 +938,37 @@ func TestProfileReplyStore(t *testing.T) { // TODO: Test profileReply.SetBody() and profileReply.Creator() } +func TestActivityStream(t *testing.T) { + miscinit(t) + + expect(t,c.Activity.Count()==0,"activity stream count should be 0") + + _, err := c.Activity.Get(-1) + recordMustNotExist(t, err, "activity item -1 shouldn't exist") + _, err = c.Activity.Get(0) + recordMustNotExist(t, err, "activity item 0 shouldn't exist") + _, err = c.Activity.Get(1) + recordMustNotExist(t, err, "activity item 1 shouldn't exist") + + a := c.Alert{ActorID: 1, TargetUserID: 1, Event: "like", ElementType: "topic", ElementID: 1} + id, err := c.Activity.Add(a) + expectNilErr(t,err) + expect(t,id==1,"new activity item id should be 1") + + expect(t,c.Activity.Count()==1,"activity stream count should be 1") + alert, err := c.Activity.Get(1) + expectNilErr(t,err) + expect(t,alert.ActorID==1,"alert actorid should be 1") + expect(t,alert.TargetUserID==1,"alert targetuserid should be 1") + expect(t,alert.Event=="like","alert event type should be like") + expect(t,alert.ElementType=="topic","alert element type should be topic") + expect(t,alert.ElementID==1,"alert element id should be 1") +} + func TestLogs(t *testing.T) { miscinit(t) gTests := func(store c.LogStore, phrase string) { - expect(t, store.GlobalCount() == 0, "There shouldn't be any "+phrase) + expect(t, store.Count() == 0, "There shouldn't be any "+phrase) logs, err := store.GetOffset(0, 25) expectNilErr(t, err) expect(t, len(logs) == 0, "The log slice should be empty") @@ -952,8 +979,8 @@ func TestLogs(t *testing.T) { gTests2 := func(store c.LogStore, phrase string) { err := store.Create("something", 0, "bumblefly", "::1", 1) expectNilErr(t, err) - count := store.GlobalCount() - expect(t, count == 1, fmt.Sprintf("store.GlobalCount should return one, not %d", count)) + count := store.Count() + expect(t, count == 1, fmt.Sprintf("store.Count should return one, not %d", count)) logs, err := store.GetOffset(0, 25) recordMustExist(t, err, "We should have at-least one "+phrase) expect(t, len(logs) == 1, "The length of the log slice should be one") @@ -1159,7 +1186,7 @@ func TestWordFilters(t *testing.T) { // TODO: Test the word filters and their store expect(t, c.WordFilters.Length() == 0, "Word filter list should be empty") expect(t, c.WordFilters.EstCount() == 0, "Word filter list should be empty") - expect(t, c.WordFilters.GlobalCount() == 0, "Word filter list should be empty") + expect(t, c.WordFilters.Count() == 0, "Word filter list should be empty") filters, err := c.WordFilters.GetAll() expectNilErr(t, err) // TODO: Slightly confusing that we don't get ErrNoRow here expect(t, len(filters) == 0, "Word filter map should be empty") @@ -1169,7 +1196,7 @@ func TestWordFilters(t *testing.T) { expectNilErr(t, err) expect(t, c.WordFilters.Length() == 1, "Word filter list should not be empty") expect(t, c.WordFilters.EstCount() == 1, "Word filter list should not be empty") - expect(t, c.WordFilters.GlobalCount() == 1, "Word filter list should not be empty") + expect(t, c.WordFilters.Count() == 1, "Word filter list should not be empty") filters, err = c.WordFilters.GetAll() expectNilErr(t, err) expect(t, len(filters) == 1, "Word filter map should not be empty") diff --git a/plugin_heythere.go b/plugin_heythere.go index c9f75c68..04f03b6c 100644 --- a/plugin_heythere.go +++ b/plugin_heythere.go @@ -6,7 +6,7 @@ func init() { c.Plugins.Add(&c.Plugin{UName: "heythere", Name: "Hey There", Author: "Azareal", URL: "https://github.com/Azareal", Init: initHeythere, Deactivate: deactivateHeythere}) } -// init_heythere is separate from init() as we don't want the plugin to run if the plugin is disabled +// initHeythere is separate from init() as we don't want the plugin to run if the plugin is disabled func initHeythere(plugin *c.Plugin) error { plugin.AddHook("topic_reply_row_assign", heythereReply) return nil diff --git a/routes/account.go b/routes/account.go index 8449a968..072000de 100644 --- a/routes/account.go +++ b/routes/account.go @@ -714,7 +714,7 @@ func AccountEditEmailTokenSubmit(w http.ResponseWriter, r *http.Request, user c. func AccountLogins(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError { accountEditHead("account_logins", w, r, &user, header) - logCount := c.LoginLogs.Count(user.ID) + logCount := c.LoginLogs.CountUser(user.ID) page, _ := strconv.Atoi(r.FormValue("page")) perPage := 12 offset, page, lastPage := c.PageOffset(logCount, page, perPage) diff --git a/routes/common.go b/routes/common.go index da7c18d5..14596ef1 100644 --- a/routes/common.go +++ b/routes/common.go @@ -111,27 +111,27 @@ func FootHeaders(w http.ResponseWriter, header *c.Header) { } } -func renderTemplate3(tmplName string, hookName string, w http.ResponseWriter, r *http.Request, header *c.Header, pi interface{}) error { - c.PrepResources(&header.CurrentUser, header, header.Theme) - if header.CurrentUser.Loggedin { - header.MetaDesc = "" - header.OGDesc = "" - } else if header.MetaDesc != "" && header.OGDesc == "" { - header.OGDesc = header.MetaDesc +func renderTemplate3(tmplName string, hookName string, w http.ResponseWriter, r *http.Request, h *c.Header, pi interface{}) error { + c.PrepResources(&h.CurrentUser, h, h.Theme) + if h.CurrentUser.Loggedin { + h.MetaDesc = "" + h.OGDesc = "" + } else if h.MetaDesc != "" && h.OGDesc == "" { + h.OGDesc = h.MetaDesc } - header.AddScript("global.js") - if header.CurrentUser.Loggedin { - header.AddScriptAsync("member.js") + h.AddScript("global.js") + if h.CurrentUser.Loggedin { + h.AddScriptAsync("member.js") } - FootHeaders(w, header) - if header.CurrentUser.IsAdmin { - header.Elapsed1 = time.Since(header.StartedAt).String() + FootHeaders(w, h) + if h.CurrentUser.IsAdmin { + h.Elapsed1 = time.Since(h.StartedAt).String() } - if c.RunPreRenderHook("pre_render_"+hookName, w, r, &header.CurrentUser, pi) { + if c.RunPreRenderHook("pre_render_"+hookName, w, r, &h.CurrentUser, pi) { return nil } - return header.Theme.RunTmpl(tmplName, pi, w) + return h.Theme.RunTmpl(tmplName, pi, w) } // TODO: Rename renderTemplate to RenderTemplate instead of using this hack to avoid breaking things diff --git a/routes/forum.go b/routes/forum.go index 4b5bd1ce..3dd16c38 100644 --- a/routes/forum.go +++ b/routes/forum.go @@ -124,11 +124,16 @@ func ViewForum(w http.ResponseWriter, r *http.Request, user c.User, header *c.He pageList := c.Paginate(forum.TopicCount, c.Config.ItemsPerPage, 5) pi := c.ForumPage{header, topicList, forum, c.Paginator{pageList, page, lastPage}} - var tmpl = forum.Tmpl + tmpl := forum.Tmpl if tmpl == "" { - tmpl = "forum" + ferr = renderTemplate("forum", w, r, header, pi) + } else { + tmpl = "forum_"+tmpl + err = renderTemplate3(tmpl, tmpl,w, r, header, pi) + if err != nil { + ferr = renderTemplate("forum", w, r, header, pi) + } } - ferr = renderTemplate(tmpl, w, r, header, pi) counters.ForumViewCounter.Bump(forum.ID) return ferr } diff --git a/routes/misc.go b/routes/misc.go index b1b69fe1..01502c10 100644 --- a/routes/misc.go +++ b/routes/misc.go @@ -13,7 +13,7 @@ import ( "github.com/Azareal/Gosora/common/phrases" ) -var cacheControlMaxAge = "max-age=" + strconv.Itoa(int(c.Day)) // TODO: Make this a c.Config value +var cacheControlMaxAge = "max-age=" + strconv.Itoa(int(c.Day)) // TODO: Make this a c.Config value var cacheControlMaxAgeWeek = "max-age=" + strconv.Itoa(int(c.Week)) // TODO: Make this a c.Config value // GET functions @@ -68,15 +68,16 @@ func CustomPage(w http.ResponseWriter, r *http.Request, user c.User, header *c.H } else if err != sql.ErrNoRows { return c.InternalError(err, w, r) } - - // ! Is this safe? - if c.DefaultTemplates.Lookup("page_"+name+".html") == nil { - return c.NotFound(w, r, header) - } - header.Title = phrases.GetTitlePhrase("page") + // TODO: Pass the page name to the pre-render hook? - return renderTemplate2("page_"+name, "tmpl_page", w, r, header, c.Page{header, tList, nil}) + err = renderTemplate3("page_"+name, "tmpl_page", w, r, header, c.Page{header, tList, nil}) + if err == c.ErrBadDefaultTemplate { + return c.NotFound(w, r, header) + } else if err != nil { + return c.InternalError(err, w, r) + } + return nil } // TODO: Set the cookie domain diff --git a/routes/panel/debug.go b/routes/panel/debug.go index 8b6ab7fb..a92dc207 100644 --- a/routes/panel/debug.go +++ b/routes/panel/debug.go @@ -60,6 +60,31 @@ func Debug(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { } topicListThawed := c.TopicListThaw.Thawed() - pi := c.PanelDebugPage{basePage, goVersion, dbVersion, uptime, openConnCount, qgen.Builder.GetAdapter().GetName(), goroutines, cpus, memStats, tlen, ulen, rlen, tcap, ucap, rcap, topicListThawed} + debugCache := c.DebugPageCache{tlen, ulen, rlen, tcap, ucap, rcap, topicListThawed} + debugDatabase := c.DebugPageDatabase{c.Topics.Count(),c.Users.Count(),c.Rstore.Count(),c.Prstore.Count(),c.Activity.Count()} + + staticSize, err := c.DirSize("./public/") + if err != nil { + return c.InternalError(err,w,r) + } + attachSize, err := c.DirSize("./attachs/") + if err != nil { + return c.InternalError(err,w,r) + } + uploadsSize, err := c.DirSize("./uploads/") + if err != nil { + return c.InternalError(err,w,r) + } + logsSize, err := c.DirSize("./logs/") + if err != nil { + return c.InternalError(err,w,r) + } + backupsSize, err := c.DirSize("./backups/") + if err != nil { + return c.InternalError(err,w,r) + } + debugDisk := c.DebugPageDisk{staticSize,attachSize,uploadsSize,logsSize,backupsSize} + + pi := c.PanelDebugPage{basePage, goVersion, dbVersion, uptime, openConnCount, qgen.Builder.GetAdapter().GetName(), goroutines, cpus, memStats, debugCache, debugDatabase, debugDisk} return renderTemplate("panel", w, r, basePage.Header, c.Panel{basePage, "panel_dashboard_right", "debug_page", "panel_debug", pi}) } diff --git a/routes/panel/logs.go b/routes/panel/logs.go index 8d0ded09..b909b6a5 100644 --- a/routes/panel/logs.go +++ b/routes/panel/logs.go @@ -18,7 +18,7 @@ func LogsRegs(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError return ferr } - logCount := c.RegLogs.GlobalCount() + logCount := c.RegLogs.Count() page, _ := strconv.Atoi(r.FormValue("page")) perPage := 10 offset, page, lastPage := c.PageOffset(logCount, page, perPage) @@ -107,7 +107,7 @@ func LogsMod(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { return ferr } - logCount := c.ModLogs.GlobalCount() + logCount := c.ModLogs.Count() page, _ := strconv.Atoi(r.FormValue("page")) perPage := 10 offset, page, lastPage := c.PageOffset(logCount, page, perPage) @@ -134,7 +134,7 @@ func LogsAdmin(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError return ferr } - logCount := c.ModLogs.GlobalCount() + logCount := c.ModLogs.Count() page, _ := strconv.Atoi(r.FormValue("page")) perPage := 10 offset, page, lastPage := c.PageOffset(logCount, page, perPage) diff --git a/routes/panel/pages.go b/routes/panel/pages.go index 54324f2c..e1a08ab1 100644 --- a/routes/panel/pages.go +++ b/routes/panel/pages.go @@ -21,7 +21,7 @@ func Pages(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { } // TODO: Test the pagination here - pageCount := c.Pages.GlobalCount() + pageCount := c.Pages.Count() page, _ := strconv.Atoi(r.FormValue("page")) perPage := 15 offset, page, lastPage := c.PageOffset(pageCount, page, perPage) diff --git a/routes/topic.go b/routes/topic.go index 65742459..2864d1ad 100644 --- a/routes/topic.go +++ b/routes/topic.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "io" + //"fmt" "log" "net/http" "os" @@ -148,7 +149,18 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user c.User, header *c.He header.Zone = "view_topic" header.ZoneID = topic.ID header.ZoneData = topic - rerr := renderTemplate("topic", w, r, header, tpage) + + var rerr c.RouteError + tmpl := forum.Tmpl + if tmpl == "" { + rerr = renderTemplate("topic", w, r, header, tpage) + } else { + tmpl = "topic_"+tmpl + err = renderTemplate3(tmpl,tmpl, w, r, header, tpage) + if err != nil { + rerr = renderTemplate("topic", w, r, header, tpage) + } + } counters.TopicViewCounter.Bump(topic.ID) // TODO: Move this into the router? counters.ForumViewCounter.Bump(topic.ParentID) return rerr @@ -848,11 +860,13 @@ func addTopicAction(action string, topic *c.Topic, user c.User) error { // TODO: Refactor this func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, stid string) c.RouteError { + //fmt.Println("i1") isJs := (r.PostFormValue("isJs") == "1") tid, err := strconv.Atoi(stid) if err != nil { return c.PreErrorJSQ(phrases.GetErrorPhrase("id_must_be_integer"), w, r, isJs) } + //fmt.Println("i2") topic, err := c.Topics.Get(tid) if err == sql.ErrNoRows { @@ -860,6 +874,7 @@ func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, stid s } else if err != nil { return c.InternalErrorJSQ(err, w, r, isJs) } + //fmt.Println("i3") // TODO: Add hooks to make use of headerLite lite, ferr := c.SimpleForumUserCheck(w, r, &user, topic.ParentID) @@ -872,6 +887,7 @@ func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, stid s if topic.CreatedBy == user.ID { return c.LocalErrorJSQ("You can't like your own topics", w, r, user, isJs) } + //fmt.Println("i4") _, err = c.Users.Get(topic.CreatedBy) if err != nil && err == sql.ErrNoRows { @@ -879,6 +895,7 @@ func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, stid s } else if err != nil { return c.InternalErrorJSQ(err, w, r, isJs) } + //fmt.Println("i5") score := 1 err = topic.Like(score, user.ID) @@ -887,6 +904,7 @@ func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, stid s } else if err != nil { return c.InternalErrorJSQ(err, w, r, isJs) } + //fmt.Println("i6") // ! Be careful about leaking per-route permission state with &user alert := c.Alert{ActorID: user.ID, TargetUserID: topic.CreatedBy, Event: "like", ElementType: "topic", ElementID: tid, Actor: &user} @@ -894,11 +912,13 @@ func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, stid s if err != nil { return c.InternalErrorJSQ(err, w, r, isJs) } + //fmt.Println("i7") skip, rerr := lite.Hooks.VhookSkippable("action_end_like_topic", topic.ID, &user) if skip || rerr != nil { return rerr } + //fmt.Println("i8") if !isJs { http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther) diff --git a/templates/forum_gallery.html b/templates/forum_gallery.html index f23e2676..c36eb410 100644 --- a/templates/forum_gallery.html +++ b/templates/forum_gallery.html @@ -43,8 +43,8 @@ {{end}} {{end}} -
- {{range .ItemList}}
+
+ {{range .ItemList}}

{{.Title}} diff --git a/templates/panel_debug.html b/templates/panel_debug.html index f8918f98..bd329125 100644 --- a/templates/panel_debug.html +++ b/templates/panel_debug.html @@ -77,16 +77,58 @@
User Cache
Reply Cache
-
{{.TCache}} / {{.TCap}}
-
{{.UCache}} / {{.UCap}}
-
{{.RCache}} / {{.RCap}}
+
{{.Cache.Topics}} / {{.Cache.TCap}}
+
{{.Cache.Users}} / {{.Cache.UCap}}
+
{{.Cache.Replies}} / {{.Cache.RCap}}
Topic List
???
???
-
{{if .TopicListThaw}}Thawed{{else}}Sleeping{{end}}
+
{{if .Cache.TopicListThaw}}Thawed{{else}}Sleeping{{end}}
?
?
+
+
+

Database

+
+
+
Topics
+
Users
+
Replies
+ +
{{.Database.Topics}}
+
{{.Database.Users}}
+
{{.Database.Replies}}
+ + +
Profile Replies
+
Activity Stream
+
???
+ +
{{.Database.ProfileReplies}}
+
{{.Database.ActivityStream}}
+
?
+
+
+

Disk

+
+
+
Static Files
+
Attachments
+
Avatars
+ +
{{bunit .Disk.Static}}
+
{{bunit .Disk.Attachments}}
+
{{bunit .Disk.Avatars}}
+ + +
Log Files
+
Backups
+
???
+ +
{{bunit .Disk.Logs}}
+
{{bunit .Disk.Backups}}
+
?
\ No newline at end of file