Merge branch 'master' into mark-as-unread

Этот коммит содержится в:
Harrison Healey
2019-11-19 09:45:03 -05:00
родитель 2f066da704 c40f0a4aea
Коммит de913e7537
108 изменённых файлов: 11124 добавлений и 1190 удалений

Просмотреть файл

@@ -8,6 +8,7 @@ import (
"fmt"
"io"
"net/http"
"strings"
"unicode/utf8"
)
@@ -217,3 +218,15 @@ func (l *BotList) Etag() string {
func MakeBotNotFoundError(userId string) *AppError {
return NewAppError("SqlBotStore.Get", "store.sql_bot.get.missing.app_error", map[string]interface{}{"user_id": userId}, "", http.StatusNotFound)
}
func IsBotDMChannel(channel *Channel, botUserID string) bool {
if channel.Type != CHANNEL_DIRECT {
return false
}
if !strings.HasPrefix(channel.Name, botUserID+"__") && !strings.HasSuffix(channel.Name, "__"+botUserID) {
return false
}
return true
}

Просмотреть файл

@@ -683,3 +683,45 @@ func TestBotListEtag(t *testing.T) {
})
}
}
func TestIsBotChannel(t *testing.T) {
for _, test := range []struct {
Name string
Channel *Channel
Expected bool
}{
{
Name: "not a direct channel",
Channel: &Channel{Type: CHANNEL_OPEN},
Expected: false,
},
{
Name: "a direct channel with another user",
Channel: &Channel{
Name: "user1__user2",
Type: CHANNEL_DIRECT,
},
Expected: false,
},
{
Name: "a direct channel with the name containing the bot's ID first",
Channel: &Channel{
Name: "botUserID__user2",
Type: CHANNEL_DIRECT,
},
Expected: true,
},
{
Name: "a direct channel with the name containing the bot's ID second",
Channel: &Channel{
Name: "user1__botUserID",
Type: CHANNEL_DIRECT,
},
Expected: true,
},
} {
t.Run(test.Name, func(t *testing.T) {
assert.Equal(t, test.Expected, IsBotDMChannel(test.Channel, "botUserID"))
})
}
}

Просмотреть файл

@@ -4472,6 +4472,21 @@ func (c *Client4) InstallPluginFromUrl(downloadUrl string, force bool) (*Manifes
return ManifestFromJson(r.Body), BuildResponse(r)
}
// InstallMarketplacePlugin will install marketplace plugin.
// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
func (c *Client4) InstallMarketplacePlugin(request *InstallMarketplacePluginRequest) (*Manifest, *Response) {
json, err := request.ToJson()
if err != nil {
return nil, &Response{Error: NewAppError("InstallMarketplacePlugin", "model.client.plugin_request_to_json.app_error", nil, err.Error(), http.StatusBadRequest)}
}
r, appErr := c.DoApiPost(c.GetPluginsRoute()+"/marketplace", json)
if appErr != nil {
return nil, BuildErrorResponse(r, appErr)
}
defer closeBody(r)
return ManifestFromJson(r.Body), BuildResponse(r)
}
// GetPlugins will return a list of plugin manifests for currently active plugins.
// WARNING: PLUGINS ARE STILL EXPERIMENTAL. THIS FUNCTION IS SUBJECT TO CHANGE.
func (c *Client4) GetPlugins() (*PluginsResponse, *Response) {

Просмотреть файл

@@ -24,6 +24,9 @@ const (
CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_USER = "clear_session_user"
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_ROLES = "inv_roles"
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_SCHEMES = "inv_schemes"
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_EMOJIS_BY_ID = "inv_emojis_by_id"
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_EMOJIS_ID_BY_NAME = "inv_emojis_id_by_name"
CLUSTER_EVENT_INVALIDATE_CACHE_FOR_CHANNEL_MEMBER_COUNTS = "inv_channel_member_counts"
CLUSTER_EVENT_CLEAR_SESSION_CACHE_FOR_ALL_USERS = "inv_all_user_sessions"
CLUSTER_EVENT_INSTALL_PLUGIN = "install_plugin"
CLUSTER_EVENT_REMOVE_PLUGIN = "remove_plugin"

Просмотреть файл

@@ -48,6 +48,7 @@ const (
GENERIC_NOTIFICATION = "generic"
GENERIC_NOTIFICATION_SERVER = "https://push-test.mattermost.com"
FULL_NOTIFICATION = "full"
ID_LOADED_NOTIFICATION = "id_loaded"
DIRECT_MESSAGE_ANY = "any"
DIRECT_MESSAGE_TEAM = "team"
@@ -1521,6 +1522,7 @@ type TeamSettings struct {
ExperimentalEnableAutomaticReplies *bool
ExperimentalHideTownSquareinLHS *bool
ExperimentalTownSquareIsReadOnly *bool
LockTeammateNameDisplay *bool
ExperimentalPrimaryTeam *string
ExperimentalDefaultChannels []string
}
@@ -1667,6 +1669,10 @@ func (s *TeamSettings) SetDefaults() {
if s.ExperimentalViewArchivedChannels == nil {
s.ExperimentalViewArchivedChannels = NewBool(false)
}
if s.LockTeammateNameDisplay == nil {
s.LockTeammateNameDisplay = NewBool(false)
}
}
type ClientRequirements struct {
@@ -2239,7 +2245,9 @@ type PluginSettings struct {
Plugins map[string]map[string]interface{}
PluginStates map[string]*PluginState
EnableMarketplace *bool
RequirePluginSignature *bool
MarketplaceUrl *string
SignaturePublicKeyFiles []string
}
func (s *PluginSettings) SetDefaults(ls LogSettings) {
@@ -2287,6 +2295,14 @@ func (s *PluginSettings) SetDefaults(ls LogSettings) {
if s.MarketplaceUrl == nil || *s.MarketplaceUrl == "" || *s.MarketplaceUrl == PLUGIN_SETTINGS_OLD_MARKETPLACE_URL {
s.MarketplaceUrl = NewString(PLUGIN_SETTINGS_DEFAULT_MARKETPLACE_URL)
}
if s.RequirePluginSignature == nil {
s.RequirePluginSignature = NewBool(false)
}
if s.SignaturePublicKeyFiles == nil {
s.SignaturePublicKeyFiles = []string{}
}
}
type GlobalRelayMessageExportSettings struct {

Просмотреть файл

@@ -170,7 +170,6 @@ func TestConfigIsValidFakeAlgorithm(t *testing.T) {
require.Equal(t, "model.config.is_valid.saml_canonical_algorithm.app_error", err.Message)
*c1.SamlSettings.CanonicalAlgorithm = temp
temp = *c1.SamlSettings.SignatureAlgorithm
*c1.SamlSettings.SignatureAlgorithm = "Fake Algorithm"
err = c1.SamlSettings.isValid()
if err == nil {

Просмотреть файл

@@ -44,6 +44,9 @@ type PostAction struct {
// The text on the button, or in the select placeholder.
Name string `json:"name,omitempty"`
// If the action is disabled.
Disabled bool `json:"disabled,omitempty"`
// DataSource indicates the data source for the select action. If left
// empty, the select is populated from Options. Other supported values
// are "users" and "channels".

Просмотреть файл

@@ -4,18 +4,25 @@
package model
import (
"bytes"
"encoding/base64"
"encoding/json"
"io"
"net/url"
"strconv"
"github.com/pkg/errors"
)
// BaseMarketplacePlugin is a Mattermost plugin received from the marketplace server.
type BaseMarketplacePlugin struct {
HomepageURL string `json:"homepage_url"`
DownloadURL string `json:"download_url"`
IconData string `json:"icon_data"`
Manifest *Manifest `json:"manifest"`
HomepageURL string `json:"homepage_url"`
IconData string `json:"icon_data"`
DownloadURL string `json:"download_url"`
ReleaseNotesURL string `json:"release_notes_url"`
// Signature represents a signature of a plugin saved in base64 encoding.
Signature string `json:"signature"`
Manifest *Manifest `json:"manifest"`
}
// MarketplacePlugin is a state aware marketplace plugin.
@@ -48,6 +55,15 @@ func MarketplacePluginsFromReader(reader io.Reader) ([]*MarketplacePlugin, error
return plugins, nil
}
// DecodeSignature Decodes signature and returns ReadSeeker.
func (plugin *BaseMarketplacePlugin) DecodeSignature() (io.ReadSeeker, error) {
signatureBytes, err := base64.StdEncoding.DecodeString(plugin.Signature)
if err != nil {
return nil, errors.Wrap(err, "Unable to decode base64 signature.")
}
return bytes.NewReader(signatureBytes), nil
}
// MarketplacePluginFilter describes the parameters to request a list of plugins.
type MarketplacePluginFilter struct {
Page int
@@ -67,3 +83,28 @@ func (filter *MarketplacePluginFilter) ApplyToURL(u *url.URL) {
q.Add("server_version", filter.ServerVersion)
u.RawQuery = q.Encode()
}
// InstallMarketplacePluginRequest struct describes parameters of the requested plugin.
type InstallMarketplacePluginRequest struct {
Id string `json:"id"`
Version string `json:"version"`
}
// PluginRequestFromReader decodes a json-encoded plugin request from the given io.Reader.
func PluginRequestFromReader(reader io.Reader) (*InstallMarketplacePluginRequest, error) {
var r *InstallMarketplacePluginRequest
err := json.NewDecoder(reader).Decode(&r)
if err != nil {
return nil, err
}
return r, nil
}
// ToJson method will return json from plugin request.
func (r *InstallMarketplacePluginRequest) ToJson() (string, error) {
b, err := json.Marshal(r)
if err != nil {
return "", err
}
return string(b), nil
}

Просмотреть файл

@@ -15,6 +15,7 @@ const (
PUSH_NOTIFY_APPLE_REACT_NATIVE = "apple_rn"
PUSH_NOTIFY_ANDROID_REACT_NATIVE = "android_rn"
PUSH_TYPE_ID_LOADED = "id_loaded"
PUSH_TYPE_MESSAGE = "message"
PUSH_TYPE_CLEAR = "clear"
PUSH_TYPE_UPDATE_BADGE = "update_badge"
@@ -39,6 +40,7 @@ type PushNotificationAck struct {
ClientReceivedAt int64 `json:"received_at"`
ClientPlatform string `json:"platform"`
NotificationType string `json:"type"`
PostId string `json:"post_id,omitempty"`
}
type PushNotification struct {
@@ -46,23 +48,23 @@ type PushNotification struct {
Platform string `json:"platform"`
ServerId string `json:"server_id"`
DeviceId string `json:"device_id"`
Category string `json:"category"`
Sound string `json:"sound"`
Message string `json:"message"`
Badge int `json:"badge"`
ContentAvailable int `json:"cont_ava"`
TeamId string `json:"team_id"`
ChannelId string `json:"channel_id"`
PostId string `json:"post_id"`
RootId string `json:"root_id"`
ChannelName string `json:"channel_name"`
Type string `json:"type"`
SenderId string `json:"sender_id"`
SenderName string `json:"sender_name"`
OverrideUsername string `json:"override_username"`
OverrideIconUrl string `json:"override_icon_url"`
FromWebhook string `json:"from_webhook"`
Version string `json:"version"`
Category string `json:"category,omitempty"`
Sound string `json:"sound,omitempty"`
Message string `json:"message,omitempty"`
Badge int `json:"badge,omitempty"`
ContentAvailable int `json:"cont_ava,omitempty"`
TeamId string `json:"team_id,omitempty"`
ChannelId string `json:"channel_id,omitempty"`
RootId string `json:"root_id,omitempty"`
ChannelName string `json:"channel_name,omitempty"`
Type string `json:"type,omitempty"`
SenderId string `json:"sender_id,omitempty"`
SenderName string `json:"sender_name,omitempty"`
OverrideUsername string `json:"override_username,omitempty"`
OverrideIconUrl string `json:"override_icon_url,omitempty"`
FromWebhook string `json:"from_webhook,omitempty"`
Version string `json:"version,omitempty"`
}
func (me *PushNotification) ToJson() string {

Просмотреть файл

@@ -268,6 +268,7 @@ func CleanTeamName(s string) string {
func (o *Team) Sanitize() {
o.Email = ""
o.InviteId = ""
}
func (t *Team) Patch(patch *TeamPatch) {

Просмотреть файл

@@ -624,3 +624,8 @@ func GetPreferredTimezone(timezone StringMap) string {
return timezone["manualTimezone"]
}
// IsSamlFile checks if filename is a SAML file.
func IsSamlFile(saml *SamlSettings, filename string) bool {
return filename == *saml.PublicCertificateFile || filename == *saml.PrivateKeyFile || filename == *saml.IdpCertificateFile
}

Просмотреть файл

@@ -52,6 +52,7 @@ const (
WEBSOCKET_EVENT_LICENSE_CHANGED = "license_changed"
WEBSOCKET_EVENT_CONFIG_CHANGED = "config_changed"
WEBSOCKET_EVENT_OPEN_DIALOG = "open_dialog"
WEBSOCKET_EVENT_GUESTS_DEACTIVATED = "guests_deactivated"
)
type WebSocketMessage interface {