4759ec9c4c
Added more features to RouteError and RouteErrorImpl to help in suppressing sensitive information. Refactored the setting update logic, so the settings are reloaded all at once rather than updated individually. Renamed the router variables in the generated router to r to reduce the number of characters we have to type there. Renamed some of the r variables in the generated router to req for consistency sake.
182 lines
4.5 KiB
Go
182 lines
4.5 KiB
Go
package common
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
"sync/atomic"
|
|
|
|
"github.com/Azareal/Gosora/query_gen"
|
|
)
|
|
|
|
var SettingBox atomic.Value // An atomic value pointing to a SettingBox
|
|
|
|
// SettingMap is a map type specifically for holding the various settings admins set to toggle features on and off or to otherwise alter Gosora's behaviour from the Control Panel
|
|
type SettingMap map[string]interface{}
|
|
|
|
type SettingStore interface {
|
|
ParseSetting(sname string, scontent string, stype string, sconstraint string) string
|
|
BypassGet(name string) (*Setting, error)
|
|
BypassGetAll(name string) ([]*Setting, error)
|
|
}
|
|
|
|
type OptionLabel struct {
|
|
Label string
|
|
Value int
|
|
Selected bool
|
|
}
|
|
|
|
type Setting struct {
|
|
Name string
|
|
Content string
|
|
Type string
|
|
Constraint string
|
|
}
|
|
|
|
type SettingStmts struct {
|
|
getAll *sql.Stmt
|
|
get *sql.Stmt
|
|
update *sql.Stmt
|
|
}
|
|
|
|
var settingStmts SettingStmts
|
|
|
|
func init() {
|
|
SettingBox.Store(SettingMap(make(map[string]interface{})))
|
|
DbInits.Add(func(acc *qgen.Accumulator) error {
|
|
settingStmts = SettingStmts{
|
|
getAll: acc.Select("settings").Columns("name, content, type, constraints").Prepare(),
|
|
get: acc.Select("settings").Columns("content, type, constraints").Where("name = ?").Prepare(),
|
|
update: acc.Update("settings").Set("content = ?").Where("name = ?").Prepare(),
|
|
}
|
|
return acc.FirstError()
|
|
})
|
|
}
|
|
|
|
func (setting *Setting) Copy() (out *Setting) {
|
|
out = &Setting{Name: ""}
|
|
*out = *setting
|
|
return out
|
|
}
|
|
|
|
func LoadSettings() error {
|
|
var sBox = SettingMap(make(map[string]interface{}))
|
|
settings, err := sBox.BypassGetAll()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, setting := range settings {
|
|
err = sBox.ParseSetting(setting.Name, setting.Content, setting.Type, setting.Constraint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
SettingBox.Store(sBox)
|
|
return nil
|
|
}
|
|
|
|
// TODO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions.
|
|
func (sBox SettingMap) ParseSetting(sname string, scontent string, stype string, constraint string) (err error) {
|
|
var ssBox = map[string]interface{}(sBox)
|
|
switch stype {
|
|
case "bool":
|
|
ssBox[sname] = (scontent == "1")
|
|
case "int":
|
|
ssBox[sname], err = strconv.Atoi(scontent)
|
|
if err != nil {
|
|
return errors.New("You were supposed to enter an integer x.x")
|
|
}
|
|
case "int64":
|
|
ssBox[sname], err = strconv.ParseInt(scontent, 10, 64)
|
|
if err != nil {
|
|
return errors.New("You were supposed to enter an integer x.x")
|
|
}
|
|
case "list":
|
|
cons := strings.Split(constraint, "-")
|
|
if len(cons) < 2 {
|
|
return errors.New("Invalid constraint! The second field wasn't set!")
|
|
}
|
|
|
|
con1, err := strconv.Atoi(cons[0])
|
|
con2, err2 := strconv.Atoi(cons[1])
|
|
if err != nil || err2 != nil {
|
|
return errors.New("Invalid contraint! The constraint field wasn't an integer!")
|
|
}
|
|
|
|
value, err := strconv.Atoi(scontent)
|
|
if err != nil {
|
|
return errors.New("Only integers are allowed in this setting x.x")
|
|
}
|
|
|
|
if value < con1 || value > con2 {
|
|
return errors.New("Only integers between a certain range are allowed in this setting")
|
|
}
|
|
ssBox[sname] = value
|
|
default:
|
|
ssBox[sname] = scontent
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (sBox SettingMap) BypassGet(name string) (*Setting, error) {
|
|
setting := &Setting{Name: name}
|
|
err := settingStmts.get.QueryRow(name).Scan(&setting.Content, &setting.Type, &setting.Constraint)
|
|
return setting, err
|
|
}
|
|
|
|
func (sBox SettingMap) BypassGetAll() (settingList []*Setting, err error) {
|
|
rows, err := settingStmts.getAll.Query()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
setting := &Setting{Name: ""}
|
|
err := rows.Scan(&setting.Name, &setting.Content, &setting.Type, &setting.Constraint)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
settingList = append(settingList, setting)
|
|
}
|
|
return settingList, rows.Err()
|
|
}
|
|
|
|
func (sBox SettingMap) Update(name string, content string) RouteError {
|
|
setting, err := sBox.BypassGet(name)
|
|
if err == ErrNoRows {
|
|
return FromError(err)
|
|
} else if err != nil {
|
|
return SysError(err.Error())
|
|
}
|
|
|
|
// TODO: Why is this here and not in a common function?
|
|
if setting.Type == "bool" {
|
|
if content == "on" || content == "1" {
|
|
content = "1"
|
|
} else {
|
|
content = "0"
|
|
}
|
|
}
|
|
|
|
err = sBox.ParseSetting(name, content, setting.Type, setting.Constraint)
|
|
if err != nil {
|
|
return FromError(err)
|
|
}
|
|
|
|
// TODO: Make this a method or function?
|
|
_, err = settingStmts.update.Exec(content, name)
|
|
if err != nil {
|
|
return SysError(err.Error())
|
|
}
|
|
|
|
err = LoadSettings()
|
|
if err != nil {
|
|
return SysError(err.Error())
|
|
}
|
|
return nil
|
|
}
|