Add the disk and database sections to the debug page.
Add the ActivityStream interface to abstract Get, Add and Count. Rename the GlobalCount methods to Count for simplicity. Simplify the variable names in the Count methods. Rename the GlobalCount method to Count and rename the original Count method to CountUser in LoginLogStore. Add a float64 case for bunit, sort of. Theme.RunTmpl now returns ErrBadDefaultTemplate instead of panicking when an interpreted template doesn't exist. Widget.Allowed now checks the zoneid. Fire the alert off in the background in AddActivityAndNotifyTarget instead of blocking the request. Use ErrBadDefaultTemplate instead of calling DefaultTemplates.Lookup directly for custom pages. Split the page struct for the debug page into multiple structs to make things more organised. Add the Count method to ProfileReplyStore. Add the Count method to ReplyStore. Add the DirSize utility function. Add a few ActivityStream tests. Secret gallery stuff.
This commit is contained in:
parent
3f6966d541
commit
05c2ac3ce4
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
6
main.go
6
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
|
||||
}
|
||||
|
|
49
misc_test.go
49
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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -43,8 +43,8 @@
|
|||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<div id="forum_topic_list" class="rowblock micro_grid" aria-label="{{lang "forum_list_aria"}}">
|
||||
{{range .ItemList}}<div class="rowitem" data-tid="{{.ID}}" style="grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));">
|
||||
<div id="forum_topic_list" class="rowblock micro_grid" aria-label="{{lang "forum_list_aria"}}" style="grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));">
|
||||
{{range .ItemList}}<div class="rowitem" data-tid="{{.ID}}">
|
||||
<div>
|
||||
<a class="rowtopic" href="{{.Link}}" itemprop="itemListElement"><img src="{{.Content}}" style="width:100%;height:160px;" /></a>
|
||||
<br /><a class="rowsmall starter" href="{{.Link}}">{{.Title}}</a>
|
||||
|
|
|
@ -77,16 +77,58 @@
|
|||
<div class="grid_item grid_stat grid_stat_head"><span>User Cache</span></div>
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>Reply Cache</span></div>
|
||||
|
||||
<div class="grid_item grid_stat"><span>{{.TCache}} / {{.TCap}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>{{.UCache}} / {{.UCap}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>{{.RCache}} / {{.RCap}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>{{.Cache.Topics}} / {{.Cache.TCap}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>{{.Cache.Users}} / {{.Cache.UCap}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>{{.Cache.Replies}} / {{.Cache.RCap}}</span></div>
|
||||
|
||||
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>Topic List</span></div>
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>???</span></div>
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>???</span></div>
|
||||
|
||||
<div class="grid_item grid_stat"><span>{{if .TopicListThaw}}Thawed{{else}}Sleeping{{end}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>{{if .Cache.TopicListThaw}}Thawed{{else}}Sleeping{{end}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>?</span></div>
|
||||
<div class="grid_item grid_stat"><span>?</span></div>
|
||||
</div>
|
||||
<div class="colstack_item colstack_head colstack_sub_head">
|
||||
<div class="rowitem"><h2>Database</h2></div>
|
||||
</div>
|
||||
<div id="panel_debug" class="colstack_grid">
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>Topics</span></div>
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>Users</span></div>
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>Replies</span></div>
|
||||
|
||||
<div class="grid_item grid_stat"><span>{{.Database.Topics}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>{{.Database.Users}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>{{.Database.Replies}}</span></div>
|
||||
|
||||
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>Profile Replies</span></div>
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>Activity Stream</span></div>
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>???</span></div>
|
||||
|
||||
<div class="grid_item grid_stat"><span>{{.Database.ProfileReplies}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>{{.Database.ActivityStream}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>?</span></div>
|
||||
</div>
|
||||
<div class="colstack_item colstack_head colstack_sub_head">
|
||||
<div class="rowitem"><h2>Disk</h2></div>
|
||||
</div>
|
||||
<div id="panel_debug" class="colstack_grid">
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>Static Files</span></div>
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>Attachments</span></div>
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>Avatars</span></div>
|
||||
|
||||
<div class="grid_item grid_stat"><span>{{bunit .Disk.Static}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>{{bunit .Disk.Attachments}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>{{bunit .Disk.Avatars}}</span></div>
|
||||
|
||||
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>Log Files</span></div>
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>Backups</span></div>
|
||||
<div class="grid_item grid_stat grid_stat_head"><span>???</span></div>
|
||||
|
||||
<div class="grid_item grid_stat"><span>{{bunit .Disk.Logs}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>{{bunit .Disk.Backups}}</span></div>
|
||||
<div class="grid_item grid_stat"><span>?</span></div>
|
||||
</div>
|
Loading…
Reference in New Issue