github.com/gigforks/mattermost-server@v4.9.1-0.20180619094218-800d97fa55d0+incompatible/model/preference.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package model 5 6 import ( 7 "encoding/json" 8 "io" 9 "net/http" 10 "regexp" 11 "strings" 12 "unicode/utf8" 13 ) 14 15 const ( 16 PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show" 17 PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step" 18 PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings" 19 PREFERENCE_CATEGORY_FLAGGED_POST = "flagged_post" 20 PREFERENCE_CATEGORY_FAVORITE_CHANNEL = "favorite_channel" 21 22 PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings" 23 PREFERENCE_NAME_COLLAPSE_SETTING = "collapse_previews" 24 25 PREFERENCE_CATEGORY_THEME = "theme" 26 // the name for theme props is the team id 27 28 PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP = "oauth_app" 29 // the name for oauth_app is the client_id and value is the current scope 30 31 PREFERENCE_CATEGORY_LAST = "last" 32 PREFERENCE_NAME_LAST_CHANNEL = "channel" 33 PREFERENCE_NAME_LAST_TEAM = "team" 34 35 PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications" 36 PREFERENCE_NAME_EMAIL_INTERVAL = "email_interval" 37 38 PREFERENCE_EMAIL_INTERVAL_NO_BATCHING_SECONDS = "30" // the "immediate" setting is actually 30s 39 PREFERENCE_EMAIL_INTERVAL_BATCHING_SECONDS = "900" // fifteen minutes is 900 seconds 40 ) 41 42 type Preference struct { 43 UserId string `json:"user_id"` 44 Category string `json:"category"` 45 Name string `json:"name"` 46 Value string `json:"value"` 47 } 48 49 func (o *Preference) ToJson() string { 50 b, _ := json.Marshal(o) 51 return string(b) 52 } 53 54 func PreferenceFromJson(data io.Reader) *Preference { 55 var o *Preference 56 json.NewDecoder(data).Decode(&o) 57 return o 58 } 59 60 func (o *Preference) IsValid() *AppError { 61 if len(o.UserId) != 26 { 62 return NewAppError("Preference.IsValid", "model.preference.is_valid.id.app_error", nil, "user_id="+o.UserId, http.StatusBadRequest) 63 } 64 65 if len(o.Category) == 0 || len(o.Category) > 32 { 66 return NewAppError("Preference.IsValid", "model.preference.is_valid.category.app_error", nil, "category="+o.Category, http.StatusBadRequest) 67 } 68 69 if len(o.Name) > 32 { 70 return NewAppError("Preference.IsValid", "model.preference.is_valid.name.app_error", nil, "name="+o.Name, http.StatusBadRequest) 71 } 72 73 if utf8.RuneCountInString(o.Value) > 2000 { 74 return NewAppError("Preference.IsValid", "model.preference.is_valid.value.app_error", nil, "value="+o.Value, http.StatusBadRequest) 75 } 76 77 if o.Category == PREFERENCE_CATEGORY_THEME { 78 var unused map[string]string 79 if err := json.NewDecoder(strings.NewReader(o.Value)).Decode(&unused); err != nil { 80 return NewAppError("Preference.IsValid", "model.preference.is_valid.theme.app_error", nil, "value="+o.Value, http.StatusBadRequest) 81 } 82 } 83 84 return nil 85 } 86 87 func (o *Preference) PreUpdate() { 88 if o.Category == PREFERENCE_CATEGORY_THEME { 89 // decode the value of theme (a map of strings to string) and eliminate any invalid values 90 var props map[string]string 91 if err := json.NewDecoder(strings.NewReader(o.Value)).Decode(&props); err != nil { 92 // just continue, the invalid preference value should get caught by IsValid before saving 93 return 94 } 95 96 colorPattern := regexp.MustCompile(`^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$`) 97 98 // blank out any invalid theme values 99 for name, value := range props { 100 if name == "image" || name == "type" || name == "codeTheme" { 101 continue 102 } 103 104 if !colorPattern.MatchString(value) { 105 props[name] = "#ffffff" 106 } 107 } 108 109 if b, err := json.Marshal(props); err == nil { 110 o.Value = string(b) 111 } 112 } 113 }