diff --git a/cmd/query_gen/spitter.go b/cmd/query_gen/spitter.go
index 9176e710..5ddd4f01 100644
--- a/cmd/query_gen/spitter.go
+++ b/cmd/query_gen/spitter.go
@@ -14,7 +14,7 @@ func NewPrimaryKeySpitter() *PrimaryKeySpitter {
func (spit *PrimaryKeySpitter) Hook(name string, args ...interface{}) error {
if name == "CreateTableStart" {
var found string
- var table = args[0].(*qgen.DB_Install_Table)
+ var table = args[0].(*qgen.DBInstallTable)
for _, key := range table.Keys {
if key.Type == "primary" {
expl := strings.Split(key.Columns, ",")
diff --git a/cmd/query_gen/tables.go b/cmd/query_gen/tables.go
index 69fda816..abfaa8f1 100644
--- a/cmd/query_gen/tables.go
+++ b/cmd/query_gen/tables.go
@@ -45,8 +45,8 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"temp_group", "int", 0, false, false, "0"}, // For temporary groups, set this to zero when a temporary group isn't in effect
},
[]tblKey{
- tblKey{"uid", "primary"},
- tblKey{"name", "unique"},
+ tblKey{"uid", "primary","",false},
+ tblKey{"name", "unique","",false},
},
)
@@ -64,7 +64,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"tag", "varchar", 50, false, false, "''"},
},
[]tblKey{
- tblKey{"gid", "primary"},
+ tblKey{"gid", "primary","",false},
},
)
@@ -83,7 +83,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"createdAt", "createdAt", 0, false, false, ""},
},
[]tblKey{
- tblKey{"uid", "primary"},
+ tblKey{"uid", "primary","",false},
},
)
@@ -128,7 +128,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"temporary", "boolean", 0, false, false, ""}, // special case for permanent bans to do the necessary bookkeeping, might be removed in the future
},
[]tblKey{
- tblKey{"uid", "primary"},
+ tblKey{"uid", "primary","",false},
},
)
@@ -138,7 +138,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
},
[]tblKey{
- tblKey{"uid", "primary"},
+ tblKey{"uid", "primary","",false},
},
)
@@ -191,7 +191,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"lastReplyerID", "int", 0, false, false, "0"},
},
[]tblKey{
- tblKey{"fid", "primary"},
+ tblKey{"fid", "primary","",false},
},
)
@@ -204,7 +204,7 @@ func createTables(adapter qgen.Adapter) error {
},
[]tblKey{
// TODO: Test to see that the compound primary key works
- tblKey{"fid,gid", "primary"},
+ tblKey{"fid,gid", "primary","",false},
},
)
@@ -240,8 +240,8 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"data", "varchar", 200, false, false, "''"},
},
[]tblKey{
- tblKey{"tid", "primary"},
- tblKey{"content", "fulltext"},
+ tblKey{"tid", "primary","",false},
+ tblKey{"content", "fulltext","",false},
},
)
@@ -264,8 +264,8 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"poll", "int", 0, false, false, "0"},
},
[]tblKey{
- tblKey{"rid", "primary"},
- tblKey{"content", "fulltext"},
+ tblKey{"rid", "primary","",false},
+ tblKey{"content", "fulltext","",false},
},
)
@@ -281,7 +281,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"extra", "varchar", 200, false, false, ""},
},
[]tblKey{
- tblKey{"attachID", "primary"},
+ tblKey{"attachID", "primary","",false},
},
)
@@ -295,7 +295,7 @@ func createTables(adapter qgen.Adapter) error {
// TODO: Add a createdBy column?
},
[]tblKey{
- tblKey{"reviseID", "primary"},
+ tblKey{"reviseID", "primary","",false},
},
)
@@ -309,7 +309,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"votes", "int", 0, false, false, "0"},
},
[]tblKey{
- tblKey{"pollID", "primary"},
+ tblKey{"pollID", "primary","",false},
},
)
@@ -344,7 +344,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"ipaddress", "varchar", 200, false, false, "0.0.0.0.0"},
},
[]tblKey{
- tblKey{"rid", "primary"},
+ tblKey{"rid", "primary","",false},
},
)
@@ -363,7 +363,10 @@ func createTables(adapter qgen.Adapter) error {
[]tblColumn{
tblColumn{"watcher", "int", 0, false, false, ""}, // TODO: Make this a foreign key
tblColumn{"asid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
- }, nil,
+ },
+ []tblKey{
+ tblKey{"asid,asid","foreign","activity_stream",true},
+ },
)
qgen.Install.CreateTable("activity_stream", "", "",
@@ -376,7 +379,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"elementID", "int", 0, false, false, ""}, /* the ID of the element being acted upon */
},
[]tblKey{
- tblKey{"asid", "primary"},
+ tblKey{"asid", "primary","",false},
},
)
@@ -398,7 +401,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"constraints", "varchar", 200, false, false, "''"},
},
[]tblKey{
- tblKey{"name", "unique"},
+ tblKey{"name", "unique","",false},
},
)
@@ -409,7 +412,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"replacement", "varchar", 200, false, false, ""},
},
[]tblKey{
- tblKey{"wfid", "primary"},
+ tblKey{"wfid", "primary","",false},
},
)
@@ -420,7 +423,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"installed", "boolean", 0, false, false, "0"},
},
[]tblKey{
- tblKey{"uname", "unique"},
+ tblKey{"uname", "unique","",false},
},
)
@@ -431,7 +434,7 @@ func createTables(adapter qgen.Adapter) error {
//tblColumn{"profileUserVars", "text", 0, false, false, "''"},
},
[]tblKey{
- tblKey{"uname", "unique"},
+ tblKey{"uname", "unique","",false},
},
)
@@ -446,7 +449,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"data", "text", 0, false, false, "''"},
},
[]tblKey{
- tblKey{"wid", "primary"},
+ tblKey{"wid", "primary","",false},
},
)
@@ -455,7 +458,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"mid", "int", 0, false, true, ""},
},
[]tblKey{
- tblKey{"mid", "primary"},
+ tblKey{"mid", "primary","",false},
},
)
@@ -479,7 +482,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"adminOnly", "boolean", 0, false, false, "0"},
},
[]tblKey{
- tblKey{"miid", "primary"},
+ tblKey{"miid", "primary","",false},
},
)
@@ -495,7 +498,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"menuID", "int", 0, false, false, "-1"}, // simple sidebar menu
},
[]tblKey{
- tblKey{"pid", "primary"},
+ tblKey{"pid", "primary","",false},
},
)
@@ -510,7 +513,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"doneAt", "createdAt", 0, false, false, ""},
},
[]tblKey{
- tblKey{"rlid", "primary"},
+ tblKey{"rlid", "primary","",false},
},
)
@@ -523,7 +526,7 @@ func createTables(adapter qgen.Adapter) error {
tblColumn{"doneAt", "createdAt", 0, false, false, ""},
},
[]tblKey{
- tblKey{"lid", "primary"},
+ tblKey{"lid", "primary","",false},
},
)
diff --git a/common/topic.go b/common/topic.go
index ae5cac61..abfab29c 100644
--- a/common/topic.go
+++ b/common/topic.go
@@ -180,6 +180,8 @@ type TopicStmts struct {
createLike *sql.Stmt
addLikesToTopic *sql.Stmt
delete *sql.Stmt
+ deleteActivity *sql.Stmt
+ deleteActivitySubs *sql.Stmt
edit *sql.Stmt
setPoll *sql.Stmt
createAction *sql.Stmt
@@ -204,6 +206,8 @@ func init() {
createLike: acc.Insert("likes").Columns("weight, targetItem, targetType, sentBy, createdAt").Fields("?,?,?,?,UTC_TIMESTAMP()").Prepare(),
addLikesToTopic: acc.Update("topics").Set("likeCount = likeCount + ?").Where("tid = ?").Prepare(),
delete: acc.Delete("topics").Where("tid = ?").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("topics").Set("title = ?, content = ?, parsed_content = ?").Where("tid = ?").Prepare(), // TODO: Only run the content update bits on non-polls, does this matter?
setPoll: acc.Update("topics").Set("content = '', parsed_content = '', 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(),
@@ -325,6 +329,14 @@ func (topic *Topic) Delete() error {
_, err = topicStmts.delete.Exec(topic.ID)
topic.cacheRemove()
+ if err != nil {
+ return err
+ }
+ _, err = topicStmts.deleteActivitySubs.Exec(topic.ID)
+ if err != nil {
+ return err
+ }
+ _, err = topicStmts.deleteActivity.Exec(topic.ID)
return err
}
diff --git a/install/mysql.go b/install/mysql.go
index 6089a51a..f4089792 100644
--- a/install/mysql.go
+++ b/install/mysql.go
@@ -10,10 +10,12 @@ import (
"bytes"
"database/sql"
"fmt"
+ "os"
"io/ioutil"
"path/filepath"
"strconv"
"strings"
+ "sync"
"github.com/Azareal/Gosora/query_gen"
_ "github.com/go-sql-driver/mysql"
@@ -93,53 +95,140 @@ func (ins *MysqlInstaller) InitDatabase() (err error) {
fmt.Println("The database was successfully created")
}
- fmt.Println("Switching to database ", ins.dbName)
+ /*fmt.Println("Switching to database ", ins.dbName)
_, err = db.Exec("USE " + ins.dbName)
+ if err != nil {
+ return err
+ }*/
+ db.Close()
+
+ db, err = sql.Open("mysql", ins.dbUsername+_dbPassword+"@tcp("+ins.dbHost+":"+ins.dbPort+")/" + ins.dbName)
if err != nil {
return err
}
+ // Make sure that the connection is alive..
+ err = db.Ping()
+ if err != nil {
+ return err
+ }
+ fmt.Println("Successfully connected to the database")
+
// Ready the query builder
+ ins.db = db
qgen.Builder.SetConn(db)
return qgen.Builder.SetAdapter("mysql")
}
+func(ins *MysqlInstaller) createTable(f os.FileInfo) error {
+ table := strings.TrimPrefix(f.Name(), "query_")
+ ext := filepath.Ext(table)
+ if ext != ".sql" {
+ return nil
+ }
+ table = strings.TrimSuffix(table, ext)
+
+ // ? - This is mainly here for tests, although it might allow the installer to overwrite a production database, so we might want to proceed with caution
+ q := "DROP TABLE IF EXISTS `" + table + "`;"
+ _, err := ins.db.Exec(q)
+ if err != nil {
+ fmt.Println("Failed query:", q)
+ fmt.Println("e:",err)
+ return err
+ }
+
+ data, err := ioutil.ReadFile("./schema/mysql/" + f.Name())
+ if err != nil {
+ return err
+ }
+ data = bytes.TrimSpace(data)
+
+ _, err = ins.db.Exec(string(data))
+ if err != nil {
+ fmt.Println("Failed query:", string(data))
+ fmt.Println("e:",err)
+ return err
+ }
+ fmt.Printf("Created table '%s'\n", table)
+
+ return nil
+}
+
func (ins *MysqlInstaller) TableDefs() (err error) {
fmt.Println("Creating the tables")
- files, _ := ioutil.ReadDir("./schema/mysql/")
- for _, f := range files {
+ files, err := ioutil.ReadDir("./schema/mysql/")
+ if err != nil {
+ return err
+ }
+
+ // TODO: Can we reduce the amount of boilerplate here?
+ after := []string{"activity_stream_matches"}
+ c1 := make(chan os.FileInfo)
+ c2 := make(chan os.FileInfo)
+ e := make(chan error)
+ var wg sync.WaitGroup
+ r := func(c chan os.FileInfo) {
+ wg.Add(1)
+ for f := range c {
+ err := ins.createTable(f)
+ if err != nil {
+ e <- err
+ }
+ }
+ wg.Done()
+ }
+ go r(c1)
+ go r(c2)
+
+ var a []os.FileInfo
+Outer:
+ for i, f := range files {
if !strings.HasPrefix(f.Name(), "query_") {
continue
}
-
- var table, ext string
- table = strings.TrimPrefix(f.Name(), "query_")
- ext = filepath.Ext(table)
+ table := strings.TrimPrefix(f.Name(), "query_")
+ ext := filepath.Ext(table)
if ext != ".sql" {
continue
}
table = strings.TrimSuffix(table, ext)
-
- // ? - This is mainly here for tests, although it might allow the installer to overwrite a production database, so we might want to proceed with caution
- _, err = ins.db.Exec("DROP TABLE IF EXISTS `" + table + "`;")
- if err != nil {
- fmt.Println("Failed query:", "DROP TABLE IF EXISTS `"+table+"`;")
- return err
+ for _, tbl := range after {
+ if tbl == table {
+ a = append(a, f)
+ continue Outer
+ }
}
-
- fmt.Printf("Creating table '%s'\n", table)
- data, err := ioutil.ReadFile("./schema/mysql/" + f.Name())
- if err != nil {
- return err
+ if i%2 == 0 {
+ c1 <- f
+ } else {
+ c2 <- f
}
- data = bytes.TrimSpace(data)
+ }
+ close(c1)
+ close(c2)
+ wg.Wait()
+ close(e)
+
+ var first error
+ for err := range e {
+ if first == nil {
+ first = err
+ }
+ }
+ if first != nil {
+ return first
+ }
- _, err = ins.db.Exec(string(data))
+ for _, f := range a {
+ if !strings.HasPrefix(f.Name(), "query_") {
+ continue
+ }
+ err := ins.createTable(f)
if err != nil {
- fmt.Println("Failed query:", string(data))
return err
}
}
+
return nil
}
diff --git a/patcher/patches.go b/patcher/patches.go
index ec4a92a9..90f07a20 100644
--- a/patcher/patches.go
+++ b/patcher/patches.go
@@ -32,6 +32,7 @@ func init() {
addPatch(17, patch17)
addPatch(18, patch18)
addPatch(19, patch19)
+ addPatch(20, patch20)
}
func patch0(scanner *bufio.Scanner) (err error) {
@@ -49,7 +50,7 @@ func patch0(scanner *bufio.Scanner) (err error) {
tblColumn{"mid", "int", 0, false, true, ""},
},
[]tblKey{
- tblKey{"mid", "primary"},
+ tblKey{"mid", "primary","",false},
},
))
if err != nil {
@@ -76,7 +77,7 @@ func patch0(scanner *bufio.Scanner) (err error) {
tblColumn{"adminOnly", "boolean", 0, false, false, "0"},
},
[]tblKey{
- tblKey{"miid", "primary"},
+ tblKey{"miid", "primary","",false},
},
))
if err != nil {
@@ -183,7 +184,7 @@ func patch3(scanner *bufio.Scanner) error {
tblColumn{"doneAt", "createdAt", 0, false, false, ""},
},
[]tblKey{
- tblKey{"rlid", "primary"},
+ tblKey{"rlid", "primary","",false},
},
))
}
@@ -246,7 +247,7 @@ func patch4(scanner *bufio.Scanner) error {
tblColumn{"menuID", "int", 0, false, false, "-1"},
},
[]tblKey{
- tblKey{"pid", "primary"},
+ tblKey{"pid", "primary","",false},
},
))
if err != nil {
@@ -289,7 +290,7 @@ func patch5(scanner *bufio.Scanner) error {
tblColumn{"createdAt", "createdAt", 0, false, false, ""},
},
[]tblKey{
- tblKey{"uid", "primary"},
+ tblKey{"uid", "primary","",false},
},
))
if err != nil {
@@ -309,7 +310,7 @@ func patch7(scanner *bufio.Scanner) error {
tblColumn{"uid", "int", 0, false, false, ""}, // TODO: Make this a foreign key
},
[]tblKey{
- tblKey{"uid", "primary"},
+ tblKey{"uid", "primary","",false},
},
))
}
@@ -391,7 +392,7 @@ func patch9(scanner *bufio.Scanner) error {
tblColumn{"doneAt", "createdAt", 0, false, false, ""},
},
[]tblKey{
- tblKey{"lid", "primary"},
+ tblKey{"lid", "primary","",false},
},
))
}
@@ -514,7 +515,7 @@ func patch12(scanner *bufio.Scanner) error {
}
func patch13(scanner *bufio.Scanner) error {
- err := execStmt(qgen.Builder.AddColumn("widgets", tblColumn{"wid", "int", 0, false, true, ""}, &tblKey{"wid", "primary"}))
+ err := execStmt(qgen.Builder.AddColumn("widgets", tblColumn{"wid", "int", 0, false, true, ""}, &tblKey{"wid", "primary","",false}))
if err != nil {
return err
}
@@ -523,15 +524,15 @@ func patch13(scanner *bufio.Scanner) error {
}
func patch14(scanner *bufio.Scanner) error {
- err := execStmt(qgen.Builder.AddKey("topics", "title", tblKey{"title", "fulltext"}))
+ err := execStmt(qgen.Builder.AddKey("topics", "title", tblKey{"title", "fulltext","",false}))
if err != nil {
return err
}
- err = execStmt(qgen.Builder.AddKey("topics", "content", tblKey{"content", "fulltext"}))
+ err = execStmt(qgen.Builder.AddKey("topics", "content", tblKey{"content", "fulltext","",false}))
if err != nil {
return err
}
- err = execStmt(qgen.Builder.AddKey("replies", "content", tblKey{"content", "fulltext"}))
+ err = execStmt(qgen.Builder.AddKey("replies", "content", tblKey{"content", "fulltext","",false}))
if err != nil {
return err
}
@@ -602,4 +603,27 @@ func patch19(scanner *bufio.Scanner) error {
tblColumn{"createdAt", "datetime", 0, false, false, ""},
}, nil,
))
-}
\ No newline at end of file
+}
+
+func patch20(scanner *bufio.Scanner) error {
+ err := acc().Select("activity_stream_matches").Cols("asid").Each(func(rows *sql.Rows) error {
+ var asid int
+ err := rows.Scan(&asid)
+ if err != nil {
+ return err
+ }
+
+ err = acc().Select("activity_stream").Cols("asid").Where("asid = ?").QueryRow(asid).Scan(&asid)
+ if err != sql.ErrNoRows {
+ return err
+ }
+
+ _, err = acc().Delete("activity_stream_matches").Where("asid = ?").Run(asid)
+ return err
+ })
+ if err != nil {
+ return err
+ }
+
+ return execStmt(qgen.Builder.AddForeignKey("activity_stream_matches", "asid","activity_stream","asid",true))
+}
diff --git a/public/global.js b/public/global.js
index ed07f3a4..dad38319 100644
--- a/public/global.js
+++ b/public/global.js
@@ -138,7 +138,7 @@ function loadAlerts(menuAlerts) {
$.ajax({
type: 'get',
dataType: 'json',
- url:'/api/?action=get&module=alerts',
+ url:'/api/?module=alerts',
success: (data) => {
if("errmsg" in data) {
setAlertError(menuAlerts,data.errmsg)
@@ -147,8 +147,8 @@ function loadAlerts(menuAlerts) {
alertList = [];
alertMapping = {};
for(var i in data.msgs) addAlert(data.msgs[i]);
- console.log("data.msgCount:",data.msgCount)
- alertCount = data.msgCount;
+ console.log("data.count:",data.count)
+ alertCount = data.count;
updateAlertList(menuAlerts)
},
error: (magic,theStatus,error) => {
diff --git a/public/init.js b/public/init.js
index 544b46f1..13c8e155 100644
--- a/public/init.js
+++ b/public/init.js
@@ -178,7 +178,7 @@ function initPhrases(loggedIn, panel = false) {
console.log("in initPhrases")
console.log("tmlInits:",tmplInits)
let e = "";
- if(loggedIn && !panel) e = ",topic_list,topic";
+ if(loggedIn && !panel) e = ",status,topic_list,topic";
else if(panel) e = ",analytics,panel"; // TODO: Request phrases for just one section of the control panel?
else e = ",status,topic_list";
fetchPhrases("alerts,paginator"+e) // TODO: Break this up?
diff --git a/query_gen/builder.go b/query_gen/builder.go
index 1e8de785..a95938e0 100644
--- a/query_gen/builder.go
+++ b/query_gen/builder.go
@@ -120,6 +120,10 @@ func (build *builder) AddKey(table string, column string, key DBTableKey) (stmt
return build.prepare(build.adapter.AddKey("", table, column, key))
}
+func (build *builder) AddForeignKey(table string, column string, ftable string, fcolumn string, cascade bool) (stmt *sql.Stmt, err error) {
+ return build.prepare(build.adapter.AddForeignKey("", table, column, ftable, fcolumn, cascade))
+}
+
func (build *builder) SimpleInsert(table string, columns string, fields string) (stmt *sql.Stmt, err error) {
return build.prepare(build.adapter.SimpleInsert("", table, columns, fields))
}
diff --git a/query_gen/install.go b/query_gen/install.go
index 797fa863..3e8a743d 100644
--- a/query_gen/install.go
+++ b/query_gen/install.go
@@ -3,17 +3,17 @@ package qgen
var Install *installer
func init() {
- Install = &installer{instructions: []DB_Install_Instruction{}}
+ Install = &installer{instructions: []DBInstallInstruction{}}
}
-type DB_Install_Instruction struct {
+type DBInstallInstruction struct {
Table string
Contents string
Type string
}
// TODO: Add methods to this to construct it OO-like
-type DB_Install_Table struct {
+type DBInstallTable struct {
Name string
Charset string
Collation string
@@ -25,8 +25,8 @@ type DB_Install_Table struct {
// TODO: Re-implement the query generation, query builder and installer adapters as layers on-top of a query text adapter
type installer struct {
adapter Adapter
- instructions []DB_Install_Instruction
- tables []*DB_Install_Table // TODO: Use this in Record() in the next commit to allow us to auto-migrate settings rather than manually patching them in on upgrade
+ instructions []DBInstallInstruction
+ tables []*DBInstallTable // TODO: Use this in Record() in the next commit to allow us to auto-migrate settings rather than manually patching them in on upgrade
plugins []QueryPlugin
}
@@ -41,7 +41,7 @@ func (install *installer) SetAdapter(name string) error {
func (install *installer) SetAdapterInstance(adapter Adapter) {
install.adapter = adapter
- install.instructions = []DB_Install_Instruction{}
+ install.instructions = []DBInstallInstruction{}
}
func (install *installer) AddPlugins(plugins ...QueryPlugin) {
@@ -49,7 +49,7 @@ func (install *installer) AddPlugins(plugins ...QueryPlugin) {
}
func (install *installer) CreateTable(table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) error {
- tableStruct := &DB_Install_Table{table, charset, collation, columns, keys}
+ tableStruct := &DBInstallTable{table, charset, collation, columns, keys}
err := install.RunHook("CreateTableStart", tableStruct)
if err != nil {
return err
@@ -62,7 +62,7 @@ func (install *installer) CreateTable(table string, charset string, collation st
if err != nil {
return err
}
- install.instructions = append(install.instructions, DB_Install_Instruction{table, res, "create-table"})
+ install.instructions = append(install.instructions, DBInstallInstruction{table, res, "create-table"})
install.tables = append(install.tables, tableStruct)
return nil
}
@@ -81,7 +81,7 @@ func (install *installer) AddIndex(table string, iname string, colname string) e
if err != nil {
return err
}
- install.instructions = append(install.instructions, DB_Install_Instruction{table, res, "index"})
+ install.instructions = append(install.instructions, DBInstallInstruction{table, res, "index"})
return nil
}
@@ -99,7 +99,7 @@ func (install *installer) SimpleInsert(table string, columns string, fields stri
if err != nil {
return err
}
- install.instructions = append(install.instructions, DB_Install_Instruction{table, res, "insert"})
+ install.instructions = append(install.instructions, DBInstallInstruction{table, res, "insert"})
return nil
}
diff --git a/query_gen/mssql.go b/query_gen/mssql.go
index e9d76b55..12438c2b 100644
--- a/query_gen/mssql.go
+++ b/query_gen/mssql.go
@@ -53,6 +53,7 @@ func (adapter *MssqlAdapter) DropTable(name string, table string) (string, error
return querystr, nil
}
+// TODO: Add support for foreign keys?
// 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
func (adapter *MssqlAdapter) CreateTable(name string, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (string, error) {
@@ -174,6 +175,25 @@ func (adapter *MssqlAdapter) AddKey(name string, table string, column string, ke
return "", errors.New("not implemented")
}
+// TODO: Implement this
+// TODO: Test to make sure everything works here
+func (adapter *MssqlAdapter) AddForeignKey(name string, table string, column string, ftable string, fcolumn string, cascade bool) (out string, e error) {
+ var c = func(str string, val bool) {
+ if e != nil || !val {
+ return
+ }
+ e = errors.New("You need a "+str+" for this table")
+ }
+ c("name",table=="")
+ c("column",column=="")
+ c("ftable",ftable=="")
+ c("fcolumn",fcolumn=="")
+ if e != nil {
+ return "", e
+ }
+ return "", errors.New("not implemented")
+}
+
func (adapter *MssqlAdapter) SimpleInsert(name string, table string, columns string, fields string) (string, error) {
if table == "" {
return "", errors.New("You need a name for this table")
diff --git a/query_gen/mysql.go b/query_gen/mysql.go
index 524258a2..d56d2a4b 100644
--- a/query_gen/mysql.go
+++ b/query_gen/mysql.go
@@ -112,11 +112,20 @@ func (adapter *MysqlAdapter) CreateTable(name string, table string, charset stri
if key.Type != "unique" {
querystr += " key"
}
- querystr += "("
- for _, column := range strings.Split(key.Columns, ",") {
- querystr += "`" + column + "`,"
+ if key.Type == "foreign" {
+ cols := strings.Split(key.Columns, ",")
+ querystr += "(`" + cols[0] + "`) REFERENCES `" + key.FTable + "`(`" + cols[1] + "`)"
+ if key.Cascade {
+ querystr += " ON DELETE CASCADE"
+ }
+ querystr += ","
+ } else {
+ querystr += "("
+ for _, column := range strings.Split(key.Columns, ",") {
+ querystr += "`" + column + "`,"
+ }
+ querystr = querystr[0:len(querystr)-1] + "),"
}
- querystr = querystr[0:len(querystr)-1] + "),"
}
}
@@ -173,12 +182,12 @@ func (adapter *MysqlAdapter) parseColumn(column DBTableColumn) (col DBTableColum
// TODO: Support AFTER column
// TODO: Test to make sure everything works here
-func (adapter *MysqlAdapter) AddColumn(name string, table string, column DBTableColumn, key *DBTableKey) (string, error) {
+func (a *MysqlAdapter) AddColumn(name string, table string, column DBTableColumn, key *DBTableKey) (string, error) {
if table == "" {
return "", errors.New("You need a name for this table")
}
- column, size, end := adapter.parseColumn(column)
+ column, size, end := a.parseColumn(column)
querystr := "ALTER TABLE `" + table + "` ADD COLUMN " + "`" + column.Name + "` " + column.Type + size + end
if key != nil {
@@ -191,12 +200,12 @@ func (adapter *MysqlAdapter) AddColumn(name string, table string, column DBTable
}
// 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
- adapter.pushStatement(name, "add-column", querystr)
+ a.pushStatement(name, "add-column", querystr)
return querystr, nil
}
// TODO: Test to make sure everything works here
-func (adapter *MysqlAdapter) AddIndex(name string, table string, iname string, colname string) (string, error) {
+func (a *MysqlAdapter) AddIndex(name string, table string, iname string, colname string) (string, error) {
if table == "" {
return "", errors.New("You need a name for this table")
}
@@ -209,23 +218,50 @@ func (adapter *MysqlAdapter) AddIndex(name string, table string, iname string, c
querystr := "ALTER TABLE `" + table + "` ADD INDEX " + "`" + iname + "` (`" + colname + "`);"
// 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
- adapter.pushStatement(name, "add-index", querystr)
+ a.pushStatement(name, "add-index", querystr)
return querystr, nil
}
// TODO: Test to make sure everything works here
// Only supports FULLTEXT right now
-func (adapter *MysqlAdapter) AddKey(name string, table string, column string, key DBTableKey) (string, error) {
+func (a *MysqlAdapter) AddKey(name string, table string, column string, key DBTableKey) (string, error) {
if table == "" {
return "", errors.New("You need a name for this table")
}
- if key.Type != "fulltext" {
+ var querystr string
+ if key.Type == "fulltext" {
+ querystr = "ALTER TABLE `" + table + "` ADD FULLTEXT(`" + column + "`)"
+ } else {
return "", errors.New("Only fulltext is supported by AddKey right now")
}
- querystr := "ALTER TABLE `" + table + "` ADD FULLTEXT(`" + column + "`)"
// 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
- adapter.pushStatement(name, "add-key", querystr)
+ a.pushStatement(name, "add-key", querystr)
+ return querystr, nil
+}
+
+func (a *MysqlAdapter) AddForeignKey(name string, table string, column string, ftable string, fcolumn string, cascade bool) (out string, e error) {
+ var c = func(str string, val bool) {
+ if e != nil || !val {
+ return
+ }
+ e = errors.New("You need a "+str+" for this table")
+ }
+ c("name",table=="")
+ c("column",column=="")
+ c("ftable",ftable=="")
+ c("fcolumn",fcolumn=="")
+ if e != nil {
+ return "", e
+ }
+
+ querystr := "ALTER TABLE `"+table+"` ADD CONSTRAINT `fk_"+column+"` FOREIGN KEY(`"+column+"`) REFERENCES `"+ftable+"`(`"+fcolumn+"`)"
+ if cascade {
+ querystr += " ON DELETE CASCADE"
+ }
+
+ // 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, "add-foreign-key", querystr)
return querystr, nil
}
diff --git a/query_gen/pgsql.go b/query_gen/pgsql.go
index f668be03..54a87aaf 100644
--- a/query_gen/pgsql.go
+++ b/query_gen/pgsql.go
@@ -147,6 +147,25 @@ func (adapter *PgsqlAdapter) AddKey(name string, table string, column string, ke
return "", errors.New("not implemented")
}
+// TODO: Implement this
+// TODO: Test to make sure everything works here
+func (adapter *PgsqlAdapter) AddForeignKey(name string, table string, column string, ftable string, fcolumn string, cascade bool) (out string, e error) {
+ var c = func(str string, val bool) {
+ if e != nil || !val {
+ return
+ }
+ e = errors.New("You need a "+str+" for this table")
+ }
+ c("name",table=="")
+ c("column",column=="")
+ c("ftable",ftable=="")
+ c("fcolumn",fcolumn=="")
+ if e != nil {
+ return "", e
+ }
+ return "", errors.New("not implemented")
+}
+
// 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
func (adapter *PgsqlAdapter) SimpleInsert(name string, table string, columns string, fields string) (string, error) {
diff --git a/query_gen/querygen.go b/query_gen/querygen.go
index 5d9483ea..f0c50ec0 100644
--- a/query_gen/querygen.go
+++ b/query_gen/querygen.go
@@ -23,6 +23,10 @@ type DBTableColumn struct {
type DBTableKey struct {
Columns string
Type string
+
+ // Foreign keys only
+ FTable string
+ Cascade bool
}
type DBSelect struct {
@@ -111,6 +115,7 @@ type Adapter interface {
AddColumn(name string, table string, column DBTableColumn, key *DBTableKey) (string, error)
AddIndex(name string, table string, iname string, colname string) (string, error)
AddKey(name string, table string, column string, key DBTableKey) (string, error)
+ AddForeignKey(name string, table string, column string, ftable string, fcolumn string, cascade bool) (out string, e error)
SimpleInsert(name string, table string, columns string, fields string) (string, error)
SimpleUpdate(up *updatePrebuilder) (string, error)
SimpleUpdateSelect(up *updatePrebuilder) (string, error) // ! Experimental
diff --git a/routes.go b/routes.go
index fb5078df..a6a47f83 100644
--- a/routes.go
+++ b/routes.go
@@ -10,6 +10,7 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
+ "io"
"errors"
"log"
"net/http"
@@ -27,7 +28,7 @@ var successJSONBytes = []byte(`{"success":"1"}`)
// TODO: Refactor this
// TODO: Use the phrase system
-var phraseLoginAlerts = []byte(`{"msgs":[{"msg":"Login to see your alerts","path":"/accounts/login"}],"msgCount":0}`)
+var phraseLoginAlerts = []byte(`{"msgs":[{"msg":"Login to see your alerts","path":"/accounts/login"}],"count":0}`)
// TODO: Refactor this endpoint
// TODO: Move this into the routes package
@@ -40,6 +41,9 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError
}
action := r.FormValue("action")
+ if action == "" {
+ action = "get"
+ }
if action != "get" && action != "set" {
return c.PreErrorJS("Invalid Action", w, r)
}
@@ -86,8 +90,8 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError
}
var msglist string
- var msgCount int
- err = stmts.getActivityCountByWatcher.QueryRow(user.ID).Scan(&msgCount)
+ var count int
+ err = stmts.getActivityCountByWatcher.QueryRow(user.ID).Scan(&count)
if err == ErrNoRows {
return c.PreErrorJS("Couldn't find the parent topic", w, r)
} else if err != nil {
@@ -141,7 +145,7 @@ func routeAPI(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError
if len(msglist) != 0 {
msglist = msglist[0 : len(msglist)-1]
}
- _, _ = w.Write([]byte(`{"msgs":[` + msglist + `],"msgCount":` + strconv.Itoa(msgCount) + `}`))
+ _, _ = io.WriteString(w, `{"msgs":[` + msglist + `],"count":` + strconv.Itoa(count) + `}`)
default:
return c.PreErrorJS("Invalid Module", w, r)
}
@@ -296,9 +300,9 @@ func routeJSAntispam(w http.ResponseWriter, r *http.Request, user c.User) c.Rout
jsToken := hex.EncodeToString(h.Sum(nil))
var innerCode = "`document.getElementByld('golden-watch').value = '" + jsToken + "';`"
- w.Write([]byte(`let hihi = ` + innerCode + `;
+ io.WriteString(w, `let hihi = ` + innerCode + `;
hihi = hihi.replace('ld','Id');
-eval(hihi);`))
+eval(hihi);`)
return nil
}
diff --git a/routes/topic.go b/routes/topic.go
index 9f3ad840..b71abeaf 100644
--- a/routes/topic.go
+++ b/routes/topic.go
@@ -452,8 +452,7 @@ func CreateTopic(w http.ResponseWriter, r *http.Request, user c.User, header *c.
}
}
- ctpage := c.CreateTopicPage{header, forumList, fid}
- return renderTemplate("create_topic", w, r, header, ctpage)
+ return renderTemplate("create_topic", w, r, header, c.CreateTopicPage{header, forumList, fid})
}
func CreateTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError {
@@ -461,7 +460,6 @@ func CreateTopicSubmit(w http.ResponseWriter, r *http.Request, user c.User) c.Ro
if err != nil {
return c.LocalError("The provided ForumID is not a valid number.", w, r, user)
}
-
// TODO: Add hooks to make use of headerLite
lite, ferr := c.SimpleForumUserCheck(w, r, &user, fid)
if ferr != nil {
diff --git a/schema/mssql/query_activity_stream_matches.sql b/schema/mssql/query_activity_stream_matches.sql
index e6e9cda6..f608225a 100644
--- a/schema/mssql/query_activity_stream_matches.sql
+++ b/schema/mssql/query_activity_stream_matches.sql
@@ -1,4 +1,5 @@
CREATE TABLE [activity_stream_matches] (
[watcher] int not null,
- [asid] int not null
+ [asid] int not null,
+ foreign key([asid],[asid])
);
\ No newline at end of file
diff --git a/schema/mysql/query_activity_stream_matches.sql b/schema/mysql/query_activity_stream_matches.sql
index d4139a9b..661128c0 100644
--- a/schema/mysql/query_activity_stream_matches.sql
+++ b/schema/mysql/query_activity_stream_matches.sql
@@ -1,4 +1,5 @@
CREATE TABLE `activity_stream_matches` (
`watcher` int not null,
- `asid` int not null
+ `asid` int not null,
+ foreign key(`asid`) REFERENCES `activity_stream`(`asid`) ON DELETE CASCADE
);
\ No newline at end of file
diff --git a/schema/pgsql/query_activity_stream_matches.sql b/schema/pgsql/query_activity_stream_matches.sql
index 3efddb73..a1c5ed9b 100644
--- a/schema/pgsql/query_activity_stream_matches.sql
+++ b/schema/pgsql/query_activity_stream_matches.sql
@@ -1,4 +1,5 @@
CREATE TABLE "activity_stream_matches" (
`watcher` int not null,
- `asid` int not null
+ `asid` int not null,
+ foreign key(`asid`,`asid`)
);
\ No newline at end of file
diff --git a/templates/header.html b/templates/header.html
index fe999d9e..8ec58175 100644
--- a/templates/header.html
+++ b/templates/header.html
@@ -7,7 +7,7 @@
{{range .Header.PreScriptsAsync}}
{{end}}
-
+
{{range .Header.ScriptsAsync}}
{{end}}
diff --git a/tickloop.go b/tickloop.go
index 599bc8e9..cfc05a09 100644
--- a/tickloop.go
+++ b/tickloop.go
@@ -5,8 +5,10 @@ import (
"log"
"sync/atomic"
"time"
+ "database/sql"
c "github.com/Azareal/Gosora/common"
+ "github.com/Azareal/Gosora/query_gen"
)
// 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?
@@ -55,6 +57,7 @@ func tickLoop(thumbChan chan bool) {
secondTicker := time.NewTicker(time.Second)
fifteenMinuteTicker := time.NewTicker(15 * time.Minute)
hourTicker := time.NewTicker(time.Hour)
+ dailyTicker := time.NewTicker(time.Hour * 24)
for {
select {
case <-halfSecondTicker.C:
@@ -122,6 +125,23 @@ func tickLoop(thumbChan chan bool) {
runTasks(c.ScheduledHourTasks)
runHook("after_hour_tick")
+ // TODO: Handle the instance going down a lot better
+ case <-dailyTicker.C:
+ // TODO: Find a more efficient way of doing this
+ err := qgen.NewAcc().Select("activity_stream").Cols("asid").EachInt(func(asid int) error {
+ count, err := qgen.NewAcc().Count("activity_stream_matches").Where("asid = ?").Total()
+ if err != sql.ErrNoRows {
+ return err
+ }
+ if count > 0 {
+ return nil
+ }
+ _, err = qgen.NewAcc().Delete("activity_stream").Where("asid = ?").Run(asid)
+ return err
+ })
+ if err != nil && err != sql.ErrNoRows {
+ c.LogError(err)
+ }
}
// TODO: Handle the daily clean-up.