Cascade delete attachments properly.
Cascade delete replied to topic events for replies properly. Cascade delete likes on topic posts properly. Cascade delete replies and their children properly. Recalculate user stats properly when items are deleted. Users can now unlike topic opening posts. Add a recalculator to fix abnormalities across upgrades. Try fixing a last_ip daily update bug. Add Existable interface. Add Delete method to LikeStore. Add Each, Exists, Create, CountUser, CountMegaUser and CountBigUser methods to ReplyStore. Add CountUser, CountMegaUser, CountBigUser methods to TopicStore. Add Each method to UserStore. Add Add, Delete and DeleteResource methods to SubscriptionStore. Add Delete, DeleteByParams, DeleteByParamsExtra and AidsByParamsExtra methods to ActivityStream. Add Exists method to ProfileReplyStore. Add DropColumn, RenameColumn and ChangeColumn to the database adapters. Shorten ipaddress column names to ip. - topics table. - replies table - users_replies table. - polls_votes table. Add extra column to activity_stream table. Fix an issue upgrading sites to MariaDB 10.3 from older versions of Gosora. Please report any other issues you find. You need to run the updater / patcher for this commit.
This commit is contained in:
parent
e1702687f7
commit
6935637867
|
@ -197,7 +197,7 @@ func setupData(client *elastic.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
oi := 0
|
oi := 0
|
||||||
err := qgen.NewAcc().Select("topics").Cols("tid, title, content, createdBy, ipaddress").Each(func(rows *sql.Rows) error {
|
err := qgen.NewAcc().Select("topics").Cols("tid,title,content,createdBy,ip").Each(func(rows *sql.Rows) error {
|
||||||
t := ESTopic{}
|
t := ESTopic{}
|
||||||
err := rows.Scan(&t.ID, &t.Title, &t.Content, &t.CreatedBy, &t.IP)
|
err := rows.Scan(&t.ID, &t.Title, &t.Content, &t.CreatedBy, &t.IP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -233,7 +233,7 @@ func setupData(client *elastic.Client) error {
|
||||||
rf(rin[i])
|
rf(rin[i])
|
||||||
}
|
}
|
||||||
oi := 0
|
oi := 0
|
||||||
err := qgen.NewAcc().Select("replies").Cols("rid, tid, content, createdBy, ipaddress").Each(func(rows *sql.Rows) error {
|
err := qgen.NewAcc().Select("replies").Cols("rid,tid,content,createdBy,ip").Each(func(rows *sql.Rows) error {
|
||||||
r := ESReply{}
|
r := ESReply{}
|
||||||
err := rows.Scan(&r.ID, &r.TID, &r.Content, &r.CreatedBy, &r.IP)
|
err := rows.Scan(&r.ID, &r.TID, &r.Content, &r.CreatedBy, &r.IP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -204,9 +204,9 @@ func seedTables(a qgen.Adapter) error {
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
qgen.Install.SimpleInsert("topics", "title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, createdBy, parentID, ipaddress", "'Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,'::1'")
|
qgen.Install.SimpleInsert("topics", "title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, createdBy, parentID, ip", "'Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,'::1'")
|
||||||
|
|
||||||
qgen.Install.SimpleInsert("replies", "tid, content, parsed_content, createdAt, createdBy, lastUpdated, lastEdit, lastEditBy, ipaddress", "1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,'::1'")
|
qgen.Install.SimpleInsert("replies", "tid, content, parsed_content, createdAt, createdBy, lastUpdated, lastEdit, lastEditBy, ip", "1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,'::1'")
|
||||||
|
|
||||||
qgen.Install.SimpleInsert("menus", "", "")
|
qgen.Install.SimpleInsert("menus", "", "")
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ func createTables(adapter qgen.Adapter) (err error) {
|
||||||
// TODO: Drop these columns?
|
// TODO: Drop these columns?
|
||||||
tC{"url_prefix", "varchar", 20, false, false, "''"},
|
tC{"url_prefix", "varchar", 20, false, false, "''"},
|
||||||
tC{"url_name", "varchar", 100, false, false, "''"},
|
tC{"url_name", "varchar", 100, false, false, "''"},
|
||||||
|
//tC{"pub_key", "text", 0, false, false, "''"},
|
||||||
|
|
||||||
tC{"level", "smallint", 0, false, false, "0"},
|
tC{"level", "smallint", 0, false, false, "0"},
|
||||||
tC{"score", "int", 0, false, false, "0"},
|
tC{"score", "int", 0, false, false, "0"},
|
||||||
|
@ -253,7 +254,7 @@ func createTables(adapter qgen.Adapter) (err error) {
|
||||||
tC{"sticky", "boolean", 0, false, false, "0"},
|
tC{"sticky", "boolean", 0, false, false, "0"},
|
||||||
// TODO: Add an index for this
|
// TODO: Add an index for this
|
||||||
tC{"parentID", "int", 0, false, false, "2"},
|
tC{"parentID", "int", 0, false, false, "2"},
|
||||||
tC{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
|
tC{"ip", "varchar", 200, false, false, "''"},
|
||||||
tC{"postCount", "int", 0, false, false, "1"},
|
tC{"postCount", "int", 0, false, false, "1"},
|
||||||
tC{"likeCount", "int", 0, false, false, "0"},
|
tC{"likeCount", "int", 0, false, false, "0"},
|
||||||
tC{"attachCount", "int", 0, false, false, "0"},
|
tC{"attachCount", "int", 0, false, false, "0"},
|
||||||
|
@ -286,7 +287,7 @@ func createTables(adapter qgen.Adapter) (err error) {
|
||||||
tC{"lastEdit", "int", 0, false, false, "0"},
|
tC{"lastEdit", "int", 0, false, false, "0"},
|
||||||
tC{"lastEditBy", "int", 0, false, false, "0"},
|
tC{"lastEditBy", "int", 0, false, false, "0"},
|
||||||
tC{"lastUpdated", "datetime", 0, false, false, ""},
|
tC{"lastUpdated", "datetime", 0, false, false, ""},
|
||||||
tC{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
|
tC{"ip", "varchar", 200, false, false, "''"},
|
||||||
tC{"likeCount", "int", 0, false, false, "0"},
|
tC{"likeCount", "int", 0, false, false, "0"},
|
||||||
tC{"attachCount", "int", 0, false, false, "0"},
|
tC{"attachCount", "int", 0, false, false, "0"},
|
||||||
tC{"words", "int", 0, false, false, "1"}, // ? - replies has a default of 1 and topics has 0? why?
|
tC{"words", "int", 0, false, false, "1"}, // ? - replies has a default of 1 and topics has 0? why?
|
||||||
|
@ -357,7 +358,7 @@ func createTables(adapter qgen.Adapter) (err error) {
|
||||||
tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
|
tC{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
|
||||||
tC{"option", "int", 0, false, false, "0"},
|
tC{"option", "int", 0, false, false, "0"},
|
||||||
tC{"castAt", "createdAt", 0, false, false, ""},
|
tC{"castAt", "createdAt", 0, false, false, ""},
|
||||||
tC{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
|
tC{"ip", "varchar", 200, false, false, "''"},
|
||||||
}, nil,
|
}, nil,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -371,7 +372,7 @@ func createTables(adapter qgen.Adapter) (err error) {
|
||||||
tC{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key
|
tC{"createdBy", "int", 0, false, false, ""}, // TODO: Make this a foreign key
|
||||||
tC{"lastEdit", "int", 0, false, false, "0"},
|
tC{"lastEdit", "int", 0, false, false, "0"},
|
||||||
tC{"lastEditBy", "int", 0, false, false, "0"},
|
tC{"lastEditBy", "int", 0, false, false, "0"},
|
||||||
tC{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
|
tC{"ip", "varchar", 200, false, false, "''"},
|
||||||
},
|
},
|
||||||
[]tblKey{
|
[]tblKey{
|
||||||
tblKey{"rid", "primary", "", false},
|
tblKey{"rid", "primary", "", false},
|
||||||
|
@ -464,6 +465,7 @@ func createTables(adapter qgen.Adapter) (err error) {
|
||||||
tC{"elementType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */
|
tC{"elementType", "varchar", 50, false, false, ""}, /* topic, post (calling it post here to differentiate it from the 'reply' event), forum, user */
|
||||||
tC{"elementID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */
|
tC{"elementID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */
|
||||||
tC{"createdAt", "createdAt", 0, false, false, ""},
|
tC{"createdAt", "createdAt", 0, false, false, ""},
|
||||||
|
tC{"extra", "varchar", 200, false, false, "''"},
|
||||||
},
|
},
|
||||||
[]tblKey{
|
[]tblKey{
|
||||||
tblKey{"asid", "primary", "", false},
|
tblKey{"asid", "primary", "", false},
|
||||||
|
|
|
@ -1,47 +1,92 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import "database/sql"
|
import (
|
||||||
import "github.com/Azareal/Gosora/query_gen"
|
"database/sql"
|
||||||
|
|
||||||
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
|
)
|
||||||
|
|
||||||
var Activity ActivityStream
|
var Activity ActivityStream
|
||||||
|
|
||||||
type ActivityStream interface {
|
type ActivityStream interface {
|
||||||
Add(a Alert) (int, error)
|
Add(a Alert) (int, error)
|
||||||
Get(id int) (Alert, error)
|
Get(id int) (Alert, error)
|
||||||
|
Delete(id int) error
|
||||||
|
DeleteByParams(event string, targetID int, targetType string) error
|
||||||
|
DeleteByParamsExtra(event string, targetID int, targetType, extra string) error
|
||||||
|
AidsByParamsExtra(event string, elementID int, elementType, extra string) (aids []int, err error)
|
||||||
Count() (count int)
|
Count() (count int)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultActivityStream struct {
|
type DefaultActivityStream struct {
|
||||||
add *sql.Stmt
|
add *sql.Stmt
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
count *sql.Stmt
|
delete *sql.Stmt
|
||||||
|
deleteByParams *sql.Stmt
|
||||||
|
deleteByParamsExtra *sql.Stmt
|
||||||
|
aidsByParamsExtra *sql.Stmt
|
||||||
|
count *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultActivityStream(acc *qgen.Accumulator) (*DefaultActivityStream, error) {
|
func NewDefaultActivityStream(acc *qgen.Accumulator) (*DefaultActivityStream, error) {
|
||||||
as := "activity_stream"
|
as := "activity_stream"
|
||||||
return &DefaultActivityStream{
|
return &DefaultActivityStream{
|
||||||
add: acc.Insert(as).Columns("actor, targetUser, event, elementType, elementID, createdAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
add: acc.Insert(as).Columns("actor,targetUser,event,elementType,elementID,createdAt,extra").Fields("?,?,?,?,?,UTC_TIMESTAMP(),?").Prepare(),
|
||||||
get: acc.Select(as).Columns("actor, targetUser, event, elementType, elementID, createdAt").Where("asid = ?").Prepare(),
|
get: acc.Select(as).Columns("actor,targetUser,event,elementType,elementID,createdAt,extra").Where("asid=?").Prepare(),
|
||||||
count: acc.Count(as).Prepare(),
|
delete: acc.Delete(as).Where("asid=?").Prepare(),
|
||||||
|
deleteByParams: acc.Delete(as).Where("event=? AND elementID=? AND elementType=?").Prepare(),
|
||||||
|
deleteByParamsExtra: acc.Delete(as).Where("event=? AND elementID=? AND elementType=? AND extra=?").Prepare(),
|
||||||
|
aidsByParamsExtra: acc.Select(as).Columns("asid").Where("event=? AND elementID=? AND elementType=? AND extra=?").Prepare(),
|
||||||
|
count: acc.Count(as).Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultActivityStream) Add(a Alert) (int, error) {
|
func (s *DefaultActivityStream) Add(a Alert) (int, error) {
|
||||||
res, err := s.add.Exec(a.ActorID, a.TargetUserID, a.Event, a.ElementType, a.ElementID)
|
res, err := s.add.Exec(a.ActorID, a.TargetUserID, a.Event, a.ElementType, a.ElementID, a.Extra)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lastID, err := res.LastInsertId()
|
lastID, err := res.LastInsertId()
|
||||||
return int(lastID), err
|
return int(lastID), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultActivityStream) Get(id int) (Alert, error) {
|
func (s *DefaultActivityStream) Get(id int) (Alert, error) {
|
||||||
a := Alert{ASID: id}
|
a := Alert{ASID: id}
|
||||||
err := s.get.QueryRow(id).Scan(&a.ActorID, &a.TargetUserID, &a.Event, &a.ElementType, &a.ElementID, &a.CreatedAt)
|
err := s.get.QueryRow(id).Scan(&a.ActorID, &a.TargetUserID, &a.Event, &a.ElementType, &a.ElementID, &a.CreatedAt, &a.Extra)
|
||||||
return a, err
|
return a, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DefaultActivityStream) Delete(id int) error {
|
||||||
|
_, err := s.delete.Exec(id)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultActivityStream) DeleteByParams(event string, elementID int, elementType string) error {
|
||||||
|
_, err := s.deleteByParams.Exec(event, elementID, elementType)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultActivityStream) DeleteByParamsExtra(event string, elementID int, elementType, extra string) error {
|
||||||
|
_, err := s.deleteByParamsExtra.Exec(event, elementID, elementType, extra)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultActivityStream) AidsByParamsExtra(event string, elementID int, elementType, extra string) (aids []int, err error) {
|
||||||
|
rows, err := s.aidsByParamsExtra.Query(event, elementID, elementType, extra)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var aid int
|
||||||
|
if err := rows.Scan(&aid); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
aids = append(aids, aid)
|
||||||
|
}
|
||||||
|
return aids, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
// Count returns the total number of activity stream items
|
// Count returns the total number of activity stream items
|
||||||
func (s *DefaultActivityStream) Count() (count int) {
|
func (s *DefaultActivityStream) Count() (count int) {
|
||||||
|
|
|
@ -12,10 +12,11 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
//"fmt"
|
//"fmt"
|
||||||
|
|
||||||
"github.com/Azareal/Gosora/common/phrases"
|
"github.com/Azareal/Gosora/common/phrases"
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Alert struct {
|
type Alert struct {
|
||||||
|
@ -25,15 +26,16 @@ type Alert struct {
|
||||||
Event string
|
Event string
|
||||||
ElementType string
|
ElementType string
|
||||||
ElementID int
|
ElementID int
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
|
Extra string
|
||||||
|
|
||||||
Actor *User
|
Actor *User
|
||||||
}
|
}
|
||||||
|
|
||||||
type AlertStmts struct {
|
type AlertStmts struct {
|
||||||
notifyWatchers *sql.Stmt
|
notifyWatchers *sql.Stmt
|
||||||
notifyOne *sql.Stmt
|
notifyOne *sql.Stmt
|
||||||
getWatchers *sql.Stmt
|
getWatchers *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
var alertStmts AlertStmts
|
var alertStmts AlertStmts
|
||||||
|
@ -45,10 +47,10 @@ func init() {
|
||||||
alertStmts = AlertStmts{
|
alertStmts = AlertStmts{
|
||||||
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=?", "", ""),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
})
|
})
|
||||||
|
@ -123,6 +125,7 @@ func BuildAlert(alert Alert, user User /* The current user */) (out string, err
|
||||||
case "post":
|
case "post":
|
||||||
topic, err := TopicByReplyID(alert.ElementID)
|
topic, err := TopicByReplyID(alert.ElementID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
DebugLogf("Unable to find linked topic by reply ID %d", alert.ElementID)
|
||||||
return "", errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic_by_reply"))
|
return "", errors.New(phrases.GetErrorPhrase("alerts_no_linked_topic_by_reply"))
|
||||||
}
|
}
|
||||||
url = topic.Link
|
url = topic.Link
|
||||||
|
@ -146,49 +149,49 @@ func BuildAlert(alert Alert, user User /* The current user */) (out string, err
|
||||||
return buildAlertString(phraseName, []string{alert.Actor.Name, area}, url, alert.Actor.Avatar, alert.ASID), nil
|
return buildAlertString(phraseName, []string{alert.Actor.Name, area}, url, alert.Actor.Avatar, alert.ASID), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildAlertString(msg string, sub []string, path string, avatar string, asid int) string {
|
func buildAlertString(msg string, sub []string, path, avatar string, asid int) string {
|
||||||
var substring string
|
var subString string
|
||||||
for _, item := range sub {
|
for _, item := range sub {
|
||||||
substring += "\"" + escapeTextInJson(item) + "\","
|
subString += "\"" + escapeTextInJson(item) + "\","
|
||||||
}
|
}
|
||||||
if len(substring) > 0 {
|
if len(subString) > 0 {
|
||||||
substring = substring[:len(substring)-1]
|
subString = subString[:len(subString)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
return `{"msg":"` + escapeTextInJson(msg) + `","sub":[` + substring + `],"path":"` + escapeTextInJson(path) + `","avatar":"` + escapeTextInJson(avatar) + `","id":` + strconv.Itoa(asid) + `}`
|
return `{"msg":"` + escapeTextInJson(msg) + `","sub":[` + subString + `],"path":"` + escapeTextInJson(path) + `","avatar":"` + escapeTextInJson(avatar) + `","id":` + strconv.Itoa(asid) + `}`
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddActivityAndNotifyAll(actor int, targetUser int, event string, elementType string, elementID int) error {
|
func AddActivityAndNotifyAll(a Alert) error {
|
||||||
id, err := Activity.Add(Alert{ActorID: actor, TargetUserID: targetUser, Event: event, ElementType: elementType, ElementID: elementID})
|
id, err := Activity.Add(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return NotifyWatchers(id)
|
return NotifyWatchers(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddActivityAndNotifyTarget(alert Alert) error {
|
func AddActivityAndNotifyTarget(a Alert) error {
|
||||||
id, err := Activity.Add(alert)
|
id, err := Activity.Add(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = NotifyOne(alert.TargetUserID, id)
|
err = NotifyOne(a.TargetUserID, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
alert.ASID = id
|
a.ASID = id
|
||||||
|
|
||||||
// Live alerts, if the target is online and WebSockets is enabled
|
// Live alerts, if the target is online and WebSockets is enabled
|
||||||
if EnableWebsockets {
|
if EnableWebsockets {
|
||||||
go func() {
|
go func() {
|
||||||
_ = WsHub.pushAlert(alert.TargetUserID, alert)
|
_ = WsHub.pushAlert(a.TargetUserID, a)
|
||||||
//fmt.Println("err:",err)
|
//fmt.Println("err:",err)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotifyOne(watcher int, asid int) error {
|
func NotifyOne(watcher, asid int) error {
|
||||||
_, err := alertStmts.notifyOne.Exec(watcher, asid)
|
_, err := alertStmts.notifyOne.Exec(watcher, asid)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -236,3 +239,7 @@ func notifyWatchers(asid int) {
|
||||||
}
|
}
|
||||||
_ = WsHub.pushAlerts(uids, alert)
|
_ = WsHub.pushAlerts(uids, alert)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DismissAlert(uid, aid int) {
|
||||||
|
_ = WsHub.PushMessage(uid, `{"event":"dismiss-alert","id":`+strconv.Itoa(aid)+`}`)
|
||||||
|
}
|
|
@ -3,8 +3,8 @@ package common
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
qgen "github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
)
|
)
|
||||||
|
@ -28,12 +28,12 @@ type AttachmentStore interface {
|
||||||
MiniGetList(originTable string, originID int) (alist []*MiniAttachment, err error)
|
MiniGetList(originTable string, originID int) (alist []*MiniAttachment, err error)
|
||||||
BulkMiniGetList(originTable string, ids []int) (amap map[int][]*MiniAttachment, err error)
|
BulkMiniGetList(originTable string, ids []int) (amap map[int][]*MiniAttachment, err error)
|
||||||
Add(sectionID int, sectionTable string, originID int, originTable string, uploadedBy int, path, extra string) (int, error)
|
Add(sectionID int, sectionTable string, originID int, originTable string, uploadedBy int, path, extra string) (int, error)
|
||||||
MoveTo(sectionID int, originID int, originTable string) error
|
MoveTo(sectionID, originID int, originTable string) error
|
||||||
MoveToByExtra(sectionID int, originTable, extra string) error
|
MoveToByExtra(sectionID int, originTable, extra string) error
|
||||||
Count() 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(id int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultAttachmentStore struct {
|
type DefaultAttachmentStore struct {
|
||||||
|
@ -55,10 +55,10 @@ func NewDefaultAttachmentStore(acc *qgen.Accumulator) (*DefaultAttachmentStore,
|
||||||
getByObj: acc.Select(a).Columns("attachID, sectionID, uploadedBy, path, extra").Where("originTable = ? AND originID = ?").Prepare(),
|
getByObj: acc.Select(a).Columns("attachID, sectionID, uploadedBy, path, extra").Where("originTable = ? AND originID = ?").Prepare(),
|
||||||
add: acc.Insert(a).Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path, extra").Fields("?,?,?,?,?,?,?").Prepare(),
|
add: acc.Insert(a).Columns("sectionID, sectionTable, originID, originTable, uploadedBy, path, extra").Fields("?,?,?,?,?,?,?").Prepare(),
|
||||||
count: acc.Count(a).Prepare(),
|
count: acc.Count(a).Prepare(),
|
||||||
countIn: acc.Count(a).Where("originTable = ? and originID = ?").Prepare(),
|
countIn: acc.Count(a).Where("originTable=? and originID=?").Prepare(),
|
||||||
countInPath: acc.Count(a).Where("path = ?").Prepare(),
|
countInPath: acc.Count(a).Where("path = ?").Prepare(),
|
||||||
move: acc.Update(a).Set("sectionID = ?").Where("originID = ? AND originTable = ?").Prepare(),
|
move: acc.Update(a).Set("sectionID=?").Where("originID=? AND originTable=?").Prepare(),
|
||||||
moveByExtra: acc.Update(a).Set("sectionID = ?").Where("originTable = ? AND extra = ?").Prepare(),
|
moveByExtra: acc.Update(a).Set("sectionID=?").Where("originTable=? AND extra=?").Prepare(),
|
||||||
delete: acc.Delete(a).Where("attachID=?").Prepare(),
|
delete: acc.Delete(a).Where("attachID=?").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ func (s *DefaultAttachmentStore) Add(sectionID int, sectionTable string, originI
|
||||||
return int(lid), err
|
return int(lid), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultAttachmentStore) MoveTo(sectionID int, originID int, originTable string) error {
|
func (s *DefaultAttachmentStore) MoveTo(sectionID, originID int, originTable string) error {
|
||||||
_, err := s.move.Exec(sectionID, originID, originTable)
|
_, err := s.move.Exec(sectionID, originID, originTable)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -185,8 +185,8 @@ func (s *DefaultAttachmentStore) CountInPath(path string) (count int) {
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultAttachmentStore) Delete(aid int) error {
|
func (s *DefaultAttachmentStore) Delete(id int) error {
|
||||||
_, err := s.delete.Exec(aid)
|
_, err := s.delete.Exec(id)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,8 @@ type LogItem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogStore interface {
|
type LogStore interface {
|
||||||
Create(action string, elementID int, elementType string, ip string, actorID int) (err error)
|
Create(action string, elementID int, elementType, ip string, actorID int) (err error)
|
||||||
CreateExtra(action string, elementID int, elementType string, ip string, actorID int, extra string) (err error)
|
CreateExtra(action string, elementID int, elementType, ip string, actorID int, extra string) (err error)
|
||||||
Count() int
|
Count() int
|
||||||
GetOffset(offset, perPage int) (logs []LogItem, err error)
|
GetOffset(offset, perPage int) (logs []LogItem, err error)
|
||||||
}
|
}
|
||||||
|
@ -43,11 +43,11 @@ func NewModLogStore(acc *qgen.Accumulator) (*SQLModLogStore, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make a store for this?
|
// TODO: Make a store for this?
|
||||||
func (s *SQLModLogStore) Create(action string, elementID int, elementType string, ip string, actorID int) (err error) {
|
func (s *SQLModLogStore) Create(action string, elementID int, elementType, ip string, actorID int) (err error) {
|
||||||
return s.CreateExtra(action, elementID, elementType, ip, actorID, "")
|
return s.CreateExtra(action, elementID, elementType, ip, actorID, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLModLogStore) CreateExtra(action string, elementID int, elementType string, ip string, actorID int, extra string) (err error) {
|
func (s *SQLModLogStore) CreateExtra(action string, elementID int, elementType, ip string, actorID int, extra string) (err error) {
|
||||||
_, err = s.create.Exec(action, elementID, elementType, ip, actorID, extra)
|
_, err = s.create.Exec(action, elementID, elementType, ip, actorID, extra)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -99,11 +99,11 @@ func NewAdminLogStore(acc *qgen.Accumulator) (*SQLAdminLogStore, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Make a store for this?
|
// TODO: Make a store for this?
|
||||||
func (s *SQLAdminLogStore) Create(action string, elementID int, elementType string, ip string, actorID int) (err error) {
|
func (s *SQLAdminLogStore) Create(action string, elementID int, elementType, ip string, actorID int) (err error) {
|
||||||
return s.CreateExtra(action, elementID, elementType, ip, actorID, "")
|
return s.CreateExtra(action, elementID, elementType, ip, actorID, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLAdminLogStore) CreateExtra(action string, elementID int, elementType string, ip string, actorID int, extra string) (err error) {
|
func (s *SQLAdminLogStore) CreateExtra(action string, elementID int, elementType, ip string, actorID int, extra string) (err error) {
|
||||||
_, err = s.create.Exec(action, elementID, elementType, ip, actorID, extra)
|
_, err = s.create.Exec(action, elementID, elementType, ip, actorID, extra)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,14 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
|
meta "github.com/Azareal/Gosora/common/meta"
|
||||||
)
|
)
|
||||||
|
|
||||||
var SoftwareVersion = Version{Major: 0, Minor: 3, Patch: 0, Tag: "dev"}
|
var SoftwareVersion = Version{Major: 0, Minor: 3, Patch: 0, Tag: "dev"}
|
||||||
|
|
||||||
|
var Meta meta.MetaStore
|
||||||
|
|
||||||
// nolint I don't want to write comments for each of these o.o
|
// nolint I don't want to write comments for each of these o.o
|
||||||
const Hour int = 60 * 60
|
const Hour int = 60 * 60
|
||||||
const Day int = Hour * 24
|
const Day int = Hour * 24
|
||||||
|
@ -57,7 +60,7 @@ type StringList []string
|
||||||
// TODO: Let admins manage this from the Control Panel
|
// TODO: Let admins manage this from the Control Panel
|
||||||
// apng is commented out for now, as we have no way of re-encoding it into a smaller file
|
// apng is commented out for now, as we have no way of re-encoding it into a smaller file
|
||||||
var AllowedFileExts = StringList{
|
var AllowedFileExts = StringList{
|
||||||
"png", "jpg", "jpe","jpeg","jif","jfi","jfif", "svg", "bmp", "gif", "tiff","tif", "webp", /*"apng",*/ // images
|
"png", "jpg", "jpe", "jpeg", "jif", "jfi", "jfif", "svg", "bmp", "gif", "tiff", "tif", "webp", /*"apng",*/ // images
|
||||||
|
|
||||||
"txt", "xml", "json", "yaml", "toml", "ini", "md", "html", "rtf", "js", "py", "rb", "css", "scss", "less", "eqcss", "pcss", "java", "ts", "cs", "c", "cc", "cpp", "cxx", "C", "c++", "h", "hh", "hpp", "hxx", "h++", "rs", "rlib", "htaccess", "gitignore", /*"go","php",*/ // text
|
"txt", "xml", "json", "yaml", "toml", "ini", "md", "html", "rtf", "js", "py", "rb", "css", "scss", "less", "eqcss", "pcss", "java", "ts", "cs", "c", "cc", "cpp", "cxx", "C", "c++", "h", "hh", "hpp", "hxx", "h++", "rs", "rlib", "htaccess", "gitignore", /*"go","php",*/ // text
|
||||||
|
|
||||||
|
@ -68,7 +71,7 @@ var AllowedFileExts = StringList{
|
||||||
"bz2", "zip", "zipx", "gz", "7z", "tar", "cab", "rar", "kgb", "pea", "xz", "zz", // archives
|
"bz2", "zip", "zipx", "gz", "7z", "tar", "cab", "rar", "kgb", "pea", "xz", "zz", // archives
|
||||||
}
|
}
|
||||||
var ImageFileExts = StringList{
|
var ImageFileExts = StringList{
|
||||||
"png", "jpg", "jpe","jpeg","jif","jfi","jfif", "svg", "bmp", "gif", "tiff","tif", "webp", /* "apng",*/
|
"png", "jpg", "jpe", "jpeg", "jif", "jfi", "jfif", "svg", "bmp", "gif", "tiff", "tif", "webp", /* "apng",*/
|
||||||
}
|
}
|
||||||
var ArchiveFileExts = StringList{
|
var ArchiveFileExts = StringList{
|
||||||
"bz2", "zip", "zipx", "gz", "7z", "tar", "cab", "rar", "kgb", "pea", "xz", "zz",
|
"bz2", "zip", "zipx", "gz", "7z", "tar", "cab", "rar", "kgb", "pea", "xz", "zz",
|
||||||
|
@ -152,3 +155,25 @@ func Log(args ...interface{}) {
|
||||||
func Logf(str string, args ...interface{}) {
|
func Logf(str string, args ...interface{}) {
|
||||||
log.Printf(str, args...)
|
log.Printf(str, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Countf(stmt *sql.Stmt, args ...interface{}) (count int) {
|
||||||
|
err := stmt.QueryRow(args...).Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
LogError(err)
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func eachall(stmt *sql.Stmt, f func(r *sql.Rows) error) error {
|
||||||
|
rows, err := stmt.Query()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
if err := f(rows); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows.Err()
|
||||||
|
}
|
|
@ -32,17 +32,18 @@ type ConvoStmts struct {
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||||
|
cpo := "conversations_posts"
|
||||||
convoStmts = ConvoStmts{
|
convoStmts = ConvoStmts{
|
||||||
fetchPost: acc.Select("conversations_posts").Columns("cid, body, post, createdBy").Where("pid = ?").Prepare(),
|
fetchPost: acc.Select(cpo).Columns("cid,body,post,createdBy").Where("pid=?").Prepare(),
|
||||||
getPosts: acc.Select("conversations_posts").Columns("pid, body, post, createdBy").Where("cid = ?").Limit("?,?").Prepare(),
|
getPosts: acc.Select(cpo).Columns("pid,body,post,createdBy").Where("cid=?").Limit("?,?").Prepare(),
|
||||||
countPosts: acc.Count("conversations_posts").Where("cid = ?").Prepare(),
|
countPosts: acc.Count(cpo).Where("cid=?").Prepare(),
|
||||||
edit: acc.Update("conversations").Set("lastReplyBy = ?, lastReplyAt = ?").Where("cid = ?").Prepare(),
|
edit: acc.Update("conversations").Set("lastReplyBy=?,lastReplyAt=?").Where("cid=?").Prepare(),
|
||||||
create: acc.Insert("conversations").Columns("createdAt, lastReplyAt").Fields("UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(),
|
create: acc.Insert("conversations").Columns("createdAt,lastReplyAt").Fields("UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(),
|
||||||
has: acc.Count("conversations_participants").Where("uid = ? AND cid = ?").Prepare(),
|
has: acc.Count("conversations_participants").Where("uid=? AND cid=?").Prepare(),
|
||||||
|
|
||||||
editPost: acc.Update("conversations_posts").Set("body = ?, post = ?").Where("pid = ?").Prepare(),
|
editPost: acc.Update(cpo).Set("body=?,post=?").Where("pid=?").Prepare(),
|
||||||
createPost: acc.Insert("conversations_posts").Columns("cid, body, post, createdBy").Fields("?,?,?,?").Prepare(),
|
createPost: acc.Insert(cpo).Columns("cid,body,post,createdBy").Fields("?,?,?,?").Prepare(),
|
||||||
deletePost: acc.Delete("conversations_posts").Where("pid = ?").Prepare(),
|
deletePost: acc.Delete(cpo).Where("pid=?").Prepare(),
|
||||||
|
|
||||||
getUsers: acc.Select("conversations_participants").Columns("uid").Where("cid = ?").Prepare(),
|
getUsers: acc.Select("conversations_participants").Columns("uid").Where("cid = ?").Prepare(),
|
||||||
}
|
}
|
||||||
|
@ -138,7 +139,7 @@ type ConversationExtra struct {
|
||||||
|
|
||||||
type ConversationStore interface {
|
type ConversationStore interface {
|
||||||
Get(id int) (*Conversation, error)
|
Get(id int) (*Conversation, error)
|
||||||
GetUser(uid int, offset int) (cos []*Conversation, err error)
|
GetUser(uid, offset int) (cos []*Conversation, err error)
|
||||||
GetUserExtra(uid int, offset int) (cos []*ConversationExtra, err error)
|
GetUserExtra(uid int, offset int) (cos []*ConversationExtra, err error)
|
||||||
GetUserCount(uid int) (count int)
|
GetUserCount(uid int) (count int)
|
||||||
Delete(id int) error
|
Delete(id int) error
|
||||||
|
@ -160,12 +161,12 @@ type DefaultConversationStore struct {
|
||||||
|
|
||||||
func NewDefaultConversationStore(acc *qgen.Accumulator) (*DefaultConversationStore, error) {
|
func NewDefaultConversationStore(acc *qgen.Accumulator) (*DefaultConversationStore, error) {
|
||||||
return &DefaultConversationStore{
|
return &DefaultConversationStore{
|
||||||
get: acc.Select("conversations").Columns("createdBy, createdAt, lastReplyBy, lastReplyAt").Where("cid = ?").Prepare(),
|
get: acc.Select("conversations").Columns("createdBy, createdAt, lastReplyBy, lastReplyAt").Where("cid=?").Prepare(),
|
||||||
getUser: acc.SimpleInnerJoin("conversations_participants AS cp", "conversations AS c", "cp.cid, c.createdBy, c.createdAt, c.lastReplyBy, c.lastReplyAt", "cp.cid = c.cid", "cp.uid = ?", "c.lastReplyAt DESC, c.createdAt DESC, c.cid DESC", "?,?"),
|
getUser: acc.SimpleInnerJoin("conversations_participants AS cp", "conversations AS c", "cp.cid, c.createdBy, c.createdAt, c.lastReplyBy, c.lastReplyAt", "cp.cid=c.cid", "cp.uid=?", "c.lastReplyAt DESC, c.createdAt DESC, c.cid DESC", "?,?"),
|
||||||
getUserCount: acc.Count("conversations_participants").Where("uid = ?").Prepare(),
|
getUserCount: acc.Count("conversations_participants").Where("uid=?").Prepare(),
|
||||||
delete: acc.Delete("conversations").Where("cid = ?").Prepare(),
|
delete: acc.Delete("conversations").Where("cid=?").Prepare(),
|
||||||
deletePosts: acc.Delete("conversations_posts").Where("cid = ?").Prepare(),
|
deletePosts: acc.Delete("conversations_posts").Where("cid=?").Prepare(),
|
||||||
deleteParticipants: acc.Delete("conversations_participants").Where("cid = ?").Prepare(),
|
deleteParticipants: acc.Delete("conversations_participants").Where("cid=?").Prepare(),
|
||||||
create: acc.Insert("conversations").Columns("createdBy, createdAt, lastReplyAt").Fields("?,UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(),
|
create: acc.Insert("conversations").Columns("createdBy, createdAt, lastReplyAt").Fields("?,UTC_TIMESTAMP(),UTC_TIMESTAMP()").Prepare(),
|
||||||
addParticipant: acc.Insert("conversations_participants").Columns("uid, cid").Fields("?,?").Prepare(),
|
addParticipant: acc.Insert("conversations_participants").Columns("uid, cid").Fields("?,?").Prepare(),
|
||||||
count: acc.Count("conversations").Prepare(),
|
count: acc.Count("conversations").Prepare(),
|
||||||
|
@ -178,7 +179,7 @@ func (s *DefaultConversationStore) Get(id int) (*Conversation, error) {
|
||||||
return co, err
|
return co, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultConversationStore) GetUser(uid int, offset int) (cos []*Conversation, err error) {
|
func (s *DefaultConversationStore) GetUser(uid, offset int) (cos []*Conversation, err error) {
|
||||||
rows, err := s.getUser.Query(uid, offset, Config.ItemsPerPage)
|
rows, err := s.getUser.Query(uid, offset, Config.ItemsPerPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -197,7 +198,7 @@ func (s *DefaultConversationStore) GetUser(uid int, offset int) (cos []*Conversa
|
||||||
return cos, rows.Err()
|
return cos, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultConversationStore) GetUserExtra(uid int, offset int) (cos []*ConversationExtra, err error) {
|
func (s *DefaultConversationStore) GetUserExtra(uid, offset int) (cos []*ConversationExtra, err error) {
|
||||||
raw, err := s.GetUser(uid, offset)
|
raw, err := s.GetUser(uid, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
p "github.com/Azareal/Gosora/common/phrases"
|
p "github.com/Azareal/Gosora/common/phrases"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SendActivationEmail(username string, email string, token string) error {
|
func SendActivationEmail(username, email, token string) error {
|
||||||
schema := "http"
|
schema := "http"
|
||||||
if Config.SslSchema {
|
if Config.SslSchema {
|
||||||
schema += "s"
|
schema += "s"
|
||||||
|
@ -21,30 +21,30 @@ func SendActivationEmail(username string, email string, token string) error {
|
||||||
return SendEmail(email, subject, msg)
|
return SendEmail(email, subject, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendValidationEmail(username string, email string, token string) error {
|
func SendValidationEmail(username, email, token string) error {
|
||||||
schema := "http"
|
schema := "http"
|
||||||
if Config.SslSchema {
|
if Config.SslSchema {
|
||||||
schema += "s"
|
schema += "s"
|
||||||
}
|
}
|
||||||
r := func(body *string) func(name, val string) {
|
r := func(body *string) func(name, val string) {
|
||||||
return func(name, val string) {
|
return func(name, val string) {
|
||||||
*body = strings.Replace(*body,"{{"+name+"}}",val,-1)
|
*body = strings.Replace(*body, "{{"+name+"}}", val, -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
subject := p.GetAccountPhrase("ValidateEmailSubject")
|
subject := p.GetAccountPhrase("ValidateEmailSubject")
|
||||||
r1 := r(&subject)
|
r1 := r(&subject)
|
||||||
r1("name",Site.Name)
|
r1("name", Site.Name)
|
||||||
body := p.GetAccountPhrase("ValidateEmailBody")
|
body := p.GetAccountPhrase("ValidateEmailBody")
|
||||||
r2 := r(&body)
|
r2 := r(&body)
|
||||||
r2("username",username)
|
r2("username", username)
|
||||||
r2("schema",schema)
|
r2("schema", schema)
|
||||||
r2("url",Site.URL)
|
r2("url", Site.URL)
|
||||||
r2("token",token)
|
r2("token", token)
|
||||||
return SendEmail(email, subject, body)
|
return SendEmail(email, subject, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Refactor this
|
// TODO: Refactor this
|
||||||
func SendEmail(email string, subject string, msg string) (err error) {
|
func SendEmail(email, subject, msg string) (err error) {
|
||||||
// This hook is useful for plugin_sendmail or for testing tools. Possibly to hook it into some sort of mail server?
|
// This hook is useful for plugin_sendmail or for testing tools. Possibly to hook it into some sort of mail server?
|
||||||
ret, hasHook := GetHookTable().VhookNeedHook("email_send_intercept", email, subject, msg)
|
ret, hasHook := GetHookTable().VhookNeedHook("email_send_intercept", email, subject, msg)
|
||||||
if hasHook {
|
if hasHook {
|
||||||
|
|
|
@ -18,28 +18,28 @@ type Email struct {
|
||||||
|
|
||||||
type EmailStore interface {
|
type EmailStore interface {
|
||||||
// TODO: Add an autoincrement key
|
// TODO: Add an autoincrement key
|
||||||
Get(user *User, email string) (Email, error)
|
Get(u *User, email string) (Email, error)
|
||||||
GetEmailsByUser(user *User) (emails []Email, err error)
|
GetEmailsByUser(u *User) (emails []Email, err error)
|
||||||
Add(uid int, email, token string) error
|
Add(uid int, email, token string) error
|
||||||
Delete(uid int, email string) error
|
Delete(uid int, email string) error
|
||||||
VerifyEmail(email string) error
|
VerifyEmail(email string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultEmailStore struct {
|
type DefaultEmailStore struct {
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
getEmailsByUser *sql.Stmt
|
getEmailsByUser *sql.Stmt
|
||||||
add *sql.Stmt
|
add *sql.Stmt
|
||||||
delete *sql.Stmt
|
delete *sql.Stmt
|
||||||
verifyEmail *sql.Stmt
|
verifyEmail *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultEmailStore(acc *qgen.Accumulator) (*DefaultEmailStore, error) {
|
func NewDefaultEmailStore(acc *qgen.Accumulator) (*DefaultEmailStore, error) {
|
||||||
e := "emails"
|
e := "emails"
|
||||||
return &DefaultEmailStore{
|
return &DefaultEmailStore{
|
||||||
get: acc.Select(e).Columns("email,validated,token").Where("uid=? AND email=?").Prepare(),
|
get: acc.Select(e).Columns("email,validated,token").Where("uid=? AND email=?").Prepare(),
|
||||||
getEmailsByUser: acc.Select(e).Columns("email,validated,token").Where("uid=?").Prepare(),
|
getEmailsByUser: acc.Select(e).Columns("email,validated,token").Where("uid=?").Prepare(),
|
||||||
add: acc.Insert(e).Columns("uid,email,validated,token").Fields("?,?,?,?").Prepare(),
|
add: acc.Insert(e).Columns("uid,email,validated,token").Fields("?,?,?,?").Prepare(),
|
||||||
delete: acc.Delete(e).Where("uid=? AND email=?").Prepare(),
|
delete: acc.Delete(e).Where("uid=? AND email=?").Prepare(),
|
||||||
|
|
||||||
// Need to fix this: Empty string isn't working, it gets set to 1 instead x.x -- Has this been fixed?
|
// Need to fix this: Empty string isn't working, it gets set to 1 instead x.x -- Has this been fixed?
|
||||||
verifyEmail: acc.Update(e).Set("validated=1,token=''").Where("email=?").Prepare(),
|
verifyEmail: acc.Update(e).Set("validated=1,token=''").Where("email=?").Prepare(),
|
||||||
|
@ -47,7 +47,7 @@ func NewDefaultEmailStore(acc *qgen.Accumulator) (*DefaultEmailStore, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultEmailStore) Get(user *User, email string) (Email, error) {
|
func (s *DefaultEmailStore) Get(user *User, email string) (Email, error) {
|
||||||
e := Email{UserID:user.ID, Primary:email !="" && user.Email==email}
|
e := Email{UserID: user.ID, Primary: email != "" && user.Email == email}
|
||||||
err := s.get.QueryRow(user.ID, email).Scan(&e.Email, &e.Validated, &e.Token)
|
err := s.get.QueryRow(user.ID, email).Scan(&e.Email, &e.Validated, &e.Token)
|
||||||
return e, err
|
return e, err
|
||||||
}
|
}
|
||||||
|
@ -72,13 +72,13 @@ func (s *DefaultEmailStore) GetEmailsByUser(user *User) (emails []Email, err err
|
||||||
return emails, rows.Err()
|
return emails, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultEmailStore) Add(uid int, email string, token string) error {
|
func (s *DefaultEmailStore) Add(uid int, email, token string) error {
|
||||||
_, err := s.add.Exec(uid, email, 0, token)
|
_, err := s.add.Exec(uid, email, 0, token)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultEmailStore) Delete(uid int, email string) error {
|
func (s *DefaultEmailStore) Delete(uid int, email string) error {
|
||||||
_, err := s.delete.Exec(uid,email)
|
_, err := s.delete.Exec(uid, email)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ func (list SFileList) JSTmplInit() error {
|
||||||
tmplName := strings.TrimSuffix(path, ".jgo")
|
tmplName := strings.TrimSuffix(path, ".jgo")
|
||||||
shortName := strings.TrimPrefix(tmplName, "template_")
|
shortName := strings.TrimPrefix(tmplName, "template_")
|
||||||
|
|
||||||
replace := func(data []byte, replaceThis string, withThis string) []byte {
|
replace := func(data []byte, replaceThis, withThis string) []byte {
|
||||||
return bytes.Replace(data, []byte(replaceThis), []byte(withThis), -1)
|
return bytes.Replace(data, []byte(replaceThis), []byte(withThis), -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ func (list SFileList) JSTmplInit() error {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
// ? Can we just use a regex? I'm thinking of going more efficient, or just outright rolling wasm, this is a temp hack in a place where performance doesn't particularly matter
|
// ? Can we just use a regex? I'm thinking of going more efficient, or just outright rolling wasm, this is a temp hack in a place where performance doesn't particularly matter
|
||||||
each := func(phrase string, handle func(index int)) {
|
each := func(phrase string, h func(index int)) {
|
||||||
//fmt.Println("find each '" + phrase + "'")
|
//fmt.Println("find each '" + phrase + "'")
|
||||||
index := endBrace
|
index := endBrace
|
||||||
if index < 0 {
|
if index < 0 {
|
||||||
|
@ -121,7 +121,7 @@ func (list SFileList) JSTmplInit() error {
|
||||||
if !foundIt {
|
if !foundIt {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
handle(index)
|
h(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
each("strconv.Itoa(", func(index int) {
|
each("strconv.Itoa(", func(index int) {
|
||||||
|
@ -292,7 +292,7 @@ func (list SFileList) Init() error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (list SFileList) Add(path string, prefix string) error {
|
func (list SFileList) Add(path, prefix string) error {
|
||||||
data, err := ioutil.ReadFile(path)
|
data, err := ioutil.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -27,9 +27,9 @@ type Forum struct {
|
||||||
Link string
|
Link string
|
||||||
Name string
|
Name string
|
||||||
Desc string
|
Desc string
|
||||||
Tmpl string
|
Tmpl string
|
||||||
Active bool
|
Active bool
|
||||||
Order int
|
Order int
|
||||||
Preset string
|
Preset string
|
||||||
ParentID int
|
ParentID int
|
||||||
ParentType string
|
ParentType string
|
||||||
|
@ -60,8 +60,8 @@ var forumStmts ForumStmts
|
||||||
func init() {
|
func init() {
|
||||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||||
forumStmts = ForumStmts{
|
forumStmts = ForumStmts{
|
||||||
update: acc.Update("forums").Set("name = ?, desc = ?, active = ?, preset = ?").Where("fid = ?").Prepare(),
|
update: acc.Update("forums").Set("name=?,desc=?,active=?,preset=?").Where("fid=?").Prepare(),
|
||||||
setPreset: acc.Update("forums").Set("preset = ?").Where("fid = ?").Prepare(),
|
setPreset: acc.Update("forums").Set("preset=?").Where("fid=?").Prepare(),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
})
|
})
|
||||||
|
@ -74,7 +74,7 @@ func (f *Forum) Copy() (fcopy Forum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write tests for this
|
// TODO: Write tests for this
|
||||||
func (f *Forum) Update(name string, desc string, active bool, preset string) error {
|
func (f *Forum) Update(name, desc string, active bool, preset string) error {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = f.Name
|
name = f.Name
|
||||||
}
|
}
|
||||||
|
@ -137,6 +137,7 @@ func (sf SortForum) Len() int {
|
||||||
func (sf SortForum) Swap(i, j int) {
|
func (sf SortForum) Swap(i, j int) {
|
||||||
sf[i], sf[j] = sf[j], sf[i]
|
sf[i], sf[j] = sf[j], sf[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
/*func (sf SortForum) Less(i,j int) bool {
|
/*func (sf SortForum) Less(i,j int) bool {
|
||||||
l := sf.less(i,j)
|
l := sf.less(i,j)
|
||||||
if l {
|
if l {
|
||||||
|
@ -156,7 +157,7 @@ func (sf SortForum) Less(i, j int) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! Don't use this outside of tests and possibly template_init.go
|
// ! Don't use this outside of tests and possibly template_init.go
|
||||||
func BlankForum(fid int, link string, name string, desc string, active bool, preset string, parentID int, parentType string, topicCount int) *Forum {
|
func BlankForum(fid int, link, name, desc string, active bool, preset string, parentID int, parentType string, topicCount int) *Forum {
|
||||||
return &Forum{ID: fid, Link: link, Name: name, Desc: desc, Active: active, Preset: preset, ParentID: parentID, ParentType: parentType, TopicCount: topicCount}
|
return &Forum{ID: fid, Link: link, Name: name, Desc: desc, Active: active, Preset: preset, ParentID: parentID, ParentType: parentType, TopicCount: topicCount}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,9 @@ func NewDefaultIPSearcher() (*DefaultIPSearcher, error) {
|
||||||
uu := "users"
|
uu := "users"
|
||||||
return &DefaultIPSearcher{
|
return &DefaultIPSearcher{
|
||||||
searchUsers: acc.Select(uu).Columns("uid").Where("last_ip=? OR last_ip LIKE CONCAT('%-',?)").Prepare(),
|
searchUsers: acc.Select(uu).Columns("uid").Where("last_ip=? OR last_ip LIKE CONCAT('%-',?)").Prepare(),
|
||||||
searchTopics: acc.Select(uu).Columns("uid").InQ("uid", acc.Select("topics").Columns("createdBy").Where("ipaddress=?")).Prepare(),
|
searchTopics: acc.Select(uu).Columns("uid").InQ("uid", acc.Select("topics").Columns("createdBy").Where("ip=?")).Prepare(),
|
||||||
searchReplies: acc.Select(uu).Columns("uid").InQ("uid", acc.Select("replies").Columns("createdBy").Where("ipaddress=?")).Prepare(),
|
searchReplies: acc.Select(uu).Columns("uid").InQ("uid", acc.Select("replies").Columns("createdBy").Where("ip=?")).Prepare(),
|
||||||
searchUsersReplies: acc.Select(uu).Columns("uid").InQ("uid", acc.Select("users_replies").Columns("createdBy").Where("ipaddress=?")).Prepare(),
|
searchUsersReplies: acc.Select(uu).Columns("uid").InQ("uid", acc.Select("users_replies").Columns("createdBy").Where("ip=?")).Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,22 +10,25 @@ var Likes LikeStore
|
||||||
|
|
||||||
type LikeStore interface {
|
type LikeStore interface {
|
||||||
BulkExists(ids []int, sentBy int, targetType string) ([]int, error)
|
BulkExists(ids []int, sentBy int, targetType string) ([]int, error)
|
||||||
|
Delete(targetID int, targetType string) error
|
||||||
Count() (count int)
|
Count() (count int)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultLikeStore struct {
|
type DefaultLikeStore struct {
|
||||||
count *sql.Stmt
|
count *sql.Stmt
|
||||||
|
delete *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultLikeStore(acc *qgen.Accumulator) (*DefaultLikeStore, error) {
|
func NewDefaultLikeStore(acc *qgen.Accumulator) (*DefaultLikeStore, error) {
|
||||||
return &DefaultLikeStore{
|
return &DefaultLikeStore{
|
||||||
count: acc.Count("likes").Prepare(),
|
count: acc.Count("likes").Prepare(),
|
||||||
|
delete: acc.Delete("likes").Where("targetItem=? AND targetType=?").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
func (s *DefaultLikeStore) BulkExists(ids []int, sentBy int, targetType string) (eids []int, err error) {
|
func (s *DefaultLikeStore) BulkExists(ids []int, sentBy int, targetType string) (eids []int, err error) {
|
||||||
rows, err := qgen.NewAcc().Select("likes").Columns("targetItem").Where("sentBy = ? AND targetType = ?").In("targetItem", ids).Query(sentBy, targetType)
|
rows, err := qgen.NewAcc().Select("likes").Columns("targetItem").Where("sentBy=? AND targetType=?").In("targetItem", ids).Query(sentBy, targetType)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -43,6 +46,11 @@ func (s *DefaultLikeStore) BulkExists(ids []int, sentBy int, targetType string)
|
||||||
return eids, rows.Err()
|
return eids, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DefaultLikeStore) Delete(targetID int, targetType string) error {
|
||||||
|
_, err := s.delete.Exec(targetID, targetType)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
// Count returns the total number of likes globally
|
// Count returns the total number of likes globally
|
||||||
func (s *DefaultLikeStore) Count() (count int) {
|
func (s *DefaultLikeStore) Count() (count int) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Menus *DefaultMenuStore
|
var Menus *DefaultMenuStore
|
||||||
|
@ -40,7 +40,7 @@ func (s *DefaultMenuStore) Get(mid int) (*MenuListHolder, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultMenuStore) Items(mid int) (mlist MenuItemList, err error) {
|
func (s *DefaultMenuStore) Items(mid int) (mlist MenuItemList, err error) {
|
||||||
err = qgen.NewAcc().Select("menu_items").Columns("miid,name,htmlID,cssClass,position,path,aria,tooltip,order,tmplName,guestOnly,memberOnly,staffOnly,adminOnly").Where("mid = " + strconv.Itoa(mid)).Orderby("order ASC").Each(func(rows *sql.Rows) error {
|
err = qgen.NewAcc().Select("menu_items").Columns("miid,name,htmlID,cssClass,position,path,aria,tooltip,order,tmplName,guestOnly,memberOnly,staffOnly,adminOnly").Where("mid=" + strconv.Itoa(mid)).Orderby("order ASC").Each(func(rows *sql.Rows) error {
|
||||||
i := MenuItem{MenuID: mid}
|
i := MenuItem{MenuID: mid}
|
||||||
err := rows.Scan(&i.ID, &i.Name, &i.HTMLID, &i.CSSClass, &i.Position, &i.Path, &i.Aria, &i.Tooltip, &i.Order, &i.TmplName, &i.GuestOnly, &i.MemberOnly, &i.SuperModOnly, &i.AdminOnly)
|
err := rows.Scan(&i.ID, &i.Name, &i.HTMLID, &i.CSSClass, &i.Position, &i.Path, &i.Aria, &i.Tooltip, &i.Order, &i.TmplName, &i.GuestOnly, &i.MemberOnly, &i.SuperModOnly, &i.AdminOnly)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import "database/sql"
|
import (
|
||||||
import "github.com/Azareal/Gosora/query_gen"
|
"database/sql"
|
||||||
|
|
||||||
var Meta MetaStore
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
|
)
|
||||||
|
|
||||||
// MetaStore is a simple key-value store for the system to stash things in when needed
|
// MetaStore is a simple key-value store for the system to stash things in when needed
|
||||||
type MetaStore interface {
|
type MetaStore interface {
|
||||||
Get(name string) (val string, err error)
|
Get(name string) (val string, err error)
|
||||||
Set(name string, val string) error
|
Set(name, val string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultMetaStore struct {
|
type DefaultMetaStore struct {
|
||||||
|
@ -32,7 +33,7 @@ func (s *DefaultMetaStore) Get(name string) (val string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use timestamped rows as a more robust method of ensuring data integrity
|
// TODO: Use timestamped rows as a more robust method of ensuring data integrity
|
||||||
func (s *DefaultMetaStore) Set(name string, val string) error {
|
func (s *DefaultMetaStore) Set(name, val string) error {
|
||||||
_, err := s.Get(name)
|
_, err := s.Get(name)
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
_, err := s.add.Exec(name)
|
_, err := s.add.Exec(name)
|
|
@ -31,7 +31,7 @@ func init() {
|
||||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||||
rl := "registration_logs"
|
rl := "registration_logs"
|
||||||
regLogStmts = RegLogStmts{
|
regLogStmts = RegLogStmts{
|
||||||
update: acc.Update(rl).Set("username = ?, email = ?, failureReason = ?, success = ?").Where("rlid = ?").Prepare(),
|
update: acc.Update(rl).Set("username=?, email=?, failureReason=?, success=?").Where("rlid=?").Prepare(),
|
||||||
create: acc.Insert(rl).Columns("username, email, failureReason, success, ipaddress, doneAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
create: acc.Insert(rl).Columns("username, email, failureReason, success, ipaddress, doneAt").Fields("?,?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
|
@ -57,7 +57,7 @@ func (l *RegLogItem) Create() (id int, err error) {
|
||||||
|
|
||||||
type RegLogStore interface {
|
type RegLogStore interface {
|
||||||
Count() (count int)
|
Count() (count int)
|
||||||
GetOffset(offset int, perPage int) (logs []RegLogItem, err error)
|
GetOffset(offset, perPage int) (logs []RegLogItem, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SQLRegLogStore struct {
|
type SQLRegLogStore struct {
|
||||||
|
@ -120,8 +120,8 @@ func init() {
|
||||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||||
ll := "login_logs"
|
ll := "login_logs"
|
||||||
loginLogStmts = LoginLogStmts{
|
loginLogStmts = LoginLogStmts{
|
||||||
update: acc.Update(ll).Set("uid = ?, success = ?").Where("lid = ?").Prepare(),
|
update: acc.Update(ll).Set("uid=?,success=?").Where("lid=?").Prepare(),
|
||||||
create: acc.Insert(ll).Columns("uid, success, ipaddress, doneAt").Fields("?,?,?,UTC_TIMESTAMP()").Prepare(),
|
create: acc.Insert(ll).Columns("uid,success,ipaddress,doneAt").Fields("?,?,?,UTC_TIMESTAMP()").Prepare(),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
})
|
})
|
||||||
|
@ -147,7 +147,7 @@ func (l *LoginLogItem) Create() (id int, err error) {
|
||||||
type LoginLogStore interface {
|
type LoginLogStore interface {
|
||||||
Count() (count int)
|
Count() (count int)
|
||||||
CountUser(uid int) (count int)
|
CountUser(uid int) (count int)
|
||||||
GetOffset(uid int, offset int, perPage int) (logs []LoginLogItem, err error)
|
GetOffset(uid, offset, perPage int) (logs []LoginLogItem, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SQLLoginLogStore struct {
|
type SQLLoginLogStore struct {
|
||||||
|
@ -160,8 +160,8 @@ func NewLoginLogStore(acc *qgen.Accumulator) (*SQLLoginLogStore, error) {
|
||||||
ll := "login_logs"
|
ll := "login_logs"
|
||||||
return &SQLLoginLogStore{
|
return &SQLLoginLogStore{
|
||||||
count: acc.Count(ll).Prepare(),
|
count: acc.Count(ll).Prepare(),
|
||||||
countForUser: acc.Count(ll).Where("uid = ?").Prepare(),
|
countForUser: acc.Count(ll).Where("uid=?").Prepare(),
|
||||||
getOffsetByUser: acc.Select(ll).Columns("lid, success, ipaddress, doneAt").Where("uid = ?").Orderby("doneAt DESC").Limit("?,?").Prepare(),
|
getOffsetByUser: acc.Select(ll).Columns("lid,success,ipaddress,doneAt").Where("uid=?").Orderby("doneAt DESC").Limit("?,?").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ func (s *SQLLoginLogStore) CountUser(uid int) (count int) {
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLLoginLogStore) GetOffset(uid int, offset int, perPage int) (logs []LoginLogItem, err error) {
|
func (s *SQLLoginLogStore) GetOffset(uid, offset, perPage int) (logs []LoginLogItem, err error) {
|
||||||
rows, err := s.getOffsetByUser.Query(uid, offset, perPage)
|
rows, err := s.getOffsetByUser.Query(uid, offset, perPage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return logs, err
|
return logs, err
|
||||||
|
|
|
@ -18,8 +18,8 @@ var customPageStmts CustomPageStmts
|
||||||
func init() {
|
func init() {
|
||||||
DbInits.Add(func(acc *qgen.Accumulator) error {
|
DbInits.Add(func(acc *qgen.Accumulator) error {
|
||||||
customPageStmts = CustomPageStmts{
|
customPageStmts = CustomPageStmts{
|
||||||
update: acc.Update("pages").Set("name = ?, title = ?, body = ?, allowedGroups = ?, menuID = ?").Where("pid = ?").Prepare(),
|
update: acc.Update("pages").Set("name=?,title=?,body=?,allowedGroups=?,menuID=?").Where("pid=?").Prepare(),
|
||||||
create: acc.Insert("pages").Columns("name, title, body, allowedGroups, menuID").Fields("?,?,?,?,?").Prepare(),
|
create: acc.Insert("pages").Columns("name,title,body,allowedGroups,menuID").Fields("?,?,?,?,?").Prepare(),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
})
|
})
|
||||||
|
@ -74,7 +74,7 @@ type PageStore interface {
|
||||||
Count() (count 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, perPage int) (pages []*CustomPage, err error)
|
||||||
Reload(id int) error
|
Reload(id int) error
|
||||||
Delete(id int) error
|
Delete(id int) error
|
||||||
}
|
}
|
||||||
|
@ -91,11 +91,11 @@ type DefaultPageStore struct {
|
||||||
func NewDefaultPageStore(acc *qgen.Accumulator) (*DefaultPageStore, error) {
|
func NewDefaultPageStore(acc *qgen.Accumulator) (*DefaultPageStore, error) {
|
||||||
pa := "pages"
|
pa := "pages"
|
||||||
return &DefaultPageStore{
|
return &DefaultPageStore{
|
||||||
get: acc.Select(pa).Columns("name, title, body, allowedGroups, menuID").Where("pid = ?").Prepare(),
|
get: acc.Select(pa).Columns("name, title, body, allowedGroups, menuID").Where("pid=?").Prepare(),
|
||||||
getByName: acc.Select(pa).Columns("pid, name, title, body, allowedGroups, menuID").Where("name = ?").Prepare(),
|
getByName: acc.Select(pa).Columns("pid, name, title, body, allowedGroups, menuID").Where("name=?").Prepare(),
|
||||||
getOffset: acc.Select(pa).Columns("pid, name, title, body, allowedGroups, menuID").Orderby("pid DESC").Limit("?,?").Prepare(),
|
getOffset: acc.Select(pa).Columns("pid, name, title, body, allowedGroups, menuID").Orderby("pid DESC").Limit("?,?").Prepare(),
|
||||||
count: acc.Count(pa).Prepare(),
|
count: acc.Count(pa).Prepare(),
|
||||||
delete: acc.Delete(pa).Where("pid = ?").Prepare(),
|
delete: acc.Delete(pa).Where("pid=?").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ type TagToAction struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
func tryStepForward(i int, step int, runes []rune) (int, bool) {
|
func tryStepForward(i, step int, runes []rune) (int, bool) {
|
||||||
i += step
|
i += step
|
||||||
if i < len(runes) {
|
if i < len(runes) {
|
||||||
return i, true
|
return i, true
|
||||||
|
@ -117,7 +117,7 @@ func tryStepForward(i int, step int, runes []rune) (int, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
func tryStepBackward(i int, step int, runes []rune) (int, bool) {
|
func tryStepBackward(i, step int, runes []rune) (int, bool) {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
return i, false
|
return i, false
|
||||||
}
|
}
|
||||||
|
@ -369,7 +369,7 @@ func PreparseMessage(msg string) string {
|
||||||
|
|
||||||
// TODO: Test this
|
// TODO: Test this
|
||||||
// TODO: Use this elsewhere in the parser?
|
// TODO: Use this elsewhere in the parser?
|
||||||
func peek(cur int, skip int, runes []rune) rune {
|
func peek(cur, skip int, runes []rune) rune {
|
||||||
if (cur + skip) < len(runes) {
|
if (cur + skip) < len(runes) {
|
||||||
return runes[cur+skip]
|
return runes[cur+skip]
|
||||||
}
|
}
|
||||||
|
@ -972,7 +972,7 @@ func parseMediaString(data string, settings *ParseSettings) (media MediaEmbed, o
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
func CoerceIntString(data string) (res int, length int) {
|
func CoerceIntString(data string) (res, length int) {
|
||||||
if !(data[0] > 47 && data[0] < 58) {
|
if !(data[0] > 47 && data[0] < 58) {
|
||||||
return 0, 1
|
return 0, 1
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,7 +174,7 @@ func PresetToLang(preset string) string {
|
||||||
|
|
||||||
// TODO: Is this racey?
|
// TODO: Is this racey?
|
||||||
// TODO: Test this along with the rest of the perms system
|
// TODO: Test this along with the rest of the perms system
|
||||||
func RebuildGroupPermissions(group *Group) error {
|
func RebuildGroupPermissions(g *Group) error {
|
||||||
var permstr []byte
|
var permstr []byte
|
||||||
log.Print("Reloading a group")
|
log.Print("Reloading a group")
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ func RebuildGroupPermissions(group *Group) error {
|
||||||
}
|
}
|
||||||
defer getGroupPerms.Close()
|
defer getGroupPerms.Close()
|
||||||
|
|
||||||
err = getGroupPerms.QueryRow(group.ID).Scan(&permstr)
|
err = getGroupPerms.QueryRow(g.ID).Scan(&permstr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -197,15 +197,15 @@ func RebuildGroupPermissions(group *Group) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
group.Perms = tmpPerms
|
g.Perms = tmpPerms
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func OverridePerms(perms *Perms, status bool) {
|
func OverridePerms(p *Perms, status bool) {
|
||||||
if status {
|
if status {
|
||||||
*perms = AllPerms
|
*p = AllPerms
|
||||||
} else {
|
} else {
|
||||||
*perms = BlankPerms
|
*p = BlankPerms
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,18 +66,17 @@ func InitPluginLangs() error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
e := func(field string, name string) error {
|
e := func(field, name string) error {
|
||||||
return errors.New("The "+field+" field must not be blank on plugin '" + name + "'")
|
return errors.New("The " + field + " field must not be blank on plugin '" + name + "'")
|
||||||
}
|
}
|
||||||
|
|
||||||
if plugin.UName == "" {
|
if plugin.UName == "" {
|
||||||
return e("UName",pluginItem)
|
return e("UName", pluginItem)
|
||||||
}
|
}
|
||||||
if plugin.Name == "" {
|
if plugin.Name == "" {
|
||||||
return e("Name",pluginItem)
|
return e("Name", pluginItem)
|
||||||
}
|
}
|
||||||
if plugin.Author == "" {
|
if plugin.Author == "" {
|
||||||
return e("Author",pluginItem)
|
return e("Author", pluginItem)
|
||||||
}
|
}
|
||||||
if plugin.Main == "" {
|
if plugin.Main == "" {
|
||||||
return errors.New("Couldn't find a main file for plugin '" + pluginItem + "'")
|
return errors.New("Couldn't find a main file for plugin '" + pluginItem + "'")
|
||||||
|
|
|
@ -22,7 +22,7 @@ type Poll struct {
|
||||||
VoteCount int
|
VoteCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Poll) CastVote(optionIndex int, uid int, ip string) error {
|
func (p *Poll) CastVote(optionIndex, uid int, ip string) error {
|
||||||
return Polls.CastVote(optionIndex, p.ID, uid, ip) // TODO: Move the query into a pollStmts rather than having it in the store
|
return Polls.CastVote(optionIndex, p.ID, uid, ip) // TODO: Move the query into a pollStmts rather than having it in the store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,9 +61,9 @@ func NewDefaultPollStore(cache PollCache) (*DefaultPollStore, error) {
|
||||||
exists: acc.Select("polls").Columns("pollID").Where("pollID = ?").Prepare(),
|
exists: acc.Select("polls").Columns("pollID").Where("pollID = ?").Prepare(),
|
||||||
createPoll: acc.Insert("polls").Columns("parentID, parentTable, type, options").Fields("?,?,?,?").Prepare(),
|
createPoll: acc.Insert("polls").Columns("parentID, parentTable, type, options").Fields("?,?,?,?").Prepare(),
|
||||||
createPollOption: acc.Insert("polls_options").Columns("pollID, option, votes").Fields("?,?,0").Prepare(),
|
createPollOption: acc.Insert("polls_options").Columns("pollID, option, votes").Fields("?,?,0").Prepare(),
|
||||||
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,ip").Fields("?,?,?,UTC_TIMESTAMP(),?").Prepare(),
|
||||||
incVoteCount: acc.Update("polls").Set("votes = votes + 1").Where("pollID = ?").Prepare(),
|
incVoteCount: acc.Update("polls").Set("votes = votes + 1").Where("pollID = ?").Prepare(),
|
||||||
incVoteCountForOption: acc.Update("polls_options").Set("votes = votes + 1").Where("option = ? AND pollID = ?").Prepare(),
|
incVoteCountForOption: acc.Update("polls_options").Set("votes = votes + 1").Where("option=? AND pollID=?").Prepare(),
|
||||||
//count: acc.SimpleCount("polls", "", ""),
|
//count: acc.SimpleCount("polls", "", ""),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package common
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"html"
|
"html"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
qgen "github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
|
@ -45,9 +46,20 @@ func BlankProfileReply(id int) *ProfileReply {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write tests for this
|
// TODO: Write tests for this
|
||||||
// TODO: Remove alerts.
|
|
||||||
func (r *ProfileReply) Delete() error {
|
func (r *ProfileReply) Delete() error {
|
||||||
_, err := profileReplyStmts.delete.Exec(r.ID)
|
_, err := profileReplyStmts.delete.Exec(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO: Better coupling between the two paramsextra queries
|
||||||
|
aids, err := Activity.AidsByParamsExtra("reply", r.ParentID, "user", strconv.Itoa(r.ID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, aid := range aids {
|
||||||
|
DismissAlert(r.ParentID, aid)
|
||||||
|
}
|
||||||
|
err = Activity.DeleteByParamsExtra("reply", r.ParentID, "user", strconv.Itoa(r.ID))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ var Prstore ProfileReplyStore
|
||||||
|
|
||||||
type ProfileReplyStore interface {
|
type ProfileReplyStore interface {
|
||||||
Get(id int) (*ProfileReply, error)
|
Get(id int) (*ProfileReply, error)
|
||||||
|
Exists(id int) bool
|
||||||
Create(profileID int, content string, createdBy int, ip string) (id int, err error)
|
Create(profileID int, content string, createdBy int, ip string) (id int, err error)
|
||||||
Count() (count int)
|
Count() (count int)
|
||||||
}
|
}
|
||||||
|
@ -18,6 +19,7 @@ type ProfileReplyStore interface {
|
||||||
// TODO: Add more methods to this like Create()
|
// TODO: Add more methods to this like Create()
|
||||||
type SQLProfileReplyStore struct {
|
type SQLProfileReplyStore struct {
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
|
exists *sql.Stmt
|
||||||
create *sql.Stmt
|
create *sql.Stmt
|
||||||
count *sql.Stmt
|
count *sql.Stmt
|
||||||
}
|
}
|
||||||
|
@ -25,8 +27,9 @@ type SQLProfileReplyStore struct {
|
||||||
func NewSQLProfileReplyStore(acc *qgen.Accumulator) (*SQLProfileReplyStore, error) {
|
func NewSQLProfileReplyStore(acc *qgen.Accumulator) (*SQLProfileReplyStore, error) {
|
||||||
ur := "users_replies"
|
ur := "users_replies"
|
||||||
return &SQLProfileReplyStore{
|
return &SQLProfileReplyStore{
|
||||||
get: acc.Select(ur).Columns("uid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress").Where("rid = ?").Prepare(),
|
get: acc.Select(ur).Columns("uid, content, createdBy, createdAt, lastEdit, lastEditBy, ip").Where("rid=?").Prepare(),
|
||||||
create: acc.Insert(ur).Columns("uid, content, parsed_content, createdAt, createdBy, ipaddress").Fields("?,?,?,UTC_TIMESTAMP(),?,?").Prepare(),
|
exists: acc.Exists(ur, "rid").Prepare(),
|
||||||
|
create: acc.Insert(ur).Columns("uid, content, parsed_content, createdAt, createdBy, ip").Fields("?,?,?,UTC_TIMESTAMP(),?,?").Prepare(),
|
||||||
count: acc.Count(ur).Prepare(),
|
count: acc.Count(ur).Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
@ -37,6 +40,14 @@ func (s *SQLProfileReplyStore) Get(id int) (*ProfileReply, error) {
|
||||||
return &r, err
|
return &r, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SQLProfileReplyStore) Exists(id int) bool {
|
||||||
|
err := s.exists.QueryRow(id).Scan(&id)
|
||||||
|
if err != nil && err != ErrNoRows {
|
||||||
|
LogError(err)
|
||||||
|
}
|
||||||
|
return err != ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SQLProfileReplyStore) Create(profileID int, content string, createdBy int, ip string) (id int, err error) {
|
func (s *SQLProfileReplyStore) Create(profileID int, content string, createdBy int, ip string) (id int, err error) {
|
||||||
if Config.DisablePostIP {
|
if Config.DisablePostIP {
|
||||||
ip = "0"
|
ip = "0"
|
||||||
|
|
|
@ -12,7 +12,7 @@ var ErrExceededRateLimit = errors.New("You're exceeding a rate limit. Please wai
|
||||||
|
|
||||||
// TODO: Persist rate limits to disk
|
// TODO: Persist rate limits to disk
|
||||||
type RateLimiter interface {
|
type RateLimiter interface {
|
||||||
LimitIP(limit string, ip string) error
|
LimitIP(limit, ip string) error
|
||||||
LimitUser(limit string, user int) error
|
LimitUser(limit string, user int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ func NewDefaultRateLimiter() *DefaultRateLimiter {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *DefaultRateLimiter) LimitIP(limit string, ip string) error {
|
func (l *DefaultRateLimiter) LimitIP(limit, ip string) error {
|
||||||
limiter, ok := l.limits[limit]
|
limiter, ok := l.limits[limit]
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrBadRateLimiter
|
return ErrBadRateLimiter
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
//"log"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Recalc RecalcInt
|
||||||
|
|
||||||
|
type RecalcInt interface {
|
||||||
|
Replies() (count int, err error)
|
||||||
|
Subscriptions() (count int, err error)
|
||||||
|
ActivityStream() (count int, err error)
|
||||||
|
Users() error
|
||||||
|
Attachments() (count int, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultRecalc struct {
|
||||||
|
getActivitySubscriptions *sql.Stmt
|
||||||
|
getActivityStream *sql.Stmt
|
||||||
|
getAttachments *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultRecalc(acc *qgen.Accumulator) (*DefaultRecalc, error) {
|
||||||
|
return &DefaultRecalc{
|
||||||
|
getActivitySubscriptions: acc.Select("activity_subscriptions").Columns("targetID,targetType").Prepare(),
|
||||||
|
getActivityStream: acc.Select("activity_stream").Columns("asid,event,elementID,elementType,extra").Prepare(),
|
||||||
|
getAttachments: acc.Select("attachments").Columns("attachID,originID,originTable").Prepare(),
|
||||||
|
}, acc.FirstError()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultRecalc) Replies() (count int, err error) {
|
||||||
|
var ltid int
|
||||||
|
err = Rstore.Each(func(r *Reply) error {
|
||||||
|
if ltid == r.ParentID && r.ParentID > 0 {
|
||||||
|
//return nil
|
||||||
|
}
|
||||||
|
if !Topics.Exists(r.ParentID) {
|
||||||
|
// TODO: Delete in chunks not one at a time?
|
||||||
|
if err := r.Delete(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultRecalc) Subscriptions() (count int, err error) {
|
||||||
|
err = eachall(s.getActivitySubscriptions, func(r *sql.Rows) error {
|
||||||
|
var targetID int
|
||||||
|
var targetType string
|
||||||
|
err := r.Scan(&targetID, &targetType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if targetType == "topic" {
|
||||||
|
if !Topics.Exists(targetID) {
|
||||||
|
// TODO: Delete in chunks not one at a time?
|
||||||
|
err := Subscriptions.DeleteResource(targetID, targetType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Existable interface {
|
||||||
|
Exists(id int) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultRecalc) ActivityStream() (count int, err error) {
|
||||||
|
err = eachall(s.getActivityStream, func(r *sql.Rows) error {
|
||||||
|
var asid, elementID int
|
||||||
|
var event, elementType, extra string
|
||||||
|
err := r.Scan(&asid, &event, &elementID, &elementType, &extra)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//log.Print("asid:",asid)
|
||||||
|
var s Existable
|
||||||
|
switch elementType {
|
||||||
|
case "user":
|
||||||
|
if event == "reply" {
|
||||||
|
extraI, _ := strconv.Atoi(extra)
|
||||||
|
if extraI > 0 {
|
||||||
|
s = Prstore
|
||||||
|
elementID = extraI
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
case "topic":
|
||||||
|
s = Topics
|
||||||
|
// TODO: Delete reply events with an empty extra field
|
||||||
|
if event == "reply" {
|
||||||
|
extraI, _ := strconv.Atoi(extra)
|
||||||
|
if extraI > 0 {
|
||||||
|
s = Rstore
|
||||||
|
elementID = extraI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "post":
|
||||||
|
s = Rstore
|
||||||
|
// TODO: Add a TopicExistsByReplyID for efficiency
|
||||||
|
/*_, err = TopicByReplyID(elementID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
// TODO: Delete in chunks not one at a time?
|
||||||
|
err := Activity.Delete(asid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
|
}*/
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !s.Exists(elementID) {
|
||||||
|
// TODO: Delete in chunks not one at a time?
|
||||||
|
err := Activity.Delete(asid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultRecalc) Users() error {
|
||||||
|
return Users.Each(func(u *User) error {
|
||||||
|
return u.RecalcPostStats()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultRecalc) Attachments() (count int, err error) {
|
||||||
|
err = eachall(s.getAttachments, func(r *sql.Rows) error {
|
||||||
|
var aid, originID int
|
||||||
|
var originType string
|
||||||
|
err := r.Scan(&aid, &originID, &originType)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var s Existable
|
||||||
|
switch originType {
|
||||||
|
case "topics":
|
||||||
|
s = Topics
|
||||||
|
case "replies":
|
||||||
|
s = Rstore
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !s.Exists(originID) {
|
||||||
|
// TODO: Delete in chunks not one at a time?
|
||||||
|
err := Attachments.Delete(aid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return count, err
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"html"
|
"html"
|
||||||
"time"
|
"time"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
qgen "github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
)
|
)
|
||||||
|
@ -69,6 +70,8 @@ type ReplyStmts struct {
|
||||||
|
|
||||||
updateTopicReplies *sql.Stmt
|
updateTopicReplies *sql.Stmt
|
||||||
updateTopicReplies2 *sql.Stmt
|
updateTopicReplies2 *sql.Stmt
|
||||||
|
|
||||||
|
getAidsOfReply *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -89,6 +92,8 @@ func init() {
|
||||||
// TODO: Optimise this to avoid firing an update if it's not the last reply in a topic. We will need to set lastReplyID properly in other places and in the patcher first so we can use it here.
|
// TODO: Optimise this to avoid firing an update if it's not the last reply in a topic. We will need to set lastReplyID properly in other places and in the patcher first so we can use it here.
|
||||||
updateTopicReplies: acc.RawPrepare("UPDATE topics t INNER JOIN replies r ON t.tid = r.tid SET t.lastReplyBy = r.createdBy, t.lastReplyAt = r.createdAt, t.lastReplyID = r.rid WHERE t.tid = ?"),
|
updateTopicReplies: acc.RawPrepare("UPDATE topics t INNER JOIN replies r ON t.tid = r.tid SET t.lastReplyBy = r.createdBy, t.lastReplyAt = r.createdAt, t.lastReplyID = r.rid WHERE t.tid = ?"),
|
||||||
updateTopicReplies2: acc.Update("topics").Set("lastReplyAt=createdAt,lastReplyBy=createdBy,lastReplyID=0").Where("postCount=1 AND tid=?").Prepare(),
|
updateTopicReplies2: acc.Update("topics").Set("lastReplyAt=createdAt,lastReplyBy=createdBy,lastReplyID=0").Where("postCount=1 AND tid=?").Prepare(),
|
||||||
|
|
||||||
|
getAidsOfReply: acc.Select("attachments").Columns("attachID").Where("originID=? AND originTable='replies'").Prepare(),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
})
|
})
|
||||||
|
@ -120,7 +125,6 @@ func (r *Reply) Like(uid int) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Refresh topic list?
|
// TODO: Refresh topic list?
|
||||||
// TODO: Restructure alerts so we can delete the "x replied to topic" ones too.
|
|
||||||
func (r *Reply) Delete() error {
|
func (r *Reply) Delete() error {
|
||||||
creator, err := Users.Get(r.CreatedBy)
|
creator, err := Users.Get(r.CreatedBy)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -158,6 +162,14 @@ func (r *Reply) Delete() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = handleReplyAttachments(r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = Activity.DeleteByParamsExtra("reply",r.ParentID,"topic",strconv.Itoa(r.ID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
_, err = replyStmts.deleteActivitySubs.Exec(r.ID)
|
_, err = replyStmts.deleteActivitySubs.Exec(r.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -11,8 +11,13 @@ var Rstore ReplyStore
|
||||||
|
|
||||||
type ReplyStore interface {
|
type ReplyStore interface {
|
||||||
Get(id int) (*Reply, error)
|
Get(id int) (*Reply, error)
|
||||||
Create(topic *Topic, content string, ip string, uid int) (id int, err error)
|
Each(f func(*Reply) error) error
|
||||||
|
Exists(id int) bool
|
||||||
|
Create(t *Topic, content string, ip string, uid int) (id int, err error)
|
||||||
Count() (count int)
|
Count() (count int)
|
||||||
|
CountUser(uid int) (count int)
|
||||||
|
CountMegaUser(uid int) (count int)
|
||||||
|
CountBigUser(uid int) (count int)
|
||||||
|
|
||||||
SetCache(cache ReplyCache)
|
SetCache(cache ReplyCache)
|
||||||
GetCache() ReplyCache
|
GetCache() ReplyCache
|
||||||
|
@ -21,9 +26,13 @@ type ReplyStore interface {
|
||||||
type SQLReplyStore struct {
|
type SQLReplyStore struct {
|
||||||
cache ReplyCache
|
cache ReplyCache
|
||||||
|
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
create *sql.Stmt
|
getAll *sql.Stmt
|
||||||
count *sql.Stmt
|
exists *sql.Stmt
|
||||||
|
create *sql.Stmt
|
||||||
|
count *sql.Stmt
|
||||||
|
countUser *sql.Stmt
|
||||||
|
countWordUser *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSQLReplyStore(acc *qgen.Accumulator, cache ReplyCache) (*SQLReplyStore, error) {
|
func NewSQLReplyStore(acc *qgen.Accumulator, cache ReplyCache) (*SQLReplyStore, error) {
|
||||||
|
@ -32,10 +41,14 @@ func NewSQLReplyStore(acc *qgen.Accumulator, cache ReplyCache) (*SQLReplyStore,
|
||||||
}
|
}
|
||||||
re := "replies"
|
re := "replies"
|
||||||
return &SQLReplyStore{
|
return &SQLReplyStore{
|
||||||
cache: cache,
|
cache: cache,
|
||||||
get: acc.Select(re).Columns("tid, content, createdBy, createdAt, lastEdit, lastEditBy, ipaddress, likeCount, attachCount, actionType").Where("rid = ?").Prepare(),
|
get: acc.Select(re).Columns("tid, content, createdBy, createdAt, lastEdit, lastEditBy, ip, likeCount, attachCount, actionType").Where("rid=?").Prepare(),
|
||||||
create: acc.Insert(re).Columns("tid, content, parsed_content, createdAt, lastUpdated, ipaddress, words, createdBy").Fields("?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?").Prepare(),
|
getAll: acc.Select(re).Columns("rid,tid, content, createdBy, createdAt, lastEdit, lastEditBy, ip, likeCount, attachCount, actionType").Prepare(),
|
||||||
count: acc.Count(re).Prepare(),
|
exists: acc.Exists(re, "rid").Prepare(),
|
||||||
|
create: acc.Insert(re).Columns("tid, content, parsed_content, createdAt, lastUpdated, ip, words, createdBy").Fields("?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?").Prepare(),
|
||||||
|
count: acc.Count(re).Prepare(),
|
||||||
|
countUser: acc.Count(re).Where("createdBy=?").Prepare(),
|
||||||
|
countWordUser: acc.Count(re).Where("createdBy=? AND words>=?").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,8 +66,38 @@ func (s *SQLReplyStore) Get(id int) (*Reply, error) {
|
||||||
return r, err
|
return r, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*func (s *SQLReplyStore) eachr(f func(*sql.Rows) error) error {
|
||||||
|
return eachall(s.getAll, f)
|
||||||
|
}*/
|
||||||
|
|
||||||
|
func (s *SQLReplyStore) Each(f func(*Reply) error) error {
|
||||||
|
rows, err := s.getAll.Query()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
r := new(Reply)
|
||||||
|
if err := rows.Scan(&r.ID, &r.ParentID, &r.Content, &r.CreatedBy, &r.CreatedAt, &r.LastEdit, &r.LastEditBy, &r.IP, &r.LikeCount, &r.AttachCount, &r.ActionType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := f(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SQLReplyStore) Exists(id int) bool {
|
||||||
|
err := s.exists.QueryRow(id).Scan(&id)
|
||||||
|
if err != nil && err != ErrNoRows {
|
||||||
|
LogError(err)
|
||||||
|
}
|
||||||
|
return err != ErrNoRows
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
func (s *SQLReplyStore) Create(t *Topic, content string, ip string, uid int) (rid int, err error) {
|
func (s *SQLReplyStore) Create(t *Topic, content, ip string, uid int) (rid int, err error) {
|
||||||
if Config.DisablePostIP {
|
if Config.DisablePostIP {
|
||||||
ip = "0"
|
ip = "0"
|
||||||
}
|
}
|
||||||
|
@ -74,11 +117,16 @@ func (s *SQLReplyStore) Create(t *Topic, content string, ip string, uid int) (ri
|
||||||
// TODO: Write a test for this
|
// TODO: Write a test for this
|
||||||
// Count returns the total number of topic replies on these forums
|
// Count returns the total number of topic replies on these forums
|
||||||
func (s *SQLReplyStore) Count() (count int) {
|
func (s *SQLReplyStore) Count() (count int) {
|
||||||
err := s.count.QueryRow().Scan(&count)
|
return Countf(s.count)
|
||||||
if err != nil {
|
}
|
||||||
LogError(err)
|
func (s *SQLReplyStore) CountUser(uid int) (count int) {
|
||||||
}
|
return Countf(s.countUser, uid)
|
||||||
return count
|
}
|
||||||
|
func (s *SQLReplyStore) CountMegaUser(uid int) (count int) {
|
||||||
|
return Countf(s.countWordUser, uid, SettingBox.Load().(SettingMap)["megapost_min_words"].(int))
|
||||||
|
}
|
||||||
|
func (s *SQLReplyStore) CountBigUser(uid int) (count int) {
|
||||||
|
return Countf(s.countWordUser, uid, SettingBox.Load().(SettingMap)["bigpost_min_words"].(int))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLReplyStore) SetCache(cache ReplyCache) {
|
func (s *SQLReplyStore) SetCache(cache ReplyCache) {
|
||||||
|
|
|
@ -17,7 +17,7 @@ var ErrAlreadyReported = errors.New("This item has already been reported")
|
||||||
|
|
||||||
// The report system mostly wraps around the topic system for simplicty
|
// The report system mostly wraps around the topic system for simplicty
|
||||||
type ReportStore interface {
|
type ReportStore interface {
|
||||||
Create(title string, content string, user *User, itemType string, itemID int) (int, error)
|
Create(title, content string, user *User, itemType string, itemID int) (int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultReportStore struct {
|
type DefaultReportStore struct {
|
||||||
|
@ -28,13 +28,13 @@ type DefaultReportStore struct {
|
||||||
func NewDefaultReportStore(acc *qgen.Accumulator) (*DefaultReportStore, error) {
|
func NewDefaultReportStore(acc *qgen.Accumulator) (*DefaultReportStore, error) {
|
||||||
t := "topics"
|
t := "topics"
|
||||||
return &DefaultReportStore{
|
return &DefaultReportStore{
|
||||||
create: acc.Insert(t).Columns("title, content, parsed_content, ipaddress, createdAt, lastReplyAt, createdBy, lastReplyBy, data, parentID, css_class").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?,'report'").Prepare(),
|
create: acc.Insert(t).Columns("title, content, parsed_content, ip, createdAt, lastReplyAt, createdBy, lastReplyBy, data, parentID, css_class").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?,'report'").Prepare(),
|
||||||
exists: acc.Count(t).Where("data = ? AND data != '' AND parentID = ?").Prepare(),
|
exists: acc.Count(t).Where("data=? AND data!='' AND parentID=?").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! There's a data race in this. If two users report one item at the exact same time, then both reports will go through
|
// ! There's a data race in this. If two users report one item at the exact same time, then both reports will go through
|
||||||
func (s *DefaultReportStore) Create(title string, content string, u *User, itemType string, itemID int) (tid int, err error) {
|
func (s *DefaultReportStore) Create(title, content string, u *User, itemType string, itemID int) (tid int, err error) {
|
||||||
var count int
|
var count int
|
||||||
err = s.exists.QueryRow(itemType+"_"+strconv.Itoa(itemID), ReportForumID).Scan(&count)
|
err = s.exists.QueryRow(itemType+"_"+strconv.Itoa(itemID), ReportForumID).Scan(&count)
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
|
|
@ -10,21 +10,39 @@ var Subscriptions SubscriptionStore
|
||||||
|
|
||||||
// ? Should we have a subscription store for each zone? topic, forum, etc?
|
// ? Should we have a subscription store for each zone? topic, forum, etc?
|
||||||
type SubscriptionStore interface {
|
type SubscriptionStore interface {
|
||||||
Add(uid int, elementID int, elementType string) error
|
Add(uid, elementID int, elementType string) error
|
||||||
|
Delete(uid, targetID int, targetType string) error
|
||||||
|
DeleteResource(targetID int, targetType string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultSubscriptionStore struct {
|
type DefaultSubscriptionStore struct {
|
||||||
add *sql.Stmt
|
add *sql.Stmt
|
||||||
|
delete *sql.Stmt
|
||||||
|
deleteResource *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultSubscriptionStore() (*DefaultSubscriptionStore, error) {
|
func NewDefaultSubscriptionStore() (*DefaultSubscriptionStore, error) {
|
||||||
acc := qgen.NewAcc()
|
acc := qgen.NewAcc()
|
||||||
|
ast := "activity_subscriptions"
|
||||||
return &DefaultSubscriptionStore{
|
return &DefaultSubscriptionStore{
|
||||||
add: acc.Insert("activity_subscriptions").Columns("user, targetID, targetType, level").Fields("?,?,?,2").Prepare(),
|
add: acc.Insert(ast).Columns("user, targetID, targetType, level").Fields("?,?,?,2").Prepare(),
|
||||||
|
delete: acc.Delete(ast).Where("user=? AND targetID=? AND targetType=?").Prepare(),
|
||||||
|
deleteResource: acc.Delete(ast).Where("targetID=? AND targetType=?").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultSubscriptionStore) Add(uid int, elementID int, elementType string) error {
|
func (s *DefaultSubscriptionStore) Add(uid, elementID int, elementType string) error {
|
||||||
_, err := s.add.Exec(uid, elementID, elementType)
|
_, err := s.add.Exec(uid, elementID, elementType)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Add a primary key to the activity subscriptions table
|
||||||
|
func (s *DefaultSubscriptionStore) Delete(uid, targetID int, targetType string) error {
|
||||||
|
_, err := s.delete.Exec(uid, targetID, targetType)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultSubscriptionStore) DeleteResource(targetID int, targetType string) error {
|
||||||
|
_, err := s.deleteResource.Exec(targetID, targetType)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Something more thread-safe
|
// TODO: Something more thread-safe
|
||||||
|
@ -44,8 +44,8 @@ func init() {
|
||||||
t := "themes"
|
t := "themes"
|
||||||
themeStmts = ThemeStmts{
|
themeStmts = ThemeStmts{
|
||||||
getAll: acc.Select(t).Columns("uname,default").Prepare(),
|
getAll: acc.Select(t).Columns("uname,default").Prepare(),
|
||||||
isDefault: acc.Select(t).Columns("default").Where("uname = ?").Prepare(),
|
isDefault: acc.Select(t).Columns("default").Where("uname=?").Prepare(),
|
||||||
update: acc.Update(t).Set("default = ?").Where("uname = ?").Prepare(),
|
update: acc.Update(t).Set("default=?").Where("uname=?").Prepare(),
|
||||||
add: acc.Insert(t).Columns("uname,default").Fields("?,?").Prepare(),
|
add: acc.Insert(t).Columns("uname,default").Fields("?,?").Prepare(),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
|
|
|
@ -31,12 +31,12 @@ func ThumbTask(thumbChan chan bool) {
|
||||||
|
|
||||||
// Has the avatar been removed or already been processed by the thumbnailer?
|
// Has the avatar been removed or already been processed by the thumbnailer?
|
||||||
if len(u.RawAvatar) < 2 || u.RawAvatar[1] == '.' {
|
if len(u.RawAvatar) < 2 || u.RawAvatar[1] == '.' {
|
||||||
_, _ = acc.Delete("users_avatar_queue").Where("uid = ?").Run(uid)
|
_, _ = acc.Delete("users_avatar_queue").Where("uid=?").Run(uid)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
_, err = os.Stat("./uploads/avatar_" + strconv.Itoa(u.ID) + u.RawAvatar)
|
_, err = os.Stat("./uploads/avatar_" + strconv.Itoa(u.ID) + u.RawAvatar)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
_, _ = acc.Delete("users_avatar_queue").Where("uid = ?").Run(uid)
|
_, _ = acc.Delete("users_avatar_queue").Where("uid=?").Run(uid)
|
||||||
return nil
|
return nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
|
@ -63,7 +63,7 @@ func ThumbTask(thumbChan chan bool) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
_, err = acc.Delete("users_avatar_queue").Where("uid = ?").Run(uid)
|
_, err = acc.Delete("users_avatar_queue").Where("uid=?").Run(uid)
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -86,18 +86,18 @@ func ThumbTask(thumbChan chan bool) {
|
||||||
var Thumbnailer ThumbnailerInt
|
var Thumbnailer ThumbnailerInt
|
||||||
|
|
||||||
type ThumbnailerInt interface {
|
type ThumbnailerInt interface {
|
||||||
Resize(format string, inPath string, tmpPath string, outPath string, width int) error
|
Resize(format, inPath, tmpPath, outPath string, width int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type RezThumbnailer struct {
|
type RezThumbnailer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (thumb *RezThumbnailer) Resize(format string, inPath string, tmpPath string, outPath string, width int) error {
|
func (thumb *RezThumbnailer) Resize(format, inPath, tmpPath, outPath string, width int) error {
|
||||||
// TODO: Sniff the aspect ratio of the image and calculate the dest height accordingly, bug make sure it isn't excessively high
|
// TODO: Sniff the aspect ratio of the image and calculate the dest height accordingly, bug make sure it isn't excessively high
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (thumb *RezThumbnailer) resize(format string, inPath string, outPath string, width int, height int) error {
|
func (thumb *RezThumbnailer) resize(format, inPath, outPath string, width, height int) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ func NewCaireThumbnailer() *CaireThumbnailer {
|
||||||
return &CaireThumbnailer{}
|
return &CaireThumbnailer{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func precodeImage(format string, inPath string, tmpPath string) error {
|
func precodeImage(format, inPath, tmpPath string) error {
|
||||||
imageFile, err := os.Open(inPath)
|
imageFile, err := os.Open(inPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -139,7 +139,7 @@ func precodeImage(format string, inPath string, tmpPath string) error {
|
||||||
return jpeg.Encode(outFile, img, nil)
|
return jpeg.Encode(outFile, img, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (thumb *CaireThumbnailer) Resize(format string, inPath string, tmpPath string, outPath string, width int) error {
|
func (thumb *CaireThumbnailer) Resize(format, inPath, tmpPath, outPath string, width int) error {
|
||||||
err := precodeImage(format, inPath, tmpPath)
|
err := precodeImage(format, inPath, tmpPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
137
common/topic.go
137
common/topic.go
|
@ -15,6 +15,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
//"log"
|
||||||
|
|
||||||
p "github.com/Azareal/Gosora/common/phrases"
|
p "github.com/Azareal/Gosora/common/phrases"
|
||||||
qgen "github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
)
|
)
|
||||||
|
@ -197,9 +199,9 @@ type TopicStmts struct {
|
||||||
createLike *sql.Stmt
|
createLike *sql.Stmt
|
||||||
addLikesToTopic *sql.Stmt
|
addLikesToTopic *sql.Stmt
|
||||||
delete *sql.Stmt
|
delete *sql.Stmt
|
||||||
|
deleteReplies *sql.Stmt
|
||||||
deleteLikesForTopic *sql.Stmt
|
deleteLikesForTopic *sql.Stmt
|
||||||
deleteActivity *sql.Stmt
|
deleteActivity *sql.Stmt
|
||||||
deleteActivitySubs *sql.Stmt
|
|
||||||
edit *sql.Stmt
|
edit *sql.Stmt
|
||||||
setPoll *sql.Stmt
|
setPoll *sql.Stmt
|
||||||
createAction *sql.Stmt
|
createAction *sql.Stmt
|
||||||
|
@ -215,8 +217,8 @@ func init() {
|
||||||
t := "topics"
|
t := "topics"
|
||||||
topicStmts = TopicStmts{
|
topicStmts = TopicStmts{
|
||||||
getRids: acc.Select("replies").Columns("rid").Where("tid = ?").Orderby("rid ASC").Limit("?,?").Prepare(),
|
getRids: acc.Select("replies").Columns("rid").Where("tid = ?").Orderby("rid ASC").Limit("?,?").Prepare(),
|
||||||
getReplies: acc.SimpleLeftJoin("replies AS r", "users AS u", "r.rid, r.content, r.createdBy, r.createdAt, r.lastEdit, r.lastEditBy, u.avatar, u.name, u.group, u.level, r.ipaddress, r.likeCount, r.attachCount, r.actionType", "r.createdBy = u.uid", "r.tid = ?", "r.rid ASC", "?,?"),
|
getReplies: acc.SimpleLeftJoin("replies AS r", "users AS u", "r.rid, r.content, r.createdBy, r.createdAt, r.lastEdit, r.lastEditBy, u.avatar, u.name, u.group, u.level, r.ip, r.likeCount, r.attachCount, r.actionType", "r.createdBy = u.uid", "r.tid = ?", "r.rid ASC", "?,?"),
|
||||||
addReplies: acc.Update(t).Set("postCount = postCount + ?, lastReplyBy = ?, lastReplyAt = UTC_TIMESTAMP()").Where("tid = ?").Prepare(),
|
addReplies: acc.Update(t).Set("postCount=postCount+?, lastReplyBy=?, lastReplyAt=UTC_TIMESTAMP()").Where("tid=?").Prepare(),
|
||||||
updateLastReply: acc.Update(t).Set("lastReplyID=?").Where("lastReplyID > ? AND tid = ?").Prepare(),
|
updateLastReply: acc.Update(t).Set("lastReplyID=?").Where("lastReplyID > ? AND tid = ?").Prepare(),
|
||||||
lock: acc.Update(t).Set("is_closed=1").Where("tid=?").Prepare(),
|
lock: acc.Update(t).Set("is_closed=1").Where("tid=?").Prepare(),
|
||||||
unlock: acc.Update(t).Set("is_closed=0").Where("tid=?").Prepare(),
|
unlock: acc.Update(t).Set("is_closed=0").Where("tid=?").Prepare(),
|
||||||
|
@ -225,17 +227,17 @@ func init() {
|
||||||
unstick: acc.Update(t).Set("sticky=0").Where("tid=?").Prepare(),
|
unstick: acc.Update(t).Set("sticky=0").Where("tid=?").Prepare(),
|
||||||
hasLikedTopic: acc.Select("likes").Columns("targetItem").Where("sentBy=? and targetItem=? and targetType='topics'").Prepare(),
|
hasLikedTopic: acc.Select("likes").Columns("targetItem").Where("sentBy=? and targetItem=? and targetType='topics'").Prepare(),
|
||||||
createLike: acc.Insert("likes").Columns("weight, targetItem, targetType, sentBy, createdAt").Fields("?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
createLike: acc.Insert("likes").Columns("weight, targetItem, targetType, sentBy, createdAt").Fields("?,?,?,?,UTC_TIMESTAMP()").Prepare(),
|
||||||
addLikesToTopic: acc.Update(t).Set("likeCount=likeCount+?").Where("tid = ?").Prepare(),
|
addLikesToTopic: acc.Update(t).Set("likeCount=likeCount+?").Where("tid=?").Prepare(),
|
||||||
delete: acc.Delete(t).Where("tid=?").Prepare(),
|
delete: acc.Delete(t).Where("tid=?").Prepare(),
|
||||||
|
deleteReplies: acc.Delete("replies").Where("tid=?").Prepare(),
|
||||||
deleteLikesForTopic: acc.Delete("likes").Where("targetItem=? AND targetType='topics'").Prepare(),
|
deleteLikesForTopic: acc.Delete("likes").Where("targetItem=? AND targetType='topics'").Prepare(),
|
||||||
deleteActivity: acc.Delete("activity_stream").Where("elementID=? AND elementType='topic'").Prepare(),
|
deleteActivity: acc.Delete("activity_stream").Where("elementID=? AND elementType='topic'").Prepare(),
|
||||||
deleteActivitySubs: acc.Delete("activity_subscriptions").Where("targetID=? AND targetType='topic'").Prepare(),
|
|
||||||
edit: acc.Update(t).Set("title=?,content=?,parsed_content=?").Where("tid=?").Prepare(), // TODO: Only run the content update bits on non-polls, does this matter?
|
edit: acc.Update(t).Set("title=?,content=?,parsed_content=?").Where("tid=?").Prepare(), // TODO: Only run the content update bits on non-polls, does this matter?
|
||||||
setPoll: acc.Update(t).Set("poll=?").Where("tid=? AND poll=0").Prepare(),
|
setPoll: acc.Update(t).Set("poll=?").Where("tid=? AND poll=0").Prepare(),
|
||||||
createAction: acc.Insert("replies").Columns("tid, actionType, ipaddress, createdBy, createdAt, lastUpdated, content, parsed_content").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''").Prepare(),
|
createAction: acc.Insert("replies").Columns("tid, actionType, ip, createdBy, createdAt, lastUpdated, content, parsed_content").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),'',''").Prepare(),
|
||||||
|
|
||||||
getTopicUser: acc.SimpleLeftJoin("topics AS t", "users AS u", "t.title, t.content, t.createdBy, t.createdAt, t.lastReplyAt, t.lastReplyBy, t.lastReplyID, t.is_closed, t.sticky, t.parentID, t.ipaddress, t.views, t.postCount, t.likeCount, t.attachCount,t.poll, u.name, u.avatar, u.group, u.level", "t.createdBy = u.uid", "tid = ?", "", ""),
|
getTopicUser: acc.SimpleLeftJoin("topics AS t", "users AS u", "t.title, t.content, t.createdBy, t.createdAt, t.lastReplyAt, t.lastReplyBy, t.lastReplyID, t.is_closed, t.sticky, t.parentID, t.ip, t.views, t.postCount, t.likeCount, t.attachCount,t.poll, u.name, u.avatar, u.group, u.level", "t.createdBy=u.uid", "tid=?", "", ""),
|
||||||
getByReplyID: acc.SimpleLeftJoin("replies AS r", "topics AS t", "t.tid, t.title, t.content, t.createdBy, t.createdAt, t.is_closed, t.sticky, t.parentID, t.ipaddress, t.views, t.postCount, t.likeCount, t.poll, t.data", "r.tid = t.tid", "rid = ?", "", ""),
|
getByReplyID: acc.SimpleLeftJoin("replies AS r", "topics AS t", "t.tid, t.title, t.content, t.createdBy, t.createdAt, t.is_closed, t.sticky, t.parentID, t.ip, t.views, t.postCount, t.likeCount, t.poll, t.data", "r.tid=t.tid", "rid=?", "", ""),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
})
|
})
|
||||||
|
@ -322,8 +324,17 @@ func (t *Topic) Like(score, uid int) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Use a transaction
|
||||||
func (t *Topic) Unlike(uid int) error {
|
func (t *Topic) Unlike(uid int) error {
|
||||||
|
err := Likes.Delete(t.ID,"topics")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = topicStmts.addLikesToTopic.Exec(-1, t.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = userStmts.decLiked.Exec(1, uid)
|
||||||
t.cacheRemove()
|
t.cacheRemove()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -345,43 +356,72 @@ func handleLikedTopicReplies(tid int) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = Activity.DeleteByParams("like", rid, "post")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows.Err()
|
return rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleTopicAttachments(tid int) error {
|
func handleTopicAttachments(tid int) error {
|
||||||
f := func(stmt *sql.Stmt) error {
|
err := handleAttachments(userStmts.getAttachmentsOfTopic, tid)
|
||||||
rows, err := stmt.Query(tid)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
var aid int
|
|
||||||
err := rows.Scan(&aid)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = DeleteAttachment(aid)
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rows.Err()
|
|
||||||
}
|
|
||||||
err := f(userStmts.getAttachmentsOfTopic)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return f(userStmts.getAttachmentsOfTopic2)
|
return handleAttachments(userStmts.getAttachmentsOfTopic2, tid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleReplyAttachments(rid int) error {
|
||||||
|
return handleAttachments(replyStmts.getAidsOfReply, rid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleAttachments(stmt *sql.Stmt, id int) error {
|
||||||
|
rows, err := stmt.Query(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var aid int
|
||||||
|
err := rows.Scan(&aid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = DeleteAttachment(aid)
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Only load a row per createdBy, maybe with group by?
|
||||||
|
func handleTopicReplies(umap map[int]struct{}, uid int, tid int) error {
|
||||||
|
rows, err := userStmts.getRepliesOfTopic.Query(uid, tid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var createdBy int
|
||||||
|
err := rows.Scan(&createdBy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
umap[createdBy] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use a transaction here
|
// TODO: Use a transaction here
|
||||||
func (t *Topic) Delete() error {
|
func (t *Topic) Delete() error {
|
||||||
creator, err := Users.Get(t.CreatedBy)
|
/*creator, err := Users.Get(t.CreatedBy)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = creator.DecreasePostStats(WordCount(t.Content), true)
|
err = creator.DecreasePostStats(WordCount(t.Content), true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -389,10 +429,10 @@ func (t *Topic) Delete() error {
|
||||||
}
|
}
|
||||||
} else if err != ErrNoRows {
|
} else if err != ErrNoRows {
|
||||||
return err
|
return err
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// TODO: Clear reply cache too
|
// TODO: Clear reply cache too
|
||||||
_, err = topicStmts.delete.Exec(t.ID)
|
_, err := topicStmts.delete.Exec(t.ID)
|
||||||
t.cacheRemove()
|
t.cacheRemove()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -405,7 +445,30 @@ func (t *Topic) Delete() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = handleLikedTopicReplies(t.ID)
|
|
||||||
|
if t.PostCount > 1 {
|
||||||
|
err = handleLikedTopicReplies(t.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
umap := make(map[int]struct{})
|
||||||
|
err = handleTopicReplies(umap, t.CreatedBy, t.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = topicStmts.deleteReplies.Exec(t.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for uid := range umap {
|
||||||
|
err = (&User{ID: uid}).RecalcPostStats()
|
||||||
|
if err != nil {
|
||||||
|
//log.Printf("err: %+v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = (&User{ID: t.CreatedBy}).RecalcPostStats()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -413,7 +476,7 @@ func (t *Topic) Delete() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = topicStmts.deleteActivitySubs.Exec(t.ID)
|
err = Subscriptions.DeleteResource(t.ID, "topic")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,13 +29,16 @@ type TopicStore interface {
|
||||||
BypassGet(id int) (*Topic, error)
|
BypassGet(id int) (*Topic, error)
|
||||||
BulkGetMap(ids []int) (list map[int]*Topic, err error)
|
BulkGetMap(ids []int) (list map[int]*Topic, err error)
|
||||||
Exists(id int) bool
|
Exists(id int) bool
|
||||||
Create(fid int, name string, content string, uid int, ip string) (tid int, err error)
|
Create(fid int, name, content string, uid int, ip string) (tid int, err error)
|
||||||
AddLastTopic(item *Topic, fid int) error // unimplemented
|
AddLastTopic(item *Topic, fid int) error // unimplemented
|
||||||
Reload(id int) error // Too much SQL logic to move into TopicCache
|
Reload(id int) error // Too much SQL logic to move into TopicCache
|
||||||
// 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)
|
||||||
Count() int
|
Count() int
|
||||||
|
CountUser(uid int) int
|
||||||
|
CountMegaUser(uid int) int
|
||||||
|
CountBigUser(uid int) int
|
||||||
|
|
||||||
SetCache(cache TopicCache)
|
SetCache(cache TopicCache)
|
||||||
GetCache() TopicCache
|
GetCache() TopicCache
|
||||||
|
@ -44,10 +47,12 @@ type TopicStore interface {
|
||||||
type DefaultTopicStore struct {
|
type DefaultTopicStore struct {
|
||||||
cache TopicCache
|
cache TopicCache
|
||||||
|
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
exists *sql.Stmt
|
exists *sql.Stmt
|
||||||
count *sql.Stmt
|
count *sql.Stmt
|
||||||
create *sql.Stmt
|
countUser *sql.Stmt
|
||||||
|
countWordUser *sql.Stmt
|
||||||
|
create *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDefaultTopicStore gives you a new instance of DefaultTopicStore
|
// NewDefaultTopicStore gives you a new instance of DefaultTopicStore
|
||||||
|
@ -58,38 +63,40 @@ func NewDefaultTopicStore(cache TopicCache) (*DefaultTopicStore, error) {
|
||||||
}
|
}
|
||||||
t := "topics"
|
t := "topics"
|
||||||
return &DefaultTopicStore{
|
return &DefaultTopicStore{
|
||||||
cache: cache,
|
cache: cache,
|
||||||
get: acc.Select(t).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(t).Columns("title, content, createdBy, createdAt, lastReplyBy, lastReplyAt, lastReplyID, is_closed, sticky, parentID, ip, views, postCount, likeCount, attachCount, poll, data").Where("tid=?").Prepare(),
|
||||||
exists: acc.Exists(t, "tid").Prepare(),
|
exists: acc.Exists(t, "tid").Prepare(),
|
||||||
count: acc.Count(t).Prepare(),
|
count: acc.Count(t).Prepare(),
|
||||||
create: acc.Insert(t).Columns("parentID, title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, ipaddress, words, createdBy").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?").Prepare(),
|
countUser: acc.Count(t).Where("createdBy=?").Prepare(),
|
||||||
|
countWordUser: acc.Count(t).Where("createdBy=? AND words>=?").Prepare(),
|
||||||
|
create: acc.Insert(t).Columns("parentID, title, content, parsed_content, createdAt, lastReplyAt, lastReplyBy, ip, words, createdBy").Fields("?,?,?,?,UTC_TIMESTAMP(),UTC_TIMESTAMP(),?,?,?,?").Prepare(),
|
||||||
}, acc.FirstError()
|
}, acc.FirstError()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultTopicStore) DirtyGet(id int) *Topic {
|
func (s *DefaultTopicStore) DirtyGet(id int) *Topic {
|
||||||
topic, err := s.cache.Get(id)
|
t, err := s.cache.Get(id)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return topic
|
return t
|
||||||
}
|
}
|
||||||
topic, err = s.BypassGet(id)
|
t, err = s.BypassGet(id)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_ = s.cache.Set(topic)
|
_ = s.cache.Set(t)
|
||||||
return topic
|
return t
|
||||||
}
|
}
|
||||||
return BlankTopic()
|
return BlankTopic()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Log weird cache errors?
|
// TODO: Log weird cache errors?
|
||||||
func (s *DefaultTopicStore) Get(id int) (topic *Topic, err error) {
|
func (s *DefaultTopicStore) Get(id int) (t *Topic, err error) {
|
||||||
topic, err = s.cache.Get(id)
|
t, err = s.cache.Get(id)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return topic, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
topic, err = s.BypassGet(id)
|
t, err = s.BypassGet(id)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_ = s.cache.Set(topic)
|
_ = s.cache.Set(t)
|
||||||
}
|
}
|
||||||
return topic, err
|
return t, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// BypassGet will always bypass the cache and pull the topic directly from the database
|
// BypassGet will always bypass the cache and pull the topic directly from the database
|
||||||
|
@ -102,6 +109,15 @@ func (s *DefaultTopicStore) BypassGet(id int) (*Topic, error) {
|
||||||
return t, err
|
return t, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*func (s *DefaultTopicStore) GetByUser(uid int) (list map[int]*Topic, err error) {
|
||||||
|
t := &Topic{ID: id}
|
||||||
|
err := s.get.QueryRow(id).Scan(&t.Title, &t.Content, &t.CreatedBy, &t.CreatedAt, &t.LastReplyBy, &t.LastReplyAt, &t.LastReplyID, &t.IsClosed, &t.Sticky, &t.ParentID, &t.IP, &t.ViewCount, &t.PostCount, &t.LikeCount, &t.AttachCount, &t.Poll, &t.Data)
|
||||||
|
if err == nil {
|
||||||
|
t.Link = BuildTopicURL(NameToSlug(t.Title), id)
|
||||||
|
}
|
||||||
|
return t, err
|
||||||
|
}*/
|
||||||
|
|
||||||
// TODO: Avoid duplicating much of this logic from user_store.go
|
// TODO: Avoid duplicating much of this logic from user_store.go
|
||||||
func (s *DefaultTopicStore) BulkGetMap(ids []int) (list map[int]*Topic, err error) {
|
func (s *DefaultTopicStore) BulkGetMap(ids []int) (list map[int]*Topic, err error) {
|
||||||
idCount := len(ids)
|
idCount := len(ids)
|
||||||
|
@ -144,7 +160,7 @@ func (s *DefaultTopicStore) BulkGetMap(ids []int) (list map[int]*Topic, err erro
|
||||||
}
|
}
|
||||||
q = q[0 : len(q)-1]
|
q = q[0 : len(q)-1]
|
||||||
|
|
||||||
rows, err := qgen.NewAcc().Select("topics").Columns("tid,title,content,createdBy,createdAt,lastReplyBy,lastReplyAt,lastReplyID,is_closed,sticky,parentID,ipaddress,views,postCount,likeCount,attachCount,poll,data").Where("tid IN(" + q + ")").Query(idList...)
|
rows, err := qgen.NewAcc().Select("topics").Columns("tid,title,content,createdBy,createdAt,lastReplyBy,lastReplyAt,lastReplyID,is_closed,sticky,parentID,ip,views,postCount,likeCount,attachCount,poll,data").Where("tid IN(" + q + ")").Query(idList...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return list, err
|
return list, err
|
||||||
}
|
}
|
||||||
|
@ -197,7 +213,7 @@ func (s *DefaultTopicStore) Exists(id int) bool {
|
||||||
return s.exists.QueryRow(id).Scan(&id) == nil
|
return s.exists.QueryRow(id).Scan(&id) == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultTopicStore) Create(fid int, name string, content string, uid int, ip string) (tid int, err error) {
|
func (s *DefaultTopicStore) Create(fid int, name, content string, uid int, ip string) (tid int, err error) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return 0, ErrNoTitle
|
return 0, ErrNoTitle
|
||||||
}
|
}
|
||||||
|
@ -236,11 +252,16 @@ func (s *DefaultTopicStore) AddLastTopic(t *Topic, fid int) error {
|
||||||
|
|
||||||
// Count returns the total number of topics on these forums
|
// Count returns the total number of topics on these forums
|
||||||
func (s *DefaultTopicStore) Count() (count int) {
|
func (s *DefaultTopicStore) Count() (count int) {
|
||||||
err := s.count.QueryRow().Scan(&count)
|
return Countf(s.count)
|
||||||
if err != nil {
|
}
|
||||||
LogError(err)
|
func (s *DefaultTopicStore) CountUser(uid int) (count int) {
|
||||||
}
|
return Countf(s.countUser, uid)
|
||||||
return count
|
}
|
||||||
|
func (s *DefaultTopicStore) CountMegaUser(uid int) (count int) {
|
||||||
|
return Countf(s.countWordUser, uid, SettingBox.Load().(SettingMap)["megapost_min_words"].(int))
|
||||||
|
}
|
||||||
|
func (s *DefaultTopicStore) CountBigUser(uid int) (count int) {
|
||||||
|
return Countf(s.countWordUser, uid, SettingBox.Load().(SettingMap)["bigpost_min_words"].(int))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultTopicStore) SetCache(cache TopicCache) {
|
func (s *DefaultTopicStore) SetCache(cache TopicCache) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
//"log"
|
||||||
|
|
||||||
qgen "github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
"github.com/go-sql-driver/mysql"
|
"github.com/go-sql-driver/mysql"
|
||||||
|
@ -137,6 +138,7 @@ type UserStmts struct {
|
||||||
incTopics *sql.Stmt
|
incTopics *sql.Stmt
|
||||||
updateLevel *sql.Stmt
|
updateLevel *sql.Stmt
|
||||||
resetStats *sql.Stmt
|
resetStats *sql.Stmt
|
||||||
|
setStats *sql.Stmt
|
||||||
|
|
||||||
decLiked *sql.Stmt
|
decLiked *sql.Stmt
|
||||||
updateLastIP *sql.Stmt
|
updateLastIP *sql.Stmt
|
||||||
|
@ -152,6 +154,7 @@ type UserStmts struct {
|
||||||
getLikedRepliesOfTopic *sql.Stmt
|
getLikedRepliesOfTopic *sql.Stmt
|
||||||
getAttachmentsOfTopic *sql.Stmt
|
getAttachmentsOfTopic *sql.Stmt
|
||||||
getAttachmentsOfTopic2 *sql.Stmt
|
getAttachmentsOfTopic2 *sql.Stmt
|
||||||
|
getRepliesOfTopic *sql.Stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
var userStmts UserStmts
|
var userStmts UserStmts
|
||||||
|
@ -168,6 +171,8 @@ func init() {
|
||||||
setName: acc.Update(u).Set("name=?").Where(w).Prepare(),
|
setName: acc.Update(u).Set("name=?").Where(w).Prepare(),
|
||||||
update: acc.Update(u).Set("name=?,email=?,group=?").Where(w).Prepare(), // TODO: Implement user_count for users_groups on things which use this
|
update: acc.Update(u).Set("name=?,email=?,group=?").Where(w).Prepare(), // TODO: Implement user_count for users_groups on things which use this
|
||||||
|
|
||||||
|
// Stat Statements
|
||||||
|
// TODO: Do +0 to avoid having as many statements?
|
||||||
incScore: acc.Update(u).Set("score=score+?").Where(w).Prepare(),
|
incScore: acc.Update(u).Set("score=score+?").Where(w).Prepare(),
|
||||||
incPosts: acc.Update(u).Set("posts=posts+?").Where(w).Prepare(),
|
incPosts: acc.Update(u).Set("posts=posts+?").Where(w).Prepare(),
|
||||||
incBigposts: acc.Update(u).Set("posts=posts+?,bigposts=bigposts+?").Where(w).Prepare(),
|
incBigposts: acc.Update(u).Set("posts=posts+?,bigposts=bigposts+?").Where(w).Prepare(),
|
||||||
|
@ -178,6 +183,7 @@ func init() {
|
||||||
incTopics: acc.SimpleUpdate(u, "topics=topics+?", w),
|
incTopics: acc.SimpleUpdate(u, "topics=topics+?", w),
|
||||||
updateLevel: acc.SimpleUpdate(u, "level=?", w),
|
updateLevel: acc.SimpleUpdate(u, "level=?", w),
|
||||||
resetStats: acc.Update(u).Set("score=0,posts=0,bigposts=0,megaposts=0,topics=0,level=0").Where(w).Prepare(),
|
resetStats: acc.Update(u).Set("score=0,posts=0,bigposts=0,megaposts=0,topics=0,level=0").Where(w).Prepare(),
|
||||||
|
setStats: acc.Update(u).Set("score=?,posts=?,bigposts=?,megaposts=?,topics=?,level=?").Where(w).Prepare(),
|
||||||
|
|
||||||
incLiked: acc.Update(u).Set("liked=liked+?,lastLiked=UTC_TIMESTAMP()").Where(w).Prepare(),
|
incLiked: acc.Update(u).Set("liked=liked+?,lastLiked=UTC_TIMESTAMP()").Where(w).Prepare(),
|
||||||
decLiked: acc.Update(u).Set("liked=liked-?").Where(w).Prepare(),
|
decLiked: acc.Update(u).Set("liked=liked-?").Where(w).Prepare(),
|
||||||
|
@ -189,12 +195,14 @@ func init() {
|
||||||
|
|
||||||
scheduleAvatarResize: acc.Insert("users_avatar_queue").Columns("uid").Fields("?").Prepare(),
|
scheduleAvatarResize: acc.Insert("users_avatar_queue").Columns("uid").Fields("?").Prepare(),
|
||||||
|
|
||||||
deletePosts: acc.Select("topics").Columns("tid,parentID,poll").Where("createdBy=?").Prepare(),
|
// Delete All Posts Statements
|
||||||
deleteProfilePosts: acc.Select("users_replies").Columns("rid").Where("createdBy=?").Prepare(),
|
deletePosts: acc.Select("topics").Columns("tid,parentID,postCount,poll").Where("createdBy=?").Prepare(),
|
||||||
|
deleteProfilePosts: acc.Select("users_replies").Columns("rid,uid").Where("createdBy=?").Prepare(),
|
||||||
deleteReplyPosts: acc.Select("replies").Columns("rid,tid").Where("createdBy=?").Prepare(),
|
deleteReplyPosts: acc.Select("replies").Columns("rid,tid").Where("createdBy=?").Prepare(),
|
||||||
getLikedRepliesOfTopic: acc.Select("replies").Columns("rid").Where("tid=? AND likeCount>0").Prepare(),
|
getLikedRepliesOfTopic: acc.Select("replies").Columns("rid").Where("tid=? AND likeCount>0").Prepare(),
|
||||||
getAttachmentsOfTopic: acc.Select("attachments").Columns("attachID").Where("originID=? AND originTable='topics'").Prepare(),
|
getAttachmentsOfTopic: acc.Select("attachments").Columns("attachID").Where("originID=? AND originTable='topics'").Prepare(),
|
||||||
getAttachmentsOfTopic2: acc.Select("attachments").Columns("attachID").Where("extra=? AND originTable='replies'").Prepare(),
|
getAttachmentsOfTopic2: acc.Select("attachments").Columns("attachID").Where("extra=? AND originTable='replies'").Prepare(),
|
||||||
|
getRepliesOfTopic: acc.Select("replies").Columns("words").Where("createdBy!=? AND tid=?").Prepare(),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
})
|
})
|
||||||
|
@ -325,6 +333,7 @@ func (u *User) Delete() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: dismiss-event
|
||||||
func (u *User) DeletePosts() error {
|
func (u *User) DeletePosts() error {
|
||||||
rows, err := userStmts.deletePosts.Query(u.ID)
|
rows, err := userStmts.deletePosts.Query(u.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -336,9 +345,10 @@ func (u *User) DeletePosts() error {
|
||||||
|
|
||||||
updatedForums := make(map[int]int) // forum[count]
|
updatedForums := make(map[int]int) // forum[count]
|
||||||
tc := Topics.GetCache()
|
tc := Topics.GetCache()
|
||||||
|
umap := make(map[int]struct{})
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var tid, parentID, poll int
|
var tid, parentID, postCount,poll int
|
||||||
err := rows.Scan(&tid, &parentID, &poll)
|
err := rows.Scan(&tid, &parentID, &postCount, &poll)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -356,15 +366,25 @@ func (u *User) DeletePosts() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = handleLikedTopicReplies(tid)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = handleTopicAttachments(tid)
|
err = handleTopicAttachments(tid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = topicStmts.deleteActivitySubs.Exec(tid)
|
if postCount > 1 {
|
||||||
|
err = handleLikedTopicReplies(tid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = handleTopicReplies(umap, u.ID, tid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = topicStmts.deleteReplies.Exec(tid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = Subscriptions.DeleteResource(tid,"topic")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -386,6 +406,12 @@ func (u *User) DeletePosts() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for uid, _ := range umap {
|
||||||
|
err = (&User{ID: uid}).RecalcPostStats()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
for fid, count := range updatedForums {
|
for fid, count := range updatedForums {
|
||||||
err := Forums.RemoveTopics(fid, count)
|
err := Forums.RemoveTopics(fid, count)
|
||||||
if err != nil && err != ErrNoRows {
|
if err != nil && err != ErrNoRows {
|
||||||
|
@ -400,8 +426,8 @@ func (u *User) DeletePosts() error {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var rid int
|
var rid, uid int
|
||||||
err := rows.Scan(&rid)
|
err := rows.Scan(&rid,&uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -409,7 +435,12 @@ func (u *User) DeletePosts() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// TODO: Remove alerts.
|
// TODO: Optimise this
|
||||||
|
// TODO: dismiss-event
|
||||||
|
err = Activity.DeleteByParamsExtra("reply",uid,"user",strconv.Itoa(rid))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err = rows.Err(); err != nil {
|
if err = rows.Err(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -454,6 +485,10 @@ func (u *User) DeletePosts() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
err = Activity.DeleteByParamsExtra("reply",tid,"topic",strconv.Itoa(rid))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
_, err = replyStmts.deleteActivitySubs.Exec(rid)
|
_, err = replyStmts.deleteActivitySubs.Exec(rid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -561,6 +596,42 @@ func (u *User) IncreasePostStats(wcount int, topic bool) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) countf(stmt *sql.Stmt) (count int) {
|
||||||
|
err := stmt.QueryRow().Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
LogError(err)
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) RecalcPostStats() error {
|
||||||
|
var score int
|
||||||
|
tcount := Topics.CountUser(u.ID)
|
||||||
|
rcount := Rstore.CountUser(u.ID)
|
||||||
|
//log.Print("tcount:", tcount)
|
||||||
|
//log.Print("rcount:", rcount)
|
||||||
|
score += tcount * 2
|
||||||
|
score += rcount
|
||||||
|
|
||||||
|
var tmega, tbig, rmega, rbig int
|
||||||
|
if tcount > 0 {
|
||||||
|
tmega = Topics.CountMegaUser(u.ID)
|
||||||
|
score += tmega * 3
|
||||||
|
tbig := Topics.CountBigUser(u.ID)
|
||||||
|
score += tbig
|
||||||
|
}
|
||||||
|
if rcount > 0 {
|
||||||
|
rmega = Rstore.CountMegaUser(u.ID)
|
||||||
|
score += rmega * 3
|
||||||
|
rbig = Rstore.CountBigUser(u.ID)
|
||||||
|
score += rbig
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := userStmts.setStats.Exec(score, tcount+rcount, tbig+rbig, tmega+rmega, tcount, GetLevel(score), u.ID)
|
||||||
|
u.CacheRemove()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (u *User) DecreasePostStats(wcount int, topic bool) (err error) {
|
func (u *User) DecreasePostStats(wcount int, topic bool) (err error) {
|
||||||
baseScore := -1
|
baseScore := -1
|
||||||
if topic {
|
if topic {
|
||||||
|
|
|
@ -21,6 +21,7 @@ type UserStore interface {
|
||||||
GetByName(name string) (*User, error)
|
GetByName(name string) (*User, error)
|
||||||
Exists(id int) bool
|
Exists(id int) bool
|
||||||
GetOffset(offset, perPage int) ([]*User, error)
|
GetOffset(offset, perPage int) ([]*User, error)
|
||||||
|
Each(f func(*User) error) error
|
||||||
//BulkGet(ids []int) ([]*User, error)
|
//BulkGet(ids []int) ([]*User, error)
|
||||||
BulkGetMap(ids []int) (map[int]*User, error)
|
BulkGetMap(ids []int) (map[int]*User, error)
|
||||||
BypassGet(id int) (*User, error)
|
BypassGet(id int) (*User, error)
|
||||||
|
@ -38,6 +39,7 @@ type DefaultUserStore struct {
|
||||||
get *sql.Stmt
|
get *sql.Stmt
|
||||||
getByName *sql.Stmt
|
getByName *sql.Stmt
|
||||||
getOffset *sql.Stmt
|
getOffset *sql.Stmt
|
||||||
|
getAll *sql.Stmt
|
||||||
exists *sql.Stmt
|
exists *sql.Stmt
|
||||||
register *sql.Stmt
|
register *sql.Stmt
|
||||||
nameExists *sql.Stmt
|
nameExists *sql.Stmt
|
||||||
|
@ -57,6 +59,7 @@ func NewDefaultUserStore(cache UserCache) (*DefaultUserStore, error) {
|
||||||
get: acc.Select(u).Columns("name, group, active, is_super_admin, session, email, avatar, message, level, score, posts, liked, last_ip, temp_group, enable_embeds").Where("uid = ?").Prepare(),
|
get: acc.Select(u).Columns("name, group, active, is_super_admin, session, email, avatar, message, level, score, posts, liked, last_ip, temp_group, enable_embeds").Where("uid = ?").Prepare(),
|
||||||
getByName: acc.Select(u).Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, level, score, posts, liked, last_ip, temp_group, enable_embeds").Where("name = ?").Prepare(),
|
getByName: acc.Select(u).Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, level, score, posts, liked, last_ip, temp_group, enable_embeds").Where("name = ?").Prepare(),
|
||||||
getOffset: acc.Select(u).Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, level, score, posts, liked, last_ip, temp_group, enable_embeds").Orderby("uid ASC").Limit("?,?").Prepare(),
|
getOffset: acc.Select(u).Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, level, score, posts, liked, last_ip, temp_group, enable_embeds").Orderby("uid ASC").Limit("?,?").Prepare(),
|
||||||
|
getAll: acc.Select(u).Columns("uid, name, group, active, is_super_admin, session, email, avatar, message, level, score, posts, liked, last_ip, temp_group, enable_embeds").Prepare(),
|
||||||
exists: acc.Exists(u, "uid").Prepare(),
|
exists: acc.Exists(u, "uid").Prepare(),
|
||||||
register: acc.Insert(u).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(u).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
|
||||||
nameExists: acc.Exists(u, "name").Prepare(),
|
nameExists: acc.Exists(u, "name").Prepare(),
|
||||||
|
@ -143,6 +146,29 @@ func (s *DefaultUserStore) GetOffset(offset, perPage int) (users []*User, err er
|
||||||
}
|
}
|
||||||
return users, rows.Err()
|
return users, rows.Err()
|
||||||
}
|
}
|
||||||
|
func (s *DefaultUserStore) Each(f func(*User) error) error {
|
||||||
|
rows, err := s.getAll.Query()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var embeds int
|
||||||
|
for rows.Next() {
|
||||||
|
u := new(User)
|
||||||
|
if err := rows.Scan(&u.ID, &u.Name, &u.Group, &u.Active, &u.IsSuperAdmin, &u.Session, &u.Email, &u.RawAvatar, &u.Message, &u.Level, &u.Score, &u.Posts, &u.Liked, &u.LastIP, &u.TempGroup, &embeds); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if embeds != -1 {
|
||||||
|
u.ParseSettings = DefaultParseSettings.CopyPtr()
|
||||||
|
u.ParseSettings.NoEmbed = embeds == 0
|
||||||
|
}
|
||||||
|
u.Init()
|
||||||
|
if err := f(u); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
|
||||||
// TODO: ID of 0 should always error?
|
// TODO: ID of 0 should always error?
|
||||||
|
@ -265,7 +291,7 @@ func (s *DefaultUserStore) Exists(id int) bool {
|
||||||
|
|
||||||
// TODO: Change active to a bool?
|
// TODO: Change active to a bool?
|
||||||
// TODO: Use unique keys for the usernames
|
// TODO: Use unique keys for the usernames
|
||||||
func (s *DefaultUserStore) Create(name string, password string, email string, group int, active bool) (int, error) {
|
func (s *DefaultUserStore) Create(name, password, email string, group int, active bool) (int, error) {
|
||||||
// TODO: Strip spaces?
|
// TODO: Strip spaces?
|
||||||
|
|
||||||
// ? This number might be a little screwy with Unicode, but it's the only consistent thing we have, as Unicode characters can be any number of bytes in theory?
|
// ? This number might be a little screwy with Unicode, but it's the only consistent thing we have, as Unicode characters can be any number of bytes in theory?
|
||||||
|
@ -297,11 +323,7 @@ func (s *DefaultUserStore) Create(name string, password string, email string, gr
|
||||||
|
|
||||||
// Count returns the total number of users registered on the forums
|
// Count returns the total number of users registered on the forums
|
||||||
func (s *DefaultUserStore) Count() (count int) {
|
func (s *DefaultUserStore) Count() (count int) {
|
||||||
err := s.count.QueryRow().Scan(&count)
|
return Countf(s.count)
|
||||||
if err != nil {
|
|
||||||
LogError(err)
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultUserStore) SetCache(cache UserCache) {
|
func (s *DefaultUserStore) SetCache(cache UserCache) {
|
||||||
|
|
|
@ -3,11 +3,11 @@ package common
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strings"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
)
|
)
|
||||||
|
|
||||||
type WidgetStmts struct {
|
type WidgetStmts struct {
|
||||||
|
@ -29,9 +29,9 @@ func init() {
|
||||||
//getList: acc.Select(w).Columns("wid, position, side, type, active, location, data").Orderby("position ASC").Prepare(),
|
//getList: acc.Select(w).Columns("wid, position, side, type, active, location, data").Orderby("position ASC").Prepare(),
|
||||||
getDockList: acc.Select(w).Columns("wid, position, type, active, location, data").Where("side = ?").Orderby("position ASC").Prepare(),
|
getDockList: acc.Select(w).Columns("wid, position, type, active, location, data").Where("side = ?").Orderby("position ASC").Prepare(),
|
||||||
//model: acc.SimpleModel(w,"position,type,active,location,data","wid"),
|
//model: acc.SimpleModel(w,"position,type,active,location,data","wid"),
|
||||||
delete: acc.Delete(w).Where("wid = ?").Prepare(),
|
delete: acc.Delete(w).Where("wid=?").Prepare(),
|
||||||
create: acc.Insert(w).Columns("position, side, type, active, location, data").Fields("?,?,?,?,?,?").Prepare(),
|
create: acc.Insert(w).Columns("position, side, type, active, location, data").Fields("?,?,?,?,?,?").Prepare(),
|
||||||
update: acc.Update(w).Set("position = ?, side = ?, type = ?, active = ?, location = ?, data = ?").Where("wid = ?").Prepare(),
|
update: acc.Update(w).Set("position=?,side=?,type=?,active=?,location=?,data=?").Where("wid=?").Prepare(),
|
||||||
}
|
}
|
||||||
return acc.FirstError()
|
return acc.FirstError()
|
||||||
})
|
})
|
||||||
|
@ -87,7 +87,7 @@ func (w *Widget) Allowed(zone string, zoneid int) bool {
|
||||||
if len(loc) == 0 {
|
if len(loc) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sloc := strings.Split(":",loc)
|
sloc := strings.Split(":", loc)
|
||||||
if len(sloc) > 1 {
|
if len(sloc) > 1 {
|
||||||
iloc, _ := strconv.Atoi(sloc[1])
|
iloc, _ := strconv.Atoi(sloc[1])
|
||||||
if zoneid != 0 && iloc != zoneid {
|
if zoneid != 0 && iloc != zoneid {
|
||||||
|
|
|
@ -26,9 +26,9 @@ type WordFilterStore interface {
|
||||||
ReloadAll() error
|
ReloadAll() error
|
||||||
GetAll() (filters map[int]*WordFilter, err error)
|
GetAll() (filters map[int]*WordFilter, err error)
|
||||||
Get(id int) (*WordFilter, error)
|
Get(id int) (*WordFilter, error)
|
||||||
Create(find string, replace string) (int, error)
|
Create(find, replace string) (int, error)
|
||||||
Delete(id int) error
|
Delete(id int) error
|
||||||
Update(id int, find string, replace string) error
|
Update(id int, find, replace string) error
|
||||||
Length() int
|
Length() int
|
||||||
EstCount() int
|
EstCount() int
|
||||||
Count() (count int)
|
Count() (count int)
|
||||||
|
@ -49,10 +49,10 @@ func NewDefaultWordFilterStore(acc *qgen.Accumulator) (*DefaultWordFilterStore,
|
||||||
wf := "word_filters"
|
wf := "word_filters"
|
||||||
store := &DefaultWordFilterStore{
|
store := &DefaultWordFilterStore{
|
||||||
getAll: acc.Select(wf).Columns("wfid,find,replacement").Prepare(),
|
getAll: acc.Select(wf).Columns("wfid,find,replacement").Prepare(),
|
||||||
get: acc.Select(wf).Columns("find,replacement").Where("wfid = ?").Prepare(),
|
get: acc.Select(wf).Columns("find,replacement").Where("wfid=?").Prepare(),
|
||||||
create: acc.Insert(wf).Columns("find,replacement").Fields("?,?").Prepare(),
|
create: acc.Insert(wf).Columns("find,replacement").Fields("?,?").Prepare(),
|
||||||
delete: acc.Delete(wf).Where("wfid = ?").Prepare(),
|
delete: acc.Delete(wf).Where("wfid=?").Prepare(),
|
||||||
update: acc.Update(wf).Set("find = ?, replacement = ?").Where("wfid = ?").Prepare(),
|
update: acc.Update(wf).Set("find=?,replacement=?").Where("wfid=?").Prepare(),
|
||||||
count: acc.Count(wf).Prepare(),
|
count: acc.Count(wf).Prepare(),
|
||||||
}
|
}
|
||||||
// TODO: Should we initialise this elsewhere?
|
// TODO: Should we initialise this elsewhere?
|
||||||
|
@ -109,7 +109,7 @@ func (s *DefaultWordFilterStore) Get(id int) (*WordFilter, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 (s *DefaultWordFilterStore) Create(find string, replace string) (int, error) {
|
func (s *DefaultWordFilterStore) Create(find, replace string) (int, error) {
|
||||||
res, err := s.create.Exec(find, replace)
|
res, err := s.create.Exec(find, replace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
@ -130,7 +130,7 @@ func (s *DefaultWordFilterStore) Delete(id int) error {
|
||||||
return s.ReloadAll()
|
return s.ReloadAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DefaultWordFilterStore) Update(id int, find string, replace string) error {
|
func (s *DefaultWordFilterStore) Update(id int, find, replace string) error {
|
||||||
_, err := s.update.Exec(find, replace, id)
|
_, err := s.update.Exec(find, replace, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
214
gen_router.go
214
gen_router.go
|
@ -158,6 +158,7 @@ var RouteMap = map[string]interface{}{
|
||||||
"routes.UnlockTopicSubmit": routes.UnlockTopicSubmit,
|
"routes.UnlockTopicSubmit": routes.UnlockTopicSubmit,
|
||||||
"routes.MoveTopicSubmit": routes.MoveTopicSubmit,
|
"routes.MoveTopicSubmit": routes.MoveTopicSubmit,
|
||||||
"routes.LikeTopicSubmit": routes.LikeTopicSubmit,
|
"routes.LikeTopicSubmit": routes.LikeTopicSubmit,
|
||||||
|
"routes.UnlikeTopicSubmit": routes.UnlikeTopicSubmit,
|
||||||
"routes.AddAttachToTopicSubmit": routes.AddAttachToTopicSubmit,
|
"routes.AddAttachToTopicSubmit": routes.AddAttachToTopicSubmit,
|
||||||
"routes.RemoveAttachFromTopicSubmit": routes.RemoveAttachFromTopicSubmit,
|
"routes.RemoveAttachFromTopicSubmit": routes.RemoveAttachFromTopicSubmit,
|
||||||
"routes.ViewTopic": routes.ViewTopic,
|
"routes.ViewTopic": routes.ViewTopic,
|
||||||
|
@ -330,39 +331,40 @@ var routeMapEnum = map[string]int{
|
||||||
"routes.UnlockTopicSubmit": 132,
|
"routes.UnlockTopicSubmit": 132,
|
||||||
"routes.MoveTopicSubmit": 133,
|
"routes.MoveTopicSubmit": 133,
|
||||||
"routes.LikeTopicSubmit": 134,
|
"routes.LikeTopicSubmit": 134,
|
||||||
"routes.AddAttachToTopicSubmit": 135,
|
"routes.UnlikeTopicSubmit": 135,
|
||||||
"routes.RemoveAttachFromTopicSubmit": 136,
|
"routes.AddAttachToTopicSubmit": 136,
|
||||||
"routes.ViewTopic": 137,
|
"routes.RemoveAttachFromTopicSubmit": 137,
|
||||||
"routes.CreateReplySubmit": 138,
|
"routes.ViewTopic": 138,
|
||||||
"routes.ReplyEditSubmit": 139,
|
"routes.CreateReplySubmit": 139,
|
||||||
"routes.ReplyDeleteSubmit": 140,
|
"routes.ReplyEditSubmit": 140,
|
||||||
"routes.ReplyLikeSubmit": 141,
|
"routes.ReplyDeleteSubmit": 141,
|
||||||
"routes.AddAttachToReplySubmit": 142,
|
"routes.ReplyLikeSubmit": 142,
|
||||||
"routes.RemoveAttachFromReplySubmit": 143,
|
"routes.AddAttachToReplySubmit": 143,
|
||||||
"routes.ProfileReplyCreateSubmit": 144,
|
"routes.RemoveAttachFromReplySubmit": 144,
|
||||||
"routes.ProfileReplyEditSubmit": 145,
|
"routes.ProfileReplyCreateSubmit": 145,
|
||||||
"routes.ProfileReplyDeleteSubmit": 146,
|
"routes.ProfileReplyEditSubmit": 146,
|
||||||
"routes.PollVote": 147,
|
"routes.ProfileReplyDeleteSubmit": 147,
|
||||||
"routes.PollResults": 148,
|
"routes.PollVote": 148,
|
||||||
"routes.AccountLogin": 149,
|
"routes.PollResults": 149,
|
||||||
"routes.AccountRegister": 150,
|
"routes.AccountLogin": 150,
|
||||||
"routes.AccountLogout": 151,
|
"routes.AccountRegister": 151,
|
||||||
"routes.AccountLoginSubmit": 152,
|
"routes.AccountLogout": 152,
|
||||||
"routes.AccountLoginMFAVerify": 153,
|
"routes.AccountLoginSubmit": 153,
|
||||||
"routes.AccountLoginMFAVerifySubmit": 154,
|
"routes.AccountLoginMFAVerify": 154,
|
||||||
"routes.AccountRegisterSubmit": 155,
|
"routes.AccountLoginMFAVerifySubmit": 155,
|
||||||
"routes.AccountPasswordReset": 156,
|
"routes.AccountRegisterSubmit": 156,
|
||||||
"routes.AccountPasswordResetSubmit": 157,
|
"routes.AccountPasswordReset": 157,
|
||||||
"routes.AccountPasswordResetToken": 158,
|
"routes.AccountPasswordResetSubmit": 158,
|
||||||
"routes.AccountPasswordResetTokenSubmit": 159,
|
"routes.AccountPasswordResetToken": 159,
|
||||||
"routes.DynamicRoute": 160,
|
"routes.AccountPasswordResetTokenSubmit": 160,
|
||||||
"routes.UploadedFile": 161,
|
"routes.DynamicRoute": 161,
|
||||||
"routes.StaticFile": 162,
|
"routes.UploadedFile": 162,
|
||||||
"routes.RobotsTxt": 163,
|
"routes.StaticFile": 163,
|
||||||
"routes.SitemapXml": 164,
|
"routes.RobotsTxt": 164,
|
||||||
"routes.OpenSearchXml": 165,
|
"routes.SitemapXml": 165,
|
||||||
"routes.BadRoute": 166,
|
"routes.OpenSearchXml": 166,
|
||||||
"routes.HTTPSRedirect": 167,
|
"routes.BadRoute": 167,
|
||||||
|
"routes.HTTPSRedirect": 168,
|
||||||
}
|
}
|
||||||
var reverseRouteMapEnum = map[int]string{
|
var reverseRouteMapEnum = map[int]string{
|
||||||
0: "routes.Overview",
|
0: "routes.Overview",
|
||||||
|
@ -500,39 +502,40 @@ var reverseRouteMapEnum = map[int]string{
|
||||||
132: "routes.UnlockTopicSubmit",
|
132: "routes.UnlockTopicSubmit",
|
||||||
133: "routes.MoveTopicSubmit",
|
133: "routes.MoveTopicSubmit",
|
||||||
134: "routes.LikeTopicSubmit",
|
134: "routes.LikeTopicSubmit",
|
||||||
135: "routes.AddAttachToTopicSubmit",
|
135: "routes.UnlikeTopicSubmit",
|
||||||
136: "routes.RemoveAttachFromTopicSubmit",
|
136: "routes.AddAttachToTopicSubmit",
|
||||||
137: "routes.ViewTopic",
|
137: "routes.RemoveAttachFromTopicSubmit",
|
||||||
138: "routes.CreateReplySubmit",
|
138: "routes.ViewTopic",
|
||||||
139: "routes.ReplyEditSubmit",
|
139: "routes.CreateReplySubmit",
|
||||||
140: "routes.ReplyDeleteSubmit",
|
140: "routes.ReplyEditSubmit",
|
||||||
141: "routes.ReplyLikeSubmit",
|
141: "routes.ReplyDeleteSubmit",
|
||||||
142: "routes.AddAttachToReplySubmit",
|
142: "routes.ReplyLikeSubmit",
|
||||||
143: "routes.RemoveAttachFromReplySubmit",
|
143: "routes.AddAttachToReplySubmit",
|
||||||
144: "routes.ProfileReplyCreateSubmit",
|
144: "routes.RemoveAttachFromReplySubmit",
|
||||||
145: "routes.ProfileReplyEditSubmit",
|
145: "routes.ProfileReplyCreateSubmit",
|
||||||
146: "routes.ProfileReplyDeleteSubmit",
|
146: "routes.ProfileReplyEditSubmit",
|
||||||
147: "routes.PollVote",
|
147: "routes.ProfileReplyDeleteSubmit",
|
||||||
148: "routes.PollResults",
|
148: "routes.PollVote",
|
||||||
149: "routes.AccountLogin",
|
149: "routes.PollResults",
|
||||||
150: "routes.AccountRegister",
|
150: "routes.AccountLogin",
|
||||||
151: "routes.AccountLogout",
|
151: "routes.AccountRegister",
|
||||||
152: "routes.AccountLoginSubmit",
|
152: "routes.AccountLogout",
|
||||||
153: "routes.AccountLoginMFAVerify",
|
153: "routes.AccountLoginSubmit",
|
||||||
154: "routes.AccountLoginMFAVerifySubmit",
|
154: "routes.AccountLoginMFAVerify",
|
||||||
155: "routes.AccountRegisterSubmit",
|
155: "routes.AccountLoginMFAVerifySubmit",
|
||||||
156: "routes.AccountPasswordReset",
|
156: "routes.AccountRegisterSubmit",
|
||||||
157: "routes.AccountPasswordResetSubmit",
|
157: "routes.AccountPasswordReset",
|
||||||
158: "routes.AccountPasswordResetToken",
|
158: "routes.AccountPasswordResetSubmit",
|
||||||
159: "routes.AccountPasswordResetTokenSubmit",
|
159: "routes.AccountPasswordResetToken",
|
||||||
160: "routes.DynamicRoute",
|
160: "routes.AccountPasswordResetTokenSubmit",
|
||||||
161: "routes.UploadedFile",
|
161: "routes.DynamicRoute",
|
||||||
162: "routes.StaticFile",
|
162: "routes.UploadedFile",
|
||||||
163: "routes.RobotsTxt",
|
163: "routes.StaticFile",
|
||||||
164: "routes.SitemapXml",
|
164: "routes.RobotsTxt",
|
||||||
165: "routes.OpenSearchXml",
|
165: "routes.SitemapXml",
|
||||||
166: "routes.BadRoute",
|
166: "routes.OpenSearchXml",
|
||||||
167: "routes.HTTPSRedirect",
|
167: "routes.BadRoute",
|
||||||
|
168: "routes.HTTPSRedirect",
|
||||||
}
|
}
|
||||||
var osMapEnum = map[string]int{
|
var osMapEnum = map[string]int{
|
||||||
"unknown": 0,
|
"unknown": 0,
|
||||||
|
@ -690,7 +693,7 @@ type HTTPSRedirect struct {}
|
||||||
|
|
||||||
func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
func (red *HTTPSRedirect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
w.Header().Set("Connection", "close")
|
w.Header().Set("Connection", "close")
|
||||||
co.RouteViewCounter.Bump(167)
|
co.RouteViewCounter.Bump(168)
|
||||||
dest := "https://" + req.Host + req.URL.String()
|
dest := "https://" + req.Host + req.URL.String()
|
||||||
http.Redirect(w, req, dest, http.StatusTemporaryRedirect)
|
http.Redirect(w, req, dest, http.StatusTemporaryRedirect)
|
||||||
}
|
}
|
||||||
|
@ -898,7 +901,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
co.GlobalViewCounter.Bump()
|
co.GlobalViewCounter.Bump()
|
||||||
|
|
||||||
if prefix == "/s" { //old prefix: /static
|
if prefix == "/s" { //old prefix: /static
|
||||||
co.RouteViewCounter.Bump(162)
|
co.RouteViewCounter.Bump(163)
|
||||||
req.URL.Path += extraData
|
req.URL.Path += extraData
|
||||||
routes.StaticFile(w, req)
|
routes.StaticFile(w, req)
|
||||||
return
|
return
|
||||||
|
@ -2274,6 +2277,19 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(134)
|
co.RouteViewCounter.Bump(134)
|
||||||
err = routes.LikeTopicSubmit(w,req,user,extraData)
|
err = routes.LikeTopicSubmit(w,req,user,extraData)
|
||||||
|
case "/topic/unlike/submit/":
|
||||||
|
err = c.NoSessionMismatch(w,req,user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = c.MemberOnly(w,req,user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
co.RouteViewCounter.Bump(135)
|
||||||
|
err = routes.UnlikeTopicSubmit(w,req,user,extraData)
|
||||||
case "/topic/attach/add/submit/":
|
case "/topic/attach/add/submit/":
|
||||||
err = c.MemberOnly(w,req,user)
|
err = c.MemberOnly(w,req,user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2289,7 +2305,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(135)
|
co.RouteViewCounter.Bump(136)
|
||||||
err = routes.AddAttachToTopicSubmit(w,req,user,extraData)
|
err = routes.AddAttachToTopicSubmit(w,req,user,extraData)
|
||||||
case "/topic/attach/remove/submit/":
|
case "/topic/attach/remove/submit/":
|
||||||
err = c.NoSessionMismatch(w,req,user)
|
err = c.NoSessionMismatch(w,req,user)
|
||||||
|
@ -2302,10 +2318,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(136)
|
co.RouteViewCounter.Bump(137)
|
||||||
err = routes.RemoveAttachFromTopicSubmit(w,req,user,extraData)
|
err = routes.RemoveAttachFromTopicSubmit(w,req,user,extraData)
|
||||||
default:
|
default:
|
||||||
co.RouteViewCounter.Bump(137)
|
co.RouteViewCounter.Bump(138)
|
||||||
head, err := c.UserCheck(w,req,&user)
|
head, err := c.UserCheck(w,req,&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -2329,7 +2345,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(138)
|
co.RouteViewCounter.Bump(139)
|
||||||
err = routes.CreateReplySubmit(w,req,user)
|
err = routes.CreateReplySubmit(w,req,user)
|
||||||
case "/reply/edit/submit/":
|
case "/reply/edit/submit/":
|
||||||
err = c.NoSessionMismatch(w,req,user)
|
err = c.NoSessionMismatch(w,req,user)
|
||||||
|
@ -2342,7 +2358,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(139)
|
co.RouteViewCounter.Bump(140)
|
||||||
err = routes.ReplyEditSubmit(w,req,user,extraData)
|
err = routes.ReplyEditSubmit(w,req,user,extraData)
|
||||||
case "/reply/delete/submit/":
|
case "/reply/delete/submit/":
|
||||||
err = c.NoSessionMismatch(w,req,user)
|
err = c.NoSessionMismatch(w,req,user)
|
||||||
|
@ -2355,7 +2371,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(140)
|
co.RouteViewCounter.Bump(141)
|
||||||
err = routes.ReplyDeleteSubmit(w,req,user,extraData)
|
err = routes.ReplyDeleteSubmit(w,req,user,extraData)
|
||||||
case "/reply/like/submit/":
|
case "/reply/like/submit/":
|
||||||
err = c.NoSessionMismatch(w,req,user)
|
err = c.NoSessionMismatch(w,req,user)
|
||||||
|
@ -2368,7 +2384,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(141)
|
co.RouteViewCounter.Bump(142)
|
||||||
err = routes.ReplyLikeSubmit(w,req,user,extraData)
|
err = routes.ReplyLikeSubmit(w,req,user,extraData)
|
||||||
case "/reply/attach/add/submit/":
|
case "/reply/attach/add/submit/":
|
||||||
err = c.MemberOnly(w,req,user)
|
err = c.MemberOnly(w,req,user)
|
||||||
|
@ -2385,7 +2401,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(142)
|
co.RouteViewCounter.Bump(143)
|
||||||
err = routes.AddAttachToReplySubmit(w,req,user,extraData)
|
err = routes.AddAttachToReplySubmit(w,req,user,extraData)
|
||||||
case "/reply/attach/remove/submit/":
|
case "/reply/attach/remove/submit/":
|
||||||
err = c.NoSessionMismatch(w,req,user)
|
err = c.NoSessionMismatch(w,req,user)
|
||||||
|
@ -2398,7 +2414,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(143)
|
co.RouteViewCounter.Bump(144)
|
||||||
err = routes.RemoveAttachFromReplySubmit(w,req,user,extraData)
|
err = routes.RemoveAttachFromReplySubmit(w,req,user,extraData)
|
||||||
}
|
}
|
||||||
case "/profile":
|
case "/profile":
|
||||||
|
@ -2414,7 +2430,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(144)
|
co.RouteViewCounter.Bump(145)
|
||||||
err = routes.ProfileReplyCreateSubmit(w,req,user)
|
err = routes.ProfileReplyCreateSubmit(w,req,user)
|
||||||
case "/profile/reply/edit/submit/":
|
case "/profile/reply/edit/submit/":
|
||||||
err = c.NoSessionMismatch(w,req,user)
|
err = c.NoSessionMismatch(w,req,user)
|
||||||
|
@ -2427,7 +2443,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(145)
|
co.RouteViewCounter.Bump(146)
|
||||||
err = routes.ProfileReplyEditSubmit(w,req,user,extraData)
|
err = routes.ProfileReplyEditSubmit(w,req,user,extraData)
|
||||||
case "/profile/reply/delete/submit/":
|
case "/profile/reply/delete/submit/":
|
||||||
err = c.NoSessionMismatch(w,req,user)
|
err = c.NoSessionMismatch(w,req,user)
|
||||||
|
@ -2440,7 +2456,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(146)
|
co.RouteViewCounter.Bump(147)
|
||||||
err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData)
|
err = routes.ProfileReplyDeleteSubmit(w,req,user,extraData)
|
||||||
}
|
}
|
||||||
case "/poll":
|
case "/poll":
|
||||||
|
@ -2456,23 +2472,23 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(147)
|
co.RouteViewCounter.Bump(148)
|
||||||
err = routes.PollVote(w,req,user,extraData)
|
err = routes.PollVote(w,req,user,extraData)
|
||||||
case "/poll/results/":
|
case "/poll/results/":
|
||||||
co.RouteViewCounter.Bump(148)
|
co.RouteViewCounter.Bump(149)
|
||||||
err = routes.PollResults(w,req,user,extraData)
|
err = routes.PollResults(w,req,user,extraData)
|
||||||
}
|
}
|
||||||
case "/accounts":
|
case "/accounts":
|
||||||
switch(req.URL.Path) {
|
switch(req.URL.Path) {
|
||||||
case "/accounts/login/":
|
case "/accounts/login/":
|
||||||
co.RouteViewCounter.Bump(149)
|
co.RouteViewCounter.Bump(150)
|
||||||
head, err := c.UserCheck(w,req,&user)
|
head, err := c.UserCheck(w,req,&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = routes.AccountLogin(w,req,user,head)
|
err = routes.AccountLogin(w,req,user,head)
|
||||||
case "/accounts/create/":
|
case "/accounts/create/":
|
||||||
co.RouteViewCounter.Bump(150)
|
co.RouteViewCounter.Bump(151)
|
||||||
head, err := c.UserCheck(w,req,&user)
|
head, err := c.UserCheck(w,req,&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -2489,7 +2505,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(151)
|
co.RouteViewCounter.Bump(152)
|
||||||
err = routes.AccountLogout(w,req,user)
|
err = routes.AccountLogout(w,req,user)
|
||||||
case "/accounts/login/submit/":
|
case "/accounts/login/submit/":
|
||||||
err = c.ParseForm(w,req,user)
|
err = c.ParseForm(w,req,user)
|
||||||
|
@ -2497,10 +2513,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(152)
|
co.RouteViewCounter.Bump(153)
|
||||||
err = routes.AccountLoginSubmit(w,req,user)
|
err = routes.AccountLoginSubmit(w,req,user)
|
||||||
case "/accounts/mfa_verify/":
|
case "/accounts/mfa_verify/":
|
||||||
co.RouteViewCounter.Bump(153)
|
co.RouteViewCounter.Bump(154)
|
||||||
head, err := c.UserCheck(w,req,&user)
|
head, err := c.UserCheck(w,req,&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -2512,7 +2528,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(154)
|
co.RouteViewCounter.Bump(155)
|
||||||
err = routes.AccountLoginMFAVerifySubmit(w,req,user)
|
err = routes.AccountLoginMFAVerifySubmit(w,req,user)
|
||||||
case "/accounts/create/submit/":
|
case "/accounts/create/submit/":
|
||||||
err = c.ParseForm(w,req,user)
|
err = c.ParseForm(w,req,user)
|
||||||
|
@ -2520,10 +2536,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(155)
|
co.RouteViewCounter.Bump(156)
|
||||||
err = routes.AccountRegisterSubmit(w,req,user)
|
err = routes.AccountRegisterSubmit(w,req,user)
|
||||||
case "/accounts/password-reset/":
|
case "/accounts/password-reset/":
|
||||||
co.RouteViewCounter.Bump(156)
|
co.RouteViewCounter.Bump(157)
|
||||||
head, err := c.UserCheck(w,req,&user)
|
head, err := c.UserCheck(w,req,&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -2535,10 +2551,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(157)
|
co.RouteViewCounter.Bump(158)
|
||||||
err = routes.AccountPasswordResetSubmit(w,req,user)
|
err = routes.AccountPasswordResetSubmit(w,req,user)
|
||||||
case "/accounts/password-reset/token/":
|
case "/accounts/password-reset/token/":
|
||||||
co.RouteViewCounter.Bump(158)
|
co.RouteViewCounter.Bump(159)
|
||||||
head, err := c.UserCheck(w,req,&user)
|
head, err := c.UserCheck(w,req,&user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -2550,7 +2566,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
co.RouteViewCounter.Bump(159)
|
co.RouteViewCounter.Bump(160)
|
||||||
err = routes.AccountPasswordResetTokenSubmit(w,req,user)
|
err = routes.AccountPasswordResetTokenSubmit(w,req,user)
|
||||||
}
|
}
|
||||||
/*case "/sitemaps": // TODO: Count these views
|
/*case "/sitemaps": // TODO: Count these views
|
||||||
|
@ -2567,7 +2583,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
h.Del("Content-Type")
|
h.Del("Content-Type")
|
||||||
h.Del("Content-Encoding")
|
h.Del("Content-Encoding")
|
||||||
}
|
}
|
||||||
co.RouteViewCounter.Bump(161)
|
co.RouteViewCounter.Bump(162)
|
||||||
req.URL.Path += extraData
|
req.URL.Path += extraData
|
||||||
// TODO: Find a way to propagate errors up from this?
|
// TODO: Find a way to propagate errors up from this?
|
||||||
r.UploadHandler(w,req) // TODO: Count these views
|
r.UploadHandler(w,req) // TODO: Count these views
|
||||||
|
@ -2577,7 +2593,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
// TODO: Add support for favicons and robots.txt files
|
// TODO: Add support for favicons and robots.txt files
|
||||||
switch(extraData) {
|
switch(extraData) {
|
||||||
case "robots.txt":
|
case "robots.txt":
|
||||||
co.RouteViewCounter.Bump(163)
|
co.RouteViewCounter.Bump(164)
|
||||||
return routes.RobotsTxt(w,req)
|
return routes.RobotsTxt(w,req)
|
||||||
case "favicon.ico":
|
case "favicon.ico":
|
||||||
gzw, ok := w.(c.GzipResponseWriter)
|
gzw, ok := w.(c.GzipResponseWriter)
|
||||||
|
@ -2591,10 +2607,10 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
routes.StaticFile(w,req)
|
routes.StaticFile(w,req)
|
||||||
return nil
|
return nil
|
||||||
case "opensearch.xml":
|
case "opensearch.xml":
|
||||||
co.RouteViewCounter.Bump(165)
|
co.RouteViewCounter.Bump(166)
|
||||||
return routes.OpenSearchXml(w,req)
|
return routes.OpenSearchXml(w,req)
|
||||||
/*case "sitemap.xml":
|
/*case "sitemap.xml":
|
||||||
co.RouteViewCounter.Bump(164)
|
co.RouteViewCounter.Bump(165)
|
||||||
return routes.SitemapXml(w,req)*/
|
return routes.SitemapXml(w,req)*/
|
||||||
}
|
}
|
||||||
return c.NotFound(w,req,nil)
|
return c.NotFound(w,req,nil)
|
||||||
|
@ -2605,7 +2621,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
r.RUnlock()
|
r.RUnlock()
|
||||||
|
|
||||||
if ok {
|
if ok {
|
||||||
co.RouteViewCounter.Bump(160) // TODO: Be more specific about *which* dynamic route it is
|
co.RouteViewCounter.Bump(161) // TODO: Be more specific about *which* dynamic route it is
|
||||||
req.URL.Path += extraData
|
req.URL.Path += extraData
|
||||||
return handle(w,req,user)
|
return handle(w,req,user)
|
||||||
}
|
}
|
||||||
|
@ -2616,7 +2632,7 @@ func (r *GenRouter) routeSwitch(w http.ResponseWriter, req *http.Request, user c
|
||||||
} else {
|
} else {
|
||||||
r.DumpRequest(req,"Bad Route")
|
r.DumpRequest(req,"Bad Route")
|
||||||
}
|
}
|
||||||
co.RouteViewCounter.Bump(166)
|
co.RouteViewCounter.Bump(167)
|
||||||
return c.NotFound(w,req,nil)
|
return c.NotFound(w,req,nil)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -791,7 +791,7 @@ func BenchmarkQueryTopicParallel(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
var tu c.TopicUser
|
var tu c.TopicUser
|
||||||
for pb.Next() {
|
for pb.Next() {
|
||||||
err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.views, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IP, &tu.ViewCount, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.Level)
|
err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ip, topics.views, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IP, &tu.ViewCount, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.Level)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
log.Fatal("No rows found!")
|
log.Fatal("No rows found!")
|
||||||
return
|
return
|
||||||
|
@ -812,7 +812,7 @@ func BenchmarkQueryPreparedTopicParallel(b *testing.B) {
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
var tu c.TopicUser
|
var tu c.TopicUser
|
||||||
|
|
||||||
getTopicUser, err := qgen.Builder.SimpleLeftJoin("topics", "users", "topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.level", "topics.createdBy = users.uid", "tid = ?", "", "")
|
getTopicUser, err := qgen.Builder.SimpleLeftJoin("topics", "users", "topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ip, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.level", "topics.createdBy = users.uid", "tid = ?", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -873,7 +873,7 @@ func BenchmarkQueriesSerial(b *testing.B) {
|
||||||
var tu c.TopicUser
|
var tu c.TopicUser
|
||||||
b.Run("topic", func(b *testing.B) {
|
b.Run("topic", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ipaddress, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IP, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.Level)
|
err := db.QueryRow("select topics.title, topics.content, topics.createdBy, topics.createdAt, topics.is_closed, topics.sticky, topics.parentID, topics.ip, topics.postCount, topics.likeCount, users.name, users.avatar, users.group, users.level from topics left join users ON topics.createdBy = users.uid where tid = ?", 1).Scan(&tu.Title, &tu.Content, &tu.CreatedBy, &tu.CreatedAt, &tu.IsClosed, &tu.Sticky, &tu.ParentID, &tu.IP, &tu.PostCount, &tu.LikeCount, &tu.CreatedByName, &tu.Avatar, &tu.Group, &tu.Level)
|
||||||
if err == ErrNoRows {
|
if err == ErrNoRows {
|
||||||
b.Fatal("No rows found!")
|
b.Fatal("No rows found!")
|
||||||
return
|
return
|
||||||
|
@ -885,7 +885,7 @@ func BenchmarkQueriesSerial(b *testing.B) {
|
||||||
})
|
})
|
||||||
b.Run("topic_replies", func(b *testing.B) {
|
b.Run("topic_replies", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
rows, err := db.Query("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group, users.level, replies.ipaddress from replies left join users ON replies.createdBy = users.uid where tid = ?", 1)
|
rows, err := db.Query("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group, users.level, replies.ip from replies left join users ON replies.createdBy = users.uid where tid = ?", 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
return
|
return
|
||||||
|
@ -907,7 +907,7 @@ func BenchmarkQueriesSerial(b *testing.B) {
|
||||||
var group int
|
var group int
|
||||||
b.Run("topic_replies_scan", func(b *testing.B) {
|
b.Run("topic_replies_scan", func(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
rows, err := db.Query("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group, users.level, replies.ipaddress from replies left join users ON replies.createdBy = users.uid where tid = ?", 1)
|
rows, err := db.Query("select replies.rid, replies.content, replies.createdBy, replies.createdAt, replies.lastEdit, replies.lastEditBy, users.avatar, users.name, users.is_super_admin, users.group, users.level, replies.ip from replies left join users ON replies.createdBy = users.uid where tid = ?", 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
return
|
return
|
||||||
|
|
13
main.go
13
main.go
|
@ -28,6 +28,7 @@ import (
|
||||||
_ "github.com/Azareal/Gosora/extend"
|
_ "github.com/Azareal/Gosora/extend"
|
||||||
co "github.com/Azareal/Gosora/common/counters"
|
co "github.com/Azareal/Gosora/common/counters"
|
||||||
p "github.com/Azareal/Gosora/common/phrases"
|
p "github.com/Azareal/Gosora/common/phrases"
|
||||||
|
meta "github.com/Azareal/Gosora/common/meta"
|
||||||
"github.com/Azareal/Gosora/query_gen"
|
"github.com/Azareal/Gosora/query_gen"
|
||||||
"github.com/Azareal/Gosora/routes"
|
"github.com/Azareal/Gosora/routes"
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
|
@ -264,6 +265,10 @@ func storeInit() (err error) {
|
||||||
}
|
}
|
||||||
// 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()
|
||||||
|
c.Recalc, err = c.NewDefaultRecalc(acc)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Print("Initialising the view counters")
|
log.Print("Initialising the view counters")
|
||||||
co.GlobalViewCounter, err = co.NewGlobalViewCounter(acc)
|
co.GlobalViewCounter, err = co.NewGlobalViewCounter(acc)
|
||||||
|
@ -312,7 +317,7 @@ func storeInit() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Print("Initialising the meta store")
|
log.Print("Initialising the meta store")
|
||||||
c.Meta, err = c.NewDefaultMetaStore(acc)
|
c.Meta, err = meta.NewDefaultMetaStore(acc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
@ -482,6 +487,12 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Print("Checking for init tasks")
|
||||||
|
err = sched()
|
||||||
|
if err != nil {
|
||||||
|
c.LogError(err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Print("Initialising the task system")
|
log.Print("Initialising the task system")
|
||||||
|
|
||||||
// Thumbnailer goroutine, we only want one image being thumbnailed at a time, otherwise they might wind up consuming all the CPU time and leave no resources left to service the actual requests
|
// Thumbnailer goroutine, we only want one image being thumbnailed at a time, otherwise they might wind up consuming all the CPU time and leave no resources left to service the actual requests
|
||||||
|
|
18
misc_test.go
18
misc_test.go
|
@ -917,7 +917,7 @@ func TestReplyStore(t *testing.T) {
|
||||||
testReplyStore(t, 5, 3, "0")
|
testReplyStore(t, 5, 3, "0")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testReplyStore(t *testing.T, newID int, newPostCount int, ip string) {
|
func testReplyStore(t *testing.T, newID, newPostCount int, ip string) {
|
||||||
replyTest2 := func(reply *c.Reply, err error, rid int, parentID int, createdBy int, content string, ip string) {
|
replyTest2 := func(reply *c.Reply, err error, rid int, parentID int, createdBy int, content string, ip string) {
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, reply.ID == rid, fmt.Sprintf("RID #%d has the wrong ID. It should be %d not %d", rid, rid, reply.ID))
|
expect(t, reply.ID == rid, fmt.Sprintf("RID #%d has the wrong ID. It should be %d not %d", rid, rid, reply.ID))
|
||||||
|
@ -1028,20 +1028,20 @@ func testProfileReplyStore(t *testing.T, newID int, ip string) {
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, prid == newID, fmt.Sprintf("The first profile reply should have an ID of %d", newID))
|
expect(t, prid == newID, fmt.Sprintf("The first profile reply should have an ID of %d", newID))
|
||||||
|
|
||||||
profileReply, err := c.Prstore.Get(newID)
|
pr, err := c.Prstore.Get(newID)
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
expect(t, profileReply.ID == newID, fmt.Sprintf("The profile reply should have an ID of %d not %d", newID, profileReply.ID))
|
expect(t, pr.ID == newID, fmt.Sprintf("The profile reply should have an ID of %d not %d", newID, pr.ID))
|
||||||
expect(t, profileReply.ParentID == 1, fmt.Sprintf("The parent ID of the profile reply should be 1 not %d", profileReply.ParentID))
|
expect(t, pr.ParentID == 1, fmt.Sprintf("The parent ID of the profile reply should be 1 not %d", pr.ParentID))
|
||||||
expect(t, profileReply.Content == "Haha", fmt.Sprintf("The profile reply's contents should be 'Haha' not '%s'", profileReply.Content))
|
expect(t, pr.Content == "Haha", fmt.Sprintf("The profile reply's contents should be 'Haha' not '%s'", pr.Content))
|
||||||
expect(t, profileReply.CreatedBy == 1, fmt.Sprintf("The profile reply's creator should be 1 not %d", profileReply.CreatedBy))
|
expect(t, pr.CreatedBy == 1, fmt.Sprintf("The profile reply's creator should be 1 not %d", pr.CreatedBy))
|
||||||
expect(t, profileReply.IP == ip, fmt.Sprintf("The profile reply's IP should be '%s' not '%s'", ip, profileReply.IP))
|
expect(t, pr.IP == ip, fmt.Sprintf("The profile reply's IP should be '%s' not '%s'", ip, pr.IP))
|
||||||
|
|
||||||
err = profileReply.Delete()
|
err = pr.Delete()
|
||||||
expectNilErr(t, err)
|
expectNilErr(t, err)
|
||||||
_, err = c.Prstore.Get(newID)
|
_, err = c.Prstore.Get(newID)
|
||||||
expect(t, err != nil, fmt.Sprintf("PRID #%d shouldn't exist after being deleted", newID))
|
expect(t, err != nil, fmt.Sprintf("PRID #%d shouldn't exist after being deleted", newID))
|
||||||
|
|
||||||
// TODO: Test profileReply.SetBody() and profileReply.Creator()
|
// TODO: Test pr.SetBody() and pr.Creator()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestActivityStream(t *testing.T) {
|
func TestActivityStream(t *testing.T) {
|
||||||
|
|
|
@ -4,7 +4,10 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
|
meta "github.com/Azareal/Gosora/common/meta"
|
||||||
qgen "github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,6 +46,7 @@ func init() {
|
||||||
addPatch(26, patch26)
|
addPatch(26, patch26)
|
||||||
addPatch(27, patch27)
|
addPatch(27, patch27)
|
||||||
addPatch(28, patch28)
|
addPatch(28, patch28)
|
||||||
|
addPatch(29, patch29)
|
||||||
}
|
}
|
||||||
|
|
||||||
func patch0(scanner *bufio.Scanner) (err error) {
|
func patch0(scanner *bufio.Scanner) (err error) {
|
||||||
|
@ -750,3 +754,107 @@ func patch27(scanner *bufio.Scanner) error {
|
||||||
func patch28(scanner *bufio.Scanner) error {
|
func patch28(scanner *bufio.Scanner) error {
|
||||||
return execStmt(qgen.Builder.AddColumn("users", tC{"enable_embeds", "int", 0, false, false, "-1"}, nil))
|
return execStmt(qgen.Builder.AddColumn("users", tC{"enable_embeds", "int", 0, false, false, "-1"}, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The word counter might run into problems with some languages where words aren't as obviously demarcated, I would advise turning it off in those cases, or if it becomes annoying in general, really.
|
||||||
|
func WordCount(input string) (count int) {
|
||||||
|
input = strings.TrimSpace(input)
|
||||||
|
if input == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var inSpace bool
|
||||||
|
for _, value := range input {
|
||||||
|
if unicode.IsSpace(value) || unicode.IsPunct(value) {
|
||||||
|
if !inSpace {
|
||||||
|
inSpace = true
|
||||||
|
}
|
||||||
|
} else if inSpace {
|
||||||
|
count++
|
||||||
|
inSpace = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func patch29(scanner *bufio.Scanner) error {
|
||||||
|
f := func(tbl, idCol string) error {
|
||||||
|
return acc().Select(tbl).Cols(idCol + ",content").Each(func(rows *sql.Rows) error {
|
||||||
|
var id int
|
||||||
|
var content string
|
||||||
|
err := rows.Scan(&id, &content)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = acc().Update(tbl).Set("words=?").Where(idCol+"=?").Exec(WordCount(content), id)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
err := f("topics", "tid")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = f("replies", "rid")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, err := meta.NewDefaultMetaStore(acc())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = meta.Set("sched", "recalc")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fixCol := func(tbl string) error {
|
||||||
|
//err := execStmt(qgen.Builder.RenameColumn(tbl, "ipaddress","ip"))
|
||||||
|
err := execStmt(qgen.Builder.ChangeColumn(tbl, "ipaddress", tC{"ip", "varchar", 200, false, false, "''"}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return execStmt(qgen.Builder.SetDefaultColumn(tbl, "ip", "varchar", ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = fixCol("topics")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = fixCol("replies")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = fixCol("polls_votes")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = fixCol("users_replies")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = execStmt(qgen.Builder.SetDefaultColumn("users", "last_ip", "varchar", ""))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = execStmt(qgen.Builder.SetDefaultColumn("replies", "lastEdit", "int", "0"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = execStmt(qgen.Builder.SetDefaultColumn("replies", "lastEditBy", "int", "0"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = execStmt(qgen.Builder.SetDefaultColumn("users_replies", "lastEdit", "int", "0"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = execStmt(qgen.Builder.SetDefaultColumn("users_replies", "lastEditBy", "int", "0"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return execStmt(qgen.Builder.AddColumn("activity_stream", tC{"extra", "varchar", 200, false, false, "''"}, nil))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -18,58 +18,58 @@ type builder struct {
|
||||||
adapter Adapter
|
adapter Adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) Accumulator() *Accumulator {
|
func (b *builder) Accumulator() *Accumulator {
|
||||||
return &Accumulator{build.conn, build.adapter, nil}
|
return &Accumulator{b.conn, b.adapter, nil}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Move this method out of builder?
|
// TODO: Move this method out of builder?
|
||||||
func (build *builder) Init(adapter string, config map[string]string) error {
|
func (b *builder) Init(adapter string, config map[string]string) error {
|
||||||
err := build.SetAdapter(adapter)
|
err := b.SetAdapter(adapter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
conn, err := build.adapter.BuildConn(config)
|
conn, err := b.adapter.BuildConn(config)
|
||||||
build.conn = conn
|
b.conn = conn
|
||||||
log.Print("err: ", err) // Is the problem here somehow?
|
log.Print("err:", err) // Is the problem here somehow?
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SetConn(conn *sql.DB) {
|
func (b *builder) SetConn(conn *sql.DB) {
|
||||||
build.conn = conn
|
b.conn = conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) GetConn() *sql.DB {
|
func (b *builder) GetConn() *sql.DB {
|
||||||
return build.conn
|
return b.conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SetAdapter(name string) error {
|
func (b *builder) SetAdapter(name string) error {
|
||||||
adap, err := GetAdapter(name)
|
adap, err := GetAdapter(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
build.adapter = adap
|
b.adapter = adap
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) GetAdapter() Adapter {
|
func (b *builder) GetAdapter() Adapter {
|
||||||
return build.adapter
|
return b.adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) DbVersion() (dbVersion string) {
|
func (b *builder) DbVersion() (dbVersion string) {
|
||||||
build.conn.QueryRow(build.adapter.DbVersion()).Scan(&dbVersion)
|
b.conn.QueryRow(b.adapter.DbVersion()).Scan(&dbVersion)
|
||||||
return dbVersion
|
return dbVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) Begin() (*sql.Tx, error) {
|
func (b *builder) Begin() (*sql.Tx, error) {
|
||||||
return build.conn.Begin()
|
return b.conn.Begin()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) Tx(handler func(*TransactionBuilder) error) error {
|
func (b *builder) Tx(h func(*TransactionBuilder) error) error {
|
||||||
tx, err := build.conn.Begin()
|
tx, err := b.conn.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = handler(&TransactionBuilder{tx, build.adapter, nil})
|
err = h(&TransactionBuilder{tx, b.adapter, nil})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
|
@ -77,83 +77,99 @@ func (build *builder) Tx(handler func(*TransactionBuilder) error) error {
|
||||||
return tx.Commit()
|
return tx.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) prepare(res string, err error) (*sql.Stmt, error) {
|
func (b *builder) prepare(res string, err error) (*sql.Stmt, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return build.conn.Prepare(res)
|
return b.conn.Prepare(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleSelect(table string, columns string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleSelect(table, columns, where, orderby, limit string) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.SimpleSelect("", table, columns, where, orderby, limit))
|
return b.prepare(b.adapter.SimpleSelect("", table, columns, where, orderby, limit))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleCount(table string, where string, limit string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleCount(table, where, limit string) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.SimpleCount("", table, where, limit))
|
return b.prepare(b.adapter.SimpleCount("", table, where, limit))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleLeftJoin(table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleLeftJoin(table1, table2, columns, joiners, where, orderby, limit string) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.SimpleLeftJoin("", table1, table2, columns, joiners, where, orderby, limit))
|
return b.prepare(b.adapter.SimpleLeftJoin("", table1, table2, columns, joiners, where, orderby, limit))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleInnerJoin(table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleInnerJoin(table1, table2, columns, joiners, where, orderby, limit string) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.SimpleInnerJoin("", table1, table2, columns, joiners, where, orderby, limit))
|
return b.prepare(b.adapter.SimpleInnerJoin("", table1, table2, columns, joiners, where, orderby, limit))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) DropTable(table string) (stmt *sql.Stmt, err error) {
|
func (b *builder) DropTable(table string) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.DropTable("", table))
|
return b.prepare(b.adapter.DropTable("", table))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) CreateTable(table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (stmt *sql.Stmt, err error) {
|
func (build *builder) CreateTable(table, charset, collation string, columns []DBTableColumn, keys []DBTableKey) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.CreateTable("", table, charset, collation, columns, keys))
|
return build.prepare(build.adapter.CreateTable("", table, charset, collation, columns, keys))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) AddColumn(table string, column DBTableColumn, key *DBTableKey) (stmt *sql.Stmt, err error) {
|
func (b *builder) AddColumn(table string, column DBTableColumn, key *DBTableKey) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.AddColumn("", table, column, key))
|
return b.prepare(b.adapter.AddColumn("", table, column, key))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) AddIndex(table string, iname string, colname string) (stmt *sql.Stmt, err error) {
|
func (b *builder) DropColumn(table, colName string) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.AddIndex("", table, iname, colname))
|
return b.prepare(b.adapter.DropColumn("", table, colName))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) AddKey(table string, column string, key DBTableKey) (stmt *sql.Stmt, err error) {
|
func (b *builder) RenameColumn(table, oldName, newName string) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.AddKey("", table, column, key))
|
return b.prepare(b.adapter.RenameColumn("", table, oldName, newName))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) AddForeignKey(table string, column string, ftable string, fcolumn string, cascade bool) (stmt *sql.Stmt, err error) {
|
func (b *builder) ChangeColumn(table, colName string, col DBTableColumn) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.AddForeignKey("", table, column, ftable, fcolumn, cascade))
|
return b.prepare(b.adapter.ChangeColumn("", table, colName, col))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleInsert(table string, columns string, fields string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SetDefaultColumn(table, colName, colType, defaultStr string) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.SimpleInsert("", table, columns, fields))
|
return b.prepare(b.adapter.SetDefaultColumn("", table, colName, colType, defaultStr))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleInsertSelect(ins DBInsert, sel DBSelect) (stmt *sql.Stmt, err error) {
|
func (b *builder) AddIndex(table, iname, colname string) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.SimpleInsertSelect("", ins, sel))
|
return b.prepare(b.adapter.AddIndex("", table, iname, colname))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleInsertLeftJoin(ins DBInsert, sel DBJoin) (stmt *sql.Stmt, err error) {
|
func (b *builder) AddKey(table, column string, key DBTableKey) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.SimpleInsertLeftJoin("", ins, sel))
|
return b.prepare(b.adapter.AddKey("", table, column, key))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleInsertInnerJoin(ins DBInsert, sel DBJoin) (stmt *sql.Stmt, err error) {
|
func (b *builder) AddForeignKey(table, column, ftable, fcolumn string, cascade bool) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.SimpleInsertInnerJoin("", ins, sel))
|
return b.prepare(b.adapter.AddForeignKey("", table, column, ftable, fcolumn, cascade))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleUpdate(table string, set string, where string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleInsert(table, columns, fields string) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.SimpleUpdate(qUpdate(table, set, where)))
|
return b.prepare(b.adapter.SimpleInsert("", table, columns, fields))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleDelete(table string, where string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleInsertSelect(ins DBInsert, sel DBSelect) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.SimpleDelete("", table, where))
|
return b.prepare(b.adapter.SimpleInsertSelect("", ins, sel))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) SimpleInsertLeftJoin(ins DBInsert, sel DBJoin) (stmt *sql.Stmt, err error) {
|
||||||
|
return b.prepare(b.adapter.SimpleInsertLeftJoin("", ins, sel))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) SimpleInsertInnerJoin(ins DBInsert, sel DBJoin) (stmt *sql.Stmt, err error) {
|
||||||
|
return b.prepare(b.adapter.SimpleInsertInnerJoin("", ins, sel))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) SimpleUpdate(table, set, where string) (stmt *sql.Stmt, err error) {
|
||||||
|
return b.prepare(b.adapter.SimpleUpdate(qUpdate(table, set, where)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) SimpleDelete(table, where string) (stmt *sql.Stmt, err error) {
|
||||||
|
return b.prepare(b.adapter.SimpleDelete("", table, where))
|
||||||
}
|
}
|
||||||
|
|
||||||
// I don't know why you need this, but here it is x.x
|
// I don't know why you need this, but here it is x.x
|
||||||
func (build *builder) Purge(table string) (stmt *sql.Stmt, err error) {
|
func (b *builder) Purge(table string) (stmt *sql.Stmt, err error) {
|
||||||
return build.prepare(build.adapter.Purge("", table))
|
return b.prepare(b.adapter.Purge("", table))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) prepareTx(tx *sql.Tx, res string, err error) (*sql.Stmt, error) {
|
func (b *builder) prepareTx(tx *sql.Tx, res string, err error) (*sql.Stmt, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -161,63 +177,63 @@ func (build *builder) prepareTx(tx *sql.Tx, res string, err error) (*sql.Stmt, e
|
||||||
}
|
}
|
||||||
|
|
||||||
// These ones support transactions
|
// These ones support transactions
|
||||||
func (build *builder) SimpleSelectTx(tx *sql.Tx, table string, columns string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleSelectTx(tx *sql.Tx, table, columns, where, orderby, limit string) (stmt *sql.Stmt, err error) {
|
||||||
res, err := build.adapter.SimpleSelect("", table, columns, where, orderby, limit)
|
res, err := b.adapter.SimpleSelect("", table, columns, where, orderby, limit)
|
||||||
return build.prepareTx(tx, res, err)
|
return b.prepareTx(tx, res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleCountTx(tx *sql.Tx, table string, where string, limit string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleCountTx(tx *sql.Tx, table, where, limit string) (stmt *sql.Stmt, err error) {
|
||||||
res, err := build.adapter.SimpleCount("", table, where, limit)
|
res, err := b.adapter.SimpleCount("", table, where, limit)
|
||||||
return build.prepareTx(tx, res, err)
|
return b.prepareTx(tx, res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleLeftJoinTx(tx *sql.Tx, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleLeftJoinTx(tx *sql.Tx, table1, table2, columns, joiners, where, orderby, limit string) (stmt *sql.Stmt, err error) {
|
||||||
res, err := build.adapter.SimpleLeftJoin("", table1, table2, columns, joiners, where, orderby, limit)
|
res, err := b.adapter.SimpleLeftJoin("", table1, table2, columns, joiners, where, orderby, limit)
|
||||||
return build.prepareTx(tx, res, err)
|
return b.prepareTx(tx, res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleInnerJoinTx(tx *sql.Tx, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleInnerJoinTx(tx *sql.Tx, table1, table2, columns, joiners, where, orderby, limit string) (stmt *sql.Stmt, err error) {
|
||||||
res, err := build.adapter.SimpleInnerJoin("", table1, table2, columns, joiners, where, orderby, limit)
|
res, err := b.adapter.SimpleInnerJoin("", table1, table2, columns, joiners, where, orderby, limit)
|
||||||
return build.prepareTx(tx, res, err)
|
return b.prepareTx(tx, res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) CreateTableTx(tx *sql.Tx, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (stmt *sql.Stmt, err error) {
|
func (b *builder) CreateTableTx(tx *sql.Tx, table, charset, collation string, columns []DBTableColumn, keys []DBTableKey) (stmt *sql.Stmt, err error) {
|
||||||
res, err := build.adapter.CreateTable("", table, charset, collation, columns, keys)
|
res, err := b.adapter.CreateTable("", table, charset, collation, columns, keys)
|
||||||
return build.prepareTx(tx, res, err)
|
return b.prepareTx(tx, res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleInsertTx(tx *sql.Tx, table string, columns string, fields string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleInsertTx(tx *sql.Tx, table, columns, fields string) (stmt *sql.Stmt, err error) {
|
||||||
res, err := build.adapter.SimpleInsert("", table, columns, fields)
|
res, err := b.adapter.SimpleInsert("", table, columns, fields)
|
||||||
return build.prepareTx(tx, res, err)
|
return b.prepareTx(tx, res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleInsertSelectTx(tx *sql.Tx, ins DBInsert, sel DBSelect) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleInsertSelectTx(tx *sql.Tx, ins DBInsert, sel DBSelect) (stmt *sql.Stmt, err error) {
|
||||||
res, err := build.adapter.SimpleInsertSelect("", ins, sel)
|
res, err := b.adapter.SimpleInsertSelect("", ins, sel)
|
||||||
return build.prepareTx(tx, res, err)
|
return b.prepareTx(tx, res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleInsertLeftJoinTx(tx *sql.Tx, ins DBInsert, sel DBJoin) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleInsertLeftJoinTx(tx *sql.Tx, ins DBInsert, sel DBJoin) (stmt *sql.Stmt, err error) {
|
||||||
res, err := build.adapter.SimpleInsertLeftJoin("", ins, sel)
|
res, err := b.adapter.SimpleInsertLeftJoin("", ins, sel)
|
||||||
return build.prepareTx(tx, res, err)
|
return b.prepareTx(tx, res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleInsertInnerJoinTx(tx *sql.Tx, ins DBInsert, sel DBJoin) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleInsertInnerJoinTx(tx *sql.Tx, ins DBInsert, sel DBJoin) (stmt *sql.Stmt, err error) {
|
||||||
res, err := build.adapter.SimpleInsertInnerJoin("", ins, sel)
|
res, err := b.adapter.SimpleInsertInnerJoin("", ins, sel)
|
||||||
return build.prepareTx(tx, res, err)
|
return b.prepareTx(tx, res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleUpdateTx(tx *sql.Tx, table string, set string, where string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleUpdateTx(tx *sql.Tx, table, set, where string) (stmt *sql.Stmt, err error) {
|
||||||
res, err := build.adapter.SimpleUpdate(qUpdate(table, set, where))
|
res, err := b.adapter.SimpleUpdate(qUpdate(table, set, where))
|
||||||
return build.prepareTx(tx, res, err)
|
return b.prepareTx(tx, res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (build *builder) SimpleDeleteTx(tx *sql.Tx, table string, where string) (stmt *sql.Stmt, err error) {
|
func (b *builder) SimpleDeleteTx(tx *sql.Tx, table, where string) (stmt *sql.Stmt, err error) {
|
||||||
res, err := build.adapter.SimpleDelete("", table, where)
|
res, err := b.adapter.SimpleDelete("", table, where)
|
||||||
return build.prepareTx(tx, res, err)
|
return b.prepareTx(tx, res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// I don't know why you need this, but here it is x.x
|
// I don't know why you need this, but here it is x.x
|
||||||
func (build *builder) PurgeTx(tx *sql.Tx, table string) (stmt *sql.Stmt, err error) {
|
func (b *builder) PurgeTx(tx *sql.Tx, table string) (stmt *sql.Stmt, err error) {
|
||||||
res, err := build.adapter.Purge("", table)
|
res, err := b.adapter.Purge("", table)
|
||||||
return build.prepareTx(tx, res, err)
|
return b.prepareTx(tx, res, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ func (a *MssqlAdapter) DropTable(name, table string) (string, error) {
|
||||||
// TODO: Add support for foreign keys?
|
// TODO: Add support for foreign keys?
|
||||||
// TODO: Convert any remaining stringy types to nvarchar
|
// TODO: Convert any remaining stringy types to nvarchar
|
||||||
// We may need to change the CreateTable API to better suit Mssql and the other database drivers which are coming up
|
// We may need to change the CreateTable API to better suit Mssql and the other database drivers which are coming up
|
||||||
func (a *MssqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) {
|
func (a *MssqlAdapter) CreateTable(name, table, charset, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ func (a *MssqlAdapter) parseColumn(column DBTableColumn) (col DBTableColumn, siz
|
||||||
|
|
||||||
// TODO: Test this, not sure if some things work
|
// TODO: Test this, not sure if some things work
|
||||||
// TODO: Add support for keys
|
// TODO: Add support for keys
|
||||||
func (a *MssqlAdapter) AddColumn(name string, table string, column DBTableColumn, key *DBTableKey) (string, error) {
|
func (a *MssqlAdapter) AddColumn(name, table string, column DBTableColumn, key *DBTableKey) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -148,6 +148,29 @@ func (a *MssqlAdapter) AddColumn(name string, table string, column DBTableColumn
|
||||||
return q, nil
|
return q, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Implement this
|
||||||
|
func (a *MssqlAdapter) DropColumn(name, table, colName string) (string, error) {
|
||||||
|
return "", errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement this
|
||||||
|
func (a *MssqlAdapter) RenameColumn(name, table, oldName, newName string) (string, error) {
|
||||||
|
return "", errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement this
|
||||||
|
func (a *MssqlAdapter) ChangeColumn(name, table, colName string, col DBTableColumn) (string, error) {
|
||||||
|
return "", errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement this
|
||||||
|
func (a *MssqlAdapter) SetDefaultColumn(name, table, colName, colType, defaultStr string) (string, error) {
|
||||||
|
if colType == "text" {
|
||||||
|
return "", errors.New("text fields cannot have default values")
|
||||||
|
}
|
||||||
|
return "", errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
// TODO: Test to make sure everything works here
|
// TODO: Test to make sure everything works here
|
||||||
func (a *MssqlAdapter) AddIndex(name, table, iname, colname string) (string, error) {
|
func (a *MssqlAdapter) AddIndex(name, table, iname, colname string) (string, error) {
|
||||||
|
|
|
@ -92,7 +92,7 @@ func (a *MysqlAdapter) DropTable(name, table string) (string, error) {
|
||||||
return q, nil
|
return q, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *MysqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) {
|
func (a *MysqlAdapter) CreateTable(name, table, charset, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -143,53 +143,95 @@ func (a *MysqlAdapter) CreateTable(name string, table string, charset string, co
|
||||||
return q, nil
|
return q, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *MysqlAdapter) parseColumn(column DBTableColumn) (col DBTableColumn, size string, end string) {
|
func (a *MysqlAdapter) DropColumn(name, table, colName string) (string, error) {
|
||||||
// Make it easier to support Cassandra in the future
|
q := "ALTER TABLE `" + table + "` DROP COLUMN `" + colName + "`;"
|
||||||
if column.Type == "createdAt" {
|
a.pushStatement(name, "drop-column", q)
|
||||||
column.Type = "datetime"
|
return q, nil
|
||||||
// MySQL doesn't support this x.x
|
}
|
||||||
/*if column.Default == "" {
|
|
||||||
column.Default = "UTC_TIMESTAMP()"
|
// ! Currently broken in MariaDB. Planned.
|
||||||
}*/
|
func (a *MysqlAdapter) RenameColumn(name, table, oldName, newName string) (string, error) {
|
||||||
} else if column.Type == "json" {
|
q := "ALTER TABLE `" + table + "` RENAME COLUMN `" + oldName + "` TO `" + newName + "`;"
|
||||||
column.Type = "text"
|
a.pushStatement(name, "rename-column", q)
|
||||||
|
return q, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *MysqlAdapter) ChangeColumn(name, table, colName string, col DBTableColumn) (string, error) {
|
||||||
|
col.Default = ""
|
||||||
|
col, size, end := a.parseColumn(col)
|
||||||
|
q := "ALTER TABLE `" + table + "` CHANGE COLUMN `" + colName + "` `" + col.Name + "` " + col.Type + size + end
|
||||||
|
a.pushStatement(name, "change-column", q)
|
||||||
|
return q, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *MysqlAdapter) SetDefaultColumn(name, table, colName, colType, defaultStr string) (string, error) {
|
||||||
|
if colType == "text" {
|
||||||
|
return "", errors.New("text fields cannot have default values")
|
||||||
}
|
}
|
||||||
if column.Size > 0 {
|
if defaultStr == "" {
|
||||||
size = "(" + strconv.Itoa(column.Size) + ")"
|
defaultStr = "''"
|
||||||
|
}
|
||||||
|
// TODO: Exclude the other variants of text like mediumtext and longtext too
|
||||||
|
expr := ""
|
||||||
|
/*if colType == "datetime" && defaultStr[len(defaultStr)-1] == ')' {
|
||||||
|
end += defaultStr
|
||||||
|
} else */if a.stringyType(colType) && defaultStr != "''" {
|
||||||
|
expr += "'" + defaultStr + "'"
|
||||||
|
} else {
|
||||||
|
expr += defaultStr
|
||||||
|
}
|
||||||
|
q := "ALTER TABLE `" + table + "` ALTER COLUMN `" + colName + "` SET DEFAULT " + expr + ";"
|
||||||
|
a.pushStatement(name, "set-default-column", q)
|
||||||
|
return q, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *MysqlAdapter) parseColumn(col DBTableColumn) (ocol DBTableColumn, size, end string) {
|
||||||
|
// Make it easier to support Cassandra in the future
|
||||||
|
if col.Type == "createdAt" {
|
||||||
|
col.Type = "datetime"
|
||||||
|
// MySQL doesn't support this x.x
|
||||||
|
/*if col.Default == "" {
|
||||||
|
col.Default = "UTC_TIMESTAMP()"
|
||||||
|
}*/
|
||||||
|
} else if col.Type == "json" {
|
||||||
|
col.Type = "text"
|
||||||
|
}
|
||||||
|
if col.Size > 0 {
|
||||||
|
size = "(" + strconv.Itoa(col.Size) + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Exclude the other variants of text like mediumtext and longtext too
|
// TODO: Exclude the other variants of text like mediumtext and longtext too
|
||||||
if column.Default != "" && column.Type != "text" {
|
if col.Default != "" && col.Type != "text" {
|
||||||
end = " DEFAULT "
|
end = " DEFAULT "
|
||||||
/*if column.Type == "datetime" && column.Default[len(column.Default)-1] == ')' {
|
/*if col.Type == "datetime" && col.Default[len(col.Default)-1] == ')' {
|
||||||
end += column.Default
|
end += column.Default
|
||||||
} else */if a.stringyType(column.Type) && column.Default != "''" {
|
} else */if a.stringyType(col.Type) && col.Default != "''" {
|
||||||
end += "'" + column.Default + "'"
|
end += "'" + col.Default + "'"
|
||||||
} else {
|
} else {
|
||||||
end += column.Default
|
end += col.Default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if column.Null {
|
if col.Null {
|
||||||
end += " null"
|
end += " null"
|
||||||
} else {
|
} else {
|
||||||
end += " not null"
|
end += " not null"
|
||||||
}
|
}
|
||||||
if column.AutoIncrement {
|
if col.AutoIncrement {
|
||||||
end += " AUTO_INCREMENT"
|
end += " AUTO_INCREMENT"
|
||||||
}
|
}
|
||||||
return column, size, end
|
return col, size, end
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Support AFTER column
|
// TODO: Support AFTER column
|
||||||
// TODO: Test to make sure everything works here
|
// TODO: Test to make sure everything works here
|
||||||
func (a *MysqlAdapter) AddColumn(name string, table string, column DBTableColumn, key *DBTableKey) (string, error) {
|
func (a *MysqlAdapter) AddColumn(name, table string, col DBTableColumn, key *DBTableKey) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
|
||||||
column, size, end := a.parseColumn(column)
|
col, size, end := a.parseColumn(col)
|
||||||
q := "ALTER TABLE `" + table + "` ADD COLUMN " + "`" + column.Name + "` " + column.Type + size + end
|
q := "ALTER TABLE `" + table + "` ADD COLUMN " + "`" + col.Name + "` " + col.Type + size + end
|
||||||
|
|
||||||
if key != nil {
|
if key != nil {
|
||||||
q += " " + key.Type
|
q += " " + key.Type
|
||||||
|
@ -225,7 +267,7 @@ func (a *MysqlAdapter) AddIndex(name, table, iname, colname string) (string, err
|
||||||
|
|
||||||
// TODO: Test to make sure everything works here
|
// TODO: Test to make sure everything works here
|
||||||
// Only supports FULLTEXT right now
|
// Only supports FULLTEXT right now
|
||||||
func (a *MysqlAdapter) AddKey(name string, table string, column string, key DBTableKey) (string, error) {
|
func (a *MysqlAdapter) AddKey(name, table, column string, key DBTableKey) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -241,7 +283,7 @@ func (a *MysqlAdapter) AddKey(name string, table string, column string, key DBTa
|
||||||
return q, nil
|
return q, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *MysqlAdapter) AddForeignKey(name string, table string, column string, ftable string, fcolumn string, cascade bool) (out string, e error) {
|
func (a *MysqlAdapter) AddForeignKey(name, table, column, ftable, fcolumn string, cascade bool) (out string, e error) {
|
||||||
c := func(str string, val bool) {
|
c := func(str string, val bool) {
|
||||||
if e != nil || !val {
|
if e != nil || !val {
|
||||||
return
|
return
|
||||||
|
@ -338,7 +380,7 @@ func (a *MysqlAdapter) SimpleReplace(name, table, columns, fields string) (strin
|
||||||
for _, field := range processFields(fields) {
|
for _, field := range processFields(fields) {
|
||||||
q += field.Name + ","
|
q += field.Name + ","
|
||||||
}
|
}
|
||||||
q = q[0 : len(q)-1] + ")"
|
q = q[0:len(q)-1] + ")"
|
||||||
|
|
||||||
// TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator
|
// TODO: Shunt the table name logic and associated stmt list up to the a higher layer to reduce the amount of unnecessary overhead in the builder / accumulator
|
||||||
a.pushStatement(name, "replace", q)
|
a.pushStatement(name, "replace", q)
|
||||||
|
@ -362,8 +404,7 @@ func (a *MysqlAdapter) SimpleUpsert(name, table, columns, fields, where string)
|
||||||
q := "INSERT INTO `" + table + "`("
|
q := "INSERT INTO `" + table + "`("
|
||||||
parsedFields := processFields(fields)
|
parsedFields := processFields(fields)
|
||||||
|
|
||||||
var insertColumns string
|
var insertColumns, insertValues string
|
||||||
var insertValues string
|
|
||||||
setBit := ") ON DUPLICATE KEY UPDATE "
|
setBit := ") ON DUPLICATE KEY UPDATE "
|
||||||
|
|
||||||
for columnID, col := range processColumns(columns) {
|
for columnID, col := range processColumns(columns) {
|
||||||
|
@ -667,7 +708,7 @@ func (a *MysqlAdapter) complexSelect(preBuilder *selectPrebuilder, sb *strings.B
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *MysqlAdapter) SimpleLeftJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) {
|
func (a *MysqlAdapter) SimpleLeftJoin(name, table1, table2, columns, joiners, where, orderby, limit string) (string, error) {
|
||||||
if table1 == "" {
|
if table1 == "" {
|
||||||
return "", errors.New("You need a name for the left table")
|
return "", errors.New("You need a name for the left table")
|
||||||
}
|
}
|
||||||
|
@ -704,7 +745,7 @@ func (a *MysqlAdapter) SimpleLeftJoin(name string, table1 string, table2 string,
|
||||||
return q, nil
|
return q, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *MysqlAdapter) SimpleInnerJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) {
|
func (a *MysqlAdapter) SimpleInnerJoin(name, table1, table2, columns, joiners, where, orderby, limit string) (string, error) {
|
||||||
if table1 == "" {
|
if table1 == "" {
|
||||||
return "", errors.New("You need a name for the left table")
|
return "", errors.New("You need a name for the left table")
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ func (a *PgsqlAdapter) DbVersion() string {
|
||||||
return "SELECT version()"
|
return "SELECT version()"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *PgsqlAdapter) DropTable(name string, table string) (string, error) {
|
func (a *PgsqlAdapter) DropTable(name, table string) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ func (a *PgsqlAdapter) DropTable(name string, table string) (string, error) {
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
// We may need to change the CreateTable API to better suit PGSQL and the other database drivers which are coming up
|
// We may need to change the CreateTable API to better suit PGSQL and the other database drivers which are coming up
|
||||||
func (a *PgsqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) {
|
func (a *PgsqlAdapter) CreateTable(name, table, charset, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -112,16 +112,39 @@ func (a *PgsqlAdapter) CreateTable(name string, table string, charset string, co
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
func (a *PgsqlAdapter) AddColumn(name string, table string, column DBTableColumn, key *DBTableKey) (string, error) {
|
func (a *PgsqlAdapter) AddColumn(name, table string, column DBTableColumn, key *DBTableKey) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Implement this
|
||||||
|
func (a *PgsqlAdapter) DropColumn(name, table, colName string) (string, error) {
|
||||||
|
return "", errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement this
|
||||||
|
func (a *PgsqlAdapter) RenameColumn(name, table, oldName, newName string) (string, error) {
|
||||||
|
return "", errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement this
|
||||||
|
func (a *PgsqlAdapter) ChangeColumn(name, table, colName string, col DBTableColumn) (string, error) {
|
||||||
|
return "", errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Implement this
|
||||||
|
func (a *PgsqlAdapter) SetDefaultColumn(name, table, colName, colType, defaultStr string) (string, error) {
|
||||||
|
if colType == "text" {
|
||||||
|
return "", errors.New("text fields cannot have default values")
|
||||||
|
}
|
||||||
|
return "", errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
// TODO: Test to make sure everything works here
|
// TODO: Test to make sure everything works here
|
||||||
func (a *PgsqlAdapter) AddIndex(name string, table string, iname string, colname string) (string, error) {
|
func (a *PgsqlAdapter) AddIndex(name, table, iname, colname string) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -136,7 +159,7 @@ func (a *PgsqlAdapter) AddIndex(name string, table string, iname string, colname
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
// TODO: Test to make sure everything works here
|
// TODO: Test to make sure everything works here
|
||||||
func (a *PgsqlAdapter) AddKey(name string, table string, column string, key DBTableKey) (string, error) {
|
func (a *PgsqlAdapter) AddKey(name, table, column string, key DBTableKey) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -148,7 +171,7 @@ func (a *PgsqlAdapter) AddKey(name string, table string, column string, key DBTa
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
// TODO: Test to make sure everything works here
|
// TODO: Test to make sure everything works here
|
||||||
func (a *PgsqlAdapter) AddForeignKey(name string, table string, column string, ftable string, fcolumn string, cascade bool) (out string, e error) {
|
func (a *PgsqlAdapter) AddForeignKey(name, table, column, ftable, fcolumn string, cascade bool) (out string, e error) {
|
||||||
var c = func(str string, val bool) {
|
var c = func(str string, val bool) {
|
||||||
if e != nil || !val {
|
if e != nil || !val {
|
||||||
return
|
return
|
||||||
|
@ -167,7 +190,7 @@ func (a *PgsqlAdapter) AddForeignKey(name string, table string, column string, f
|
||||||
|
|
||||||
// TODO: Test this
|
// TODO: Test this
|
||||||
// ! We need to get the last ID out of this somehow, maybe add returning to every query? Might require some sort of wrapper over the sql statements
|
// ! We need to get the last ID out of this somehow, maybe add returning to every query? Might require some sort of wrapper over the sql statements
|
||||||
func (a *PgsqlAdapter) SimpleInsert(name string, table string, columns string, fields string) (string, error) {
|
func (a *PgsqlAdapter) SimpleInsert(name, table, columns, fields string) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -225,7 +248,7 @@ func (a *PgsqlAdapter) SimpleReplace(name, table, columns, fields string) (strin
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
func (a *PgsqlAdapter) SimpleUpsert(name string, table string, columns string, fields string, where string) (string, error) {
|
func (a *PgsqlAdapter) SimpleUpsert(name, table, columns, fields, where string) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -307,7 +330,7 @@ func (a *PgsqlAdapter) SimpleUpdateSelect(up *updatePrebuilder) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
func (a *PgsqlAdapter) SimpleDelete(name string, table string, where string) (string, error) {
|
func (a *PgsqlAdapter) SimpleDelete(name, table, where string) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -330,7 +353,7 @@ func (a *PgsqlAdapter) ComplexDelete(b *deletePrebuilder) (string, error) {
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
// We don't want to accidentally wipe tables, so we'll have a separate method for purging tables instead
|
// We don't want to accidentally wipe tables, so we'll have a separate method for purging tables instead
|
||||||
func (a *PgsqlAdapter) Purge(name string, table string) (string, error) {
|
func (a *PgsqlAdapter) Purge(name, table string) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -338,7 +361,7 @@ func (a *PgsqlAdapter) Purge(name string, table string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
func (a *PgsqlAdapter) SimpleSelect(name string, table string, columns string, where string, orderby string, limit string) (string, error) {
|
func (a *PgsqlAdapter) SimpleSelect(name, table, columns, where, orderby, limit string) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -360,7 +383,7 @@ func (a *PgsqlAdapter) ComplexSelect(prebuilder *selectPrebuilder) (string, erro
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
func (a *PgsqlAdapter) SimpleLeftJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) {
|
func (a *PgsqlAdapter) SimpleLeftJoin(name, table1, table2, columns, joiners, where, orderby, limit string) (string, error) {
|
||||||
if table1 == "" {
|
if table1 == "" {
|
||||||
return "", errors.New("You need a name for the left table")
|
return "", errors.New("You need a name for the left table")
|
||||||
}
|
}
|
||||||
|
@ -377,7 +400,7 @@ func (a *PgsqlAdapter) SimpleLeftJoin(name string, table1 string, table2 string,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
func (a *PgsqlAdapter) SimpleInnerJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error) {
|
func (a *PgsqlAdapter) SimpleInnerJoin(name, table1, table2, columns, joiners, where, orderby, limit string) (string, error) {
|
||||||
if table1 == "" {
|
if table1 == "" {
|
||||||
return "", errors.New("You need a name for the left table")
|
return "", errors.New("You need a name for the left table")
|
||||||
}
|
}
|
||||||
|
@ -409,7 +432,7 @@ func (a *PgsqlAdapter) SimpleInsertInnerJoin(name string, ins DBInsert, sel DBJo
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
func (a *PgsqlAdapter) SimpleCount(name string, table string, where string, limit string) (string, error) {
|
func (a *PgsqlAdapter) SimpleCount(name, table, where, limit string) (string, error) {
|
||||||
if table == "" {
|
if table == "" {
|
||||||
return "", errors.New("You need a name for this table")
|
return "", errors.New("You need a name for this table")
|
||||||
}
|
}
|
||||||
|
@ -471,7 +494,7 @@ func _gen_pgsql() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal methods, not exposed in the interface
|
// Internal methods, not exposed in the interface
|
||||||
func (a *PgsqlAdapter) pushStatement(name string, stype string, q string) {
|
func (a *PgsqlAdapter) pushStatement(name, stype, q string) {
|
||||||
if name == "" {
|
if name == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,22 +130,26 @@ type Adapter interface {
|
||||||
BuildConn(config map[string]string) (*sql.DB, error)
|
BuildConn(config map[string]string) (*sql.DB, error)
|
||||||
DbVersion() string
|
DbVersion() string
|
||||||
|
|
||||||
DropTable(name string, table string) (string, error)
|
DropTable(name, table string) (string, error)
|
||||||
CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error)
|
CreateTable(name, table, charset, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error)
|
||||||
// TODO: Some way to add indices and keys
|
// TODO: Some way to add indices and keys
|
||||||
// TODO: Test this
|
// TODO: Test this
|
||||||
AddColumn(name string, table string, column DBTableColumn, key *DBTableKey) (string, error)
|
AddColumn(name, table string, column DBTableColumn, key *DBTableKey) (string, error)
|
||||||
AddIndex(name string, table string, iname string, colname string) (string, error)
|
DropColumn(name, table, colname string) (string, error)
|
||||||
AddKey(name string, table string, column string, key DBTableKey) (string, error)
|
RenameColumn(name, table, oldName, newName string) (string, error)
|
||||||
AddForeignKey(name string, table string, column string, ftable string, fcolumn string, cascade bool) (out string, e error)
|
ChangeColumn(name, table, colName string, col DBTableColumn) (string, error)
|
||||||
SimpleInsert(name string, table string, columns string, fields string) (string, error)
|
SetDefaultColumn(name, table, colName, colType, defaultStr string) (string, error)
|
||||||
|
AddIndex(name, table, iname, colname string) (string, error)
|
||||||
|
AddKey(name, table, column string, key DBTableKey) (string, error)
|
||||||
|
AddForeignKey(name, table, column, ftable, fcolumn string, cascade bool) (out string, e error)
|
||||||
|
SimpleInsert(name, table, columns, fields string) (string, error)
|
||||||
SimpleUpdate(b *updatePrebuilder) (string, error)
|
SimpleUpdate(b *updatePrebuilder) (string, error)
|
||||||
SimpleUpdateSelect(b *updatePrebuilder) (string, error) // ! Experimental
|
SimpleUpdateSelect(b *updatePrebuilder) (string, error) // ! Experimental
|
||||||
SimpleDelete(name string, table string, where string) (string, error)
|
SimpleDelete(name, table, where string) (string, error)
|
||||||
Purge(name string, table string) (string, error)
|
Purge(name, table string) (string, error)
|
||||||
SimpleSelect(name string, table string, columns string, where string, orderby string, limit string) (string, error)
|
SimpleSelect(name, table, columns, where, orderby, limit string) (string, error)
|
||||||
ComplexDelete(b *deletePrebuilder) (string, error)
|
ComplexDelete(b *deletePrebuilder) (string, error)
|
||||||
SimpleLeftJoin(name string, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (string, error)
|
SimpleLeftJoin(name, table1, table2, columns, joiners, where, orderby, limit string) (string, error)
|
||||||
SimpleInnerJoin(string, string, string, string, string, string, string, string) (string, error)
|
SimpleInnerJoin(string, string, string, string, string, string, string, string) (string, error)
|
||||||
SimpleInsertSelect(string, DBInsert, DBSelect) (string, error)
|
SimpleInsertSelect(string, DBInsert, DBSelect) (string, error)
|
||||||
SimpleInsertLeftJoin(string, DBInsert, DBJoin) (string, error)
|
SimpleInsertLeftJoin(string, DBInsert, DBJoin) (string, error)
|
||||||
|
@ -192,9 +196,9 @@ func PrepareMySQLUpsertCallback(db *sql.DB, query string) (*MySQLUpsertCallback,
|
||||||
type LitStr string
|
type LitStr string
|
||||||
|
|
||||||
// TODO: Test this
|
// TODO: Test this
|
||||||
func InterfaceMapToInsertStrings(data map[string]interface{}, order string) (cols string, values string) {
|
func InterfaceMapToInsertStrings(data map[string]interface{}, order string) (cols, values string) {
|
||||||
var done = make(map[string]bool)
|
done := make(map[string]bool)
|
||||||
var addValue = func(value interface{}) {
|
addValue := func(value interface{}) {
|
||||||
switch value := value.(type) {
|
switch value := value.(type) {
|
||||||
case string:
|
case string:
|
||||||
values += "'" + strings.Replace(value, "'", "\\'", -1) + "',"
|
values += "'" + strings.Replace(value, "'", "\\'", -1) + "',"
|
||||||
|
|
|
@ -127,6 +127,7 @@ func topicRoutes() *RouteGroup {
|
||||||
Action("routes.UnlockTopicSubmit", "/topic/unlock/submit/", "extraData"),
|
Action("routes.UnlockTopicSubmit", "/topic/unlock/submit/", "extraData"),
|
||||||
Action("routes.MoveTopicSubmit", "/topic/move/submit/", "extraData"),
|
Action("routes.MoveTopicSubmit", "/topic/move/submit/", "extraData"),
|
||||||
Action("routes.LikeTopicSubmit", "/topic/like/submit/", "extraData"),
|
Action("routes.LikeTopicSubmit", "/topic/like/submit/", "extraData"),
|
||||||
|
Action("routes.UnlikeTopicSubmit", "/topic/unlike/submit/", "extraData"),
|
||||||
UploadAction("routes.AddAttachToTopicSubmit", "/topic/attach/add/submit/", "extraData").MaxSizeVar("int(c.Config.MaxRequestSize)"),
|
UploadAction("routes.AddAttachToTopicSubmit", "/topic/attach/add/submit/", "extraData").MaxSizeVar("int(c.Config.MaxRequestSize)"),
|
||||||
Action("routes.RemoveAttachFromTopicSubmit", "/topic/attach/remove/submit/", "extraData"),
|
Action("routes.RemoveAttachFromTopicSubmit", "/topic/attach/remove/submit/", "extraData"),
|
||||||
)
|
)
|
||||||
|
|
|
@ -66,7 +66,7 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError
|
||||||
}
|
}
|
||||||
// Don't want to throw an internal error due to a socket closing
|
// Don't want to throw an internal error due to a socket closing
|
||||||
if c.EnableWebsockets && count > 0 {
|
if c.EnableWebsockets && count > 0 {
|
||||||
_ = c.WsHub.PushMessage(user.ID, `{"event":"dismiss-alert","id":`+strconv.Itoa(id)+`}`)
|
c.DismissAlert(user.ID, id)
|
||||||
}
|
}
|
||||||
w.Write(successJSONBytes)
|
w.Write(successJSONBytes)
|
||||||
// TODO: Split this into it's own function
|
// TODO: Split this into it's own function
|
||||||
|
|
|
@ -39,13 +39,13 @@ func ProfileReplyCreateSubmit(w http.ResponseWriter, r *http.Request, user c.Use
|
||||||
return c.LocalError("You can't make a blank post", w, r, user)
|
return c.LocalError("You can't make a blank post", w, r, user)
|
||||||
}
|
}
|
||||||
// TODO: Fully parse the post and store it in the parsed column
|
// TODO: Fully parse the post and store it in the parsed column
|
||||||
_, err = c.Prstore.Create(profileOwner.ID, content, user.ID, user.GetIP())
|
prid, err := c.Prstore.Create(profileOwner.ID, content, user.ID, user.GetIP())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.InternalError(err, w, r)
|
return c.InternalError(err, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ! 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: profileOwner.ID, Event: "reply", ElementType: "user", ElementID: profileOwner.ID, Actor: &user}
|
alert := c.Alert{ActorID: user.ID, TargetUserID: profileOwner.ID, Event: "reply", ElementType: "user", ElementID: profileOwner.ID, Actor: &user, Extra: strconv.Itoa(prid)}
|
||||||
err = c.AddActivityAndNotifyTarget(alert)
|
err = c.AddActivityAndNotifyTarget(alert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.InternalError(err, w, r)
|
return c.InternalError(err, w, r)
|
||||||
|
|
|
@ -136,7 +136,7 @@ func CreateReplySubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro
|
||||||
return c.InternalErrorJSQ(err, w, r, js)
|
return c.InternalErrorJSQ(err, w, r, js)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.AddActivityAndNotifyAll(user.ID, topic.CreatedBy, "reply", "topic", tid)
|
c.AddActivityAndNotifyAll(c.Alert{ActorID: user.ID, TargetUserID: topic.CreatedBy, Event: "reply", ElementType: "topic", ElementID: tid, Extra: strconv.Itoa(rid)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.InternalErrorJSQ(err, w, r, js)
|
return c.InternalErrorJSQ(err, w, r, js)
|
||||||
}
|
}
|
||||||
|
|
|
@ -346,10 +346,10 @@ func CreateTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro
|
||||||
return c.NoPermissions(w, r, user)
|
return c.NoPermissions(w, r, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
tname := c.SanitiseSingleLine(r.PostFormValue("name"))
|
name := c.SanitiseSingleLine(r.PostFormValue("name"))
|
||||||
content := c.PreparseMessage(r.PostFormValue("content"))
|
content := c.PreparseMessage(r.PostFormValue("content"))
|
||||||
// TODO: Fully parse the post and store it in the parsed column
|
// TODO: Fully parse the post and store it in the parsed column
|
||||||
tid, err := c.Topics.Create(fid, tname, content, user.ID, user.GetIP())
|
tid, err := c.Topics.Create(fid, name, content, user.ID, user.GetIP())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err {
|
switch err {
|
||||||
case c.ErrNoRows:
|
case c.ErrNoRows:
|
||||||
|
@ -959,3 +959,55 @@ func LikeTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, stid s
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func UnlikeTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User, stid string) c.RouteError {
|
||||||
|
js := r.PostFormValue("js") == "1"
|
||||||
|
tid, err := strconv.Atoi(stid)
|
||||||
|
if err != nil {
|
||||||
|
return c.PreErrorJSQ(phrases.GetErrorPhrase("id_must_be_integer"), w, r, js)
|
||||||
|
}
|
||||||
|
|
||||||
|
topic, err := c.Topics.Get(tid)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return c.PreErrorJSQ("The requested topic doesn't exist.", w, r, js)
|
||||||
|
} else if err != nil {
|
||||||
|
return c.InternalErrorJSQ(err, w, r, js)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Add hooks to make use of headerLite
|
||||||
|
lite, ferr := c.SimpleForumUserCheck(w, r, &user, topic.ParentID)
|
||||||
|
if ferr != nil {
|
||||||
|
return ferr
|
||||||
|
}
|
||||||
|
if !user.Perms.ViewTopic || !user.Perms.LikeItem {
|
||||||
|
return c.NoPermissionsJSQ(w, r, user, js)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.Users.Get(topic.CreatedBy)
|
||||||
|
if err != nil && err == sql.ErrNoRows {
|
||||||
|
return c.LocalErrorJSQ("The target user doesn't exist", w, r, user, js)
|
||||||
|
} else if err != nil {
|
||||||
|
return c.InternalErrorJSQ(err, w, r, js)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = topic.Unlike(user.ID)
|
||||||
|
if err != nil {
|
||||||
|
return c.InternalErrorJSQ(err, w, r, js)
|
||||||
|
}
|
||||||
|
// TODO: Push dismiss-event alerts to the users.
|
||||||
|
err = c.Activity.DeleteByParams("like", topic.ID, "topic")
|
||||||
|
if err != nil {
|
||||||
|
return c.InternalErrorJSQ(err, w, r, js)
|
||||||
|
}
|
||||||
|
|
||||||
|
skip, rerr := lite.Hooks.VhookSkippable("action_end_unlike_topic", topic.ID, &user)
|
||||||
|
if skip || rerr != nil {
|
||||||
|
return rerr
|
||||||
|
}
|
||||||
|
|
||||||
|
if !js {
|
||||||
|
http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther)
|
||||||
|
} else {
|
||||||
|
_, _ = w.Write(successJSONBytes)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -27,8 +27,8 @@ INSERT INTO [forums_permissions] ([gid],[fid],[permissions]) VALUES (3,2,'{"View
|
||||||
INSERT INTO [forums_permissions] ([gid],[fid],[permissions]) VALUES (4,2,'{"ViewTopic":true}');
|
INSERT INTO [forums_permissions] ([gid],[fid],[permissions]) VALUES (4,2,'{"ViewTopic":true}');
|
||||||
INSERT INTO [forums_permissions] ([gid],[fid],[permissions]) VALUES (5,2,'{"ViewTopic":true}');
|
INSERT INTO [forums_permissions] ([gid],[fid],[permissions]) VALUES (5,2,'{"ViewTopic":true}');
|
||||||
INSERT INTO [forums_permissions] ([gid],[fid],[permissions]) VALUES (6,2,'{"ViewTopic":true}');
|
INSERT INTO [forums_permissions] ([gid],[fid],[permissions]) VALUES (6,2,'{"ViewTopic":true}');
|
||||||
INSERT INTO [topics] ([title],[content],[parsed_content],[createdAt],[lastReplyAt],[lastReplyBy],[createdBy],[parentID],[ipaddress]) VALUES ('Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',GETUTCDATE(),GETUTCDATE(),1,1,2,'::1');
|
INSERT INTO [topics] ([title],[content],[parsed_content],[createdAt],[lastReplyAt],[lastReplyBy],[createdBy],[parentID],[ip]) VALUES ('Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',GETUTCDATE(),GETUTCDATE(),1,1,2,'::1');
|
||||||
INSERT INTO [replies] ([tid],[content],[parsed_content],[createdAt],[createdBy],[lastUpdated],[lastEdit],[lastEditBy],[ipaddress]) VALUES (1,'A reply!','A reply!',GETUTCDATE(),1,GETUTCDATE(),0,0,'::1');
|
INSERT INTO [replies] ([tid],[content],[parsed_content],[createdAt],[createdBy],[lastUpdated],[lastEdit],[lastEditBy],[ip]) VALUES (1,'A reply!','A reply!',GETUTCDATE(),1,GETUTCDATE(),0,0,'::1');
|
||||||
INSERT INTO [menus] () VALUES ();
|
INSERT INTO [menus] () VALUES ();
|
||||||
INSERT INTO [menu_items] ([mid],[name],[htmlID],[position],[path],[aria],[tooltip],[order]) VALUES (1,'{lang.menu_forums}','menu_forums','left','/forums/','{lang.menu_forums_aria}','{lang.menu_forums_tooltip}',0);
|
INSERT INTO [menu_items] ([mid],[name],[htmlID],[position],[path],[aria],[tooltip],[order]) VALUES (1,'{lang.menu_forums}','menu_forums','left','/forums/','{lang.menu_forums_aria}','{lang.menu_forums_tooltip}',0);
|
||||||
INSERT INTO [menu_items] ([mid],[name],[htmlID],[cssClass],[position],[path],[aria],[tooltip],[order]) VALUES (1,'{lang.menu_topics}','menu_topics','menu_topics','left','/topics/','{lang.menu_topics_aria}','{lang.menu_topics_tooltip}',1);
|
INSERT INTO [menu_items] ([mid],[name],[htmlID],[cssClass],[position],[path],[aria],[tooltip],[order]) VALUES (1,'{lang.menu_topics}','menu_topics','menu_topics','left','/topics/','{lang.menu_topics_aria}','{lang.menu_topics_tooltip}',1);
|
||||||
|
|
|
@ -6,5 +6,6 @@ CREATE TABLE [activity_stream] (
|
||||||
[elementType] nvarchar (50) not null,
|
[elementType] nvarchar (50) not null,
|
||||||
[elementID] int not null,
|
[elementID] int not null,
|
||||||
[createdAt] datetime not null,
|
[createdAt] datetime not null,
|
||||||
|
[extra] nvarchar (200) DEFAULT '' not null,
|
||||||
primary key([asid])
|
primary key([asid])
|
||||||
);
|
);
|
|
@ -3,5 +3,5 @@ CREATE TABLE [polls_votes] (
|
||||||
[uid] int not null,
|
[uid] int not null,
|
||||||
[option] int DEFAULT 0 not null,
|
[option] int DEFAULT 0 not null,
|
||||||
[castAt] datetime not null,
|
[castAt] datetime not null,
|
||||||
[ipaddress] nvarchar (200) DEFAULT '0.0.0.0.0' not null
|
[ip] nvarchar (200) DEFAULT '' not null
|
||||||
);
|
);
|
|
@ -8,7 +8,7 @@ CREATE TABLE [replies] (
|
||||||
[lastEdit] int DEFAULT 0 not null,
|
[lastEdit] int DEFAULT 0 not null,
|
||||||
[lastEditBy] int DEFAULT 0 not null,
|
[lastEditBy] int DEFAULT 0 not null,
|
||||||
[lastUpdated] datetime not null,
|
[lastUpdated] datetime not null,
|
||||||
[ipaddress] nvarchar (200) DEFAULT '0.0.0.0.0' not null,
|
[ip] nvarchar (200) DEFAULT '' not null,
|
||||||
[likeCount] int DEFAULT 0 not null,
|
[likeCount] int DEFAULT 0 not null,
|
||||||
[attachCount] int DEFAULT 0 not null,
|
[attachCount] int DEFAULT 0 not null,
|
||||||
[words] int DEFAULT 1 not null,
|
[words] int DEFAULT 1 not null,
|
||||||
|
|
|
@ -11,7 +11,7 @@ CREATE TABLE [topics] (
|
||||||
[is_closed] bit DEFAULT 0 not null,
|
[is_closed] bit DEFAULT 0 not null,
|
||||||
[sticky] bit DEFAULT 0 not null,
|
[sticky] bit DEFAULT 0 not null,
|
||||||
[parentID] int DEFAULT 2 not null,
|
[parentID] int DEFAULT 2 not null,
|
||||||
[ipaddress] nvarchar (200) DEFAULT '0.0.0.0.0' not null,
|
[ip] nvarchar (200) DEFAULT '' not null,
|
||||||
[postCount] int DEFAULT 1 not null,
|
[postCount] int DEFAULT 1 not null,
|
||||||
[likeCount] int DEFAULT 0 not null,
|
[likeCount] int DEFAULT 0 not null,
|
||||||
[attachCount] int DEFAULT 0 not null,
|
[attachCount] int DEFAULT 0 not null,
|
||||||
|
|
|
@ -7,6 +7,6 @@ CREATE TABLE [users_replies] (
|
||||||
[createdBy] int not null,
|
[createdBy] int not null,
|
||||||
[lastEdit] int DEFAULT 0 not null,
|
[lastEdit] int DEFAULT 0 not null,
|
||||||
[lastEditBy] int DEFAULT 0 not null,
|
[lastEditBy] int DEFAULT 0 not null,
|
||||||
[ipaddress] nvarchar (200) DEFAULT '0.0.0.0.0' not null,
|
[ip] nvarchar (200) DEFAULT '' not null,
|
||||||
primary key([rid])
|
primary key([rid])
|
||||||
);
|
);
|
|
@ -35,8 +35,8 @@ INSERT INTO `forums_permissions`(`gid`,`fid`,`permissions`) VALUES (3,2,'{"ViewT
|
||||||
INSERT INTO `forums_permissions`(`gid`,`fid`,`permissions`) VALUES (4,2,'{"ViewTopic":true}');
|
INSERT INTO `forums_permissions`(`gid`,`fid`,`permissions`) VALUES (4,2,'{"ViewTopic":true}');
|
||||||
INSERT INTO `forums_permissions`(`gid`,`fid`,`permissions`) VALUES (5,2,'{"ViewTopic":true}');
|
INSERT INTO `forums_permissions`(`gid`,`fid`,`permissions`) VALUES (5,2,'{"ViewTopic":true}');
|
||||||
INSERT INTO `forums_permissions`(`gid`,`fid`,`permissions`) VALUES (6,2,'{"ViewTopic":true}');
|
INSERT INTO `forums_permissions`(`gid`,`fid`,`permissions`) VALUES (6,2,'{"ViewTopic":true}');
|
||||||
INSERT INTO `topics`(`title`,`content`,`parsed_content`,`createdAt`,`lastReplyAt`,`lastReplyBy`,`createdBy`,`parentID`,`ipaddress`) VALUES ('Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,'::1');
|
INSERT INTO `topics`(`title`,`content`,`parsed_content`,`createdAt`,`lastReplyAt`,`lastReplyBy`,`createdBy`,`parentID`,`ip`) VALUES ('Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,'::1');
|
||||||
INSERT INTO `replies`(`tid`,`content`,`parsed_content`,`createdAt`,`createdBy`,`lastUpdated`,`lastEdit`,`lastEditBy`,`ipaddress`) VALUES (1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,'::1');
|
INSERT INTO `replies`(`tid`,`content`,`parsed_content`,`createdAt`,`createdBy`,`lastUpdated`,`lastEdit`,`lastEditBy`,`ip`) VALUES (1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,'::1');
|
||||||
INSERT INTO `menus`() VALUES ();
|
INSERT INTO `menus`() VALUES ();
|
||||||
INSERT INTO `menu_items`(`mid`,`name`,`htmlID`,`position`,`path`,`aria`,`tooltip`,`order`) VALUES (1,'{lang.menu_forums}','menu_forums','left','/forums/','{lang.menu_forums_aria}','{lang.menu_forums_tooltip}',0);
|
INSERT INTO `menu_items`(`mid`,`name`,`htmlID`,`position`,`path`,`aria`,`tooltip`,`order`) VALUES (1,'{lang.menu_forums}','menu_forums','left','/forums/','{lang.menu_forums_aria}','{lang.menu_forums_tooltip}',0);
|
||||||
INSERT INTO `menu_items`(`mid`,`name`,`htmlID`,`cssClass`,`position`,`path`,`aria`,`tooltip`,`order`) VALUES (1,'{lang.menu_topics}','menu_topics','menu_topics','left','/topics/','{lang.menu_topics_aria}','{lang.menu_topics_tooltip}',1);
|
INSERT INTO `menu_items`(`mid`,`name`,`htmlID`,`cssClass`,`position`,`path`,`aria`,`tooltip`,`order`) VALUES (1,'{lang.menu_topics}','menu_topics','menu_topics','left','/topics/','{lang.menu_topics_aria}','{lang.menu_topics_tooltip}',1);
|
||||||
|
|
|
@ -6,5 +6,6 @@ CREATE TABLE `activity_stream` (
|
||||||
`elementType` varchar(50) not null,
|
`elementType` varchar(50) not null,
|
||||||
`elementID` int not null,
|
`elementID` int not null,
|
||||||
`createdAt` datetime not null,
|
`createdAt` datetime not null,
|
||||||
|
`extra` varchar(200) DEFAULT '' not null,
|
||||||
primary key(`asid`)
|
primary key(`asid`)
|
||||||
);
|
);
|
|
@ -3,5 +3,5 @@ CREATE TABLE `polls_votes` (
|
||||||
`uid` int not null,
|
`uid` int not null,
|
||||||
`option` int DEFAULT 0 not null,
|
`option` int DEFAULT 0 not null,
|
||||||
`castAt` datetime not null,
|
`castAt` datetime not null,
|
||||||
`ipaddress` varchar(200) DEFAULT '0.0.0.0.0' not null
|
`ip` varchar(200) DEFAULT '' not null
|
||||||
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
|
@ -8,7 +8,7 @@ CREATE TABLE `replies` (
|
||||||
`lastEdit` int DEFAULT 0 not null,
|
`lastEdit` int DEFAULT 0 not null,
|
||||||
`lastEditBy` int DEFAULT 0 not null,
|
`lastEditBy` int DEFAULT 0 not null,
|
||||||
`lastUpdated` datetime not null,
|
`lastUpdated` datetime not null,
|
||||||
`ipaddress` varchar(200) DEFAULT '0.0.0.0.0' not null,
|
`ip` varchar(200) DEFAULT '' not null,
|
||||||
`likeCount` int DEFAULT 0 not null,
|
`likeCount` int DEFAULT 0 not null,
|
||||||
`attachCount` int DEFAULT 0 not null,
|
`attachCount` int DEFAULT 0 not null,
|
||||||
`words` int DEFAULT 1 not null,
|
`words` int DEFAULT 1 not null,
|
||||||
|
|
|
@ -11,7 +11,7 @@ CREATE TABLE `topics` (
|
||||||
`is_closed` boolean DEFAULT 0 not null,
|
`is_closed` boolean DEFAULT 0 not null,
|
||||||
`sticky` boolean DEFAULT 0 not null,
|
`sticky` boolean DEFAULT 0 not null,
|
||||||
`parentID` int DEFAULT 2 not null,
|
`parentID` int DEFAULT 2 not null,
|
||||||
`ipaddress` varchar(200) DEFAULT '0.0.0.0.0' not null,
|
`ip` varchar(200) DEFAULT '' not null,
|
||||||
`postCount` int DEFAULT 1 not null,
|
`postCount` int DEFAULT 1 not null,
|
||||||
`likeCount` int DEFAULT 0 not null,
|
`likeCount` int DEFAULT 0 not null,
|
||||||
`attachCount` int DEFAULT 0 not null,
|
`attachCount` int DEFAULT 0 not null,
|
||||||
|
|
|
@ -7,6 +7,6 @@ CREATE TABLE `users_replies` (
|
||||||
`createdBy` int not null,
|
`createdBy` int not null,
|
||||||
`lastEdit` int DEFAULT 0 not null,
|
`lastEdit` int DEFAULT 0 not null,
|
||||||
`lastEditBy` int DEFAULT 0 not null,
|
`lastEditBy` int DEFAULT 0 not null,
|
||||||
`ipaddress` varchar(200) DEFAULT '0.0.0.0.0' not null,
|
`ip` varchar(200) DEFAULT '' not null,
|
||||||
primary key(`rid`)
|
primary key(`rid`)
|
||||||
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
) CHARSET=utf8mb4 COLLATE utf8mb4_general_ci;
|
|
@ -27,8 +27,8 @@ INSERT INTO "forums_permissions"("gid","fid","permissions") VALUES (3,2,'{"ViewT
|
||||||
INSERT INTO "forums_permissions"("gid","fid","permissions") VALUES (4,2,'{"ViewTopic":true}');
|
INSERT INTO "forums_permissions"("gid","fid","permissions") VALUES (4,2,'{"ViewTopic":true}');
|
||||||
INSERT INTO "forums_permissions"("gid","fid","permissions") VALUES (5,2,'{"ViewTopic":true}');
|
INSERT INTO "forums_permissions"("gid","fid","permissions") VALUES (5,2,'{"ViewTopic":true}');
|
||||||
INSERT INTO "forums_permissions"("gid","fid","permissions") VALUES (6,2,'{"ViewTopic":true}');
|
INSERT INTO "forums_permissions"("gid","fid","permissions") VALUES (6,2,'{"ViewTopic":true}');
|
||||||
INSERT INTO "topics"("title","content","parsed_content","createdAt","lastReplyAt","lastReplyBy","createdBy","parentID","ipaddress") VALUES ('Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,'::1');
|
INSERT INTO "topics"("title","content","parsed_content","createdAt","lastReplyAt","lastReplyBy","createdBy","parentID","ip") VALUES ('Test Topic','A topic automatically generated by the software.','A topic automatically generated by the software.',UTC_TIMESTAMP(),UTC_TIMESTAMP(),1,1,2,'::1');
|
||||||
INSERT INTO "replies"("tid","content","parsed_content","createdAt","createdBy","lastUpdated","lastEdit","lastEditBy","ipaddress") VALUES (1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,'::1');
|
INSERT INTO "replies"("tid","content","parsed_content","createdAt","createdBy","lastUpdated","lastEdit","lastEditBy","ip") VALUES (1,'A reply!','A reply!',UTC_TIMESTAMP(),1,UTC_TIMESTAMP(),0,0,'::1');
|
||||||
INSERT INTO "menus"() VALUES ();
|
INSERT INTO "menus"() VALUES ();
|
||||||
INSERT INTO "menu_items"("mid","name","htmlID","position","path","aria","tooltip","order") VALUES (1,'{lang.menu_forums}','menu_forums','left','/forums/','{lang.menu_forums_aria}','{lang.menu_forums_tooltip}',0);
|
INSERT INTO "menu_items"("mid","name","htmlID","position","path","aria","tooltip","order") VALUES (1,'{lang.menu_forums}','menu_forums','left','/forums/','{lang.menu_forums_aria}','{lang.menu_forums_tooltip}',0);
|
||||||
INSERT INTO "menu_items"("mid","name","htmlID","cssClass","position","path","aria","tooltip","order") VALUES (1,'{lang.menu_topics}','menu_topics','menu_topics','left','/topics/','{lang.menu_topics_aria}','{lang.menu_topics_tooltip}',1);
|
INSERT INTO "menu_items"("mid","name","htmlID","cssClass","position","path","aria","tooltip","order") VALUES (1,'{lang.menu_topics}','menu_topics','menu_topics','left','/topics/','{lang.menu_topics_aria}','{lang.menu_topics_tooltip}',1);
|
||||||
|
|
|
@ -6,5 +6,6 @@ CREATE TABLE "activity_stream" (
|
||||||
`elementType` varchar (50) not null,
|
`elementType` varchar (50) not null,
|
||||||
`elementID` int not null,
|
`elementID` int not null,
|
||||||
`createdAt` timestamp not null,
|
`createdAt` timestamp not null,
|
||||||
|
`extra` varchar (200) DEFAULT '' not null,
|
||||||
primary key(`asid`)
|
primary key(`asid`)
|
||||||
);
|
);
|
|
@ -3,5 +3,5 @@ CREATE TABLE "polls_votes" (
|
||||||
`uid` int not null,
|
`uid` int not null,
|
||||||
`option` int DEFAULT 0 not null,
|
`option` int DEFAULT 0 not null,
|
||||||
`castAt` timestamp not null,
|
`castAt` timestamp not null,
|
||||||
`ipaddress` varchar (200) DEFAULT '0.0.0.0.0' not null
|
`ip` varchar (200) DEFAULT '' not null
|
||||||
);
|
);
|
|
@ -8,7 +8,7 @@ CREATE TABLE "replies" (
|
||||||
`lastEdit` int DEFAULT 0 not null,
|
`lastEdit` int DEFAULT 0 not null,
|
||||||
`lastEditBy` int DEFAULT 0 not null,
|
`lastEditBy` int DEFAULT 0 not null,
|
||||||
`lastUpdated` timestamp not null,
|
`lastUpdated` timestamp not null,
|
||||||
`ipaddress` varchar (200) DEFAULT '0.0.0.0.0' not null,
|
`ip` varchar (200) DEFAULT '' not null,
|
||||||
`likeCount` int DEFAULT 0 not null,
|
`likeCount` int DEFAULT 0 not null,
|
||||||
`attachCount` int DEFAULT 0 not null,
|
`attachCount` int DEFAULT 0 not null,
|
||||||
`words` int DEFAULT 1 not null,
|
`words` int DEFAULT 1 not null,
|
||||||
|
|
|
@ -11,7 +11,7 @@ CREATE TABLE "topics" (
|
||||||
`is_closed` boolean DEFAULT 0 not null,
|
`is_closed` boolean DEFAULT 0 not null,
|
||||||
`sticky` boolean DEFAULT 0 not null,
|
`sticky` boolean DEFAULT 0 not null,
|
||||||
`parentID` int DEFAULT 2 not null,
|
`parentID` int DEFAULT 2 not null,
|
||||||
`ipaddress` varchar (200) DEFAULT '0.0.0.0.0' not null,
|
`ip` varchar (200) DEFAULT '' not null,
|
||||||
`postCount` int DEFAULT 1 not null,
|
`postCount` int DEFAULT 1 not null,
|
||||||
`likeCount` int DEFAULT 0 not null,
|
`likeCount` int DEFAULT 0 not null,
|
||||||
`attachCount` int DEFAULT 0 not null,
|
`attachCount` int DEFAULT 0 not null,
|
||||||
|
|
|
@ -7,6 +7,6 @@ CREATE TABLE "users_replies" (
|
||||||
`createdBy` int not null,
|
`createdBy` int not null,
|
||||||
`lastEdit` int DEFAULT 0 not null,
|
`lastEdit` int DEFAULT 0 not null,
|
||||||
`lastEditBy` int DEFAULT 0 not null,
|
`lastEditBy` int DEFAULT 0 not null,
|
||||||
`ipaddress` varchar (200) DEFAULT '0.0.0.0.0' not null,
|
`ip` varchar (200) DEFAULT '' not null,
|
||||||
primary key(`rid`)
|
primary key(`rid`)
|
||||||
);
|
);
|
|
@ -83,7 +83,9 @@
|
||||||
<div class="controls button_container{{if .Topic.LikeCount}} has_likes{{end}}">
|
<div class="controls button_container{{if .Topic.LikeCount}} has_likes{{end}}">
|
||||||
<div class="action_button_left">
|
<div class="action_button_left">
|
||||||
{{if .CurrentUser.Loggedin}}
|
{{if .CurrentUser.Loggedin}}
|
||||||
{{if .CurrentUser.Perms.LikeItem}}{{if ne .CurrentUser.ID .Topic.CreatedBy}}<a href="/topic/like/submit/{{.Topic.ID}}?s={{.CurrentUser.Session}}" class="action_button like_item {{if .Topic.Liked}}remove{{else}}add{{end}}_like" aria-label="{{lang "topic.like_aria"}}" data-action="like"></a>{{end}}{{end}}
|
{{if .CurrentUser.Perms.LikeItem}}{{if ne .CurrentUser.ID .Topic.CreatedBy}}
|
||||||
|
{{if .Topic.Liked}}<a href="/topic/unlike/submit/{{.Topic.ID}}?s={{.CurrentUser.Session}}" class="action_button like_item remove_like" aria-label="{{lang "topic.unlike_aria"}}" data-action="unlike"></a>{{else}}<a href="/topic/like/submit/{{.Topic.ID}}?s={{.CurrentUser.Session}}" class="action_button like_item add_like" aria-label="{{lang "topic.like_aria"}}" data-action="like"></a>{{end}}
|
||||||
|
{{end}}{{end}}
|
||||||
<a href="" class="action_button quote_item" aria-label="{{lang "topic.quote_aria"}}" data-action="quote"></a>
|
<a href="" class="action_button quote_item" aria-label="{{lang "topic.quote_aria"}}" data-action="quote"></a>
|
||||||
{{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}}
|
{{if not .Topic.IsClosed or .CurrentUser.Perms.CloseTopic}}
|
||||||
{{if .CurrentUser.Perms.EditTopic}}<a href="/topic/edit/{{.Topic.ID}}" class="action_button open_edit" aria-label="{{lang "topic.edit_aria"}}" data-action="edit"></a>{{end}}
|
{{if .CurrentUser.Perms.EditTopic}}<a href="/topic/edit/{{.Topic.ID}}" class="action_button open_edit" aria-label="{{lang "topic.edit_aria"}}" data-action="edit"></a>{{end}}
|
||||||
|
|
56
tickloop.go
56
tickloop.go
|
@ -2,7 +2,6 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
@ -10,6 +9,7 @@ import (
|
||||||
|
|
||||||
c "github.com/Azareal/Gosora/common"
|
c "github.com/Azareal/Gosora/common"
|
||||||
qgen "github.com/Azareal/Gosora/query_gen"
|
qgen "github.com/Azareal/Gosora/query_gen"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Name the tasks so we can figure out which one it was when something goes wrong? Or maybe toss it up WithStack down there?
|
// TODO: Name the tasks so we can figure out which one it was when something goes wrong? Or maybe toss it up WithStack down there?
|
||||||
|
@ -189,7 +189,7 @@ func dailies() {
|
||||||
|
|
||||||
if c.Config.DisablePostIP {
|
if c.Config.DisablePostIP {
|
||||||
f := func(tbl string) {
|
f := func(tbl string) {
|
||||||
_, err := qgen.NewAcc().Update(tbl).Set("ipaddress='0'").Where("ipaddress!='0'").Exec()
|
_, err := qgen.NewAcc().Update(tbl).Set("ip='0'").Where("ip!='0'").Exec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.LogError(err)
|
c.LogError(err)
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ func dailies() {
|
||||||
} else if c.Config.PostIPCutoff > -1 {
|
} else if c.Config.PostIPCutoff > -1 {
|
||||||
// TODO: Use unixtime to remove this MySQLesque logic?
|
// TODO: Use unixtime to remove this MySQLesque logic?
|
||||||
f := func(tbl string) {
|
f := func(tbl string) {
|
||||||
_, err := qgen.NewAcc().Update(tbl).Set("ipaddress='0'").DateOlderThan("createdAt", c.Config.PostIPCutoff, "day").Where("ipaddress!='0'").Exec()
|
_, err := qgen.NewAcc().Update(tbl).Set("ip='0'").DateOlderThan("createdAt", c.Config.PostIPCutoff, "day").Where("ip!='0'").Exec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.LogError(err)
|
c.LogError(err)
|
||||||
}
|
}
|
||||||
|
@ -211,13 +211,13 @@ func dailies() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Config.DisablePollIP {
|
if c.Config.DisablePollIP {
|
||||||
_, err := qgen.NewAcc().Update("polls_votes").Set("ipaddress='0'").Where("ipaddress!='0'").Exec()
|
_, err := qgen.NewAcc().Update("polls_votes").Set("ip='0'").Where("ip!='0'").Exec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.LogError(err)
|
c.LogError(err)
|
||||||
}
|
}
|
||||||
} else if c.Config.PollIPCutoff > -1 {
|
} else if c.Config.PollIPCutoff > -1 {
|
||||||
// TODO: Use unixtime to remove this MySQLesque logic?
|
// TODO: Use unixtime to remove this MySQLesque logic?
|
||||||
_, err := qgen.NewAcc().Update("polls_votes").Set("ipaddress='0'").DateOlderThan("castAt", c.Config.PollIPCutoff, "day").Where("ipaddress!='0'").Exec()
|
_, err := qgen.NewAcc().Update("polls_votes").Set("ip='0'").DateOlderThan("castAt", c.Config.PollIPCutoff, "day").Where("ip!='0'").Exec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.LogError(err)
|
c.LogError(err)
|
||||||
}
|
}
|
||||||
|
@ -237,7 +237,7 @@ func dailies() {
|
||||||
c.LogError(err)
|
c.LogError(err)
|
||||||
}*/
|
}*/
|
||||||
mon := time.Now().Month()
|
mon := time.Now().Month()
|
||||||
_, err := qgen.NewAcc().Update("users").Set("last_ip=0").Where("last_ip!=0 AND last_ip NOT LIKE '" + strconv.Itoa(int(mon)) + "-%'").Exec()
|
_, err := qgen.NewAcc().Update("users").Set("last_ip=0").Where("last_ip!='0' AND last_ip NOT LIKE '" + strconv.Itoa(int(mon)) + "-%'").Exec()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.LogError(err)
|
c.LogError(err)
|
||||||
}
|
}
|
||||||
|
@ -250,3 +250,47 @@ func dailies() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sched() error {
|
||||||
|
schedStr, err := c.Meta.Get("sched")
|
||||||
|
// TODO: Report this error back correctly...
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if schedStr == "recalc" {
|
||||||
|
log.Print("Cleaning up orphaned data.")
|
||||||
|
|
||||||
|
count, err := c.Recalc.Replies()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
log.Printf("Deleted %d orphaned replies.", count)
|
||||||
|
|
||||||
|
count, err = c.Recalc.Subscriptions()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
log.Printf("Deleted %d orphaned subscriptions.", count)
|
||||||
|
|
||||||
|
count, err = c.Recalc.ActivityStream()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
log.Printf("Deleted %d orphaned activity stream items.", count)
|
||||||
|
|
||||||
|
err = c.Recalc.Users()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
log.Print("Recalculated user post stats.")
|
||||||
|
|
||||||
|
count, err = c.Recalc.Attachments()
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithStack(err)
|
||||||
|
}
|
||||||
|
log.Printf("Deleted %d orphaned attachments.", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue