2019-01-21 12:27:59 +00:00
package common
import (
"database/sql"
"encoding/json"
"strings"
2019-06-01 12:31:48 +00:00
"strconv"
2019-01-21 12:27:59 +00:00
"sync/atomic"
"github.com/Azareal/Gosora/query_gen"
)
type WidgetStmts struct {
//getList *sql.Stmt
getDockList * sql . Stmt
delete * sql . Stmt
create * sql . Stmt
update * sql . Stmt
2019-10-30 06:41:05 +00:00
//qgen.SimpleModel
2019-01-21 12:27:59 +00:00
}
var widgetStmts WidgetStmts
func init ( ) {
DbInits . Add ( func ( acc * qgen . Accumulator ) error {
2019-10-30 06:41:05 +00:00
w := "widgets"
2019-01-21 12:27:59 +00:00
widgetStmts = WidgetStmts {
2019-10-30 06:41:05 +00:00
//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 ( ) ,
//model: acc.SimpleModel(w,"position,type,active,location,data","wid"),
delete : acc . Delete ( w ) . Where ( "wid = ?" ) . 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 ( ) ,
2019-01-21 12:27:59 +00:00
}
return acc . FirstError ( )
} )
}
// TODO: Shrink this struct for common uses in the templates? Would that really make things go faster?
type Widget struct {
ID int
Enabled bool
Location string // Coming Soon: overview, topics, topic / topic_view, forums, forum, global
Position int
RawBody string
Body string
Side string
Type string
Literal bool
TickMask atomic . Value
2019-11-10 02:37:53 +00:00
InitFunc func ( w * Widget , schedule * WidgetScheduler ) error
ShutdownFunc func ( w * Widget ) error
BuildFunc func ( w * Widget , hvars interface { } ) ( string , error )
TickFunc func ( w * Widget ) error
2019-01-21 12:27:59 +00:00
}
2019-11-10 02:37:53 +00:00
func ( w * Widget ) Delete ( ) error {
_ , err := widgetStmts . delete . Exec ( w . ID )
2019-01-21 12:27:59 +00:00
if err != nil {
return err
}
// Reload the dock
// TODO: Better synchronisation
2019-11-10 02:37:53 +00:00
Widgets . delete ( w . ID )
widgets , err := getDockWidgets ( w . Side )
2019-01-21 12:27:59 +00:00
if err != nil {
return err
}
2019-11-10 02:37:53 +00:00
setDock ( w . Side , widgets )
2019-01-21 12:27:59 +00:00
return nil
}
2019-11-10 02:37:53 +00:00
func ( w * Widget ) Copy ( ) ( owidget * Widget ) {
2019-01-21 12:27:59 +00:00
owidget = & Widget { }
2019-11-10 02:37:53 +00:00
* owidget = * w
2019-01-21 12:27:59 +00:00
return owidget
}
// TODO: Test this
// TODO: Add support for zone:id. Perhaps, carry a ZoneID property around in *Header? It might allow some weirdness like frontend[5] which matches any zone with an ID of 5 but it would be a tad faster than verifying each zone, although it might be problematic if users end up relying on this behaviour for areas which don't pass IDs to the widgets system but *probably* should
2019-06-01 12:31:48 +00:00
// TODO: Add a selector which also matches topics inside a specific forum?
2019-11-10 02:37:53 +00:00
func ( w * Widget ) Allowed ( zone string , zoneid int ) bool {
for _ , loc := range strings . Split ( w . Location , "|" ) {
2019-06-01 12:31:48 +00:00
if len ( loc ) == 0 {
continue
}
sloc := strings . Split ( ":" , loc )
if len ( sloc ) > 1 {
iloc , _ := strconv . Atoi ( sloc [ 1 ] )
if zoneid != 0 && iloc != zoneid {
continue
}
}
2019-01-21 12:27:59 +00:00
if loc == "global" || loc == zone {
return true
2019-06-01 12:31:48 +00:00
} else if loc [ 0 ] == '!' {
2019-01-21 12:27:59 +00:00
loc = loc [ 1 : ]
if loc != "global" && loc != zone {
return true
}
}
}
return false
}
// TODO: Refactor
2019-11-10 02:37:53 +00:00
func ( w * Widget ) Build ( hvars interface { } ) ( string , error ) {
if w . Literal {
return w . Body , nil
2019-01-21 12:27:59 +00:00
}
2019-11-10 02:37:53 +00:00
if w . BuildFunc != nil {
return w . BuildFunc ( w , hvars )
2019-01-21 12:27:59 +00:00
}
2019-10-30 06:41:05 +00:00
header := hvars . ( * Header )
2019-11-10 02:37:53 +00:00
err := header . Theme . RunTmpl ( w . Body , hvars , header . Writer )
2019-01-21 12:27:59 +00:00
return "" , err
}
type WidgetEdit struct {
* Widget
Data map [ string ] string
}
2019-11-10 02:37:53 +00:00
func ( w * WidgetEdit ) Create ( ) ( int , error ) {
data , err := json . Marshal ( w . Data )
2019-01-21 12:27:59 +00:00
if err != nil {
2019-11-10 02:37:53 +00:00
return 0 , err
2019-01-21 12:27:59 +00:00
}
2019-11-10 02:37:53 +00:00
res , err := widgetStmts . create . Exec ( w . Position , w . Side , w . Type , w . Enabled , w . Location , data )
2019-01-21 12:27:59 +00:00
if err != nil {
2019-11-10 02:37:53 +00:00
return 0 , err
2019-01-21 12:27:59 +00:00
}
// Reload the dock
2019-11-10 02:37:53 +00:00
widgets , err := getDockWidgets ( w . Side )
2019-01-21 12:27:59 +00:00
if err != nil {
2019-11-10 02:37:53 +00:00
return 0 , err
2019-01-21 12:27:59 +00:00
}
2019-11-10 02:37:53 +00:00
setDock ( w . Side , widgets )
wid64 , err := res . LastInsertId ( )
return int ( wid64 ) , err
2019-01-21 12:27:59 +00:00
}
2019-11-10 02:37:53 +00:00
func ( w * WidgetEdit ) Commit ( ) error {
data , err := json . Marshal ( w . Data )
2019-01-21 12:27:59 +00:00
if err != nil {
return err
}
2019-11-10 02:37:53 +00:00
_ , err = widgetStmts . update . Exec ( w . Position , w . Side , w . Type , w . Enabled , w . Location , data , w . ID )
2019-01-21 12:27:59 +00:00
if err != nil {
return err
}
// Reload the dock
2019-11-10 02:37:53 +00:00
widgets , err := getDockWidgets ( w . Side )
2019-01-21 12:27:59 +00:00
if err != nil {
return err
}
2019-11-10 02:37:53 +00:00
setDock ( w . Side , widgets )
2019-01-21 12:27:59 +00:00
return nil
}