2017-08-16 14:37:37 -07:00
|
|
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
2016-04-10 14:39:38 -07:00
|
|
|
// See License.txt for license information.
|
|
|
|
|
|
|
|
package model
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2017-03-25 13:04:10 -07:00
|
|
|
"net/http"
|
2016-04-10 14:39:38 -07:00
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
"unicode/utf8"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2017-03-25 13:04:10 -07:00
|
|
|
TEAM_OPEN = "O"
|
|
|
|
TEAM_INVITE = "I"
|
|
|
|
TEAM_ALLOWED_DOMAINS_MAX_LENGTH = 500
|
|
|
|
TEAM_COMPANY_NAME_MAX_LENGTH = 64
|
|
|
|
TEAM_DESCRIPTION_MAX_LENGTH = 255
|
|
|
|
TEAM_DISPLAY_NAME_MAX_RUNES = 64
|
|
|
|
TEAM_EMAIL_MAX_LENGTH = 128
|
|
|
|
TEAM_NAME_MAX_LENGTH = 64
|
|
|
|
TEAM_NAME_MIN_LENGTH = 2
|
2016-04-10 14:39:38 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
type Team struct {
|
2018-11-18 09:55:05 -08:00
|
|
|
Id string `json:"id"`
|
|
|
|
CreateAt int64 `json:"create_at"`
|
|
|
|
UpdateAt int64 `json:"update_at"`
|
|
|
|
DeleteAt int64 `json:"delete_at"`
|
|
|
|
DisplayName string `json:"display_name"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Description string `json:"description"`
|
|
|
|
Email string `json:"email"`
|
|
|
|
Type string `json:"type"`
|
|
|
|
CompanyName string `json:"company_name"`
|
|
|
|
AllowedDomains string `json:"allowed_domains"`
|
|
|
|
InviteId string `json:"invite_id"`
|
|
|
|
AllowOpenInvite bool `json:"allow_open_invite"`
|
|
|
|
LastTeamIconUpdate int64 `json:"last_team_icon_update,omitempty"`
|
|
|
|
SchemeId *string `json:"scheme_id"`
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
2017-08-16 14:37:37 -07:00
|
|
|
type TeamPatch struct {
|
|
|
|
DisplayName *string `json:"display_name"`
|
|
|
|
Description *string `json:"description"`
|
|
|
|
CompanyName *string `json:"company_name"`
|
2018-11-18 09:55:05 -08:00
|
|
|
AllowedDomains *string `json:"allowed_domains"`
|
2017-08-16 14:37:37 -07:00
|
|
|
InviteId *string `json:"invite_id"`
|
|
|
|
AllowOpenInvite *bool `json:"allow_open_invite"`
|
|
|
|
}
|
|
|
|
|
2018-11-18 09:55:05 -08:00
|
|
|
type TeamForExport struct {
|
|
|
|
Team
|
|
|
|
SchemeName *string
|
|
|
|
}
|
|
|
|
|
2016-04-10 14:39:38 -07:00
|
|
|
type Invites struct {
|
|
|
|
Invites []map[string]string `json:"invites"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func InvitesFromJson(data io.Reader) *Invites {
|
2018-11-18 09:55:05 -08:00
|
|
|
var o *Invites
|
|
|
|
json.NewDecoder(data).Decode(&o)
|
|
|
|
return o
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
2017-03-25 13:04:10 -07:00
|
|
|
func (o *Invites) ToEmailList() []string {
|
|
|
|
emailList := make([]string, len(o.Invites))
|
|
|
|
for _, invite := range o.Invites {
|
|
|
|
emailList = append(emailList, invite["email"])
|
|
|
|
}
|
|
|
|
return emailList
|
|
|
|
}
|
|
|
|
|
2016-04-10 14:39:38 -07:00
|
|
|
func (o *Invites) ToJson() string {
|
2018-11-18 09:55:05 -08:00
|
|
|
b, _ := json.Marshal(o)
|
|
|
|
return string(b)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func (o *Team) ToJson() string {
|
2018-11-18 09:55:05 -08:00
|
|
|
b, _ := json.Marshal(o)
|
|
|
|
return string(b)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func TeamFromJson(data io.Reader) *Team {
|
2018-11-18 09:55:05 -08:00
|
|
|
var o *Team
|
|
|
|
json.NewDecoder(data).Decode(&o)
|
|
|
|
return o
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func TeamMapToJson(u map[string]*Team) string {
|
2018-11-18 09:55:05 -08:00
|
|
|
b, _ := json.Marshal(u)
|
|
|
|
return string(b)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func TeamMapFromJson(data io.Reader) map[string]*Team {
|
|
|
|
var teams map[string]*Team
|
2018-11-18 09:55:05 -08:00
|
|
|
json.NewDecoder(data).Decode(&teams)
|
|
|
|
return teams
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
2017-03-25 13:04:10 -07:00
|
|
|
func TeamListToJson(t []*Team) string {
|
2018-11-18 09:55:05 -08:00
|
|
|
b, _ := json.Marshal(t)
|
|
|
|
return string(b)
|
2017-03-25 13:04:10 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func TeamListFromJson(data io.Reader) []*Team {
|
|
|
|
var teams []*Team
|
2018-11-18 09:55:05 -08:00
|
|
|
json.NewDecoder(data).Decode(&teams)
|
|
|
|
return teams
|
2017-03-25 13:04:10 -07:00
|
|
|
}
|
|
|
|
|
2016-04-10 14:39:38 -07:00
|
|
|
func (o *Team) Etag() string {
|
|
|
|
return Etag(o.Id, o.UpdateAt)
|
|
|
|
}
|
|
|
|
|
2016-11-12 13:00:53 -08:00
|
|
|
func (o *Team) IsValid() *AppError {
|
2016-04-10 14:39:38 -07:00
|
|
|
|
|
|
|
if len(o.Id) != 26 {
|
2017-03-25 13:04:10 -07:00
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.id.app_error", nil, "", http.StatusBadRequest)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if o.CreateAt == 0 {
|
2017-03-25 13:04:10 -07:00
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.create_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if o.UpdateAt == 0 {
|
2017-03-25 13:04:10 -07:00
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.update_at.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
2017-03-25 13:04:10 -07:00
|
|
|
if len(o.Email) > TEAM_EMAIL_MAX_LENGTH {
|
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(o.Email) > 0 && !IsValidEmail(o.Email) {
|
2017-03-25 13:04:10 -07:00
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.email.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
2017-03-25 13:04:10 -07:00
|
|
|
if utf8.RuneCountInString(o.DisplayName) == 0 || utf8.RuneCountInString(o.DisplayName) > TEAM_DISPLAY_NAME_MAX_RUNES {
|
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.name.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
2017-03-25 13:04:10 -07:00
|
|
|
if len(o.Name) > TEAM_NAME_MAX_LENGTH {
|
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.url.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
2017-01-16 14:59:50 -08:00
|
|
|
}
|
|
|
|
|
2017-03-25 13:04:10 -07:00
|
|
|
if len(o.Description) > TEAM_DESCRIPTION_MAX_LENGTH {
|
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.description.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
2016-11-12 13:00:53 -08:00
|
|
|
if IsReservedTeamName(o.Name) {
|
2017-03-25 13:04:10 -07:00
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.reserved.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if !IsValidTeamName(o.Name) {
|
2017-03-25 13:04:10 -07:00
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.characters.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if !(o.Type == TEAM_OPEN || o.Type == TEAM_INVITE) {
|
2017-03-25 13:04:10 -07:00
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.type.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
2017-03-25 13:04:10 -07:00
|
|
|
if len(o.CompanyName) > TEAM_COMPANY_NAME_MAX_LENGTH {
|
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.company.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
2017-03-25 13:04:10 -07:00
|
|
|
if len(o.AllowedDomains) > TEAM_ALLOWED_DOMAINS_MAX_LENGTH {
|
|
|
|
return NewAppError("Team.IsValid", "model.team.is_valid.domains.app_error", nil, "id="+o.Id, http.StatusBadRequest)
|
2016-04-10 14:39:38 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *Team) PreSave() {
|
|
|
|
if o.Id == "" {
|
|
|
|
o.Id = NewId()
|
|
|
|
}
|
|
|
|
|
|
|
|
o.CreateAt = GetMillis()
|
|
|
|
o.UpdateAt = o.CreateAt
|
|
|
|
|
|
|
|
if len(o.InviteId) == 0 {
|
|
|
|
o.InviteId = NewId()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *Team) PreUpdate() {
|
|
|
|
o.UpdateAt = GetMillis()
|
|
|
|
}
|
|
|
|
|
|
|
|
func IsReservedTeamName(s string) bool {
|
|
|
|
s = strings.ToLower(s)
|
|
|
|
|
|
|
|
for _, value := range reservedName {
|
|
|
|
if strings.Index(s, value) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func IsValidTeamName(s string) bool {
|
|
|
|
|
2017-08-16 14:37:37 -07:00
|
|
|
if !IsValidAlphaNum(s) {
|
2016-04-10 14:39:38 -07:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-03-25 13:04:10 -07:00
|
|
|
if len(s) < TEAM_NAME_MIN_LENGTH {
|
2016-04-10 14:39:38 -07:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
var validTeamNameCharacter = regexp.MustCompile(`^[a-z0-9-]$`)
|
|
|
|
|
|
|
|
func CleanTeamName(s string) string {
|
|
|
|
s = strings.ToLower(strings.Replace(s, " ", "-", -1))
|
|
|
|
|
|
|
|
for _, value := range reservedName {
|
|
|
|
if strings.Index(s, value) == 0 {
|
|
|
|
s = strings.Replace(s, value, "", -1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s = strings.TrimSpace(s)
|
|
|
|
|
|
|
|
for _, c := range s {
|
|
|
|
char := fmt.Sprintf("%c", c)
|
|
|
|
if !validTeamNameCharacter.MatchString(char) {
|
|
|
|
s = strings.Replace(s, char, "", -1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s = strings.Trim(s, "-")
|
|
|
|
|
|
|
|
if !IsValidTeamName(s) {
|
|
|
|
s = NewId()
|
|
|
|
}
|
|
|
|
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *Team) Sanitize() {
|
|
|
|
o.Email = ""
|
2016-05-15 14:02:30 -07:00
|
|
|
}
|
2017-08-16 14:37:37 -07:00
|
|
|
|
|
|
|
func (t *Team) Patch(patch *TeamPatch) {
|
|
|
|
if patch.DisplayName != nil {
|
|
|
|
t.DisplayName = *patch.DisplayName
|
|
|
|
}
|
|
|
|
|
|
|
|
if patch.Description != nil {
|
|
|
|
t.Description = *patch.Description
|
|
|
|
}
|
|
|
|
|
|
|
|
if patch.CompanyName != nil {
|
|
|
|
t.CompanyName = *patch.CompanyName
|
|
|
|
}
|
|
|
|
|
2018-11-18 09:55:05 -08:00
|
|
|
if patch.AllowedDomains != nil {
|
|
|
|
t.AllowedDomains = *patch.AllowedDomains
|
|
|
|
}
|
|
|
|
|
2017-08-16 14:37:37 -07:00
|
|
|
if patch.InviteId != nil {
|
|
|
|
t.InviteId = *patch.InviteId
|
|
|
|
}
|
|
|
|
|
|
|
|
if patch.AllowOpenInvite != nil {
|
|
|
|
t.AllowOpenInvite = *patch.AllowOpenInvite
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TeamPatch) ToJson() string {
|
|
|
|
b, err := json.Marshal(t)
|
|
|
|
if err != nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return string(b)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TeamPatchFromJson(data io.Reader) *TeamPatch {
|
|
|
|
decoder := json.NewDecoder(data)
|
|
|
|
var team TeamPatch
|
|
|
|
err := decoder.Decode(&team)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return &team
|
|
|
|
}
|