github.com/crspeller/mattermost-server@v0.0.0-20190328001957-a200beb3d111/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  	PREFERENCE_CATEGORY_SIDEBAR_SETTINGS    = "sidebar_settings"
    22  
    23  	PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings"
    24  	PREFERENCE_NAME_CHANNEL_DISPLAY_MODE = "channel_display_mode"
    25  	PREFERENCE_NAME_COLLAPSE_SETTING     = "collapse_previews"
    26  	PREFERENCE_NAME_MESSAGE_DISPLAY      = "message_display"
    27  	PREFERENCE_NAME_NAME_FORMAT          = "name_format"
    28  	PREFERENCE_NAME_USE_MILITARY_TIME    = "use_military_time"
    29  
    30  	PREFERENCE_CATEGORY_THEME = "theme"
    31  	// the name for theme props is the team id
    32  
    33  	PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP = "oauth_app"
    34  	// the name for oauth_app is the client_id and value is the current scope
    35  
    36  	PREFERENCE_CATEGORY_LAST     = "last"
    37  	PREFERENCE_NAME_LAST_CHANNEL = "channel"
    38  	PREFERENCE_NAME_LAST_TEAM    = "team"
    39  
    40  	PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications"
    41  	PREFERENCE_NAME_EMAIL_INTERVAL    = "email_interval"
    42  
    43  	PREFERENCE_EMAIL_INTERVAL_NO_BATCHING_SECONDS = "30"  // the "immediate" setting is actually 30s
    44  	PREFERENCE_EMAIL_INTERVAL_BATCHING_SECONDS    = "900" // fifteen minutes is 900 seconds
    45  	PREFERENCE_EMAIL_INTERVAL_IMMEDIATELY         = "immediately"
    46  	PREFERENCE_EMAIL_INTERVAL_FIFTEEN             = "fifteen"
    47  	PREFERENCE_EMAIL_INTERVAL_FIFTEEN_AS_SECONDS  = "900"
    48  	PREFERENCE_EMAIL_INTERVAL_HOUR                = "hour"
    49  	PREFERENCE_EMAIL_INTERVAL_HOUR_AS_SECONDS     = "3600"
    50  )
    51  
    52  type Preference struct {
    53  	UserId   string `json:"user_id"`
    54  	Category string `json:"category"`
    55  	Name     string `json:"name"`
    56  	Value    string `json:"value"`
    57  }
    58  
    59  func (o *Preference) ToJson() string {
    60  	b, _ := json.Marshal(o)
    61  	return string(b)
    62  }
    63  
    64  func PreferenceFromJson(data io.Reader) *Preference {
    65  	var o *Preference
    66  	json.NewDecoder(data).Decode(&o)
    67  	return o
    68  }
    69  
    70  func (o *Preference) IsValid() *AppError {
    71  	if len(o.UserId) != 26 {
    72  		return NewAppError("Preference.IsValid", "model.preference.is_valid.id.app_error", nil, "user_id="+o.UserId, http.StatusBadRequest)
    73  	}
    74  
    75  	if len(o.Category) == 0 || len(o.Category) > 32 {
    76  		return NewAppError("Preference.IsValid", "model.preference.is_valid.category.app_error", nil, "category="+o.Category, http.StatusBadRequest)
    77  	}
    78  
    79  	if len(o.Name) > 32 {
    80  		return NewAppError("Preference.IsValid", "model.preference.is_valid.name.app_error", nil, "name="+o.Name, http.StatusBadRequest)
    81  	}
    82  
    83  	if utf8.RuneCountInString(o.Value) > 2000 {
    84  		return NewAppError("Preference.IsValid", "model.preference.is_valid.value.app_error", nil, "value="+o.Value, http.StatusBadRequest)
    85  	}
    86  
    87  	if o.Category == PREFERENCE_CATEGORY_THEME {
    88  		var unused map[string]string
    89  		if err := json.NewDecoder(strings.NewReader(o.Value)).Decode(&unused); err != nil {
    90  			return NewAppError("Preference.IsValid", "model.preference.is_valid.theme.app_error", nil, "value="+o.Value, http.StatusBadRequest)
    91  		}
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  func (o *Preference) PreUpdate() {
    98  	if o.Category == PREFERENCE_CATEGORY_THEME {
    99  		// decode the value of theme (a map of strings to string) and eliminate any invalid values
   100  		var props map[string]string
   101  		if err := json.NewDecoder(strings.NewReader(o.Value)).Decode(&props); err != nil {
   102  			// just continue, the invalid preference value should get caught by IsValid before saving
   103  			return
   104  		}
   105  
   106  		colorPattern := regexp.MustCompile(`^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$`)
   107  
   108  		// blank out any invalid theme values
   109  		for name, value := range props {
   110  			if name == "image" || name == "type" || name == "codeTheme" {
   111  				continue
   112  			}
   113  
   114  			if !colorPattern.MatchString(value) {
   115  				props[name] = "#ffffff"
   116  			}
   117  		}
   118  
   119  		if b, err := json.Marshal(props); err == nil {
   120  			o.Value = string(b)
   121  		}
   122  	}
   123  }