summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mattermost/mattermost-server/v5/model/preference.go
blob: ee0d21aa98e2e496428ef6d132a1aa5fba604a75 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

package model

import (
	"encoding/json"
	"io"
	"net/http"
	"regexp"
	"strings"
	"unicode/utf8"
)

const (
	PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show"
	PREFERENCE_CATEGORY_GROUP_CHANNEL_SHOW  = "group_channel_show"
	PREFERENCE_CATEGORY_TUTORIAL_STEPS      = "tutorial_step"
	PREFERENCE_CATEGORY_ADVANCED_SETTINGS   = "advanced_settings"
	PREFERENCE_CATEGORY_FLAGGED_POST        = "flagged_post"
	PREFERENCE_CATEGORY_FAVORITE_CHANNEL    = "favorite_channel"
	PREFERENCE_CATEGORY_SIDEBAR_SETTINGS    = "sidebar_settings"

	PREFERENCE_CATEGORY_DISPLAY_SETTINGS      = "display_settings"
	PREFERENCE_NAME_COLLAPSED_THREADS_ENABLED = "collapsed_reply_threads"
	PREFERENCE_NAME_CHANNEL_DISPLAY_MODE      = "channel_display_mode"
	PREFERENCE_NAME_COLLAPSE_SETTING          = "collapse_previews"
	PREFERENCE_NAME_MESSAGE_DISPLAY           = "message_display"
	PREFERENCE_NAME_NAME_FORMAT               = "name_format"
	PREFERENCE_NAME_USE_MILITARY_TIME         = "use_military_time"
	PREFERENCE_RECOMMENDED_NEXT_STEPS         = "recommended_next_steps"

	PREFERENCE_CATEGORY_THEME = "theme"
	// the name for theme props is the team id

	PREFERENCE_CATEGORY_AUTHORIZED_OAUTH_APP = "oauth_app"
	// the name for oauth_app is the client_id and value is the current scope

	PREFERENCE_CATEGORY_LAST     = "last"
	PREFERENCE_NAME_LAST_CHANNEL = "channel"
	PREFERENCE_NAME_LAST_TEAM    = "team"

	PREFERENCE_CATEGORY_CUSTOM_STATUS            = "custom_status"
	PREFERENCE_NAME_RECENT_CUSTOM_STATUSES       = "recent_custom_statuses"
	PREFERENCE_NAME_CUSTOM_STATUS_TUTORIAL_STATE = "custom_status_tutorial_state"

	PREFERENCE_CUSTOM_STATUS_MODAL_VIEWED = "custom_status_modal_viewed"

	PREFERENCE_CATEGORY_NOTIFICATIONS = "notifications"
	PREFERENCE_NAME_EMAIL_INTERVAL    = "email_interval"

	PREFERENCE_EMAIL_INTERVAL_NO_BATCHING_SECONDS = "30"  // the "immediate" setting is actually 30s
	PREFERENCE_EMAIL_INTERVAL_BATCHING_SECONDS    = "900" // fifteen minutes is 900 seconds
	PREFERENCE_EMAIL_INTERVAL_IMMEDIATELY         = "immediately"
	PREFERENCE_EMAIL_INTERVAL_FIFTEEN             = "fifteen"
	PREFERENCE_EMAIL_INTERVAL_FIFTEEN_AS_SECONDS  = "900"
	PREFERENCE_EMAIL_INTERVAL_HOUR                = "hour"
	PREFERENCE_EMAIL_INTERVAL_HOUR_AS_SECONDS     = "3600"
)

type Preference struct {
	UserId   string `json:"user_id"`
	Category string `json:"category"`
	Name     string `json:"name"`
	Value    string `json:"value"`
}

func (o *Preference) ToJson() string {
	b, _ := json.Marshal(o)
	return string(b)
}

func PreferenceFromJson(data io.Reader) *Preference {
	var o *Preference
	json.NewDecoder(data).Decode(&o)
	return o
}

func (o *Preference) IsValid() *AppError {
	if !IsValidId(o.UserId) {
		return NewAppError("Preference.IsValid", "model.preference.is_valid.id.app_error", nil, "user_id="+o.UserId, http.StatusBadRequest)
	}

	if o.Category == "" || len(o.Category) > 32 {
		return NewAppError("Preference.IsValid", "model.preference.is_valid.category.app_error", nil, "category="+o.Category, http.StatusBadRequest)
	}

	if len(o.Name) > 32 {
		return NewAppError("Preference.IsValid", "model.preference.is_valid.name.app_error", nil, "name="+o.Name, http.StatusBadRequest)
	}

	if utf8.RuneCountInString(o.Value) > 2000 {
		return NewAppError("Preference.IsValid", "model.preference.is_valid.value.app_error", nil, "value="+o.Value, http.StatusBadRequest)
	}

	if o.Category == PREFERENCE_CATEGORY_THEME {
		var unused map[string]string
		if err := json.NewDecoder(strings.NewReader(o.Value)).Decode(&unused); err != nil {
			return NewAppError("Preference.IsValid", "model.preference.is_valid.theme.app_error", nil, "value="+o.Value, http.StatusBadRequest)
		}
	}

	return nil
}

func (o *Preference) PreUpdate() {
	if o.Category == PREFERENCE_CATEGORY_THEME {
		// decode the value of theme (a map of strings to string) and eliminate any invalid values
		var props map[string]string
		if err := json.NewDecoder(strings.NewReader(o.Value)).Decode(&props); err != nil {
			// just continue, the invalid preference value should get caught by IsValid before saving
			return
		}

		colorPattern := regexp.MustCompile(`^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$`)

		// blank out any invalid theme values
		for name, value := range props {
			if name == "image" || name == "type" || name == "codeTheme" {
				continue
			}

			if !colorPattern.MatchString(value) {
				props[name] = "#ffffff"
			}
		}

		if b, err := json.Marshal(props); err == nil {
			o.Value = string(b)
		}
	}
}