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 (
"database/sql"
2017-11-23 05:37:08 +00:00
"errors"
2017-11-11 23:34:27 +00:00
"strconv"
"strings"
"sync/atomic"
2018-10-27 03:21:02 +00:00
"github.com/Azareal/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 {
ParseSetting ( sname string , scontent string , stype string , sconstraint string ) string
BypassGet ( name string ) ( * Setting , error )
2017-11-23 05:37:08 +00:00
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 {
Label string
Value int
2016-12-21 02:30:32 +00:00
Selected bool
}
2016-12-09 13:46:29 +00:00
2017-09-03 04:50:31 +00:00
type Setting struct {
Name string
Content string
Type string
2016-12-21 02:30:32 +00:00
Constraint string
2016-12-09 13:46:29 +00:00
}
2017-11-11 23:34:27 +00:00
type SettingStmts struct {
2017-11-13 09:23:43 +00:00
getAll * sql . Stmt
get * sql . Stmt
2017-11-23 05:37:08 +00:00
update * sql . Stmt
2017-11-11 23:34:27 +00:00
}
var settingStmts SettingStmts
2016-12-21 02:30:32 +00:00
func init ( ) {
2017-11-10 03:33:11 +00:00
SettingBox . Store ( SettingMap ( make ( map [ string ] interface { } ) ) )
2017-11-12 03:29:05 +00:00
DbInits . Add ( func ( acc * qgen . Accumulator ) error {
2019-10-27 23:13:24 +00:00
s := "settings"
2017-11-11 23:34:27 +00:00
settingStmts = SettingStmts {
2019-10-27 23:13:24 +00:00
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 ( ) ,
2017-11-11 23:34:27 +00:00
}
return acc . FirstError ( )
} )
2016-12-21 02:30:32 +00:00
}
2019-10-06 00:34:09 +00:00
func ( s * Setting ) Copy ( ) ( out * Setting ) {
2018-05-27 09:36:35 +00:00
out = & Setting { Name : "" }
2019-10-06 00:34:09 +00:00
* out = * s
2018-05-27 09:36:35 +00:00
return out
}
2017-06-14 09:55:47 +00:00
func LoadSettings ( ) error {
2019-10-06 00:34:09 +00:00
sBox := SettingMap ( make ( map [ string ] interface { } ) )
2017-11-23 05:37:08 +00:00
settings , err := sBox . BypassGetAll ( )
2017-06-14 09:55:47 +00:00
if err != nil {
return err
}
2019-10-27 23:13:24 +00:00
for _ , s := range settings {
err = sBox . ParseSetting ( s . Name , s . Content , s . Type , s . Constraint )
2017-06-14 09:55:47 +00:00
if err != nil {
return err
}
}
2017-08-20 09:39:02 +00:00
2017-11-10 03:33:11 +00:00
SettingBox . Store ( sBox )
2017-06-14 09:55:47 +00:00
return nil
}
2017-09-10 16:57:22 +00:00
// TODO: Add better support for HTML attributes (html-attribute). E.g. Meta descriptions.
2017-11-23 05:37:08 +00:00
func ( sBox SettingMap ) ParseSetting ( sname string , scontent string , stype string , constraint string ) ( err error ) {
2019-10-27 23:13:24 +00:00
ssBox := map [ string ] interface { } ( sBox )
2017-11-23 05:37:08 +00:00
switch stype {
case "bool" :
2017-08-20 09:39:02 +00:00
ssBox [ sname ] = ( scontent == "1" )
2017-11-23 05:37:08 +00:00
case "int" :
2017-08-20 09:39:02 +00:00
ssBox [ sname ] , err = strconv . Atoi ( scontent )
2016-12-09 13:46:29 +00:00
if err != nil {
2018-11-13 06:51:34 +00:00
return errors . New ( "You were supposed to enter an integer x.x" )
2016-12-09 13:46:29 +00:00
}
2017-11-23 05:37:08 +00:00
case "int64" :
2017-08-20 09:39:02 +00:00
ssBox [ sname ] , err = strconv . ParseInt ( scontent , 10 , 64 )
2016-12-09 13:46:29 +00:00
if err != nil {
2018-11-13 06:51:34 +00:00
return errors . New ( "You were supposed to enter an integer x.x" )
2016-12-09 13:46:29 +00:00
}
2017-11-23 05:37:08 +00:00
case "list" :
2017-09-03 04:50:31 +00:00
cons := strings . Split ( constraint , "-" )
2016-12-21 02:30:32 +00:00
if len ( cons ) < 2 {
2017-11-23 05:37:08 +00:00
return errors . New ( "Invalid constraint! The second field wasn't set!" )
2016-12-21 02:30:32 +00:00
}
2017-06-10 07:58:15 +00:00
2016-12-21 02:30:32 +00:00
con1 , err := strconv . Atoi ( cons [ 0 ] )
2017-11-08 07:28:33 +00:00
con2 , err2 := strconv . Atoi ( cons [ 1 ] )
if err != nil || err2 != nil {
2018-11-13 06:51:34 +00:00
return errors . New ( "Invalid contraint! The constraint field wasn't an integer!" )
2016-12-21 02:30:32 +00:00
}
2017-06-10 07:58:15 +00:00
2017-09-03 04:50:31 +00:00
value , err := strconv . Atoi ( scontent )
2016-12-21 02:30:32 +00:00
if err != nil {
2018-11-13 06:51:34 +00:00
return errors . New ( "Only integers are allowed in this setting x.x" )
2016-12-21 02:30:32 +00:00
}
2017-06-10 07:58:15 +00:00
2016-12-21 02:30:32 +00:00
if value < con1 || value > con2 {
2018-11-13 06:51:34 +00:00
return errors . New ( "Only integers between a certain range are allowed in this setting" )
2016-12-21 02:30:32 +00:00
}
2017-08-20 09:39:02 +00:00
ssBox [ sname ] = value
2017-11-23 05:37:08 +00:00
default :
2017-08-20 09:39:02 +00:00
ssBox [ sname ] = scontent
2016-12-09 13:46:29 +00:00
}
2017-11-23 05:37:08 +00:00
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 ) {
2019-10-06 00:34:09 +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 ) {
rows , err := settingStmts . getAll . Query ( )
if err != nil {
return nil , err
}
defer rows . Close ( )
for rows . Next ( ) {
2019-10-06 00:34:09 +00:00
s := & Setting { Name : "" }
err := rows . Scan ( & s . Name , & s . Content , & s . Type , & s . Constraint )
2017-11-23 05:37:08 +00:00
if err != nil {
return nil , err
}
2019-10-06 00:34:09 +00:00
settingList = append ( settingList , s )
2017-11-23 05:37:08 +00:00
}
return settingList , rows . Err ( )
}
2018-11-13 06:51:34 +00:00
func ( sBox SettingMap ) Update ( name string , content string ) RouteError {
2019-10-06 00:34:09 +00:00
s , err := sBox . BypassGet ( name )
2017-11-23 05:37:08 +00:00
if err == ErrNoRows {
2018-11-13 06:51:34 +00:00
return FromError ( err )
} else if err != nil {
return SysError ( err . Error ( ) )
2017-11-23 05:37:08 +00:00
}
// TODO: Why is this here and not in a common function?
2019-10-06 00:34:09 +00:00
if s . Type == "bool" {
2017-11-23 05:37:08 +00:00
if content == "on" || content == "1" {
content = "1"
} else {
content = "0"
}
}
2019-10-06 00:34:09 +00:00
err = sBox . ParseSetting ( name , content , s . Type , s . Constraint )
2018-11-13 06:51:34 +00:00
if err != nil {
return FromError ( err )
}
2017-11-23 05:37:08 +00:00
// TODO: Make this a method or function?
_ , err = settingStmts . update . Exec ( content , name )
if err != nil {
2018-11-13 06:51:34 +00:00
return SysError ( err . Error ( ) )
2017-11-23 05:37:08 +00:00
}
2018-11-13 06:51:34 +00:00
err = LoadSettings ( )
2017-11-23 05:37:08 +00:00
if err != nil {
2018-11-13 06:51:34 +00:00
return SysError ( err . Error ( ) )
2017-11-23 05:37:08 +00:00
}
return nil
}