github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/common/widget.go (about) 1 package common 2 3 import ( 4 "database/sql" 5 "encoding/json" 6 "strconv" 7 "strings" 8 "sync/atomic" 9 10 qgen "github.com/Azareal/Gosora/query_gen" 11 ) 12 13 type WidgetStmts struct { 14 //getList *sql.Stmt 15 getDockList *sql.Stmt 16 delete *sql.Stmt 17 create *sql.Stmt 18 update *sql.Stmt 19 20 //qgen.SimpleModel 21 } 22 23 var widgetStmts WidgetStmts 24 25 func init() { 26 DbInits.Add(func(acc *qgen.Accumulator) error { 27 w := "widgets" 28 widgetStmts = WidgetStmts{ 29 //getList: acc.Select(w).Columns("wid,position,side,type,active,location,data").Orderby("position ASC").Prepare(), 30 getDockList: acc.Select(w).Columns("wid,position,type,active,location,data").Where("side=?").Orderby("position ASC").Prepare(), 31 //model: acc.SimpleModel(w,"position,type,active,location,data","wid"), 32 delete: acc.Delete(w).Where("wid=?").Prepare(), 33 create: acc.Insert(w).Columns("position,side,type,active,location,data").Fields("?,?,?,?,?,?").Prepare(), 34 update: acc.Update(w).Set("position=?,side=?,type=?,active=?,location=?,data=?").Where("wid=?").Prepare(), 35 } 36 return acc.FirstError() 37 }) 38 } 39 40 // TODO: Shrink this struct for common uses in the templates? Would that really make things go faster? 41 type Widget struct { 42 ID int 43 Enabled bool 44 Location string // Coming Soon: overview, topics, topic / topic_view, forums, forum, global 45 Position int 46 RawBody string 47 Body string 48 Side string 49 Type string 50 51 Literal bool 52 TickMask atomic.Value 53 InitFunc func(w *Widget, sched *WidgetScheduler) error 54 ShutdownFunc func(w *Widget) error 55 BuildFunc func(w *Widget, hvars interface{}) (string, error) 56 TickFunc func(w *Widget) error 57 } 58 59 func (w *Widget) Delete() error { 60 _, err := widgetStmts.delete.Exec(w.ID) 61 if err != nil { 62 return err 63 } 64 65 // Reload the dock 66 // TODO: Better synchronisation 67 Widgets.delete(w.ID) 68 widgets, err := getDockWidgets(w.Side) 69 if err != nil { 70 return err 71 } 72 setDock(w.Side, widgets) 73 return nil 74 } 75 76 func (w *Widget) Copy() (ow *Widget) { 77 ow = &Widget{} 78 *ow = *w 79 return ow 80 } 81 82 // TODO: Test this 83 // 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 84 // TODO: Add a selector which also matches topics inside a specific forum? 85 func (w *Widget) Allowed(zone string, zoneid int) bool { 86 for _, loc := range strings.Split(w.Location, "|") { 87 if len(loc) == 0 { 88 continue 89 } 90 sloc := strings.Split(":", loc) 91 if len(sloc) > 1 { 92 iloc, _ := strconv.Atoi(sloc[1]) 93 if zoneid != 0 && iloc != zoneid { 94 continue 95 } 96 } 97 if loc == "global" || loc == zone { 98 return true 99 } else if loc[0] == '!' { 100 loc = loc[1:] 101 if loc != "global" && loc != zone { 102 return true 103 } 104 } 105 } 106 return false 107 } 108 109 // TODO: Refactor 110 func (w *Widget) Build(hvars interface{}) (string, error) { 111 if w.Literal { 112 return w.Body, nil 113 } 114 if w.BuildFunc != nil { 115 return w.BuildFunc(w, hvars) 116 } 117 header := hvars.(*Header) 118 err := header.Theme.RunTmpl(w.Body, hvars, header.Writer) 119 return "", err 120 } 121 122 type WidgetEdit struct { 123 *Widget 124 Data map[string]string 125 } 126 127 func (w *WidgetEdit) Create() (int, error) { 128 data, err := json.Marshal(w.Data) 129 if err != nil { 130 return 0, err 131 } 132 res, err := widgetStmts.create.Exec(w.Position, w.Side, w.Type, w.Enabled, w.Location, data) 133 if err != nil { 134 return 0, err 135 } 136 137 // Reload the dock 138 widgets, err := getDockWidgets(w.Side) 139 if err != nil { 140 return 0, err 141 } 142 setDock(w.Side, widgets) 143 144 wid64, err := res.LastInsertId() 145 return int(wid64), err 146 } 147 148 func (w *WidgetEdit) Commit() error { 149 data, err := json.Marshal(w.Data) 150 if err != nil { 151 return err 152 } 153 _, err = widgetStmts.update.Exec(w.Position, w.Side, w.Type, w.Enabled, w.Location, data, w.ID) 154 if err != nil { 155 return err 156 } 157 158 // Reload the dock 159 widgets, err := getDockWidgets(w.Side) 160 if err != nil { 161 return err 162 } 163 setDock(w.Side, widgets) 164 return nil 165 }