2017-11-10 03:33:11 +00:00
package common
2017-09-03 04:50:31 +00:00
2017-11-11 23:34:27 +00:00
import (
2022-02-21 03:53:13 +00:00
"database/sql"
"errors"
"strconv"
"strings"
"sync/atomic"
2017-11-11 23:34:27 +00:00
2022-02-21 03:53:13 +00:00
qgen "git.tuxpa.in/a/gosora/query_gen"
2017-11-11 23:34:27 +00:00
)
2016-12-21 02:30:32 +00:00
2017-11-13 09:23:43 +00:00
var SettingBox atomic . Value // An atomic value pointing to a SettingBox
2017-11-10 03:33:11 +00:00
// 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 { }
2017-09-03 04:50:31 +00:00
2017-11-13 09:23:43 +00:00
type SettingStore interface {
2022-02-21 03:32:53 +00:00
ParseSetting ( name , content , typ , constraint string ) string
BypassGet ( name string ) ( * Setting , error )
BypassGetAll ( name string ) ( [ ] * Setting , error )
2017-11-13 09:23:43 +00:00
}
2016-12-21 02:30:32 +00:00
2017-09-03 04:50:31 +00:00
type OptionLabel struct {
2022-02-21 03:32:53 +00:00
Label string
Value int
Selected bool
2016-12-21 02:30:32 +00:00
}
2016-12-09 13:46:29 +00:00
2017-09-03 04:50:31 +00:00
type Setting struct {
2022-02-21 03:32:53 +00:00
Name string
Content string
Type string
Constraint string
2016-12-09 13:46:29 +00:00
}
2017-11-11 23:34:27 +00:00
type SettingStmts struct {
2022-02-21 03:32:53 +00:00
getAll * sql . Stmt
get * sql . Stmt
update * sql . Stmt
2017-11-11 23:34:27 +00:00
}
var settingStmts SettingStmts
2016-12-21 02:30:32 +00:00
func init ( ) {
2022-02-21 03:32:53 +00:00
SettingBox . Store ( SettingMap ( make ( map [ string ] interface { } ) ) )
DbInits . Add ( func ( acc * qgen . Accumulator ) error {
s := "settings"
settingStmts = SettingStmts {
getAll : acc . Select ( s ) . Columns ( "name,content,type,constraints" ) . Prepare ( ) ,
get : acc . Select ( s ) . Columns ( "content,type,constraints" ) . Where ( "name=?" ) . Prepare ( ) ,
update : acc . Update ( s ) . Set ( "content=?" ) . Where ( "name=?" ) . Prepare ( ) ,
}
return acc . FirstError ( )
} )
2016-12-21 02:30:32 +00:00
}
2020-03-21 07:44:33 +00:00
func ( s * Setting ) Copy ( ) ( o * Setting ) {
2022-02-21 03:32:53 +00:00
o = & Setting { Name : "" }
* o = * s
return o
2018-05-27 09:36:35 +00:00
}
2017-06-14 09:55:47 +00:00
func LoadSettings ( ) error {
2022-02-21 03:32:53 +00:00
sBox := SettingMap ( make ( map [ string ] interface { } ) )
settings , err := sBox . BypassGetAll ( )
if err != nil {
return err
}
for _ , s := range settings {
err = sBox . ParseSetting ( s . Name , s . Content , s . Type , s . Constraint )
if err != nil {
return err
}
}
SettingBox . Store ( sBox )
return nil
2017-06-14 09:55:47 +00:00
}
2017-09-10 16:57:22 +00:00
// TODO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions.
2020-03-21 07:44:33 +00:00
func ( sBox SettingMap ) ParseSetting ( name , content , typ , constraint string ) ( err error ) {
2022-02-21 03:32:53 +00:00
ssBox := map [ string ] interface { } ( sBox )
switch typ {
case "bool" :
ssBox [ name ] = ( content == "1" )
case "int" :
ssBox [ name ] , err = strconv . Atoi ( content )
if err != nil {
return errors . New ( "You were supposed to enter an integer x.x" )
}
case "int64" :
ssBox [ name ] , err = strconv . ParseInt ( content , 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!" )
}
val , err := strconv . Atoi ( content )
if err != nil {
return errors . New ( "Only integers are allowed in this setting x.x" )
}
if val < con1 || val > con2 {
return errors . New ( "Only integers between a certain range are allowed in this setting" )
}
ssBox [ name ] = val
default :
ssBox [ name ] = content
}
return nil
2017-06-10 07:58:15 +00:00
}
2017-11-13 09:23:43 +00:00
func ( sBox SettingMap ) BypassGet ( name string ) ( * Setting , error ) {
2022-02-21 03:32:53 +00:00
s := & Setting { Name : name }
err := settingStmts . get . QueryRow ( name ) . Scan ( & s . Content , & s . Type , & s . Constraint )
return s , err
2017-11-13 09:23:43 +00:00
}
2017-11-23 05:37:08 +00:00
func ( sBox SettingMap ) BypassGetAll ( ) ( settingList [ ] * Setting , err error ) {
2022-02-21 03:32:53 +00:00
rows , err := settingStmts . getAll . Query ( )
if err != nil {
return nil , err
}
defer rows . Close ( )
for rows . Next ( ) {
s := & Setting { Name : "" }
err := rows . Scan ( & s . Name , & s . Content , & s . Type , & s . Constraint )
if err != nil {
return nil , err
}
settingList = append ( settingList , s )
}
return settingList , rows . Err ( )
2017-11-23 05:37:08 +00:00
}
2020-02-01 06:56:04 +00:00
func ( sBox SettingMap ) Update ( name , content string ) RouteError {
2022-02-21 03:32:53 +00:00
s , 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 s . Type == "bool" {
if content == "on" || content == "1" {
content = "1"
} else {
content = "0"
}
}
err = sBox . ParseSetting ( name , content , s . Type , s . 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
2017-11-23 05:37:08 +00:00
}