Implement guestbook settings #20
| @ -59,13 +59,19 @@ func main() { | ||||
| 		sessionManager:    sessionManager, | ||||
| 		websites:          &models.WebsiteModel{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}, | ||||
| 		formDecoder:       formDecoder, | ||||
| 		debug:             *debug, | ||||
| 		timezones:         getAvailableTimezones(), | ||||
| 	} | ||||
| 
 | ||||
| 	err = app.users.InitializeSettingsMap() | ||||
| 	if err != nil { | ||||
| 		logger.Error(err.Error()) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 
 | ||||
| 	tlsConfig := &tls.Config{ | ||||
| 		CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256}, | ||||
| 	} | ||||
|  | ||||
| @ -5,6 +5,14 @@ import ( | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type GuestbookSettings struct { | ||||
| 	IsCommentingEnabled   bool | ||||
| 	ReenableCommenting    time.Time | ||||
| 	IsVisible             bool | ||||
| 	FilteredWords         []string | ||||
| 	AllowRemoteHostAccess bool | ||||
| } | ||||
| 
 | ||||
| type Guestbook struct { | ||||
| 	ID        int64 | ||||
| 	ShortId   uint64 | ||||
| @ -13,6 +21,7 @@ type Guestbook struct { | ||||
| 	Created   time.Time | ||||
| 	Deleted   time.Time | ||||
| 	IsActive  bool | ||||
| 	Settings  GuestbookSettings | ||||
| } | ||||
| 
 | ||||
| type GuestbookModel struct { | ||||
| @ -70,3 +79,47 @@ func (m *GuestbookModel) GetAll(userId int64) ([]Guestbook, error) { | ||||
| 	} | ||||
| 	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" | ||||
| ) | ||||
| 
 | ||||
| type SettingModel struct { | ||||
| 	DB *sql.DB | ||||
| } | ||||
| 
 | ||||
| type SettingConfig struct { | ||||
| } | ||||
| 
 | ||||
| type SettingGroup int | ||||
| 
 | ||||
| const ( | ||||
| @ -48,14 +55,16 @@ func (s *AllowedSettingValue) Caption() string { | ||||
| } | ||||
| 
 | ||||
| type Setting struct { | ||||
| 	id            int | ||||
| 	description   string | ||||
| 	constrained   bool | ||||
| 	dataType      SettingDataType | ||||
| 	settingGroup  SettingGroup | ||||
| 	minValue      string // TODO: Maybe should be int? | ||||
| 	maxValue      string | ||||
| 	allowedValues map[int]AllowedSettingValue | ||||
| 	id               int | ||||
| 	description      string | ||||
| 	constrained      bool | ||||
| 	dataType         SettingDataType | ||||
| 	dataTypeDesc     string | ||||
| 	settingGroup     SettingGroup | ||||
| 	settingGroupDesc string | ||||
| 	minValue         string // TODO: Maybe should be int? | ||||
| 	maxValue         string | ||||
| 	allowedValues    map[int]AllowedSettingValue | ||||
| } | ||||
| 
 | ||||
| func (s *Setting) Id() int { | ||||
| @ -173,6 +182,18 @@ func (s *Setting) validateAlphanum(value string) bool { | ||||
| 	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) { | ||||
| 	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 = ? | ||||
|  | ||||
| @ -10,14 +10,14 @@ import ( | ||||
| 	"golang.org/x/crypto/bcrypt" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	u_timezone = 1 | ||||
| ) | ||||
| 
 | ||||
| type UserSettings struct { | ||||
| 	LocalTimezone *time.Location | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	USER_TIMEZONE = "local_timezone" | ||||
| ) | ||||
| 
 | ||||
| type User struct { | ||||
| 	ID             int64 | ||||
| 	ShortId        uint64 | ||||
| @ -35,6 +35,38 @@ type UserModel struct { | ||||
| 	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 { | ||||
| 	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 12) | ||||
| 	if err != nil { | ||||
| @ -169,7 +201,7 @@ func (m *UserModel) GetSettings(userId int64) (UserSettings, error) { | ||||
| 			return settings, err | ||||
| 		} | ||||
| 		switch id { | ||||
| 		case u_timezone: | ||||
| 		case m.Settings[USER_TIMEZONE].id: | ||||
| 			settings.LocalTimezone, err = time.LoadLocation(unconstrainedValue.String) | ||||
| 			if err != nil { | ||||
| 				panic(err) | ||||
| @ -180,8 +212,9 @@ func (m *UserModel) GetSettings(userId int64) (UserSettings, error) { | ||||
| } | ||||
| 
 | ||||
| func (m *UserModel) initializeUserSettings(userId int64, settings UserSettings) error { | ||||
| 	stmt := `INSERT INTO user_settings (UserId, SettingId, AllowedSettingValueId, UnconstrainedValue) VALUES (?, ?, ?, ?)` | ||||
| 	_, err := m.DB.Exec(stmt, userId, u_timezone, nil, settings.LocalTimezone.String()) | ||||
| 	stmt := `INSERT INTO user_settings (UserId, SettingId, AllowedSettingValueId, UnconstrainedValue)  | ||||
| 		VALUES (?, ?, ?, ?)` | ||||
| 	_, err := m.DB.Exec(stmt, userId, m.Settings[USER_TIMEZONE].id, nil, settings.LocalTimezone.String()) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @ -189,7 +222,7 @@ func (m *UserModel) initializeUserSettings(userId int64, settings UserSettings) | ||||
| } | ||||
| 
 | ||||
| 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 { | ||||
| 		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 { | ||||
| 	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) | ||||
| 			WHERE userId = ? | ||||
| 			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 { | ||||
| 		return err | ||||
| 	} | ||||
| @ -211,21 +246,18 @@ func (m *UserModel) UpdateSetting(userId int64, setting Setting, value string) e | ||||
| 		return err | ||||
| 	} | ||||
| 	if rows != 1 { | ||||
| 		return err | ||||
| 		return ErrInvalidSettingValue | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (m *UserModel) SetLocalTimezone(userId int64, timezone string) error { | ||||
| 	valid, err := validateSetting(m.DB, u_timezone, timezone) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	setting := m.Settings[USER_TIMEZONE] | ||||
| 	valid := setting.Validate(timezone) | ||||
| 	if !valid { | ||||
| 		return ErrInvalidSettingValue | ||||
| 	} | ||||
| 	stmt := `UPDATE user_settings SET UnconstrainedValue = ? WHERE UserId = ?` | ||||
| 	_, err = m.DB.Exec(stmt, timezone, userId) | ||||
| 	err := m.UpdateSetting(userId, setting, timezone) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| // Code generated by templ - DO NOT EDIT. | ||||
| 
 | ||||
| // templ: version: v0.3.857 | ||||
| // templ: version: v0.3.833 | ||||
| package views | ||||
| 
 | ||||
| //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. | ||||
| 
 | ||||
| // templ: version: v0.3.857 | ||||
| // templ: version: v0.3.833 | ||||
| package views | ||||
| 
 | ||||
| //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. | ||||
| 
 | ||||
| // templ: version: v0.3.857 | ||||
| // templ: version: v0.3.833 | ||||
| package views | ||||
| 
 | ||||
| //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. | ||||
| 
 | ||||
| // templ: version: v0.3.857 | ||||
| // templ: version: v0.3.833 | ||||
| package views | ||||
| 
 | ||||
| //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. | ||||
| 
 | ||||
| // templ: version: v0.3.857 | ||||
| // templ: version: v0.3.833 | ||||
| package views | ||||
| 
 | ||||
| //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. | ||||
| 
 | ||||
| // templ: version: v0.3.857 | ||||
| // templ: version: v0.3.833 | ||||
| package views | ||||
| 
 | ||||
| //lint:file-ignore SA4006 This context is only used if a nested component is present. | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user