Implement guestbook settings #20
| @ -59,13 +59,19 @@ func main() { | |||||||
| 		sessionManager:    sessionManager, | 		sessionManager:    sessionManager, | ||||||
| 		websites:          &models.WebsiteModel{DB: db}, | 		websites:          &models.WebsiteModel{DB: db}, | ||||||
| 		guestbooks:        &models.GuestbookModel{DB: db}, | 		guestbooks:        &models.GuestbookModel{DB: db}, | ||||||
| 		users:             &models.UserModel{DB: db}, | 		users:             &models.UserModel{DB: db, Settings: make(map[string]models.Setting)}, | ||||||
| 		guestbookComments: &models.GuestbookCommentModel{DB: db}, | 		guestbookComments: &models.GuestbookCommentModel{DB: db}, | ||||||
| 		formDecoder:       formDecoder, | 		formDecoder:       formDecoder, | ||||||
| 		debug:             *debug, | 		debug:             *debug, | ||||||
| 		timezones:         getAvailableTimezones(), | 		timezones:         getAvailableTimezones(), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	err = app.users.InitializeSettingsMap() | ||||||
|  | 	if err != nil { | ||||||
|  | 		logger.Error(err.Error()) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	tlsConfig := &tls.Config{ | 	tlsConfig := &tls.Config{ | ||||||
| 		CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256}, | 		CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256}, | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -5,6 +5,14 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type GuestbookSettings struct { | ||||||
|  | 	IsCommentingEnabled   bool | ||||||
|  | 	ReenableCommenting    time.Time | ||||||
|  | 	IsVisible             bool | ||||||
|  | 	FilteredWords         []string | ||||||
|  | 	AllowRemoteHostAccess bool | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type Guestbook struct { | type Guestbook struct { | ||||||
| 	ID        int64 | 	ID        int64 | ||||||
| 	ShortId   uint64 | 	ShortId   uint64 | ||||||
| @ -13,6 +21,7 @@ type Guestbook struct { | |||||||
| 	Created   time.Time | 	Created   time.Time | ||||||
| 	Deleted   time.Time | 	Deleted   time.Time | ||||||
| 	IsActive  bool | 	IsActive  bool | ||||||
|  | 	Settings  GuestbookSettings | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type GuestbookModel struct { | type GuestbookModel struct { | ||||||
| @ -70,3 +79,47 @@ func (m *GuestbookModel) GetAll(userId int64) ([]Guestbook, error) { | |||||||
| 	} | 	} | ||||||
| 	return guestbooks, nil | 	return guestbooks, nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) initializeGuestbookSettings(guestbookId int64, settings UserSettings) error { | ||||||
|  | 	stmt := `INSERT INTO guestbook_settings (GuestbookId, SettingId, AllowedSettingValueId, UnconstrainedValue) VALUES  | ||||||
|  | 	(?, ?, ?, ?), | ||||||
|  | 	(?, ?, ?, ?), | ||||||
|  | 	(?, ?, ?, ?), | ||||||
|  | 	(?, ?, ?, ?), | ||||||
|  | 	(?, ?, ?, ?)` | ||||||
|  | 	_ = len(stmt) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) UpdateSetting(guestbookId int64, value string) { | ||||||
|  | 	stmt := `UPDATE guestbook_settings SET | ||||||
|  | 		AllowedSettingValueId=IFNULL((SELECT Id FROM allowed_setting_values WHERE SettingId = guestbook_settings.SettingId AND ItemValue = ?), AllowedSettingValueId),  | ||||||
|  | 		UnconstrainedValue=(SELECT ? FROM settings WHERE settings.Id = guestbook_settings.SettingId AND settings.Constrained=0) | ||||||
|  | 	WHERE GuestbookId = ? | ||||||
|  | 		AND SettingId = (SELECT Id from Settings WHERE Description=?);` | ||||||
|  | 	_ = len(stmt) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) SetCommentingEnabled(guestbookId int64, enabled bool) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) SetReenableCommentingDate(guestbookId int64, reenableTime time.Time) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) SetVisible(guestbookId int64, visible bool) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) AddFilteredWord(guestbookId int64, word string) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) RemoveFilteredWord(guestbookId int64, word string) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) SetRemoteHostAccess(guestbookId int64, allowed bool) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | |||||||
| @ -7,6 +7,13 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | type SettingModel struct { | ||||||
|  | 	DB *sql.DB | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type SettingConfig struct { | ||||||
|  | } | ||||||
|  | 
 | ||||||
| type SettingGroup int | type SettingGroup int | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| @ -52,7 +59,9 @@ type Setting struct { | |||||||
| 	description      string | 	description      string | ||||||
| 	constrained      bool | 	constrained      bool | ||||||
| 	dataType         SettingDataType | 	dataType         SettingDataType | ||||||
|  | 	dataTypeDesc     string | ||||||
| 	settingGroup     SettingGroup | 	settingGroup     SettingGroup | ||||||
|  | 	settingGroupDesc string | ||||||
| 	minValue         string // TODO: Maybe should be int? | 	minValue         string // TODO: Maybe should be int? | ||||||
| 	maxValue         string | 	maxValue         string | ||||||
| 	allowedValues    map[int]AllowedSettingValue | 	allowedValues    map[int]AllowedSettingValue | ||||||
| @ -173,6 +182,18 @@ func (s *Setting) validateAlphanum(value string) bool { | |||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (s Setting) Validate(value string) bool { | ||||||
|  | 	switch s.dataType { | ||||||
|  | 	case SettingTypeInt: | ||||||
|  | 		return s.validateInt(value) | ||||||
|  | 	case SettingTypeAlphanum: | ||||||
|  | 		return s.validateAlphanum(value) | ||||||
|  | 	case SettingTypeDate: | ||||||
|  | 		return s.validateDatetime(value) | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func validateSetting(db *sql.DB, settingId int, value string) (bool, error) { | func validateSetting(db *sql.DB, settingId int, value string) (bool, error) { | ||||||
| 	stmt := `SELECT s.Id, Description, Constrained, DataType, SettingGroup, MinValue, MaxValue, a.Id  | 	stmt := `SELECT s.Id, Description, Constrained, DataType, SettingGroup, MinValue, MaxValue, a.Id  | ||||||
| 	FROM settings AS s LEFT JOIN allowed_setting_values AS a ON s.Id = a.SettingId AND a.ItemValue = ? | 	FROM settings AS s LEFT JOIN allowed_setting_values AS a ON s.Id = a.SettingId AND a.ItemValue = ? | ||||||
|  | |||||||
| @ -10,14 +10,14 @@ import ( | |||||||
| 	"golang.org/x/crypto/bcrypt" | 	"golang.org/x/crypto/bcrypt" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( |  | ||||||
| 	u_timezone = 1 |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| type UserSettings struct { | type UserSettings struct { | ||||||
| 	LocalTimezone *time.Location | 	LocalTimezone *time.Location | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | const ( | ||||||
|  | 	USER_TIMEZONE = "local_timezone" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| type User struct { | type User struct { | ||||||
| 	ID             int64 | 	ID             int64 | ||||||
| 	ShortId        uint64 | 	ShortId        uint64 | ||||||
| @ -35,6 +35,38 @@ type UserModel struct { | |||||||
| 	Settings map[string]Setting | 	Settings map[string]Setting | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (m *UserModel) InitializeSettingsMap() error { | ||||||
|  | 	if m.Settings == nil { | ||||||
|  | 		m.Settings = make(map[string]Setting) | ||||||
|  | 	} | ||||||
|  | 	stmt := `SELECT settings.Id, settings.Description, Constrained, d.Id, d.Description, g.Id, g.Description, MinValue, MaxValue | ||||||
|  |         FROM settings | ||||||
|  |         LEFT JOIN setting_data_types d ON settings.DataType = d.Id | ||||||
|  |         LEFT JOIN setting_groups g ON settings.SettingGroup = g.Id | ||||||
|  |         WHERE SettingGroup = (SELECT Id FROM setting_groups WHERE Description = 'user' LIMIT 1)` | ||||||
|  | 	result, err := m.DB.Query(stmt) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	for result.Next() { | ||||||
|  | 		var s Setting | ||||||
|  | 		var mn sql.NullString | ||||||
|  | 		var mx sql.NullString | ||||||
|  | 		err := result.Scan(&s.id, &s.description, &s.constrained, &s.dataType, &s.dataTypeDesc, &s.settingGroup, &s.settingGroupDesc, &mn, &mx) | ||||||
|  | 		if mn.Valid { | ||||||
|  | 			s.minValue = mn.String | ||||||
|  | 		} | ||||||
|  | 		if mx.Valid { | ||||||
|  | 			s.maxValue = mx.String | ||||||
|  | 		} | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		m.Settings[s.description] = s | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (m *UserModel) Insert(shortId uint64, username string, email string, password string, settings UserSettings) error { | func (m *UserModel) Insert(shortId uint64, username string, email string, password string, settings UserSettings) error { | ||||||
| 	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 12) | 	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 12) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @ -169,7 +201,7 @@ func (m *UserModel) GetSettings(userId int64) (UserSettings, error) { | |||||||
| 			return settings, err | 			return settings, err | ||||||
| 		} | 		} | ||||||
| 		switch id { | 		switch id { | ||||||
| 		case u_timezone: | 		case m.Settings[USER_TIMEZONE].id: | ||||||
| 			settings.LocalTimezone, err = time.LoadLocation(unconstrainedValue.String) | 			settings.LocalTimezone, err = time.LoadLocation(unconstrainedValue.String) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				panic(err) | 				panic(err) | ||||||
| @ -180,8 +212,9 @@ func (m *UserModel) GetSettings(userId int64) (UserSettings, error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *UserModel) initializeUserSettings(userId int64, settings UserSettings) error { | func (m *UserModel) initializeUserSettings(userId int64, settings UserSettings) error { | ||||||
| 	stmt := `INSERT INTO user_settings (UserId, SettingId, AllowedSettingValueId, UnconstrainedValue) VALUES (?, ?, ?, ?)` | 	stmt := `INSERT INTO user_settings (UserId, SettingId, AllowedSettingValueId, UnconstrainedValue)  | ||||||
| 	_, err := m.DB.Exec(stmt, userId, u_timezone, nil, settings.LocalTimezone.String()) | 		VALUES (?, ?, ?, ?)` | ||||||
|  | 	_, err := m.DB.Exec(stmt, userId, m.Settings[USER_TIMEZONE].id, nil, settings.LocalTimezone.String()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @ -189,7 +222,7 @@ func (m *UserModel) initializeUserSettings(userId int64, settings UserSettings) | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *UserModel) UpdateUserSettings(userId int64, settings UserSettings) error { | func (m *UserModel) UpdateUserSettings(userId int64, settings UserSettings) error { | ||||||
| 	err := m.UpdateSetting(userId, m.Settings["local_timezone"], settings.LocalTimezone.String()) | 	err := m.UpdateSetting(userId, m.Settings[USER_TIMEZONE], settings.LocalTimezone.String()) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @ -198,11 +231,13 @@ func (m *UserModel) UpdateUserSettings(userId int64, settings UserSettings) erro | |||||||
| 
 | 
 | ||||||
| func (m *UserModel) UpdateSetting(userId int64, setting Setting, value string) error { | func (m *UserModel) UpdateSetting(userId int64, setting Setting, value string) error { | ||||||
| 	stmt := `UPDATE user_settings SET | 	stmt := `UPDATE user_settings SET | ||||||
| 				AllowedSettingValueId=(SELECT Id FROM allowed_setting_values WHERE SettingId = user_settings.SettingId AND ItemValue = ?), | 				AllowedSettingValueId=IFNULL( | ||||||
|  | 					(SELECT Id FROM allowed_setting_values WHERE SettingId = user_settings.SettingId AND ItemValue = ?), AllowedSettingValueId | ||||||
|  | 				), | ||||||
| 				UnconstrainedValue=(SELECT ? FROM settings WHERE settings.Id = user_settings.SettingId AND settings.Constrained=0) | 				UnconstrainedValue=(SELECT ? FROM settings WHERE settings.Id = user_settings.SettingId AND settings.Constrained=0) | ||||||
| 			WHERE userId = ? | 			WHERE userId = ? | ||||||
| 			AND SettingId = (SELECT Id from Settings WHERE Description=?);` | 			AND SettingId = (SELECT Id from Settings WHERE Description=?);` | ||||||
| 	result, err := m.DB.Exec(stmt, value, value, userId) | 	result, err := m.DB.Exec(stmt, value, value, userId, setting.description) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @ -211,21 +246,18 @@ func (m *UserModel) UpdateSetting(userId int64, setting Setting, value string) e | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if rows != 1 { | 	if rows != 1 { | ||||||
| 		return err | 		return ErrInvalidSettingValue | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *UserModel) SetLocalTimezone(userId int64, timezone string) error { | func (m *UserModel) SetLocalTimezone(userId int64, timezone string) error { | ||||||
| 	valid, err := validateSetting(m.DB, u_timezone, timezone) | 	setting := m.Settings[USER_TIMEZONE] | ||||||
| 	if err != nil { | 	valid := setting.Validate(timezone) | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	if !valid { | 	if !valid { | ||||||
| 		return ErrInvalidSettingValue | 		return ErrInvalidSettingValue | ||||||
| 	} | 	} | ||||||
| 	stmt := `UPDATE user_settings SET UnconstrainedValue = ? WHERE UserId = ?` | 	err := m.UpdateSetting(userId, setting, timezone) | ||||||
| 	_, err = m.DB.Exec(stmt, timezone, userId) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| // Code generated by templ - DO NOT EDIT. | // Code generated by templ - DO NOT EDIT. | ||||||
| 
 | 
 | ||||||
| // templ: version: v0.3.857 | // templ: version: v0.3.833 | ||||||
| package views | package views | ||||||
| 
 | 
 | ||||||
| //lint:file-ignore SA4006 This context is only used if a nested component is present. | //lint:file-ignore SA4006 This context is only used if a nested component is present. | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| // Code generated by templ - DO NOT EDIT. | // Code generated by templ - DO NOT EDIT. | ||||||
| 
 | 
 | ||||||
| // templ: version: v0.3.857 | // templ: version: v0.3.833 | ||||||
| package views | package views | ||||||
| 
 | 
 | ||||||
| //lint:file-ignore SA4006 This context is only used if a nested component is present. | //lint:file-ignore SA4006 This context is only used if a nested component is present. | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| // Code generated by templ - DO NOT EDIT. | // Code generated by templ - DO NOT EDIT. | ||||||
| 
 | 
 | ||||||
| // templ: version: v0.3.857 | // templ: version: v0.3.833 | ||||||
| package views | package views | ||||||
| 
 | 
 | ||||||
| //lint:file-ignore SA4006 This context is only used if a nested component is present. | //lint:file-ignore SA4006 This context is only used if a nested component is present. | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| // Code generated by templ - DO NOT EDIT. | // Code generated by templ - DO NOT EDIT. | ||||||
| 
 | 
 | ||||||
| // templ: version: v0.3.857 | // templ: version: v0.3.833 | ||||||
| package views | package views | ||||||
| 
 | 
 | ||||||
| //lint:file-ignore SA4006 This context is only used if a nested component is present. | //lint:file-ignore SA4006 This context is only used if a nested component is present. | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| // Code generated by templ - DO NOT EDIT. | // Code generated by templ - DO NOT EDIT. | ||||||
| 
 | 
 | ||||||
| // templ: version: v0.3.857 | // templ: version: v0.3.833 | ||||||
| package views | package views | ||||||
| 
 | 
 | ||||||
| //lint:file-ignore SA4006 This context is only used if a nested component is present. | //lint:file-ignore SA4006 This context is only used if a nested component is present. | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| // Code generated by templ - DO NOT EDIT. | // Code generated by templ - DO NOT EDIT. | ||||||
| 
 | 
 | ||||||
| // templ: version: v0.3.857 | // templ: version: v0.3.833 | ||||||
| package views | package views | ||||||
| 
 | 
 | ||||||
| //lint:file-ignore SA4006 This context is only used if a nested component is present. | //lint:file-ignore SA4006 This context is only used if a nested component is present. | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user