Files
MyClub/internal/controllers/page_element_style_validator.go
T
Tomas Dvorak b9cea0cd77 dev day #79
2025-11-02 01:04:02 +01:00

125 lines
3.5 KiB
Go

package controllers
import (
"encoding/json"
"fmt"
)
// StyleValidator validates and normalizes style objects before saving
type StyleValidator struct{}
// ValidateAndNormalizeStyles ensures style data is properly structured
func (sv *StyleValidator) ValidateAndNormalizeStyles(settings map[string]interface{}) error {
if settings == nil {
return nil
}
// If styles exist as a top-level key, ensure it's a valid object
if stylesRaw, exists := settings["styles"]; exists {
switch styles := stylesRaw.(type) {
case map[string]interface{}:
// Valid object - ensure all values are valid CSS values
for key, val := range styles {
if !sv.isValidCSSValue(val) {
return fmt.Errorf("invalid CSS value for property '%s': %v", key, val)
}
}
case string:
// Try to parse as JSON
var styleMap map[string]interface{}
if err := json.Unmarshal([]byte(styles), &styleMap); err != nil {
return fmt.Errorf("styles must be a valid JSON object or map")
}
// Replace with parsed object
settings["styles"] = styleMap
default:
return fmt.Errorf("styles must be an object, got %T", styles)
}
}
// Validate customCSS if present
if customCSS, exists := settings["customCSS"]; exists {
if _, ok := customCSS.(string); !ok {
return fmt.Errorf("customCSS must be a string, got %T", customCSS)
}
}
return nil
}
// isValidCSSValue checks if a value is a valid CSS value type
func (sv *StyleValidator) isValidCSSValue(val interface{}) bool {
switch val.(type) {
case string, int, int64, float64, bool:
return true
default:
return false
}
}
// MergeStyles merges new styles into existing settings preserving other fields
func (sv *StyleValidator) MergeStyles(existingSettings map[string]interface{}, newStyles map[string]interface{}) map[string]interface{} {
if existingSettings == nil {
existingSettings = make(map[string]interface{})
}
// If newStyles contains a "styles" key, merge it
if newStylesMap, exists := newStyles["styles"]; exists {
if existingStylesRaw, exists := existingSettings["styles"]; exists {
// Merge with existing styles
if existingStyles, ok := existingStylesRaw.(map[string]interface{}); ok {
if newStylesObj, ok := newStylesMap.(map[string]interface{}); ok {
// Merge newStylesObj into existingStyles
for k, v := range newStylesObj {
existingStyles[k] = v
}
existingSettings["styles"] = existingStyles
} else {
// Replace entirely if type mismatch
existingSettings["styles"] = newStylesMap
}
} else {
// Replace if existing was not a map
existingSettings["styles"] = newStylesMap
}
} else {
// No existing styles - set directly
existingSettings["styles"] = newStyles["styles"]
}
}
// Merge other top-level keys
for k, v := range newStyles {
if k != "styles" {
existingSettings[k] = v
}
}
return existingSettings
}
// ExtractStylesForSave prepares settings object for database save
// Ensures styles are properly nested under settings.styles
func (sv *StyleValidator) ExtractStylesForSave(input map[string]interface{}) map[string]interface{} {
settings := make(map[string]interface{})
// Copy all non-style fields
for k, v := range input {
if k != "styles" && k != "customCSS" {
settings[k] = v
}
}
// Nest styles under styles key
if stylesRaw, exists := input["styles"]; exists {
settings["styles"] = stylesRaw
}
// Also nest customCSS if present
if customCSS, exists := input["customCSS"]; exists {
settings["customCSS"] = customCSS
}
return settings
}