package common import ( "database/sql" "encoding/json" "strconv" "strings" "sync/atomic" qgen "github.com/Azareal/Gosora/query_gen" ) type WidgetStmts struct { //getList *sql.Stmt getDockList *sql.Stmt delete *sql.Stmt create *sql.Stmt update *sql.Stmt //qgen.SimpleModel } var widgetStmts WidgetStmts func init() { DbInits.Add(func(acc *qgen.Accumulator) error { w := "widgets" widgetStmts = WidgetStmts{ //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(), } 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 InitFunc func(w *Widget, sched *WidgetScheduler) error ShutdownFunc func(w *Widget) error BuildFunc func(w *Widget, hvars interface{}) (string, error) TickFunc func(w *Widget) error } func (w *Widget) Delete() error { _, err := widgetStmts.delete.Exec(w.ID) if err != nil { return err } // Reload the dock // TODO: Better synchronisation Widgets.delete(w.ID) widgets, err := getDockWidgets(w.Side) if err != nil { return err } setDock(w.Side, widgets) return nil } func (w *Widget) Copy() (ow *Widget) { ow = &Widget{} *ow = *w return ow } // TODO: Test this // TODO: Add support for zone:id. Perhaps, carry a ZoneID property around in *Header? It might allow some weirdness like frontend[5] which matches any zone with an ID of 5 but it would be a tad faster than verifying each zone, although it might be problematic if users end up relying on this behaviour for areas which don't pass IDs to the widgets system but *probably* should // TODO: Add a selector which also matches topics inside a specific forum? func (w *Widget) Allowed(zone string, zoneid int) bool { for _, loc := range strings.Split(w.Location, "|") { if len(loc) == 0 { continue } sloc := strings.Split(":", loc) if len(sloc) > 1 { iloc, _ := strconv.Atoi(sloc[1]) if zoneid != 0 && iloc != zoneid { continue } } if loc == "global" || loc == zone { return true } else if loc[0] == '!' { loc = loc[1:] if loc != "global" && loc != zone { return true } } } return false } // TODO: Refactor func (w *Widget) Build(hvars interface{}) (string, error) { if w.Literal { return w.Body, nil } if w.BuildFunc != nil { return w.BuildFunc(w, hvars) } header := hvars.(*Header) err := header.Theme.RunTmpl(w.Body, hvars, header.Writer) return "", err } type WidgetEdit struct { *Widget Data map[string]string } func (w *WidgetEdit) Create() (int, error) { data, err := json.Marshal(w.Data) if err != nil { return 0, err } res, err := widgetStmts.create.Exec(w.Position, w.Side, w.Type, w.Enabled, w.Location, data) if err != nil { return 0, err } // Reload the dock widgets, err := getDockWidgets(w.Side) if err != nil { return 0, err } setDock(w.Side, widgets) wid64, err := res.LastInsertId() return int(wid64), err } func (w *WidgetEdit) Commit() error { data, err := json.Marshal(w.Data) if err != nil { return err } _, err = widgetStmts.update.Exec(w.Position, w.Side, w.Type, w.Enabled, w.Location, data, w.ID) if err != nil { return err } // Reload the dock widgets, err := getDockWidgets(w.Side) if err != nil { return err } setDock(w.Side, widgets) return nil }