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"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
//"fmt"
|
||||||
|
|
||||||
"github.com/Azareal/Gosora/common/phrases"
|
"github.com/Azareal/Gosora/common/phrases"
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
"github.com/Azareal/Gosora/query_gen"
|
||||||
|
@ -30,11 +31,9 @@ type Alert struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type AlertStmts struct {
|
type AlertStmts struct {
|
||||||
addActivity *sql.Stmt
|
|
||||||
notifyWatchers *sql.Stmt
|
notifyWatchers *sql.Stmt
|
||||||
notifyOne *sql.Stmt
|
notifyOne *sql.Stmt
|
||||||
getWatchers *sql.Stmt
|
getWatchers *sql.Stmt
|
||||||
getActivityEntry *sql.Stmt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var alertStmts AlertStmts
|
var alertStmts AlertStmts
|
||||||
|
@ -44,14 +43,12 @@ var alertStmts AlertStmts
|
||||||
func init() {
|
func init() {
|
||||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||||
alertStmts = AlertStmts{
|
alertStmts = AlertStmts{
|
||||||
addActivity: acc.Insert("activity_stream").Columns("actor, targetUser, event, elementType, elementID, createdAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
|
||||||
notifyWatchers: acc.SimpleInsertInnerJoin(
|
notifyWatchers: acc.SimpleInsertInnerJoin(
|
||||||
qgen.DBInsert{"activity_stream_matches", "watcher, asid", ""},
|
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 = ?", "", ""},
|
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(),
|
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 = ?", "", ""),
|
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()
|
return acc.FirstError()
|
||||||
})
|
})
|
||||||
|
@ -79,7 +76,6 @@ func BuildAlert(alert Alert, user User /* The current user */) (out string, err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
if alert.Event == "friend_invite" {
|
if alert.Event == "friend_invite" {
|
||||||
return buildAlertString(".new_friend_invite", []string{alert.Actor.Name}, alert.Actor.Link, alert.Actor.Avatar, alert.ASID), nil
|
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 {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
lastID, err := res.LastInsertId()
|
return NotifyWatchers(id)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return NotifyWatchers(lastID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddActivityAndNotifyTarget(alert Alert) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
lastID, err := res.LastInsertId()
|
err = NotifyOne(alert.TargetUserID, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
alert.ASID = id
|
||||||
err = NotifyOne(alert.TargetUserID, lastID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
alert.ASID = int(lastID)
|
|
||||||
|
|
||||||
// Live alerts, if the target is online and WebSockets is enabled
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotifyOne(watcher int, asid int64) error {
|
func NotifyOne(watcher int, asid int) error {
|
||||||
_, err := alertStmts.notifyOne.Exec(watcher, asid)
|
_, err := alertStmts.notifyOne.Exec(watcher, asid)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotifyWatchers(asid int64) error {
|
func NotifyWatchers(asid int) error {
|
||||||
_, err := alertStmts.notifyWatchers.Exec(asid)
|
_, err := alertStmts.notifyWatchers.Exec(asid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -214,7 +206,7 @@ func NotifyWatchers(asid int64) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func notifyWatchers(asid int64) {
|
func notifyWatchers(asid int) {
|
||||||
rows, err := alertStmts.getWatchers.Query(asid)
|
rows, err := alertStmts.getWatchers.Query(asid)
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
|
@ -238,12 +230,10 @@ func notifyWatchers(asid int64) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var alert = Alert{ASID: int(asid)}
|
alert, err := Activity.Get(asid)
|
||||||
err = alertStmts.getActivityEntry.QueryRow(asid).Scan(&alert.ActorID, &alert.TargetUserID, &alert.Event, &alert.ElementType, &alert.ElementID, &alert.CreatedAt)
|
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = WsHub.pushAlerts(uids, alert)
|
_ = 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)
|
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
|
MoveTo(sectionID int, originID int, originTable string) error
|
||||||
MoveToByExtra(sectionID int, originTable string, extra string) error
|
MoveToByExtra(sectionID int, originTable string, extra string) error
|
||||||
GlobalCount() int
|
Count() int
|
||||||
CountIn(originTable string, oid int) int
|
CountIn(originTable string, oid int) int
|
||||||
CountInPath(path string) int
|
CountInPath(path string) int
|
||||||
Delete(aid int) error
|
Delete(aid int) error
|
||||||
|
@ -149,18 +149,18 @@ func (store *DefaultAttachmentStore) Add(sectionID int, sectionTable string, ori
|
||||||
return int(lid), err
|
return int(lid), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *DefaultAttachmentStore) MoveTo(sectionID int, originID int, originTable string) error {
|
func (s *DefaultAttachmentStore) MoveTo(sectionID int, originID int, originTable string) error {
|
||||||
_, err := store.move.Exec(sectionID, originID, originTable)
|
_, err := s.move.Exec(sectionID, originID, originTable)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *DefaultAttachmentStore) MoveToByExtra(sectionID int, originTable string, extra string) error {
|
func (s *DefaultAttachmentStore) MoveToByExtra(sectionID int, originTable string, extra string) error {
|
||||||
_, err := store.moveByExtra.Exec(sectionID, originTable, extra)
|
_, err := s.moveByExtra.Exec(sectionID, originTable, extra)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *DefaultAttachmentStore) GlobalCount() (count int) {
|
func (s *DefaultAttachmentStore) Count() (count int) {
|
||||||
err := store.count.QueryRow().Scan(&count)
|
err := s.count.QueryRow().Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ type LogItem struct {
|
||||||
|
|
||||||
type LogStore interface {
|
type LogStore interface {
|
||||||
Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error)
|
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)
|
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?
|
// TODO: Make a store for this?
|
||||||
func (store *SQLModLogStore) Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) {
|
func (s *SQLModLogStore) Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) {
|
||||||
_, err = store.create.Exec(action, elementID, elementType, ipaddress, actorID)
|
_, err = s.create.Exec(action, elementID, elementType, ipaddress, actorID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *SQLModLogStore) GlobalCount() (logCount int) {
|
func (s *SQLModLogStore) Count() (count int) {
|
||||||
err := store.count.QueryRow().Scan(&logCount)
|
err := s.count.QueryRow().Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
return logCount
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildLogList(rows *sql.Rows) (logs []LogItem, err error) {
|
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?
|
// TODO: Make a store for this?
|
||||||
func (store *SQLAdminLogStore) Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) {
|
func (s *SQLAdminLogStore) Create(action string, elementID int, elementType string, ipaddress string, actorID int) (err error) {
|
||||||
_, err = store.create.Exec(action, elementID, elementType, ipaddress, actorID)
|
_, err = s.create.Exec(action, elementID, elementType, ipaddress, actorID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *SQLAdminLogStore) GlobalCount() (logCount int) {
|
func (s *SQLAdminLogStore) Count() (count int) {
|
||||||
err := store.count.QueryRow().Scan(&logCount)
|
err := s.count.QueryRow().Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
return logCount
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *SQLAdminLogStore) GetOffset(offset int, perPage int) (logs []LogItem, err error) {
|
func (s *SQLAdminLogStore) GetOffset(offset int, perPage int) (logs []LogItem, err error) {
|
||||||
rows, err := store.getOffset.Query(offset, perPage)
|
rows, err := s.getOffset.Query(offset, perPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return logs, err
|
return logs, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ type DataStore interface {
|
||||||
DirtyGet(id int) interface{}
|
DirtyGet(id int) interface{}
|
||||||
Get(id int) (interface{}, error)
|
Get(id int) (interface{}, error)
|
||||||
BypassGet(id int) (interface{}, error)
|
BypassGet(id int) (interface{}, error)
|
||||||
//GlobalCount()
|
//Count() int
|
||||||
}
|
}
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
|
|
|
@ -47,7 +47,7 @@ type ForumStore interface {
|
||||||
Create(forumName string, forumDesc string, active bool, preset string) (int, error)
|
Create(forumName string, forumDesc string, active bool, preset string) (int, error)
|
||||||
UpdateOrder(updateMap map[int]int) error
|
UpdateOrder(updateMap map[int]int) error
|
||||||
|
|
||||||
GlobalCount() int
|
Count() int
|
||||||
}
|
}
|
||||||
|
|
||||||
type ForumCache interface {
|
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?
|
// 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
|
// Count returns the total number of forums
|
||||||
func (s *MemoryForumStore) GlobalCount() (fcount int) {
|
func (s *MemoryForumStore) Count() (count int) {
|
||||||
err := s.count.QueryRow().Scan(&fcount)
|
err := s.count.QueryRow().Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
return fcount
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Work on SqlForumStore
|
// TODO: Work on SqlForumStore
|
||||||
|
|
|
@ -25,7 +25,7 @@ type GroupStore interface {
|
||||||
GetAll() ([]*Group, error)
|
GetAll() ([]*Group, error)
|
||||||
GetRange(lower int, higher int) ([]*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
|
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 {
|
type GroupCache interface {
|
||||||
|
@ -336,14 +336,14 @@ func (mgs *MemoryGroupStore) GetRange(lower int, higher int) (groups []*Group, e
|
||||||
return groups, nil
|
return groups, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mgs *MemoryGroupStore) Length() int {
|
func (s *MemoryGroupStore) Length() int {
|
||||||
mgs.RLock()
|
s.RLock()
|
||||||
defer mgs.RUnlock()
|
defer s.RUnlock()
|
||||||
return mgs.groupCount
|
return s.groupCount
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mgs *MemoryGroupStore) GlobalCount() (count int) {
|
func (s *MemoryGroupStore) Count() (count int) {
|
||||||
err := mgs.count.QueryRow().Scan(&count)
|
err := s.count.QueryRow().Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (log *RegLogItem) Create() (id int, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type RegLogStore interface {
|
type RegLogStore interface {
|
||||||
GlobalCount() (logCount int)
|
Count() (count int)
|
||||||
GetOffset(offset int, perPage int) (logs []RegLogItem, err error)
|
GetOffset(offset int, perPage int) (logs []RegLogItem, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,12 +71,12 @@ func NewRegLogStore(acc *qgen.Accumulator) (*SQLRegLogStore, error) {
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *SQLRegLogStore) GlobalCount() (logCount int) {
|
func (s *SQLRegLogStore) Count() (count int) {
|
||||||
err := store.count.QueryRow().Scan(&logCount)
|
err := s.count.QueryRow().Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
return logCount
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *SQLRegLogStore) GetOffset(offset int, perPage int) (logs []RegLogItem, err error) {
|
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 {
|
type LoginLogStore interface {
|
||||||
GlobalCount() (logCount int)
|
Count() (count int)
|
||||||
Count(uid int) (logCount int)
|
CountUser(uid int) (count int)
|
||||||
GetOffset(uid int, offset int, perPage int) (logs []LoginLogItem, err error)
|
GetOffset(uid int, offset int, perPage int) (logs []LoginLogItem, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,20 +161,20 @@ func NewLoginLogStore(acc *qgen.Accumulator) (*SQLLoginLogStore, error) {
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *SQLLoginLogStore) GlobalCount() (logCount int) {
|
func (s *SQLLoginLogStore) Count() (count int) {
|
||||||
err := store.count.QueryRow().Scan(&logCount)
|
err := s.count.QueryRow().Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
return logCount
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *SQLLoginLogStore) Count(uid int) (logCount int) {
|
func (s *SQLLoginLogStore) CountUser(uid int) (count int) {
|
||||||
err := store.countForUser.QueryRow(uid).Scan(&logCount)
|
err := s.countForUser.QueryRow(uid).Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
return logCount
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *SQLLoginLogStore) GetOffset(uid int, offset int, perPage int) (logs []LoginLogItem, err error) {
|
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
|
// 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 {
|
type PageStore interface {
|
||||||
GlobalCount() (pageCount int)
|
Count() (count int)
|
||||||
Get(id int) (*CustomPage, error)
|
Get(id int) (*CustomPage, error)
|
||||||
GetByName(name string) (*CustomPage, error)
|
GetByName(name string) (*CustomPage, error)
|
||||||
GetOffset(offset int, perPage int) (pages []*CustomPage, err error)
|
GetOffset(offset int, perPage int) (pages []*CustomPage, err error)
|
||||||
|
@ -99,15 +99,15 @@ func NewDefaultPageStore(acc *qgen.Accumulator) (*DefaultPageStore, error) {
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *DefaultPageStore) GlobalCount() (pageCount int) {
|
func (s *DefaultPageStore) Count() (count int) {
|
||||||
err := store.count.QueryRow().Scan(&pageCount)
|
err := s.count.QueryRow().Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
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 == "" {
|
if raw == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -594,6 +594,34 @@ type PanelRegLogsPage struct {
|
||||||
Paginator
|
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 {
|
type PanelDebugPage struct {
|
||||||
*BasePanelPage
|
*BasePanelPage
|
||||||
GoVersion string
|
GoVersion string
|
||||||
|
@ -607,15 +635,9 @@ type PanelDebugPage struct {
|
||||||
CPUs int
|
CPUs int
|
||||||
MemStats runtime.MemStats
|
MemStats runtime.MemStats
|
||||||
|
|
||||||
TCache int
|
Cache DebugPageCache
|
||||||
UCache int
|
Database DebugPageDatabase
|
||||||
RCache int
|
Disk DebugPageDisk
|
||||||
|
|
||||||
TCap int
|
|
||||||
UCap int
|
|
||||||
RCap int
|
|
||||||
|
|
||||||
TopicListThaw bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type PageSimple struct {
|
type PageSimple struct {
|
||||||
|
|
|
@ -51,7 +51,7 @@ type PollStore interface {
|
||||||
Create(parent Pollable, pollType int, pollOptions map[int]string) (int, error)
|
Create(parent Pollable, pollType int, pollOptions map[int]string) (int, error)
|
||||||
CastVote(optionIndex int, pollID int, uid int, ipaddress string) error
|
CastVote(optionIndex int, pollID int, uid int, ipaddress string) error
|
||||||
Reload(id int) error
|
Reload(id int) error
|
||||||
//GlobalCount() int
|
//Count() int
|
||||||
|
|
||||||
SetCache(cache PollCache)
|
SetCache(cache PollCache)
|
||||||
GetCache() PollCache
|
GetCache() PollCache
|
||||||
|
@ -68,7 +68,7 @@ type DefaultPollStore struct {
|
||||||
incrementVoteCount *sql.Stmt
|
incrementVoteCount *sql.Stmt
|
||||||
incrementVoteCountForOption *sql.Stmt
|
incrementVoteCountForOption *sql.Stmt
|
||||||
delete *sql.Stmt
|
delete *sql.Stmt
|
||||||
//pollCount *sql.Stmt
|
//count *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultPollStore(cache PollCache) (*DefaultPollStore, error) {
|
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(),
|
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(),
|
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(),
|
incrementVoteCountForOption: acc.Update("polls_options").Set("votes = votes + 1").Where("option = ? AND pollID = ?").Prepare(),
|
||||||
//pollCount: acc.SimpleCount("polls", "", ""),
|
//count: acc.SimpleCount("polls", "", ""),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ package common
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Prstore ProfileReplyStore
|
var Prstore ProfileReplyStore
|
||||||
|
@ -11,6 +11,7 @@ var Prstore ProfileReplyStore
|
||||||
type ProfileReplyStore interface {
|
type ProfileReplyStore interface {
|
||||||
Get(id int) (*ProfileReply, error)
|
Get(id int) (*ProfileReply, error)
|
||||||
Create(profileID int, content string, createdBy int, ipaddress string) (id int, err 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
|
// TODO: Refactor this to stop using the global stmt store
|
||||||
|
@ -18,23 +19,25 @@ type ProfileReplyStore interface {
|
||||||
type SQLProfileReplyStore struct {
|
type SQLProfileReplyStore struct {
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
create *sql.Stmt
|
create *sql.Stmt
|
||||||
|
count *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSQLProfileReplyStore(acc *qgen.Accumulator) (*SQLProfileReplyStore, error) {
|
func NewSQLProfileReplyStore(acc *qgen.Accumulator) (*SQLProfileReplyStore, error) {
|
||||||
return &SQLProfileReplyStore{
|
return &SQLProfileReplyStore{
|
||||||
get: acc.Select("users_replies").Columns("uid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress").Where("rid = ?").Prepare(),
|
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(),
|
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()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *SQLProfileReplyStore) Get(id int) (*ProfileReply, error) {
|
func (s *SQLProfileReplyStore) Get(id int) (*ProfileReply, error) {
|
||||||
reply := ProfileReply{ID: id}
|
r := ProfileReply{ID: id}
|
||||||
err := store.get.QueryRow(id).Scan(&reply.ParentID, &reply.Content, &reply.CreatedBy, &reply.CreatedAt, &reply.LastEdit, &reply.LastEditBy, &reply.IPAddress)
|
err := s.get.QueryRow(id).Scan(&r.ParentID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.IPAddress)
|
||||||
return &reply, err
|
return &r, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *SQLProfileReplyStore) Create(profileID int, content string, createdBy int, ipaddress string) (id int, err error) {
|
func (s *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)
|
res, err := s.create.Exec(profileID, content, ParseMessage(content, 0, ""), createdBy, ipaddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -42,7 +45,16 @@ func (store *SQLProfileReplyStore) Create(profileID int, content string, created
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should we reload the user?
|
// Should we reload the user?
|
||||||
return int(lastID), err
|
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 {
|
type ReplyStore interface {
|
||||||
Get(id int) (*Reply, error)
|
Get(id int) (*Reply, error)
|
||||||
Create(topic *Topic, content string, ipaddress string, uid int) (id int, err error)
|
Create(topic *Topic, content string, ipaddress string, uid int) (id int, err error)
|
||||||
|
Count() (count int)
|
||||||
|
|
||||||
SetCache(cache ReplyCache)
|
SetCache(cache ReplyCache)
|
||||||
GetCache() ReplyCache
|
GetCache() ReplyCache
|
||||||
|
@ -19,6 +20,7 @@ type SQLReplyStore struct {
|
||||||
|
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
create *sql.Stmt
|
create *sql.Stmt
|
||||||
|
count *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSQLReplyStore(acc *qgen.Accumulator, cache ReplyCache) (*SQLReplyStore, error) {
|
func NewSQLReplyStore(acc *qgen.Accumulator, cache ReplyCache) (*SQLReplyStore, error) {
|
||||||
|
@ -29,22 +31,22 @@ func NewSQLReplyStore(acc *qgen.Accumulator, cache ReplyCache) (*SQLReplyStore,
|
||||||
cache: cache,
|
cache: cache,
|
||||||
get: acc.Select("replies").Columns("tid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount, attachCount, actionType").Where("rid = ?").Prepare(),
|
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(),
|
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()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLReplyStore) Get(id int) (*Reply, error) {
|
func (s *SQLReplyStore) Get(id int) (*Reply, error) {
|
||||||
//log.Print("SQLReplyStore.Get")
|
r, err := s.cache.Get(id)
|
||||||
reply, err := s.cache.Get(id)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return reply, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
reply = &Reply{ID: id}
|
r = &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)
|
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 {
|
if err == nil {
|
||||||
_ = s.cache.Set(reply)
|
_ = s.cache.Set(r)
|
||||||
}
|
}
|
||||||
return reply, err
|
return r, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// 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)
|
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) {
|
func (s *SQLReplyStore) SetCache(cache ReplyCache) {
|
||||||
s.cache = cache
|
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'")
|
//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
|
// 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.Users = Users.Count()
|
||||||
stats.Groups = Groups.GlobalCount()
|
stats.Groups = Groups.Count()
|
||||||
stats.Forums = Forums.GlobalCount()
|
stats.Forums = Forums.Count()
|
||||||
stats.Pages = Pages.GlobalCount()
|
stats.Pages = Pages.Count()
|
||||||
stats.Settings = len(header.Settings)
|
stats.Settings = len(header.Settings)
|
||||||
stats.WordFilters = WordFilters.EstCount()
|
stats.WordFilters = WordFilters.EstCount()
|
||||||
stats.Themes = len(Themes)
|
stats.Themes = len(Themes)
|
||||||
|
|
|
@ -745,6 +745,8 @@ func initDefaultTmplFuncMap() {
|
||||||
byteFloat, unit = ConvertByteUnit(float64(bytes))
|
byteFloat, unit = ConvertByteUnit(float64(bytes))
|
||||||
case uint64:
|
case uint64:
|
||||||
byteFloat, unit = ConvertByteUnit(float64(bytes))
|
byteFloat, unit = ConvertByteUnit(float64(bytes))
|
||||||
|
case float64:
|
||||||
|
byteFloat, unit = ConvertByteUnit(bytes)
|
||||||
default:
|
default:
|
||||||
panic("bytes is not an int, int64 or uint64")
|
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 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 {
|
type Theme struct {
|
||||||
Path string // Redirect this file to another folder
|
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 {
|
if !ok {
|
||||||
mapping = template
|
mapping = template
|
||||||
}
|
}
|
||||||
|
if theme.IntTmplHandle.Lookup(mapping+".html") == nil {
|
||||||
|
return ErrBadDefaultTemplate
|
||||||
|
}
|
||||||
return theme.IntTmplHandle.ExecuteTemplate(w, mapping+".html", pi)
|
return theme.IntTmplHandle.ExecuteTemplate(w, mapping+".html", pi)
|
||||||
default:
|
default:
|
||||||
log.Print("theme ", theme)
|
log.Print("theme ", theme)
|
||||||
|
|
|
@ -36,7 +36,7 @@ type TopicStore interface {
|
||||||
// TODO: Implement these two methods
|
// TODO: Implement these two methods
|
||||||
//Replies(tid int) ([]*Reply, error)
|
//Replies(tid int) ([]*Reply, error)
|
||||||
//RepliesRange(tid int, lower int, higher int) ([]*Reply, error)
|
//RepliesRange(tid int, lower int, higher int) ([]*Reply, error)
|
||||||
GlobalCount() int
|
Count() int
|
||||||
|
|
||||||
SetCache(cache TopicCache)
|
SetCache(cache TopicCache)
|
||||||
GetCache() TopicCache
|
GetCache() TopicCache
|
||||||
|
@ -47,7 +47,7 @@ type DefaultTopicStore struct {
|
||||||
|
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
exists *sql.Stmt
|
exists *sql.Stmt
|
||||||
topicCount *sql.Stmt
|
count *sql.Stmt
|
||||||
create *sql.Stmt
|
create *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) {
|
||||||
cache: cache,
|
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(),
|
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(),
|
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(),
|
create: acc.Insert("topics").Columns("parentID, title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, ipaddress, words, createdBy").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
@ -239,13 +239,13 @@ func (mts *DefaultTopicStore) AddLastTopic(item *Topic, fid int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalCount returns the total number of topics on these forums
|
// Count returns the total number of topics on these forums
|
||||||
func (mts *DefaultTopicStore) GlobalCount() (tcount int) {
|
func (s *DefaultTopicStore) Count() (count int) {
|
||||||
err := mts.topicCount.QueryRow().Scan(&tcount)
|
err := s.count.QueryRow().Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
return tcount
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mts *DefaultTopicStore) SetCache(cache TopicCache) {
|
func (mts *DefaultTopicStore) SetCache(cache TopicCache) {
|
||||||
|
|
|
@ -26,7 +26,7 @@ type UserStore interface {
|
||||||
BypassGet(id int) (*User, error)
|
BypassGet(id int) (*User, error)
|
||||||
Create(username string, password string, email string, group int, active bool) (int, error)
|
Create(username string, password string, email string, group int, active bool) (int, error)
|
||||||
Reload(id int) error
|
Reload(id int) error
|
||||||
GlobalCount() int
|
Count() int
|
||||||
|
|
||||||
SetCache(cache UserCache)
|
SetCache(cache UserCache)
|
||||||
GetCache() UserCache
|
GetCache() UserCache
|
||||||
|
@ -41,7 +41,7 @@ type DefaultUserStore struct {
|
||||||
exists *sql.Stmt
|
exists *sql.Stmt
|
||||||
register *sql.Stmt
|
register *sql.Stmt
|
||||||
usernameExists *sql.Stmt
|
usernameExists *sql.Stmt
|
||||||
userCount *sql.Stmt
|
count *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultUserStore gives you a new instance of DefaultUserStore
|
// NewDefaultUserStore gives you a new instance of DefaultUserStore
|
||||||
|
@ -59,7 +59,7 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
|
||||||
exists: acc.SimpleSelect("users", "uid", "uid = ?", "", ""),
|
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
|
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 = ?", "", ""),
|
usernameExists: acc.SimpleSelect("users", "name", "name = ?", "", ""),
|
||||||
userCount: acc.Count("users").Prepare(),
|
count: acc.Count("users").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,24 +282,24 @@ func (mus *DefaultUserStore) Create(username string, password string, email stri
|
||||||
return int(lastID), err
|
return int(lastID), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalCount returns the total number of users registered on the forums
|
// Count returns the total number of users registered on the forums
|
||||||
func (mus *DefaultUserStore) GlobalCount() (ucount int) {
|
func (s *DefaultUserStore) Count() (count int) {
|
||||||
err := mus.userCount.QueryRow().Scan(&ucount)
|
err := s.count.QueryRow().Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
return ucount
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mus *DefaultUserStore) SetCache(cache UserCache) {
|
func (s *DefaultUserStore) SetCache(cache UserCache) {
|
||||||
mus.cache = cache
|
s.cache = cache
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We're temporarily doing this so that you can do ucache != nil in getTopicUser. Refactor it.
|
// TODO: We're temporarily doing this so that you can do ucache != nil in getTopicUser. Refactor it.
|
||||||
func (mus *DefaultUserStore) GetCache() UserCache {
|
func (s *DefaultUserStore) GetCache() UserCache {
|
||||||
_, ok := mus.cache.(*NullUserCache)
|
_, ok := s.cache.(*NullUserCache)
|
||||||
if ok {
|
if ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return mus.cache
|
return s.cache
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"path/filepath"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
|
@ -509,3 +510,17 @@ func BuildSlug(slug string, id int) string {
|
||||||
}
|
}
|
||||||
return slug + "." + strconv.Itoa(id)
|
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"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
"github.com/Azareal/Gosora/query_gen"
|
||||||
|
@ -76,11 +77,22 @@ func (widget *Widget) Copy() (owidget *Widget) {
|
||||||
|
|
||||||
// TODO: Test this
|
// 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
|
// 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, "|") {
|
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 {
|
if loc == "global" || loc == zone {
|
||||||
return true
|
return true
|
||||||
} else if len(loc) > 0 && loc[0] == '!' {
|
} else if loc[0] == '!' {
|
||||||
loc = loc[1:]
|
loc = loc[1:]
|
||||||
if loc != "global" && loc != zone {
|
if loc != "global" && loc != zone {
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -166,7 +166,7 @@ func HasWidgets(dock string, header *Header) bool {
|
||||||
if !widget.Enabled {
|
if !widget.Enabled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if widget.Allowed(header.Zone) {
|
if widget.Allowed(header.Zone,header.ZoneID) {
|
||||||
wcount++
|
wcount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,7 +210,7 @@ func BuildWidget(dock string, header *Header) (sbody string) {
|
||||||
if !widget.Enabled {
|
if !widget.Enabled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if widget.Allowed(header.Zone) {
|
if widget.Allowed(header.Zone,header.ZoneID) {
|
||||||
item, err := widget.Build(header)
|
item, err := widget.Build(header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
|
|
|
@ -24,7 +24,7 @@ type WordFilterStore interface {
|
||||||
Update(id int, find string, replacement string) error
|
Update(id int, find string, replacement string) error
|
||||||
Length() int
|
Length() int
|
||||||
EstCount() int
|
EstCount() int
|
||||||
GlobalCount() (count int)
|
Count() (count int)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultWordFilterStore struct {
|
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
|
// 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)
|
var wordFilters = make(map[int]*WordFilter)
|
||||||
filters, err := store.bypassGetAll()
|
filters, err := s.bypassGetAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -64,13 +64,13 @@ func (store *DefaultWordFilterStore) ReloadAll() error {
|
||||||
wordFilters[filter.ID] = filter
|
wordFilters[filter.ID] = filter
|
||||||
}
|
}
|
||||||
|
|
||||||
store.box.Store(wordFilters)
|
s.box.Store(wordFilters)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ? - Return pointers to word filters intead to save memory? -- A map is a pointer.
|
// ? - Return pointers to word filters intead to save memory? -- A map is a pointer.
|
||||||
func (store *DefaultWordFilterStore) bypassGetAll() (filters []*WordFilter, err error) {
|
func (s *DefaultWordFilterStore) bypassGetAll() (filters []*WordFilter, err error) {
|
||||||
rows, err := store.getAll.Query()
|
rows, err := s.getAll.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// 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) {
|
func (s *DefaultWordFilterStore) GetAll() (filters map[int]*WordFilter, err error) {
|
||||||
return store.box.Load().(map[int]*WordFilter), nil
|
return s.box.Load().(map[int]*WordFilter), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create adds a new word filter to the database and refreshes the memory cache
|
// Create adds a new word filter to the database and refreshes the memory cache
|
||||||
func (store *DefaultWordFilterStore) Create(find string, replacement string) error {
|
func (s *DefaultWordFilterStore) Create(find string, replacement string) error {
|
||||||
_, err := store.create.Exec(find, replacement)
|
_, err := s.create.Exec(find, replacement)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return store.ReloadAll()
|
return s.ReloadAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete removes a word filter from the database and refreshes the memory cache
|
// Delete removes a word filter from the database and refreshes the memory cache
|
||||||
func (store *DefaultWordFilterStore) Delete(id int) error {
|
func (s *DefaultWordFilterStore) Delete(id int) error {
|
||||||
_, err := store.delete.Exec(id)
|
_, err := s.delete.Exec(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return store.ReloadAll()
|
return s.ReloadAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *DefaultWordFilterStore) Update(id int, find string, replacement string) error {
|
func (s *DefaultWordFilterStore) Update(id int, find string, replacement string) error {
|
||||||
_, err := store.update.Exec(find, replacement, id)
|
_, err := s.update.Exec(find, replacement, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// Length gets the number of word filters currently in memory, for the DefaultWordFilterStore, this should be all of them
|
||||||
func (store *DefaultWordFilterStore) Length() int {
|
func (s *DefaultWordFilterStore) Length() int {
|
||||||
return len(store.box.Load().(map[int]*WordFilter))
|
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
|
// 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 {
|
func (s *DefaultWordFilterStore) EstCount() int {
|
||||||
return len(store.box.Load().(map[int]*WordFilter))
|
return len(s.box.Load().(map[int]*WordFilter))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalCount gets the total number of word filters directly from the database
|
// Count gets the total number of word filters directly from the database
|
||||||
func (store *DefaultWordFilterStore) GlobalCount() (count int) {
|
func (s *DefaultWordFilterStore) Count() (count int) {
|
||||||
err := store.count.QueryRow().Scan(&count)
|
err := s.count.QueryRow().Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
LogError(err)
|
LogError(err)
|
||||||
}
|
}
|
||||||
|
|
6
main.go
6
main.go
|
@ -243,6 +243,10 @@ func storeInit() (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
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
|
// TODO: Let the admin choose other thumbnailers, maybe ones defined in plugins
|
||||||
c.Thumbnailer = c.NewCaireThumbnailer()
|
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?
|
// 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 {
|
if ucache != nil {
|
||||||
ucap := ucache.GetCapacity()
|
ucap := ucache.GetCapacity()
|
||||||
if ucache.Length() <= ucap || c.Users.GlobalCount() <= ucap {
|
if ucache.Length() <= ucap || c.Users.Count() <= ucap {
|
||||||
couldNotDealloc = false
|
couldNotDealloc = false
|
||||||
continue
|
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, !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)))
|
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
|
var awaitingActivation = 5
|
||||||
// TODO: Write tests for the registration validators
|
// TODO: Write tests for the registration validators
|
||||||
|
@ -452,7 +452,7 @@ func topicStoreTest(t *testing.T, newID int) {
|
||||||
ok = c.Topics.Exists(1)
|
ok = c.Topics.Exists(1)
|
||||||
expect(t, ok, "TID #1 should exist")
|
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))
|
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)
|
//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, 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))
|
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))
|
expect(t, count == 2, fmt.Sprintf("Global count for topics should be 2, not %d", count))
|
||||||
|
|
||||||
var iFrag = func(cond bool) string {
|
var iFrag = func(cond bool) string {
|
||||||
|
@ -541,7 +541,7 @@ func TestForumStore(t *testing.T) {
|
||||||
fcache, ok := c.Forums.(c.ForumCache)
|
fcache, ok := c.Forums.(c.ForumCache)
|
||||||
expect(t, ok, "Unable to cast ForumStore to 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")
|
expect(t, fcache.Length() == 2, "The forum cache length should be 2")
|
||||||
|
|
||||||
_, err := c.Forums.Get(-1)
|
_, 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, 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.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")
|
expect(t, fcache.Length() == 3, "The forum cache length should be 3")
|
||||||
|
|
||||||
forum, err = c.Forums.Get(3)
|
forum, err = c.Forums.Get(3)
|
||||||
|
@ -618,7 +618,7 @@ func TestForumStore(t *testing.T) {
|
||||||
|
|
||||||
expectNilErr(t, c.Forums.Delete(3))
|
expectNilErr(t, c.Forums.Delete(3))
|
||||||
expect(t, forum.ID == 3, fmt.Sprintf("forum pointer shenanigans"))
|
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, fcache.Length() == 2, "The forum cache length should be 2")
|
||||||
expect(t, !c.Forums.Exists(3), "FID #3 shouldn't exist after being deleted")
|
expect(t, !c.Forums.Exists(3), "FID #3 shouldn't exist after being deleted")
|
||||||
_, err = c.Forums.Get(3)
|
_, err = c.Forums.Get(3)
|
||||||
|
@ -938,10 +938,37 @@ func TestProfileReplyStore(t *testing.T) {
|
||||||
// TODO: Test profileReply.SetBody() and profileReply.Creator()
|
// 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) {
|
func TestLogs(t *testing.T) {
|
||||||
miscinit(t)
|
miscinit(t)
|
||||||
gTests := func(store c.LogStore, phrase string) {
|
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)
|
logs, err := store.GetOffset(0, 25)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, len(logs) == 0, "The log slice should be empty")
|
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) {
|
gTests2 := func(store c.LogStore, phrase string) {
|
||||||
err := store.Create("something", 0, "bumblefly", "::1", 1)
|
err := store.Create("something", 0, "bumblefly", "::1", 1)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
count := store.GlobalCount()
|
count := store.Count()
|
||||||
expect(t, count == 1, fmt.Sprintf("store.GlobalCount should return one, not %d", count))
|
expect(t, count == 1, fmt.Sprintf("store.Count should return one, not %d", count))
|
||||||
logs, err := store.GetOffset(0, 25)
|
logs, err := store.GetOffset(0, 25)
|
||||||
recordMustExist(t, err, "We should have at-least one "+phrase)
|
recordMustExist(t, err, "We should have at-least one "+phrase)
|
||||||
expect(t, len(logs) == 1, "The length of the log slice should be one")
|
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
|
// TODO: Test the word filters and their store
|
||||||
expect(t, c.WordFilters.Length() == 0, "Word filter list should be empty")
|
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.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()
|
filters, err := c.WordFilters.GetAll()
|
||||||
expectNilErr(t, err) // TODO: Slightly confusing that we don't get ErrNoRow here
|
expectNilErr(t, err) // TODO: Slightly confusing that we don't get ErrNoRow here
|
||||||
expect(t, len(filters) == 0, "Word filter map should be empty")
|
expect(t, len(filters) == 0, "Word filter map should be empty")
|
||||||
|
@ -1169,7 +1196,7 @@ func TestWordFilters(t *testing.T) {
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, c.WordFilters.Length() == 1, "Word filter list should not be empty")
|
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.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()
|
filters, err = c.WordFilters.GetAll()
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, len(filters) == 1, "Word filter map should not be empty")
|
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})
|
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 {
|
func initHeythere(plugin *c.Plugin) error {
|
||||||
plugin.AddHook("topic_reply_row_assign", heythereReply)
|
plugin.AddHook("topic_reply_row_assign", heythereReply)
|
||||||
return nil
|
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 {
|
func AccountLogins(w http.ResponseWriter, r *http.Request, user c.User, header *c.Header) c.RouteError {
|
||||||
accountEditHead("account_logins", w, r, &user, header)
|
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"))
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
perPage := 12
|
perPage := 12
|
||||||
offset, page, lastPage := c.PageOffset(logCount, page, perPage)
|
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 {
|
func renderTemplate3(tmplName string, hookName string, w http.ResponseWriter, r *http.Request, h *c.Header, pi interface{}) error {
|
||||||
c.PrepResources(&header.CurrentUser, header, header.Theme)
|
c.PrepResources(&h.CurrentUser, h, h.Theme)
|
||||||
if header.CurrentUser.Loggedin {
|
if h.CurrentUser.Loggedin {
|
||||||
header.MetaDesc = ""
|
h.MetaDesc = ""
|
||||||
header.OGDesc = ""
|
h.OGDesc = ""
|
||||||
} else if header.MetaDesc != "" && header.OGDesc == "" {
|
} else if h.MetaDesc != "" && h.OGDesc == "" {
|
||||||
header.OGDesc = header.MetaDesc
|
h.OGDesc = h.MetaDesc
|
||||||
}
|
}
|
||||||
header.AddScript("global.js")
|
h.AddScript("global.js")
|
||||||
if header.CurrentUser.Loggedin {
|
if h.CurrentUser.Loggedin {
|
||||||
header.AddScriptAsync("member.js")
|
h.AddScriptAsync("member.js")
|
||||||
}
|
}
|
||||||
|
|
||||||
FootHeaders(w, header)
|
FootHeaders(w, h)
|
||||||
if header.CurrentUser.IsAdmin {
|
if h.CurrentUser.IsAdmin {
|
||||||
header.Elapsed1 = time.Since(header.StartedAt).String()
|
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 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
|
// 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)
|
pageList := c.Paginate(forum.TopicCount, c.Config.ItemsPerPage, 5)
|
||||||
pi := c.ForumPage{header, topicList, forum, c.Paginator{pageList, page, lastPage}}
|
pi := c.ForumPage{header, topicList, forum, c.Paginator{pageList, page, lastPage}}
|
||||||
var tmpl = forum.Tmpl
|
tmpl := forum.Tmpl
|
||||||
if 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)
|
counters.ForumViewCounter.Bump(forum.ID)
|
||||||
return ferr
|
return ferr
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"github.com/Azareal/Gosora/common/phrases"
|
"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
|
var cacheControlMaxAgeWeek = "max-age=" + strconv.Itoa(int(c.Week)) // TODO: Make this a c.Config value
|
||||||
|
|
||||||
// GET functions
|
// 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 {
|
} else if err != sql.ErrNoRows {
|
||||||
return c.InternalError(err, w, r)
|
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")
|
header.Title = phrases.GetTitlePhrase("page")
|
||||||
|
|
||||||
// TODO: Pass the page name to the pre-render hook?
|
// 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
|
// 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()
|
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})
|
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
|
return ferr
|
||||||
}
|
}
|
||||||
|
|
||||||
logCount := c.RegLogs.GlobalCount()
|
logCount := c.RegLogs.Count()
|
||||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
perPage := 10
|
perPage := 10
|
||||||
offset, page, lastPage := c.PageOffset(logCount, page, perPage)
|
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
|
return ferr
|
||||||
}
|
}
|
||||||
|
|
||||||
logCount := c.ModLogs.GlobalCount()
|
logCount := c.ModLogs.Count()
|
||||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
perPage := 10
|
perPage := 10
|
||||||
offset, page, lastPage := c.PageOffset(logCount, page, perPage)
|
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
|
return ferr
|
||||||
}
|
}
|
||||||
|
|
||||||
logCount := c.ModLogs.GlobalCount()
|
logCount := c.ModLogs.Count()
|
||||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
perPage := 10
|
perPage := 10
|
||||||
offset, page, lastPage := c.PageOffset(logCount, page, perPage)
|
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
|
// TODO: Test the pagination here
|
||||||
pageCount := c.Pages.GlobalCount()
|
pageCount := c.Pages.Count()
|
||||||
page, _ := strconv.Atoi(r.FormValue("page"))
|
page, _ := strconv.Atoi(r.FormValue("page"))
|
||||||
perPage := 15
|
perPage := 15
|
||||||
offset, page, lastPage := c.PageOffset(pageCount, page, perPage)
|
offset, page, lastPage := c.PageOffset(pageCount, page, perPage)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
//"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -148,7 +149,18 @@ func ViewTopic(w http.ResponseWriter, r *http.Request, user c.User, header *c.He
|
||||||
header.Zone = "view_topic"
|
header.Zone = "view_topic"
|
||||||
header.ZoneID = topic.ID
|
header.ZoneID = topic.ID
|
||||||
header.ZoneData = topic
|
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.TopicViewCounter.Bump(topic.ID) // TODO: Move this into the router?
|
||||||
counters.ForumViewCounter.Bump(topic.ParentID)
|
counters.ForumViewCounter.Bump(topic.ParentID)
|
||||||
return rerr
|
return rerr
|
||||||
|
@ -848,11 +860,13 @@ func addTopicAction(action string, topic *c.Topic, user c.User) error {
|
||||||
|
|
||||||
// TODO: Refactor this
|
// TODO: Refactor this
|
||||||
func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, stid string) c.RouteError {
|
func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, stid string) c.RouteError {
|
||||||
|
//fmt.Println("i1")
|
||||||
isJs := (r.PostFormValue("isJs") == "1")
|
isJs := (r.PostFormValue("isJs") == "1")
|
||||||
tid, err := strconv.Atoi(stid)
|
tid, err := strconv.Atoi(stid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.PreErrorJSQ(phrases.GetErrorPhrase("id_must_be_integer"), w, r, isJs)
|
return c.PreErrorJSQ(phrases.GetErrorPhrase("id_must_be_integer"), w, r, isJs)
|
||||||
}
|
}
|
||||||
|
//fmt.Println("i2")
|
||||||
|
|
||||||
topic, err := c.Topics.Get(tid)
|
topic, err := c.Topics.Get(tid)
|
||||||
if err == sql.ErrNoRows {
|
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 {
|
} else if err != nil {
|
||||||
return c.InternalErrorJSQ(err, w, r, isJs)
|
return c.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
//fmt.Println("i3")
|
||||||
|
|
||||||
// TODO: Add hooks to make use of headerLite
|
// TODO: Add hooks to make use of headerLite
|
||||||
lite, ferr := c.SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
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 {
|
if topic.CreatedBy == user.ID {
|
||||||
return c.LocalErrorJSQ("You can't like your own topics", w, r, user, isJs)
|
return c.LocalErrorJSQ("You can't like your own topics", w, r, user, isJs)
|
||||||
}
|
}
|
||||||
|
//fmt.Println("i4")
|
||||||
|
|
||||||
_, err = c.Users.Get(topic.CreatedBy)
|
_, err = c.Users.Get(topic.CreatedBy)
|
||||||
if err != nil && err == sql.ErrNoRows {
|
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 {
|
} else if err != nil {
|
||||||
return c.InternalErrorJSQ(err, w, r, isJs)
|
return c.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
//fmt.Println("i5")
|
||||||
|
|
||||||
score := 1
|
score := 1
|
||||||
err = topic.Like(score, user.ID)
|
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 {
|
} else if err != nil {
|
||||||
return c.InternalErrorJSQ(err, w, r, isJs)
|
return c.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
//fmt.Println("i6")
|
||||||
|
|
||||||
// ! Be careful about leaking per-route permission state with &user
|
// ! 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}
|
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 {
|
if err != nil {
|
||||||
return c.InternalErrorJSQ(err, w, r, isJs)
|
return c.InternalErrorJSQ(err, w, r, isJs)
|
||||||
}
|
}
|
||||||
|
//fmt.Println("i7")
|
||||||
|
|
||||||
skip, rerr := lite.Hooks.VhookSkippable("action_end_like_topic", topic.ID, &user)
|
skip, rerr := lite.Hooks.VhookSkippable("action_end_like_topic", topic.ID, &user)
|
||||||
if skip || rerr != nil {
|
if skip || rerr != nil {
|
||||||
return rerr
|
return rerr
|
||||||
}
|
}
|
||||||
|
//fmt.Println("i8")
|
||||||
|
|
||||||
if !isJs {
|
if !isJs {
|
||||||
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
||||||
|
|
|
@ -43,8 +43,8 @@
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
<div id="forum_topic_list" class="rowblock micro_grid" aria-label="{{lang "forum_list_aria"}}">
|
<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}}" style="grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));">
|
{{range .ItemList}}<div class="rowitem" data-tid="{{.ID}}">
|
||||||
<div>
|
<div>
|
||||||
<a class="rowtopic" href="{{.Link}}" itemprop="itemListElement"><img src="{{.Content}}" style="width:100%;height:160px;" /></a>
|
<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>
|
<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>User Cache</span></div>
|
||||||
<div class="grid_item grid_stat grid_stat_head"><span>Reply 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>{{.Cache.Topics}} / {{.Cache.TCap}}</span></div>
|
||||||
<div class="grid_item grid_stat"><span>{{.UCache}} / {{.UCap}}</span></div>
|
<div class="grid_item grid_stat"><span>{{.Cache.Users}} / {{.Cache.UCap}}</span></div>
|
||||||
<div class="grid_item grid_stat"><span>{{.RCache}} / {{.RCap}}</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>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 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 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>
|
</div>
|
Loading…
Reference in New Issue