bf851bd9fc
Added support for dyntmpl to the template system. The Account Dashboard now sort of uses dyntmpl, more work needed here. Renamed the pre_render_view_topic hook to pre_render_topic. Added the GetCurrentLangPack() function. Added the alerts_no_new_alerts phrase. Added the account_level_list phrase. Refactored the route rename logic in the patcher to cut down on the amount of boilerplate. Added more route renames to the patcher. You will need to run the patcher / updater in this commit.
296 lines
7.2 KiB
Go
296 lines
7.2 KiB
Go
package qgen
|
|
|
|
import (
|
|
"database/sql"
|
|
"strconv"
|
|
)
|
|
|
|
type accDeleteBuilder struct {
|
|
table string
|
|
where string
|
|
|
|
build *Accumulator
|
|
}
|
|
|
|
func (builder *accDeleteBuilder) Where(where string) *accDeleteBuilder {
|
|
if builder.where != "" {
|
|
builder.where += " AND "
|
|
}
|
|
builder.where += where
|
|
return builder
|
|
}
|
|
|
|
func (builder *accDeleteBuilder) Prepare() *sql.Stmt {
|
|
return builder.build.SimpleDelete(builder.table, builder.where)
|
|
}
|
|
|
|
func (builder *accDeleteBuilder) Run(args ...interface{}) (int, error) {
|
|
stmt := builder.Prepare()
|
|
if stmt == nil {
|
|
return 0, builder.build.FirstError()
|
|
}
|
|
|
|
res, err := stmt.Exec(args...)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
lastID, err := res.LastInsertId()
|
|
return int(lastID), err
|
|
}
|
|
|
|
type accUpdateBuilder struct {
|
|
table string
|
|
set string
|
|
where string
|
|
|
|
build *Accumulator
|
|
}
|
|
|
|
func (update *accUpdateBuilder) Set(set string) *accUpdateBuilder {
|
|
update.set = set
|
|
return update
|
|
}
|
|
|
|
func (update *accUpdateBuilder) Where(where string) *accUpdateBuilder {
|
|
if update.where != "" {
|
|
update.where += " AND "
|
|
}
|
|
update.where += where
|
|
return update
|
|
}
|
|
|
|
func (update *accUpdateBuilder) Prepare() *sql.Stmt {
|
|
return update.build.SimpleUpdate(update.table, update.set, update.where)
|
|
}
|
|
|
|
type AccSelectBuilder struct {
|
|
table string
|
|
columns string
|
|
where string
|
|
orderby string
|
|
limit string
|
|
dateCutoff *dateCutoff // We might want to do this in a slightly less hacky way
|
|
inChain *AccSelectBuilder
|
|
inColumn string
|
|
|
|
build *Accumulator
|
|
}
|
|
|
|
func (selectItem *AccSelectBuilder) Columns(columns string) *AccSelectBuilder {
|
|
selectItem.columns = columns
|
|
return selectItem
|
|
}
|
|
|
|
func (selectItem *AccSelectBuilder) Where(where string) *AccSelectBuilder {
|
|
if selectItem.where != "" {
|
|
selectItem.where += " AND "
|
|
}
|
|
selectItem.where += where
|
|
return selectItem
|
|
}
|
|
|
|
// TODO: Don't implement the SQL at the accumulator level but the adapter level
|
|
func (selectItem *AccSelectBuilder) In(column string, inList []int) *AccSelectBuilder {
|
|
if len(inList) == 0 {
|
|
return selectItem
|
|
}
|
|
|
|
var where = column + " IN("
|
|
for _, item := range inList {
|
|
where += strconv.Itoa(item) + ","
|
|
}
|
|
where = where[:len(where)-1] + ")"
|
|
if selectItem.where != "" {
|
|
where += " AND " + selectItem.where
|
|
}
|
|
|
|
selectItem.where = where
|
|
return selectItem
|
|
}
|
|
|
|
func (selectItem *AccSelectBuilder) InQ(column string, subBuilder *AccSelectBuilder) *AccSelectBuilder {
|
|
selectItem.inChain = subBuilder
|
|
selectItem.inColumn = column
|
|
return selectItem
|
|
}
|
|
|
|
func (selectItem *AccSelectBuilder) DateCutoff(column string, quantity int, unit string) *AccSelectBuilder {
|
|
selectItem.dateCutoff = &dateCutoff{column, quantity, unit}
|
|
return selectItem
|
|
}
|
|
|
|
func (selectItem *AccSelectBuilder) Orderby(orderby string) *AccSelectBuilder {
|
|
selectItem.orderby = orderby
|
|
return selectItem
|
|
}
|
|
|
|
func (selectItem *AccSelectBuilder) Limit(limit string) *AccSelectBuilder {
|
|
selectItem.limit = limit
|
|
return selectItem
|
|
}
|
|
|
|
func (selectItem *AccSelectBuilder) Prepare() *sql.Stmt {
|
|
// TODO: Phase out the procedural API and use the adapter's OO API? The OO API might need a bit more work before we do that and it needs to be rolled out to MSSQL.
|
|
if selectItem.dateCutoff != nil || selectItem.inChain != nil {
|
|
selectBuilder := selectItem.build.GetAdapter().Builder().Select().FromAcc(selectItem)
|
|
return selectItem.build.prepare(selectItem.build.GetAdapter().ComplexSelect(selectBuilder))
|
|
}
|
|
return selectItem.build.SimpleSelect(selectItem.table, selectItem.columns, selectItem.where, selectItem.orderby, selectItem.limit)
|
|
}
|
|
|
|
func (builder *AccSelectBuilder) query() (string, error) {
|
|
// TODO: Phase out the procedural API and use the adapter's OO API? The OO API might need a bit more work before we do that and it needs to be rolled out to MSSQL.
|
|
if builder.dateCutoff != nil || builder.inChain != nil {
|
|
selectBuilder := builder.build.GetAdapter().Builder().Select().FromAcc(builder)
|
|
return builder.build.GetAdapter().ComplexSelect(selectBuilder)
|
|
}
|
|
return builder.build.adapter.SimpleSelect("_builder", builder.table, builder.columns, builder.where, builder.orderby, builder.limit)
|
|
}
|
|
|
|
func (selectItem *AccSelectBuilder) Query(args ...interface{}) (*sql.Rows, error) {
|
|
stmt := selectItem.Prepare()
|
|
if stmt != nil {
|
|
return stmt.Query(args...)
|
|
}
|
|
return nil, selectItem.build.FirstError()
|
|
}
|
|
|
|
type AccRowWrap struct {
|
|
row *sql.Row
|
|
err error
|
|
}
|
|
|
|
func (wrap *AccRowWrap) Scan(dest ...interface{}) error {
|
|
if wrap.err != nil {
|
|
return wrap.err
|
|
}
|
|
return wrap.row.Scan(dest...)
|
|
}
|
|
|
|
// TODO: Test to make sure the errors are passed up properly
|
|
func (builder *AccSelectBuilder) QueryRow(args ...interface{}) *AccRowWrap {
|
|
stmt := builder.Prepare()
|
|
if stmt != nil {
|
|
return &AccRowWrap{stmt.QueryRow(args...), nil}
|
|
}
|
|
return &AccRowWrap{nil, builder.build.FirstError()}
|
|
}
|
|
|
|
// Experimental, reduces lines
|
|
func (builder *AccSelectBuilder) Each(handle func(*sql.Rows) error) error {
|
|
query, err := builder.query()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rows, err := builder.build.query(query)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
err = handle(rows)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return rows.Err()
|
|
}
|
|
func (builder *AccSelectBuilder) EachInt(handle func(int) error) error {
|
|
query, err := builder.query()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
rows, err := builder.build.query(query)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var theInt int
|
|
err = rows.Scan(&theInt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = handle(theInt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return rows.Err()
|
|
}
|
|
|
|
type accInsertBuilder struct {
|
|
table string
|
|
columns string
|
|
fields string
|
|
|
|
build *Accumulator
|
|
}
|
|
|
|
func (insert *accInsertBuilder) Columns(columns string) *accInsertBuilder {
|
|
insert.columns = columns
|
|
return insert
|
|
}
|
|
|
|
func (insert *accInsertBuilder) Fields(fields string) *accInsertBuilder {
|
|
insert.fields = fields
|
|
return insert
|
|
}
|
|
|
|
func (insert *accInsertBuilder) Prepare() *sql.Stmt {
|
|
return insert.build.SimpleInsert(insert.table, insert.columns, insert.fields)
|
|
}
|
|
|
|
func (builder *accInsertBuilder) Exec(args ...interface{}) (res sql.Result, err error) {
|
|
query, err := builder.build.adapter.SimpleInsert("_builder", builder.table, builder.columns, builder.fields)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
return builder.build.exec(query, args...)
|
|
}
|
|
|
|
func (builder *accInsertBuilder) Run(args ...interface{}) (int, error) {
|
|
query, err := builder.build.adapter.SimpleInsert("_builder", builder.table, builder.columns, builder.fields)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
res, err := builder.build.exec(query, args...)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
lastID, err := res.LastInsertId()
|
|
return int(lastID), err
|
|
}
|
|
|
|
type accCountBuilder struct {
|
|
table string
|
|
where string
|
|
limit string
|
|
|
|
build *Accumulator
|
|
}
|
|
|
|
func (count *accCountBuilder) Where(where string) *accCountBuilder {
|
|
if count.where != "" {
|
|
count.where += " AND "
|
|
}
|
|
count.where += where
|
|
return count
|
|
}
|
|
|
|
func (count *accCountBuilder) Limit(limit string) *accCountBuilder {
|
|
count.limit = limit
|
|
return count
|
|
}
|
|
|
|
// TODO: Add QueryRow for this and use it in statistics.go
|
|
func (count *accCountBuilder) Prepare() *sql.Stmt {
|
|
return count.build.SimpleCount(count.table, count.where, count.limit)
|
|
}
|
|
|
|
// TODO: Add a Sum builder for summing viewchunks up into one number for the dashboard?
|