Merge branch 'master' into post-metadata
Этот коммит содержится в:
@@ -139,7 +139,7 @@ func (a *App) InvalidateAllCaches() *model.AppError {
|
||||
|
||||
func (a *App) InvalidateAllCachesSkipSend() {
|
||||
mlog.Info("Purging all caches")
|
||||
a.sessionCache.Purge()
|
||||
a.Srv.sessionCache.Purge()
|
||||
ClearStatusCache()
|
||||
a.Srv.Store.Channel().ClearCaches()
|
||||
a.Srv.Store.User().ClearCaches()
|
||||
@@ -212,8 +212,8 @@ func (a *App) RecycleDatabaseConnection() {
|
||||
oldStore := a.Srv.Store
|
||||
|
||||
mlog.Warn("Attempting to recycle the database connection.")
|
||||
a.Srv.Store = a.newStore()
|
||||
a.Jobs.Store = a.Srv.Store
|
||||
a.Srv.Store = a.Srv.newStore()
|
||||
a.Srv.Jobs.Store = a.Srv.Store
|
||||
|
||||
if a.Srv.Store != oldStore {
|
||||
time.Sleep(20 * time.Second)
|
||||
|
||||
178
app/app.go
178
app/app.go
@@ -4,19 +4,15 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"path"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/throttled/throttled"
|
||||
|
||||
"github.com/mattermost/mattermost-server/einterfaces"
|
||||
ejobs "github.com/mattermost/mattermost-server/einterfaces/jobs"
|
||||
@@ -24,7 +20,6 @@ import (
|
||||
tjobs "github.com/mattermost/mattermost-server/jobs/interfaces"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/plugin"
|
||||
"github.com/mattermost/mattermost-server/services/httpservice"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
"github.com/mattermost/mattermost-server/store/sqlstore"
|
||||
@@ -35,26 +30,10 @@ const ADVANCED_PERMISSIONS_MIGRATION_KEY = "AdvancedPermissionsMigrationComplete
|
||||
const EMOJIS_PERMISSIONS_MIGRATION_KEY = "EmojisPermissionsMigrationComplete"
|
||||
|
||||
type App struct {
|
||||
goroutineCount int32
|
||||
goroutineExitSignal chan struct{}
|
||||
|
||||
Srv *Server
|
||||
|
||||
Log *mlog.Logger
|
||||
|
||||
Plugins *plugin.Environment
|
||||
PluginConfigListenerId string
|
||||
|
||||
EmailBatching *EmailBatchingJob
|
||||
EmailRateLimiter *throttled.GCRARateLimiter
|
||||
|
||||
Hubs []*Hub
|
||||
HubsStopCheckingForDeadlock chan bool
|
||||
|
||||
PushNotificationsHub PushNotificationsHub
|
||||
|
||||
Jobs *jobs.JobServer
|
||||
|
||||
AccountMigration einterfaces.AccountMigrationInterface
|
||||
Cluster einterfaces.ClusterInterface
|
||||
Compliance einterfaces.ComplianceInterface
|
||||
@@ -66,42 +45,6 @@ type App struct {
|
||||
Mfa einterfaces.MfaInterface
|
||||
Saml einterfaces.SamlInterface
|
||||
|
||||
config atomic.Value
|
||||
envConfig map[string]interface{}
|
||||
configFile string
|
||||
configListeners map[string]func(*model.Config, *model.Config)
|
||||
clusterLeaderListeners sync.Map
|
||||
|
||||
licenseValue atomic.Value
|
||||
clientLicenseValue atomic.Value
|
||||
licenseListeners map[string]func()
|
||||
|
||||
timezones atomic.Value
|
||||
|
||||
siteURL string
|
||||
|
||||
newStore func() store.Store
|
||||
|
||||
htmlTemplateWatcher *utils.HTMLTemplateWatcher
|
||||
sessionCache *utils.Cache
|
||||
configListenerId string
|
||||
licenseListenerId string
|
||||
logListenerId string
|
||||
clusterLeaderListenerId string
|
||||
disableConfigWatch bool
|
||||
configWatcher *utils.ConfigWatcher
|
||||
asymmetricSigningKey *ecdsa.PrivateKey
|
||||
|
||||
pluginCommands []*PluginCommand
|
||||
pluginCommandsLock sync.RWMutex
|
||||
|
||||
clientConfig map[string]string
|
||||
clientConfigHash string
|
||||
limitedClientConfig map[string]string
|
||||
diagnosticId string
|
||||
|
||||
phase2PermissionsMigrationComplete bool
|
||||
|
||||
HTTPService httpservice.HTTPService
|
||||
}
|
||||
|
||||
@@ -118,15 +61,15 @@ func New(options ...Option) (outApp *App, outErr error) {
|
||||
rootRouter := mux.NewRouter()
|
||||
|
||||
app := &App{
|
||||
goroutineExitSignal: make(chan struct{}, 1),
|
||||
Srv: &Server{
|
||||
RootRouter: rootRouter,
|
||||
goroutineExitSignal: make(chan struct{}, 1),
|
||||
RootRouter: rootRouter,
|
||||
configFile: "config.json",
|
||||
configListeners: make(map[string]func(*model.Config, *model.Config)),
|
||||
licenseListeners: map[string]func(){},
|
||||
sessionCache: utils.NewLru(model.SESSION_CACHE_SIZE),
|
||||
clientConfig: make(map[string]string),
|
||||
},
|
||||
sessionCache: utils.NewLru(model.SESSION_CACHE_SIZE),
|
||||
configFile: "config.json",
|
||||
configListeners: make(map[string]func(*model.Config, *model.Config)),
|
||||
clientConfig: make(map[string]string),
|
||||
licenseListeners: map[string]func(){},
|
||||
}
|
||||
|
||||
app.HTTPService = httpservice.MakeHTTPService(app)
|
||||
@@ -151,7 +94,7 @@ func New(options ...Option) (outApp *App, outErr error) {
|
||||
}
|
||||
model.AppErrorInit(utils.T)
|
||||
|
||||
if err := app.LoadConfig(app.configFile); err != nil {
|
||||
if err := app.LoadConfig(app.Srv.configFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -164,7 +107,7 @@ func New(options ...Option) (outApp *App, outErr error) {
|
||||
// Use this app logger as the global logger (eventually remove all instances of global logging)
|
||||
mlog.InitGlobalLogger(app.Log)
|
||||
|
||||
app.logListenerId = app.AddConfigListener(func(_, after *model.Config) {
|
||||
app.Srv.logListenerId = app.AddConfigListener(func(_, after *model.Config) {
|
||||
app.Log.ChangeLevels(utils.MloggerConfigFromLoggerConfig(&after.LogSettings))
|
||||
})
|
||||
|
||||
@@ -176,22 +119,22 @@ func New(options ...Option) (outApp *App, outErr error) {
|
||||
return nil, errors.Wrapf(err, "unable to load Mattermost translation files")
|
||||
}
|
||||
|
||||
app.configListenerId = app.AddConfigListener(func(_, _ *model.Config) {
|
||||
app.Srv.configListenerId = app.AddConfigListener(func(_, _ *model.Config) {
|
||||
app.configOrLicenseListener()
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CONFIG_CHANGED, "", "", "", nil)
|
||||
|
||||
message.Add("config", app.ClientConfigWithComputed())
|
||||
app.Go(func() {
|
||||
app.Srv.Go(func() {
|
||||
app.Publish(message)
|
||||
})
|
||||
})
|
||||
app.licenseListenerId = app.AddLicenseListener(func() {
|
||||
app.Srv.licenseListenerId = app.AddLicenseListener(func() {
|
||||
app.configOrLicenseListener()
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LICENSE_CHANGED, "", "", "", nil)
|
||||
message.Add("license", app.GetSanitizedClientLicense())
|
||||
app.Go(func() {
|
||||
app.Srv.Go(func() {
|
||||
app.Publish(message)
|
||||
})
|
||||
|
||||
@@ -205,8 +148,8 @@ func New(options ...Option) (outApp *App, outErr error) {
|
||||
|
||||
app.initEnterprise()
|
||||
|
||||
if app.newStore == nil {
|
||||
app.newStore = func() store.Store {
|
||||
if app.Srv.newStore == nil {
|
||||
app.Srv.newStore = func() store.Store {
|
||||
return store.NewLayeredStore(sqlstore.NewSqlSupplier(app.Config().SqlSettings, app.Metrics), app.Metrics, app.Cluster)
|
||||
}
|
||||
}
|
||||
@@ -214,10 +157,10 @@ func New(options ...Option) (outApp *App, outErr error) {
|
||||
if htmlTemplateWatcher, err := utils.NewHTMLTemplateWatcher("templates"); err != nil {
|
||||
mlog.Error(fmt.Sprintf("Failed to parse server templates %v", err))
|
||||
} else {
|
||||
app.htmlTemplateWatcher = htmlTemplateWatcher
|
||||
app.Srv.htmlTemplateWatcher = htmlTemplateWatcher
|
||||
}
|
||||
|
||||
app.Srv.Store = app.newStore()
|
||||
app.Srv.Store = app.Srv.newStore()
|
||||
|
||||
if err := app.ensureAsymmetricSigningKey(); err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to ensure asymmetric signing key")
|
||||
@@ -235,9 +178,9 @@ func New(options ...Option) (outApp *App, outErr error) {
|
||||
app.initJobs()
|
||||
})
|
||||
|
||||
app.clusterLeaderListenerId = app.AddClusterLeaderChangedListener(func() {
|
||||
app.Srv.clusterLeaderListenerId = app.AddClusterLeaderChangedListener(func() {
|
||||
mlog.Info("Cluster leader changed. Determining if job schedulers should be running:", mlog.Bool("isLeader", app.IsLeader()))
|
||||
app.Jobs.Schedulers.HandleClusterLeaderChange(app.IsLeader())
|
||||
app.Srv.Jobs.Schedulers.HandleClusterLeaderChange(app.IsLeader())
|
||||
})
|
||||
|
||||
subpath, err := utils.GetSubpathFromConfig(app.Config())
|
||||
@@ -281,26 +224,26 @@ func (a *App) Shutdown() {
|
||||
a.StopPushNotificationsHubWorkers()
|
||||
|
||||
a.ShutDownPlugins()
|
||||
a.WaitForGoroutines()
|
||||
a.Srv.WaitForGoroutines()
|
||||
|
||||
if a.Srv.Store != nil {
|
||||
a.Srv.Store.Close()
|
||||
}
|
||||
a.Srv = nil
|
||||
|
||||
if a.htmlTemplateWatcher != nil {
|
||||
a.htmlTemplateWatcher.Close()
|
||||
if a.Srv.htmlTemplateWatcher != nil {
|
||||
a.Srv.htmlTemplateWatcher.Close()
|
||||
}
|
||||
|
||||
a.RemoveConfigListener(a.configListenerId)
|
||||
a.RemoveLicenseListener(a.licenseListenerId)
|
||||
a.RemoveConfigListener(a.logListenerId)
|
||||
a.RemoveClusterLeaderChangedListener(a.clusterLeaderListenerId)
|
||||
a.RemoveConfigListener(a.Srv.configListenerId)
|
||||
a.RemoveLicenseListener(a.Srv.licenseListenerId)
|
||||
a.RemoveConfigListener(a.Srv.logListenerId)
|
||||
a.RemoveClusterLeaderChangedListener(a.Srv.clusterLeaderListenerId)
|
||||
mlog.Info("Server stopped")
|
||||
|
||||
a.DisableConfigWatch()
|
||||
|
||||
a.HTTPService.Close()
|
||||
a.Srv = nil
|
||||
}
|
||||
|
||||
var accountMigrationInterface func(*App) einterfaces.AccountMigrationInterface
|
||||
@@ -441,39 +384,39 @@ func (a *App) initEnterprise() {
|
||||
}
|
||||
|
||||
func (a *App) initJobs() {
|
||||
a.Jobs = jobs.NewJobServer(a, a.Srv.Store)
|
||||
a.Srv.Jobs = jobs.NewJobServer(a, a.Srv.Store)
|
||||
if jobsDataRetentionJobInterface != nil {
|
||||
a.Jobs.DataRetentionJob = jobsDataRetentionJobInterface(a)
|
||||
a.Srv.Jobs.DataRetentionJob = jobsDataRetentionJobInterface(a)
|
||||
}
|
||||
if jobsMessageExportJobInterface != nil {
|
||||
a.Jobs.MessageExportJob = jobsMessageExportJobInterface(a)
|
||||
a.Srv.Jobs.MessageExportJob = jobsMessageExportJobInterface(a)
|
||||
}
|
||||
if jobsElasticsearchAggregatorInterface != nil {
|
||||
a.Jobs.ElasticsearchAggregator = jobsElasticsearchAggregatorInterface(a)
|
||||
a.Srv.Jobs.ElasticsearchAggregator = jobsElasticsearchAggregatorInterface(a)
|
||||
}
|
||||
if jobsElasticsearchIndexerInterface != nil {
|
||||
a.Jobs.ElasticsearchIndexer = jobsElasticsearchIndexerInterface(a)
|
||||
a.Srv.Jobs.ElasticsearchIndexer = jobsElasticsearchIndexerInterface(a)
|
||||
}
|
||||
if jobsLdapSyncInterface != nil {
|
||||
a.Jobs.LdapSync = jobsLdapSyncInterface(a)
|
||||
a.Srv.Jobs.LdapSync = jobsLdapSyncInterface(a)
|
||||
}
|
||||
if jobsMigrationsInterface != nil {
|
||||
a.Jobs.Migrations = jobsMigrationsInterface(a)
|
||||
a.Srv.Jobs.Migrations = jobsMigrationsInterface(a)
|
||||
}
|
||||
a.Jobs.Workers = a.Jobs.InitWorkers()
|
||||
a.Jobs.Schedulers = a.Jobs.InitSchedulers()
|
||||
a.Srv.Jobs.Workers = a.Srv.Jobs.InitWorkers()
|
||||
a.Srv.Jobs.Schedulers = a.Srv.Jobs.InitSchedulers()
|
||||
}
|
||||
|
||||
func (a *App) DiagnosticId() string {
|
||||
return a.diagnosticId
|
||||
return a.Srv.diagnosticId
|
||||
}
|
||||
|
||||
func (a *App) SetDiagnosticId(id string) {
|
||||
a.diagnosticId = id
|
||||
a.Srv.diagnosticId = id
|
||||
}
|
||||
|
||||
func (a *App) EnsureDiagnosticId() {
|
||||
if a.diagnosticId != "" {
|
||||
if a.Srv.diagnosticId != "" {
|
||||
return
|
||||
}
|
||||
if result := <-a.Srv.Store.System().Get(); result.Err == nil {
|
||||
@@ -486,36 +429,13 @@ func (a *App) EnsureDiagnosticId() {
|
||||
<-a.Srv.Store.System().Save(systemId)
|
||||
}
|
||||
|
||||
a.diagnosticId = id
|
||||
}
|
||||
}
|
||||
|
||||
// Go creates a goroutine, but maintains a record of it to ensure that execution completes before
|
||||
// the app is destroyed.
|
||||
func (a *App) Go(f func()) {
|
||||
atomic.AddInt32(&a.goroutineCount, 1)
|
||||
|
||||
go func() {
|
||||
f()
|
||||
|
||||
atomic.AddInt32(&a.goroutineCount, -1)
|
||||
select {
|
||||
case a.goroutineExitSignal <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// WaitForGoroutines blocks until all goroutines created by App.Go exit.
|
||||
func (a *App) WaitForGoroutines() {
|
||||
for atomic.LoadInt32(&a.goroutineCount) != 0 {
|
||||
<-a.goroutineExitSignal
|
||||
a.Srv.diagnosticId = id
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) HTMLTemplates() *template.Template {
|
||||
if a.htmlTemplateWatcher != nil {
|
||||
return a.htmlTemplateWatcher.Templates()
|
||||
if a.Srv.htmlTemplateWatcher != nil {
|
||||
return a.Srv.htmlTemplateWatcher.Templates()
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -598,7 +518,7 @@ func (a *App) SetPhase2PermissionsMigrationStatus(isComplete bool) error {
|
||||
return res.Err
|
||||
}
|
||||
}
|
||||
a.phase2PermissionsMigrationComplete = isComplete
|
||||
a.Srv.phase2PermissionsMigrationComplete = isComplete
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -672,7 +592,7 @@ func (a *App) DoEmojisPermissionsMigration() {
|
||||
}
|
||||
|
||||
func (a *App) StartElasticsearch() {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.Elasticsearch.Start(); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
@@ -680,19 +600,19 @@ func (a *App) StartElasticsearch() {
|
||||
|
||||
a.AddConfigListener(func(oldConfig *model.Config, newConfig *model.Config) {
|
||||
if !*oldConfig.ElasticsearchSettings.EnableIndexing && *newConfig.ElasticsearchSettings.EnableIndexing {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.Elasticsearch.Start(); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
})
|
||||
} else if *oldConfig.ElasticsearchSettings.EnableIndexing && !*newConfig.ElasticsearchSettings.EnableIndexing {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.Elasticsearch.Stop(); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
})
|
||||
} else if *oldConfig.ElasticsearchSettings.Password != *newConfig.ElasticsearchSettings.Password || *oldConfig.ElasticsearchSettings.Username != *newConfig.ElasticsearchSettings.Username || *oldConfig.ElasticsearchSettings.ConnectionUrl != *newConfig.ElasticsearchSettings.ConnectionUrl || *oldConfig.ElasticsearchSettings.Sniff != *newConfig.ElasticsearchSettings.Sniff {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if *oldConfig.ElasticsearchSettings.EnableIndexing {
|
||||
if err := a.Elasticsearch.Stop(); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
@@ -707,13 +627,13 @@ func (a *App) StartElasticsearch() {
|
||||
|
||||
a.AddLicenseListener(func() {
|
||||
if a.License() != nil {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.Elasticsearch.Start(); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
})
|
||||
} else {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.Elasticsearch.Stop(); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
|
||||
@@ -246,8 +246,12 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) {
|
||||
restrictPrivateChannel := *th.App.Config().TeamSettings.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelManagement
|
||||
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.DEPRECATED_DO_NOT_USE_RestrictPublicChannelManagement = restrictPublicChannel })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelManagement = restrictPrivateChannel })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.TeamSettings.DEPRECATED_DO_NOT_USE_RestrictPublicChannelManagement = restrictPublicChannel
|
||||
})
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.TeamSettings.DEPRECATED_DO_NOT_USE_RestrictPrivateChannelManagement = restrictPrivateChannel
|
||||
})
|
||||
}()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
@@ -433,8 +437,8 @@ func TestDoAdvancedPermissionsMigration(t *testing.T) {
|
||||
postEditTimeLimit := *th.App.Config().ServiceSettings.PostEditTimeLimit
|
||||
|
||||
defer func() {
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.DEPRECATED_DO_NOT_USE_AllowEditPost = allowEditPost})
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.PostEditTimeLimit = postEditTimeLimit})
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.DEPRECATED_DO_NOT_USE_AllowEditPost = allowEditPost })
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.PostEditTimeLimit = postEditTimeLimit })
|
||||
}()
|
||||
|
||||
th.App.UpdateConfig(func(cfg *model.Config) {
|
||||
|
||||
@@ -212,9 +212,9 @@ func (a *App) CreateChannel(channel *model.Channel, addMember bool) (*model.Chan
|
||||
}
|
||||
|
||||
if a.PluginsReady() {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.ChannelHasBeenCreated(pluginContext, sc)
|
||||
return true
|
||||
}, plugin.ChannelHasBeenCreatedId)
|
||||
@@ -239,9 +239,9 @@ func (a *App) CreateDirectChannel(userId string, otherUserId string) (*model.Cha
|
||||
a.InvalidateCacheForUser(otherUserId)
|
||||
|
||||
if a.PluginsReady() {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.ChannelHasBeenCreated(pluginContext, channel)
|
||||
return true
|
||||
}, plugin.ChannelHasBeenCreatedId)
|
||||
@@ -663,6 +663,10 @@ func (a *App) UpdateChannelMemberNotifyProps(data map[string]string, channelId s
|
||||
member.NotifyProps[model.PUSH_NOTIFY_PROP] = push
|
||||
}
|
||||
|
||||
if ignoreChannelMentions, exists := data[model.IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP]; exists {
|
||||
member.NotifyProps[model.IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP] = ignoreChannelMentions
|
||||
}
|
||||
|
||||
result := <-a.Srv.Store.Channel().UpdateMember(member)
|
||||
if result.Err != nil {
|
||||
return nil, result.Err
|
||||
@@ -854,9 +858,9 @@ func (a *App) AddChannelMember(userId string, channel *model.Channel, userReques
|
||||
}
|
||||
|
||||
if a.PluginsReady() {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasJoinedChannel(pluginContext, cm, userRequestor)
|
||||
return true
|
||||
}, plugin.UserHasJoinedChannelId)
|
||||
@@ -866,7 +870,7 @@ func (a *App) AddChannelMember(userId string, channel *model.Channel, userReques
|
||||
if userRequestorId == "" || userId == userRequestorId {
|
||||
a.postJoinChannelMessage(user, channel)
|
||||
} else {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
a.PostAddToChannelMessage(userRequestor, user, channel, postRootId)
|
||||
})
|
||||
}
|
||||
@@ -1244,9 +1248,9 @@ func (a *App) JoinChannel(channel *model.Channel, userId string) *model.AppError
|
||||
}
|
||||
|
||||
if a.PluginsReady() {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasJoinedChannel(pluginContext, cm, nil)
|
||||
return true
|
||||
}, plugin.UserHasJoinedChannelId)
|
||||
@@ -1336,7 +1340,7 @@ func (a *App) LeaveChannel(channelId string, userId string) *model.AppError {
|
||||
return nil
|
||||
}
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
a.postLeaveChannelMessage(user, channel)
|
||||
})
|
||||
|
||||
@@ -1451,9 +1455,9 @@ func (a *App) removeUserFromChannel(userIdToRemove string, removerUserId string,
|
||||
actorUser, _ = a.GetUser(removerUserId)
|
||||
}
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasLeftChannel(pluginContext, cm, actorUser)
|
||||
return true
|
||||
}, plugin.UserHasLeftChannelId)
|
||||
@@ -1489,7 +1493,7 @@ func (a *App) RemoveUserFromChannel(userIdToRemove string, removerUserId string,
|
||||
if userIdToRemove == removerUserId {
|
||||
a.postLeaveChannelMessage(user, channel)
|
||||
} else {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
a.postRemoveFromChannelMessage(removerUserId, user, channel)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,19 +13,19 @@ import (
|
||||
// be called.
|
||||
func (a *App) AddClusterLeaderChangedListener(listener func()) string {
|
||||
id := model.NewId()
|
||||
a.clusterLeaderListeners.Store(id, listener)
|
||||
a.Srv.clusterLeaderListeners.Store(id, listener)
|
||||
return id
|
||||
}
|
||||
|
||||
// Removes a listener function by the unique ID returned when AddConfigListener was called
|
||||
func (a *App) RemoveClusterLeaderChangedListener(id string) {
|
||||
a.clusterLeaderListeners.Delete(id)
|
||||
a.Srv.clusterLeaderListeners.Delete(id)
|
||||
}
|
||||
|
||||
func (a *App) InvokeClusterLeaderChangedListeners() {
|
||||
mlog.Info("Cluster leader changed. Invoking ClusterLeaderChanged listeners.")
|
||||
a.Go(func() {
|
||||
a.clusterLeaderListeners.Range(func(_, listener interface{}) bool {
|
||||
a.Srv.Go(func() {
|
||||
a.Srv.clusterLeaderListeners.Range(func(_, listener interface{}) bool {
|
||||
listener.(func())()
|
||||
return true
|
||||
})
|
||||
|
||||
@@ -78,7 +78,7 @@ func (me *EchoProvider) DoCommand(a *App, args *model.CommandArgs, message strin
|
||||
}
|
||||
|
||||
echoSem <- true
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
defer func() { <-echoSem }()
|
||||
post := &model.Post{}
|
||||
post.ChannelId = args.ChannelId
|
||||
|
||||
@@ -51,5 +51,5 @@ func (me *LeaveProvider) DoCommand(a *App, args *model.CommandArgs, message stri
|
||||
return &model.CommandResponse{Text: args.T("api.command_leave.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
return &model.CommandResponse{GotoLocation: args.SiteURL + "/" + team.Name + "/channels/" + model.DEFAULT_CHANNEL, Text: args.T("api.command_leave.success"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
return &model.CommandResponse{GotoLocation: args.SiteURL + "/" + team.Name + "/channels/" + model.DEFAULT_CHANNEL}
|
||||
}
|
||||
|
||||
86
app/command_leave_test.go
Обычный файл
86
app/command_leave_test.go
Обычный файл
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestLeaveProviderDoCommand(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
lp := LeaveProvider{}
|
||||
|
||||
publicChannel, _ := th.App.CreateChannel(&model.Channel{
|
||||
DisplayName: "AA",
|
||||
Name: "aa" + model.NewId() + "a",
|
||||
Type: model.CHANNEL_OPEN,
|
||||
TeamId: th.BasicTeam.Id,
|
||||
CreatorId: th.BasicUser.Id,
|
||||
}, false)
|
||||
|
||||
privateChannel, _ := th.App.CreateChannel(&model.Channel{
|
||||
DisplayName: "BB",
|
||||
Name: "aa" + model.NewId() + "a",
|
||||
Type: model.CHANNEL_OPEN,
|
||||
TeamId: th.BasicTeam.Id,
|
||||
CreatorId: th.BasicUser.Id,
|
||||
}, false)
|
||||
|
||||
th.App.AddUserToTeam(th.BasicTeam.Id, th.BasicUser.Id, th.BasicUser.Id)
|
||||
th.App.AddUserToChannel(th.BasicUser, publicChannel)
|
||||
th.App.AddUserToChannel(th.BasicUser, privateChannel)
|
||||
|
||||
args := &model.CommandArgs{
|
||||
T: func(s string, args ...interface{}) string { return s },
|
||||
}
|
||||
|
||||
// Should error when no Channel ID in args
|
||||
actual := lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "api.command_leave.fail.app_error", actual.Text)
|
||||
assert.Equal(t, model.COMMAND_RESPONSE_TYPE_EPHEMERAL, actual.ResponseType)
|
||||
|
||||
// Should error when no Team ID in args
|
||||
args.ChannelId = publicChannel.Id
|
||||
actual = lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "api.command_leave.fail.app_error", actual.Text)
|
||||
assert.Equal(t, model.COMMAND_RESPONSE_TYPE_EPHEMERAL, actual.ResponseType)
|
||||
|
||||
// Leave a public channel
|
||||
siteURL := "http://localhost:8065"
|
||||
args.TeamId = th.BasicTeam.Id
|
||||
args.SiteURL = siteURL
|
||||
actual = lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "", actual.Text)
|
||||
assert.Equal(t, siteURL+"/"+th.BasicTeam.Name+"/channels/"+model.DEFAULT_CHANNEL, actual.GotoLocation)
|
||||
assert.Equal(t, "", actual.ResponseType)
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
member, err := th.App.GetChannelMember(publicChannel.Id, th.BasicUser.Id)
|
||||
if member == nil {
|
||||
t.Errorf("Expected member object, got nil")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil object, got %s", err)
|
||||
}
|
||||
|
||||
// Leave a private channel
|
||||
args.ChannelId = privateChannel.Id
|
||||
actual = lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "", actual.Text)
|
||||
|
||||
// Should not leave a default channel
|
||||
defaultChannel, _ := th.App.GetChannelByName(model.DEFAULT_CHANNEL, th.BasicTeam.Id, false)
|
||||
args.ChannelId = defaultChannel.Id
|
||||
actual = lp.DoCommand(th.App, args, "")
|
||||
assert.Equal(t, "api.channel.leave.default.app_error", actual.Text)
|
||||
}
|
||||
@@ -36,7 +36,7 @@ func (a *App) SaveComplianceReport(job *model.Compliance) (*model.Compliance, *m
|
||||
}
|
||||
|
||||
job = result.Data.(*model.Compliance)
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
a.Compliance.RunComplianceJob(job)
|
||||
})
|
||||
|
||||
|
||||
@@ -28,15 +28,15 @@ const (
|
||||
)
|
||||
|
||||
func (a *App) Config() *model.Config {
|
||||
if cfg := a.config.Load(); cfg != nil {
|
||||
if cfg := a.Srv.config.Load(); cfg != nil {
|
||||
return cfg.(*model.Config)
|
||||
}
|
||||
return &model.Config{}
|
||||
}
|
||||
|
||||
func (a *App) EnvironmentConfig() map[string]interface{} {
|
||||
if a.envConfig != nil {
|
||||
return a.envConfig
|
||||
if a.Srv.envConfig != nil {
|
||||
return a.Srv.envConfig
|
||||
}
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ func (a *App) UpdateConfig(f func(*model.Config)) {
|
||||
old := a.Config()
|
||||
updated := old.Clone()
|
||||
f(updated)
|
||||
a.config.Store(updated)
|
||||
a.Srv.config.Store(updated)
|
||||
|
||||
a.InvokeConfigListeners(old, updated)
|
||||
}
|
||||
@@ -62,11 +62,10 @@ func (a *App) LoadConfig(configFile string) *model.AppError {
|
||||
return err
|
||||
}
|
||||
*cfg.ServiceSettings.SiteURL = strings.TrimRight(*cfg.ServiceSettings.SiteURL, "/")
|
||||
a.config.Store(cfg)
|
||||
a.Srv.config.Store(cfg)
|
||||
|
||||
a.configFile = configPath
|
||||
a.envConfig = envConfig
|
||||
a.siteURL = *cfg.ServiceSettings.SiteURL
|
||||
a.Srv.configFile = configPath
|
||||
a.Srv.envConfig = envConfig
|
||||
|
||||
a.InvokeConfigListeners(old, cfg)
|
||||
return nil
|
||||
@@ -74,7 +73,7 @@ func (a *App) LoadConfig(configFile string) *model.AppError {
|
||||
|
||||
func (a *App) ReloadConfig() *model.AppError {
|
||||
debug.FreeOSMemory()
|
||||
if err := a.LoadConfig(a.configFile); err != nil {
|
||||
if err := a.LoadConfig(a.Srv.configFile); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -84,37 +83,37 @@ func (a *App) ReloadConfig() *model.AppError {
|
||||
}
|
||||
|
||||
func (a *App) ConfigFileName() string {
|
||||
return a.configFile
|
||||
return a.Srv.configFile
|
||||
}
|
||||
|
||||
func (a *App) ClientConfig() map[string]string {
|
||||
return a.clientConfig
|
||||
return a.Srv.clientConfig
|
||||
}
|
||||
|
||||
func (a *App) ClientConfigHash() string {
|
||||
return a.clientConfigHash
|
||||
return a.Srv.clientConfigHash
|
||||
}
|
||||
|
||||
func (a *App) LimitedClientConfig() map[string]string {
|
||||
return a.limitedClientConfig
|
||||
return a.Srv.limitedClientConfig
|
||||
}
|
||||
|
||||
func (a *App) EnableConfigWatch() {
|
||||
if a.configWatcher == nil && !a.disableConfigWatch {
|
||||
if a.Srv.configWatcher == nil && !a.Srv.disableConfigWatch {
|
||||
configWatcher, err := utils.NewConfigWatcher(a.ConfigFileName(), func() {
|
||||
a.ReloadConfig()
|
||||
})
|
||||
if err != nil {
|
||||
mlog.Error(fmt.Sprint(err))
|
||||
}
|
||||
a.configWatcher = configWatcher
|
||||
a.Srv.configWatcher = configWatcher
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) DisableConfigWatch() {
|
||||
if a.configWatcher != nil {
|
||||
a.configWatcher.Close()
|
||||
a.configWatcher = nil
|
||||
if a.Srv.configWatcher != nil {
|
||||
a.Srv.configWatcher.Close()
|
||||
a.Srv.configWatcher = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,17 +122,17 @@ func (a *App) DisableConfigWatch() {
|
||||
// for the listener that can later be used to remove it.
|
||||
func (a *App) AddConfigListener(listener func(*model.Config, *model.Config)) string {
|
||||
id := model.NewId()
|
||||
a.configListeners[id] = listener
|
||||
a.Srv.configListeners[id] = listener
|
||||
return id
|
||||
}
|
||||
|
||||
// Removes a listener function by the unique ID returned when AddConfigListener was called
|
||||
func (a *App) RemoveConfigListener(id string) {
|
||||
delete(a.configListeners, id)
|
||||
delete(a.Srv.configListeners, id)
|
||||
}
|
||||
|
||||
func (a *App) InvokeConfigListeners(old, current *model.Config) {
|
||||
for _, listener := range a.configListeners {
|
||||
for _, listener := range a.Srv.configListeners {
|
||||
listener(old, current)
|
||||
}
|
||||
}
|
||||
@@ -141,7 +140,7 @@ func (a *App) InvokeConfigListeners(old, current *model.Config) {
|
||||
// EnsureAsymmetricSigningKey ensures that an asymmetric signing key exists and future calls to
|
||||
// AsymmetricSigningKey will always return a valid signing key.
|
||||
func (a *App) ensureAsymmetricSigningKey() error {
|
||||
if a.asymmetricSigningKey != nil {
|
||||
if a.Srv.asymmetricSigningKey != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -202,7 +201,7 @@ func (a *App) ensureAsymmetricSigningKey() error {
|
||||
default:
|
||||
return fmt.Errorf("unknown curve: " + key.ECDSAKey.Curve)
|
||||
}
|
||||
a.asymmetricSigningKey = &ecdsa.PrivateKey{
|
||||
a.Srv.asymmetricSigningKey = &ecdsa.PrivateKey{
|
||||
PublicKey: ecdsa.PublicKey{
|
||||
Curve: curve,
|
||||
X: key.ECDSAKey.X,
|
||||
@@ -240,31 +239,31 @@ func (a *App) ensureInstallationDate() error {
|
||||
|
||||
// AsymmetricSigningKey will return a private key that can be used for asymmetric signing.
|
||||
func (a *App) AsymmetricSigningKey() *ecdsa.PrivateKey {
|
||||
return a.asymmetricSigningKey
|
||||
return a.Srv.asymmetricSigningKey
|
||||
}
|
||||
|
||||
func (a *App) regenerateClientConfig() {
|
||||
a.clientConfig = utils.GenerateClientConfig(a.Config(), a.DiagnosticId(), a.License())
|
||||
a.Srv.clientConfig = utils.GenerateClientConfig(a.Config(), a.DiagnosticId(), a.License())
|
||||
|
||||
if a.clientConfig["EnableCustomTermsOfService"] == "true" {
|
||||
if a.Srv.clientConfig["EnableCustomTermsOfService"] == "true" {
|
||||
termsOfService, err := a.GetLatestTermsOfService()
|
||||
if err != nil {
|
||||
mlog.Err(err)
|
||||
} else {
|
||||
a.clientConfig["CustomTermsOfServiceId"] = termsOfService.Id
|
||||
a.Srv.clientConfig["CustomTermsOfServiceId"] = termsOfService.Id
|
||||
}
|
||||
}
|
||||
|
||||
a.limitedClientConfig = utils.GenerateLimitedClientConfig(a.Config(), a.DiagnosticId(), a.License())
|
||||
a.Srv.limitedClientConfig = utils.GenerateLimitedClientConfig(a.Config(), a.DiagnosticId(), a.License())
|
||||
|
||||
if key := a.AsymmetricSigningKey(); key != nil {
|
||||
der, _ := x509.MarshalPKIXPublicKey(&key.PublicKey)
|
||||
a.clientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der)
|
||||
a.limitedClientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der)
|
||||
a.Srv.clientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der)
|
||||
a.Srv.limitedClientConfig["AsymmetricSigningPublicKey"] = base64.StdEncoding.EncodeToString(der)
|
||||
}
|
||||
|
||||
clientConfigJSON, _ := json.Marshal(a.clientConfig)
|
||||
a.clientConfigHash = fmt.Sprintf("%x", md5.Sum(clientConfigJSON))
|
||||
clientConfigJSON, _ := json.Marshal(a.Srv.clientConfig)
|
||||
a.Srv.clientConfigHash = fmt.Sprintf("%x", md5.Sum(clientConfigJSON))
|
||||
}
|
||||
|
||||
func (a *App) Desanitize(cfg *model.Config) {
|
||||
@@ -322,7 +321,7 @@ func (a *App) GetCookieDomain() string {
|
||||
}
|
||||
|
||||
func (a *App) GetSiteURL() string {
|
||||
return a.siteURL
|
||||
return *a.Config().ServiceSettings.SiteURL
|
||||
}
|
||||
|
||||
// ClientConfigWithComputed gets the configuration in a format suitable for sending to the client.
|
||||
|
||||
@@ -35,11 +35,12 @@ func TestLoadConfig(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
tempConfig.Close()
|
||||
|
||||
a := App{}
|
||||
a := App{
|
||||
Srv: &Server{},
|
||||
}
|
||||
appErr := a.LoadConfig(tempConfig.Name())
|
||||
require.Nil(t, appErr)
|
||||
|
||||
assert.Equal(t, "http://localhost:8065", a.siteURL)
|
||||
assert.Equal(t, "http://localhost:8065", *a.GetConfig().ServiceSettings.SiteURL)
|
||||
}
|
||||
|
||||
|
||||
@@ -408,13 +408,14 @@ func (a *App) trackConfig() {
|
||||
})
|
||||
|
||||
a.SendDiagnostic(TRACK_CONFIG_SUPPORT, map[string]interface{}{
|
||||
"isdefault_terms_of_service_link": isDefault(*cfg.SupportSettings.TermsOfServiceLink, model.SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK),
|
||||
"isdefault_privacy_policy_link": isDefault(*cfg.SupportSettings.PrivacyPolicyLink, model.SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK),
|
||||
"isdefault_about_link": isDefault(*cfg.SupportSettings.AboutLink, model.SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK),
|
||||
"isdefault_help_link": isDefault(*cfg.SupportSettings.HelpLink, model.SUPPORT_SETTINGS_DEFAULT_HELP_LINK),
|
||||
"isdefault_report_a_problem_link": isDefault(*cfg.SupportSettings.ReportAProblemLink, model.SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK),
|
||||
"isdefault_support_email": isDefault(*cfg.SupportSettings.SupportEmail, model.SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL),
|
||||
"custom_terms_of_service_enabled": *cfg.SupportSettings.CustomTermsOfServiceEnabled,
|
||||
"isdefault_terms_of_service_link": isDefault(*cfg.SupportSettings.TermsOfServiceLink, model.SUPPORT_SETTINGS_DEFAULT_TERMS_OF_SERVICE_LINK),
|
||||
"isdefault_privacy_policy_link": isDefault(*cfg.SupportSettings.PrivacyPolicyLink, model.SUPPORT_SETTINGS_DEFAULT_PRIVACY_POLICY_LINK),
|
||||
"isdefault_about_link": isDefault(*cfg.SupportSettings.AboutLink, model.SUPPORT_SETTINGS_DEFAULT_ABOUT_LINK),
|
||||
"isdefault_help_link": isDefault(*cfg.SupportSettings.HelpLink, model.SUPPORT_SETTINGS_DEFAULT_HELP_LINK),
|
||||
"isdefault_report_a_problem_link": isDefault(*cfg.SupportSettings.ReportAProblemLink, model.SUPPORT_SETTINGS_DEFAULT_REPORT_A_PROBLEM_LINK),
|
||||
"isdefault_support_email": isDefault(*cfg.SupportSettings.SupportEmail, model.SUPPORT_SETTINGS_DEFAULT_SUPPORT_EMAIL),
|
||||
"custom_terms_of_service_enabled": *cfg.SupportSettings.CustomTermsOfServiceEnabled,
|
||||
"custom_terms_of_service_re_acceptance_period": *cfg.SupportSettings.CustomTermsOfServiceReAcceptancePeriod,
|
||||
})
|
||||
|
||||
a.SendDiagnostic(TRACK_CONFIG_LDAP, map[string]interface{}{
|
||||
@@ -590,7 +591,7 @@ func (a *App) trackPlugins() {
|
||||
settingsCount := 0
|
||||
|
||||
pluginStates := a.Config().PluginSettings.PluginStates
|
||||
plugins, _ := a.Plugins.Available()
|
||||
plugins, _ := a.Srv.Plugins.Available()
|
||||
|
||||
if pluginStates != nil && plugins != nil {
|
||||
for _, plugin := range plugins {
|
||||
|
||||
@@ -42,7 +42,7 @@ func (a *App) SetupInviteEmailRateLimiting() error {
|
||||
return errors.Wrap(err, "Unable to setup email rate limiting GCRA rate limiter.")
|
||||
}
|
||||
|
||||
a.EmailRateLimiter = rateLimiter
|
||||
a.Srv.EmailRateLimiter = rateLimiter
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -286,11 +286,11 @@ func (a *App) SendMfaChangeEmail(email string, activated bool, locale, siteURL s
|
||||
}
|
||||
|
||||
func (a *App) SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string) {
|
||||
if a.EmailRateLimiter == nil {
|
||||
if a.Srv.EmailRateLimiter == nil {
|
||||
a.Log.Error("Email invite not sent, rate limiting could not be setup.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id))
|
||||
return
|
||||
}
|
||||
rateLimited, result, err := a.EmailRateLimiter.RateLimit(senderUserId, len(invites))
|
||||
rateLimited, result, err := a.Srv.EmailRateLimiter.RateLimit(senderUserId, len(invites))
|
||||
if err != nil {
|
||||
a.Log.Error("Error rate limiting invite email.", mlog.String("user_id", senderUserId), mlog.String("team_id", team.Id), mlog.Err(err))
|
||||
return
|
||||
|
||||
@@ -25,13 +25,13 @@ const (
|
||||
|
||||
func (a *App) InitEmailBatching() {
|
||||
if *a.Config().EmailSettings.EnableEmailBatching {
|
||||
if a.EmailBatching == nil {
|
||||
a.EmailBatching = NewEmailBatchingJob(a, *a.Config().EmailSettings.EmailBatchingBufferSize)
|
||||
if a.Srv.EmailBatching == nil {
|
||||
a.Srv.EmailBatching = NewEmailBatchingJob(a, *a.Config().EmailSettings.EmailBatchingBufferSize)
|
||||
}
|
||||
|
||||
// note that we don't support changing EmailBatchingBufferSize without restarting the server
|
||||
|
||||
a.EmailBatching.Start()
|
||||
a.Srv.EmailBatching.Start()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ func (a *App) AddNotificationEmailToBatch(user *model.User, post *model.Post, te
|
||||
return model.NewAppError("AddNotificationEmailToBatch", "api.email_batching.add_notification_email_to_batch.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
if !a.EmailBatching.Add(user, post, team) {
|
||||
if !a.Srv.EmailBatching.Add(user, post, team) {
|
||||
mlog.Error("Email batching job's receiving channel was full. Please increase the EmailBatchingBufferSize.")
|
||||
return model.NewAppError("AddNotificationEmailToBatch", "api.email_batching.add_notification_email_to_batch.channel_full.app_error", nil, "", http.StatusInternalServerError)
|
||||
}
|
||||
@@ -188,7 +188,7 @@ func (job *EmailBatchingJob) checkPendingNotifications(now time.Time, handler fu
|
||||
|
||||
// send the email notification if it's been long enough
|
||||
if now.Sub(time.Unix(batchStartTime/1000, 0)) > time.Duration(interval)*time.Second {
|
||||
job.app.Go(func(userId string, notifications []*batchedNotification) func() {
|
||||
job.app.Srv.Go(func(userId string, notifications []*batchedNotification) func() {
|
||||
return func() {
|
||||
handler(userId, notifications)
|
||||
}
|
||||
|
||||
@@ -206,17 +206,21 @@ func (a *App) buildUserChannelMemberships(userId string, teamId string) (*[]User
|
||||
var memberships []UserChannelImportData
|
||||
|
||||
result := <-a.Srv.Store.Channel().GetChannelMembersForExport(userId, teamId)
|
||||
|
||||
if result.Err != nil {
|
||||
return nil, result.Err
|
||||
}
|
||||
|
||||
members := result.Data.([]*model.ChannelMemberForExport)
|
||||
|
||||
for _, member := range members {
|
||||
memberships = append(memberships, *ImportUserChannelDataFromChannelMember(member))
|
||||
category := model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL
|
||||
preferences, err := a.GetPreferenceByCategoryForUser(userId, category)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, member := range members {
|
||||
memberships = append(memberships, *ImportUserChannelDataFromChannelMemberAndPreferences(member, &preferences))
|
||||
}
|
||||
return &memberships, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ func ImportUserTeamDataFromTeamMember(member *model.TeamMemberForExport) *UserTe
|
||||
}
|
||||
}
|
||||
|
||||
func ImportUserChannelDataFromChannelMember(member *model.ChannelMemberForExport) *UserChannelImportData {
|
||||
func ImportUserChannelDataFromChannelMemberAndPreferences(member *model.ChannelMemberForExport, preferences *model.Preferences) *UserChannelImportData {
|
||||
rolesList := strings.Fields(member.Roles)
|
||||
if member.SchemeAdmin {
|
||||
rolesList = append(rolesList, model.CHANNEL_ADMIN_ROLE_ID)
|
||||
@@ -95,11 +95,19 @@ func ImportUserChannelDataFromChannelMember(member *model.ChannelMemberForExport
|
||||
notifyProps.MarkUnread = &markUnread
|
||||
}
|
||||
|
||||
favorite := false
|
||||
for _, preference := range *preferences {
|
||||
if member.ChannelId == preference.Name {
|
||||
favorite = true
|
||||
}
|
||||
}
|
||||
|
||||
roles := strings.Join(rolesList, " ")
|
||||
return &UserChannelImportData{
|
||||
Name: &member.ChannelName,
|
||||
Roles: &roles,
|
||||
NotifyProps: ¬ifyProps,
|
||||
Favorite: &favorite,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@ package app
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -60,7 +61,7 @@ func TestExportUserNotifyProps(t *testing.T) {
|
||||
require.Equal(t, userNotifyProps[model.MENTION_KEYS_NOTIFY_PROP], *exportNotifyProps.MentionKeys)
|
||||
}
|
||||
|
||||
func TestExportUserChannelsNotifyProps(t *testing.T) {
|
||||
func TestExportUserChannels(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
channel := th.BasicChannel
|
||||
@@ -71,22 +72,34 @@ func TestExportUserChannelsNotifyProps(t *testing.T) {
|
||||
model.DESKTOP_NOTIFY_PROP: model.USER_NOTIFY_ALL,
|
||||
model.PUSH_NOTIFY_PROP: model.USER_NOTIFY_NONE,
|
||||
}
|
||||
preference := model.Preference{
|
||||
UserId: user.Id,
|
||||
Category: model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL,
|
||||
Name: channel.Id,
|
||||
Value: "true",
|
||||
}
|
||||
var preferences model.Preferences
|
||||
preferences = append(preferences, preference)
|
||||
channelMember := model.ChannelMember{
|
||||
ChannelId: channel.Id,
|
||||
UserId: user.Id,
|
||||
}
|
||||
th.App.Srv.Store.Channel().SaveMember(&channelMember)
|
||||
th.App.Srv.Store.Preference().Save(&preferences)
|
||||
th.App.UpdateChannelMemberNotifyProps(notifyProps, channel.Id, user.Id)
|
||||
exportData, _ := th.App.buildUserChannelMemberships(user.Id, team.Id)
|
||||
assert.Equal(t, len(*exportData), 3)
|
||||
for _, data := range *exportData {
|
||||
if *data.Name == channelName {
|
||||
assert.Equal(t, *data.NotifyProps.Desktop, "all")
|
||||
assert.Equal(t, *data.NotifyProps.Mobile, "none")
|
||||
assert.Equal(t, *data.NotifyProps.MarkUnread, "all") // default value
|
||||
assert.True(t, *data.Favorite)
|
||||
} else { // default values
|
||||
assert.Equal(t, *data.NotifyProps.Desktop, "default")
|
||||
assert.Equal(t, *data.NotifyProps.Mobile, "default")
|
||||
assert.Equal(t, *data.NotifyProps.MarkUnread, "all")
|
||||
assert.False(t, *data.Favorite)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func (a *App) isExtensionSupportEnabled() bool {
|
||||
|
||||
21
app/file.go
21
app/file.go
@@ -397,6 +397,25 @@ func (a *App) UploadFiles(teamId string, channelId string, userId string, files
|
||||
return resStruct, nil
|
||||
}
|
||||
|
||||
// UploadFile uploads a single file in form of a completely constructed byte array for a channel.
|
||||
func (a *App) UploadFile(data []byte, channelId string, filename string) (*model.FileInfo, *model.AppError) {
|
||||
info, _, appError := a.DoUploadFileExpectModification(time.Now(), "noteam", channelId, "nouser", filename, data)
|
||||
|
||||
if appError != nil {
|
||||
return nil, appError
|
||||
}
|
||||
|
||||
if info.PreviewPath != "" || info.ThumbnailPath != "" {
|
||||
previewPathList := []string{info.PreviewPath}
|
||||
thumbnailPathList := []string{info.ThumbnailPath}
|
||||
imageDataList := [][]byte{data}
|
||||
|
||||
a.HandleImages(previewPathList, thumbnailPathList, imageDataList)
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (a *App) DoUploadFile(now time.Time, rawTeamId string, rawChannelId string, rawUserId string, rawFilename string, data []byte) (*model.FileInfo, *model.AppError) {
|
||||
info, _, err := a.DoUploadFileExpectModification(now, rawTeamId, rawChannelId, rawUserId, rawFilename, data)
|
||||
return info, err
|
||||
@@ -444,7 +463,7 @@ func (a *App) DoUploadFileExpectModification(now time.Time, rawTeamId string, ra
|
||||
if a.PluginsReady() {
|
||||
var rejectionError *model.AppError
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
var newBytes bytes.Buffer
|
||||
replacementInfo, rejectionReason := hooks.FileWillBeUploaded(pluginContext, info, bytes.NewReader(data), &newBytes)
|
||||
if rejectionReason != "" {
|
||||
|
||||
@@ -107,6 +107,30 @@ func TestDoUploadFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUploadFile(t *testing.T) {
|
||||
th := Setup()
|
||||
defer th.TearDown()
|
||||
|
||||
channelId := model.NewId()
|
||||
filename := "test"
|
||||
data := []byte("abcd")
|
||||
|
||||
info1, err := th.App.UploadFile(data, channelId, filename)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
defer func() {
|
||||
<-th.App.Srv.Store.FileInfo().PermanentDelete(info1.Id)
|
||||
th.App.RemoveFile(info1.Path)
|
||||
}()
|
||||
}
|
||||
|
||||
if info1.Path != fmt.Sprintf("%v/teams/noteam/channels/%v/users/nouser/%v/%v",
|
||||
time.Now().Format("20060102"), channelId, info1.Id, filename) {
|
||||
t.Fatal("stored file at incorrect path", info1.Path)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetInfoForFilename(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -40,9 +40,9 @@ func (a *App) GetJobsByType(jobType string, offset int, limit int) ([]*model.Job
|
||||
}
|
||||
|
||||
func (a *App) CreateJob(job *model.Job) (*model.Job, *model.AppError) {
|
||||
return a.Jobs.CreateJob(job.Type, job.Data)
|
||||
return a.Srv.Jobs.CreateJob(job.Type, job.Data)
|
||||
}
|
||||
|
||||
func (a *App) CancelJob(jobId string) *model.AppError {
|
||||
return a.Jobs.RequestCancellation(jobId)
|
||||
return a.Srv.Jobs.RequestCancellation(jobId)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
func (a *App) SyncLdap() {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
|
||||
if license := a.License(); license != nil && *license.Features.LDAP && *a.Config().LdapSettings.EnableSync {
|
||||
if ldapI := a.Ldap; ldapI != nil {
|
||||
@@ -67,7 +67,7 @@ func (a *App) SwitchEmailToLdap(email, password, code, ldapLoginId, ldapPassword
|
||||
return "", err
|
||||
}
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.SendSignInChangeEmail(user.Email, "AD/LDAP", user.Locale, a.GetSiteURL()); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
@@ -113,7 +113,7 @@ func (a *App) SwitchLdapToEmail(ldapPassword, code, email, newPassword string) (
|
||||
|
||||
T := utils.GetUserTranslations(user.Locale)
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, a.GetSiteURL()); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
|
||||
@@ -93,10 +93,10 @@ func (a *App) SaveLicense(licenseBytes []byte) (*model.License, *model.AppError)
|
||||
// doesn't start until the server is restarted, which prevents the 'run job now' buttons in system console from
|
||||
// functioning as expected
|
||||
if *a.Config().JobSettings.RunJobs {
|
||||
a.Jobs.StartWorkers()
|
||||
a.Srv.Jobs.StartWorkers()
|
||||
}
|
||||
if *a.Config().JobSettings.RunScheduler {
|
||||
a.Jobs.StartSchedulers()
|
||||
a.Srv.Jobs.StartSchedulers()
|
||||
}
|
||||
|
||||
return license, nil
|
||||
@@ -104,13 +104,13 @@ func (a *App) SaveLicense(licenseBytes []byte) (*model.License, *model.AppError)
|
||||
|
||||
// License returns the currently active license or nil if the application is unlicensed.
|
||||
func (a *App) License() *model.License {
|
||||
license, _ := a.licenseValue.Load().(*model.License)
|
||||
license, _ := a.Srv.licenseValue.Load().(*model.License)
|
||||
return license
|
||||
}
|
||||
|
||||
func (a *App) SetLicense(license *model.License) bool {
|
||||
defer func() {
|
||||
for _, listener := range a.licenseListeners {
|
||||
for _, listener := range a.Srv.licenseListeners {
|
||||
listener()
|
||||
}
|
||||
}()
|
||||
@@ -119,14 +119,14 @@ func (a *App) SetLicense(license *model.License) bool {
|
||||
license.Features.SetDefaults()
|
||||
|
||||
if !license.IsExpired() {
|
||||
a.licenseValue.Store(license)
|
||||
a.clientLicenseValue.Store(utils.GetClientLicense(license))
|
||||
a.Srv.licenseValue.Store(license)
|
||||
a.Srv.clientLicenseValue.Store(utils.GetClientLicense(license))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
a.licenseValue.Store((*model.License)(nil))
|
||||
a.clientLicenseValue.Store(map[string]string(nil))
|
||||
a.Srv.licenseValue.Store((*model.License)(nil))
|
||||
a.Srv.clientLicenseValue.Store(map[string]string(nil))
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -141,18 +141,18 @@ func (a *App) ValidateAndSetLicenseBytes(b []byte) {
|
||||
}
|
||||
|
||||
func (a *App) SetClientLicense(m map[string]string) {
|
||||
a.clientLicenseValue.Store(m)
|
||||
a.Srv.clientLicenseValue.Store(m)
|
||||
}
|
||||
|
||||
func (a *App) ClientLicense() map[string]string {
|
||||
if clientLicense, _ := a.clientLicenseValue.Load().(map[string]string); clientLicense != nil {
|
||||
if clientLicense, _ := a.Srv.clientLicenseValue.Load().(map[string]string); clientLicense != nil {
|
||||
return clientLicense
|
||||
}
|
||||
return map[string]string{"IsLicensed": "false"}
|
||||
}
|
||||
|
||||
func (a *App) RemoveLicense() *model.AppError {
|
||||
if license, _ := a.licenseValue.Load().(*model.License); license == nil {
|
||||
if license, _ := a.Srv.licenseValue.Load().(*model.License); license == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -174,12 +174,12 @@ func (a *App) RemoveLicense() *model.AppError {
|
||||
|
||||
func (a *App) AddLicenseListener(listener func()) string {
|
||||
id := model.NewId()
|
||||
a.licenseListeners[id] = listener
|
||||
a.Srv.licenseListeners[id] = listener
|
||||
return id
|
||||
}
|
||||
|
||||
func (a *App) RemoveLicenseListener(id string) {
|
||||
delete(a.licenseListeners, id)
|
||||
delete(a.Srv.licenseListeners, id)
|
||||
}
|
||||
|
||||
func (a *App) GetClientLicenseEtag(useSanitized bool) string {
|
||||
|
||||
@@ -69,7 +69,7 @@ func (a *App) AuthenticateUserForLogin(id, loginId, password, mfaToken string, l
|
||||
if a.PluginsReady() {
|
||||
var rejectionReason string
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
rejectionReason = hooks.UserWillLogIn(pluginContext, user)
|
||||
return rejectionReason == ""
|
||||
}, plugin.UserWillLogInId)
|
||||
@@ -78,9 +78,9 @@ func (a *App) AuthenticateUserForLogin(id, loginId, password, mfaToken string, l
|
||||
return nil, model.NewAppError("AuthenticateUserForLogin", "Login rejected by plugin: "+rejectionReason, nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasLoggedIn(pluginContext, user)
|
||||
return true
|
||||
}, plugin.UserHasLoggedInId)
|
||||
|
||||
@@ -78,13 +78,13 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
|
||||
}
|
||||
|
||||
if post.Type != model.POST_AUTO_RESPONDER {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
a.SendAutoResponse(channel, otherUser)
|
||||
})
|
||||
}
|
||||
|
||||
} else {
|
||||
keywords := a.GetMentionKeywordsInChannel(profileMap, post.Type != model.POST_HEADER_CHANGE && post.Type != model.POST_PURPOSE_CHANGE)
|
||||
keywords := a.GetMentionKeywordsInChannel(profileMap, post.Type != model.POST_HEADER_CHANGE && post.Type != model.POST_PURPOSE_CHANGE, channelMemberNotifyPropsMap)
|
||||
|
||||
m := GetExplicitMentions(post, keywords)
|
||||
|
||||
@@ -127,7 +127,7 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
|
||||
if result := <-a.Srv.Store.User().GetProfilesByUsernames(m.OtherPotentialMentions, team.Id); result.Err == nil {
|
||||
outOfChannelMentions := result.Data.([]*model.User)
|
||||
if channel.Type != model.CHANNEL_GROUP {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
a.sendOutOfChannelMentions(sender, post, outOfChannelMentions)
|
||||
})
|
||||
}
|
||||
@@ -560,7 +560,7 @@ func GetMentionsEnabledFields(post *model.Post) model.StringArray {
|
||||
|
||||
// Given a map of user IDs to profiles, returns a list of mention
|
||||
// keywords for all users in the channel.
|
||||
func (a *App) GetMentionKeywordsInChannel(profiles map[string]*model.User, lookForSpecialMentions bool) map[string][]string {
|
||||
func (a *App) GetMentionKeywordsInChannel(profiles map[string]*model.User, lookForSpecialMentions bool, channelMemberNotifyPropsMap map[string]model.StringMap) map[string][]string {
|
||||
keywords := make(map[string][]string)
|
||||
|
||||
for id, profile := range profiles {
|
||||
@@ -582,9 +582,16 @@ func (a *App) GetMentionKeywordsInChannel(profiles map[string]*model.User, lookF
|
||||
keywords[profile.FirstName] = append(keywords[profile.FirstName], profile.Id)
|
||||
}
|
||||
|
||||
ignoreChannelMentions := false
|
||||
if ignoreChannelMentionsNotifyProp, ok := channelMemberNotifyPropsMap[profile.Id][model.IGNORE_CHANNEL_MENTIONS_NOTIFY_PROP]; ok {
|
||||
if ignoreChannelMentionsNotifyProp == model.IGNORE_CHANNEL_MENTIONS_ON {
|
||||
ignoreChannelMentions = true
|
||||
}
|
||||
}
|
||||
|
||||
// Add @channel and @all to keywords if user has them turned on
|
||||
if lookForSpecialMentions {
|
||||
if int64(len(profiles)) <= *a.Config().TeamSettings.MaxNotificationsPerChannel && profile.NotifyProps["channel"] == "true" {
|
||||
if int64(len(profiles)) <= *a.Config().TeamSettings.MaxNotificationsPerChannel && profile.NotifyProps["channel"] == "true" && !ignoreChannelMentions {
|
||||
keywords["@channel"] = append(keywords["@channel"], profile.Id)
|
||||
keywords["@all"] = append(keywords["@all"], profile.Id)
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ func (a *App) sendNotificationEmail(notification *postNotification, user *model.
|
||||
teamURL := a.GetSiteURL() + "/" + team.Name
|
||||
var bodyText = a.getNotificationEmailBody(user, post, channel, channelName, senderName, team.Name, teamURL, emailNotificationContentsType, useMilitaryTime, translateFunc)
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.SendMail(user.Email, html.UnescapeString(subjectText), bodyText); err != nil {
|
||||
mlog.Error(fmt.Sprint("Error to send the email", user.Email, err))
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ func (a *App) sendPushNotification(notification *postNotification, user *model.U
|
||||
channelName := notification.GetChannelName(nameFormat, user.Id)
|
||||
senderName := notification.GetSenderName(nameFormat, cfg.ServiceSettings.EnablePostUsernameOverride)
|
||||
|
||||
c := a.PushNotificationsHub.GetGoChannelFromUserId(user.Id)
|
||||
c := a.Srv.PushNotificationsHub.GetGoChannelFromUserId(user.Id)
|
||||
c <- PushNotification{
|
||||
notificationType: NOTIFICATION_TYPE_MESSAGE,
|
||||
post: post,
|
||||
@@ -217,7 +217,7 @@ func (a *App) ClearPushNotificationSync(userId string, channelId string) {
|
||||
}
|
||||
|
||||
func (a *App) ClearPushNotification(userId string, channelId string) {
|
||||
channel := a.PushNotificationsHub.GetGoChannelFromUserId(userId)
|
||||
channel := a.Srv.PushNotificationsHub.GetGoChannelFromUserId(userId)
|
||||
channel <- PushNotification{
|
||||
notificationType: NOTIFICATION_TYPE_CLEAR,
|
||||
userId: userId,
|
||||
@@ -232,7 +232,7 @@ func (a *App) CreatePushNotificationsHub() {
|
||||
for x := 0; x < PUSH_NOTIFICATION_HUB_WORKERS; x++ {
|
||||
hub.Channels = append(hub.Channels, make(chan PushNotification, PUSH_NOTIFICATIONS_HUB_BUFFER_PER_WORKER))
|
||||
}
|
||||
a.PushNotificationsHub = hub
|
||||
a.Srv.PushNotificationsHub = hub
|
||||
}
|
||||
|
||||
func (a *App) pushNotificationWorker(notifications chan PushNotification) {
|
||||
@@ -259,13 +259,13 @@ func (a *App) pushNotificationWorker(notifications chan PushNotification) {
|
||||
|
||||
func (a *App) StartPushNotificationsHubWorkers() {
|
||||
for x := 0; x < PUSH_NOTIFICATION_HUB_WORKERS; x++ {
|
||||
channel := a.PushNotificationsHub.Channels[x]
|
||||
a.Go(func() { a.pushNotificationWorker(channel) })
|
||||
channel := a.Srv.PushNotificationsHub.Channels[x]
|
||||
a.Srv.Go(func() { a.pushNotificationWorker(channel) })
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) StopPushNotificationsHubWorkers() {
|
||||
for _, channel := range a.PushNotificationsHub.Channels {
|
||||
for _, channel := range a.Srv.PushNotificationsHub.Channels {
|
||||
close(channel)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -625,8 +625,14 @@ func TestGetMentionKeywords(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
channelMemberNotifyPropsMap1Off := map[string]model.StringMap{
|
||||
user1.Id: {
|
||||
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
|
||||
},
|
||||
}
|
||||
|
||||
profiles := map[string]*model.User{user1.Id: user1}
|
||||
mentions := th.App.GetMentionKeywordsInChannel(profiles, true)
|
||||
mentions := th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap1Off)
|
||||
if len(mentions) != 3 {
|
||||
t.Fatal("should've returned three mention keywords")
|
||||
} else if ids, ok := mentions["user"]; !ok || ids[0] != user1.Id {
|
||||
@@ -647,8 +653,14 @@ func TestGetMentionKeywords(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
channelMemberNotifyPropsMap2Off := map[string]model.StringMap{
|
||||
user2.Id: {
|
||||
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
|
||||
},
|
||||
}
|
||||
|
||||
profiles = map[string]*model.User{user2.Id: user2}
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true)
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap2Off)
|
||||
if len(mentions) != 2 {
|
||||
t.Fatal("should've returned two mention keyword")
|
||||
} else if ids, ok := mentions["First"]; !ok || ids[0] != user2.Id {
|
||||
@@ -665,8 +677,14 @@ func TestGetMentionKeywords(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
// Channel-wide mentions are not ignored on channel level
|
||||
channelMemberNotifyPropsMap3Off := map[string]model.StringMap{
|
||||
user3.Id: {
|
||||
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
|
||||
},
|
||||
}
|
||||
profiles = map[string]*model.User{user3.Id: user3}
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true)
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap3Off)
|
||||
if len(mentions) != 3 {
|
||||
t.Fatal("should've returned three mention keywords")
|
||||
} else if ids, ok := mentions["@channel"]; !ok || ids[0] != user3.Id {
|
||||
@@ -675,6 +693,45 @@ func TestGetMentionKeywords(t *testing.T) {
|
||||
t.Fatal("should've returned mention key of @all")
|
||||
}
|
||||
|
||||
// Channel member notify props is set to default
|
||||
channelMemberNotifyPropsMapDefault := map[string]model.StringMap{
|
||||
user3.Id: {
|
||||
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_DEFAULT,
|
||||
},
|
||||
}
|
||||
profiles = map[string]*model.User{user3.Id: user3}
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMapDefault)
|
||||
if len(mentions) != 3 {
|
||||
t.Fatal("should've returned three mention keywords")
|
||||
} else if ids, ok := mentions["@channel"]; !ok || ids[0] != user3.Id {
|
||||
t.Fatal("should've returned mention key of @channel")
|
||||
} else if ids, ok := mentions["@all"]; !ok || ids[0] != user3.Id {
|
||||
t.Fatal("should've returned mention key of @all")
|
||||
}
|
||||
|
||||
// Channel member notify props is empty
|
||||
channelMemberNotifyPropsMapEmpty := map[string]model.StringMap{}
|
||||
profiles = map[string]*model.User{user3.Id: user3}
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMapEmpty)
|
||||
if len(mentions) != 3 {
|
||||
t.Fatal("should've returned three mention keywords")
|
||||
} else if ids, ok := mentions["@channel"]; !ok || ids[0] != user3.Id {
|
||||
t.Fatal("should've returned mention key of @channel")
|
||||
} else if ids, ok := mentions["@all"]; !ok || ids[0] != user3.Id {
|
||||
t.Fatal("should've returned mention key of @all")
|
||||
}
|
||||
|
||||
// Channel-wide mentions are ignored channel level
|
||||
channelMemberNotifyPropsMap3On := map[string]model.StringMap{
|
||||
user3.Id: {
|
||||
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_ON,
|
||||
},
|
||||
}
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap3On)
|
||||
if len(mentions) == 0 {
|
||||
t.Fatal("should've not returned any keywords")
|
||||
}
|
||||
|
||||
// user with all types of mentions enabled
|
||||
user4 := &model.User{
|
||||
Id: model.NewId(),
|
||||
@@ -687,8 +744,15 @@ func TestGetMentionKeywords(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
// Channel-wide mentions are not ignored on channel level
|
||||
channelMemberNotifyPropsMap4Off := map[string]model.StringMap{
|
||||
user4.Id: {
|
||||
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
|
||||
},
|
||||
}
|
||||
|
||||
profiles = map[string]*model.User{user4.Id: user4}
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true)
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap4Off)
|
||||
if len(mentions) != 6 {
|
||||
t.Fatal("should've returned six mention keywords")
|
||||
} else if ids, ok := mentions["user"]; !ok || ids[0] != user4.Id {
|
||||
@@ -705,6 +769,25 @@ func TestGetMentionKeywords(t *testing.T) {
|
||||
t.Fatal("should've returned mention key of @all")
|
||||
}
|
||||
|
||||
// Channel-wide mentions are ignored on channel level
|
||||
channelMemberNotifyPropsMap4On := map[string]model.StringMap{
|
||||
user4.Id: {
|
||||
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_ON,
|
||||
},
|
||||
}
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap4On)
|
||||
if len(mentions) != 4 {
|
||||
t.Fatal("should've returned four mention keywords")
|
||||
} else if ids, ok := mentions["user"]; !ok || ids[0] != user4.Id {
|
||||
t.Fatal("should've returned mention key of user")
|
||||
} else if ids, ok := mentions["@user"]; !ok || ids[0] != user4.Id {
|
||||
t.Fatal("should've returned mention key of @user")
|
||||
} else if ids, ok := mentions["mention"]; !ok || ids[0] != user4.Id {
|
||||
t.Fatal("should've returned mention key of mention")
|
||||
} else if ids, ok := mentions["First"]; !ok || ids[0] != user4.Id {
|
||||
t.Fatal("should've returned mention key of First")
|
||||
}
|
||||
|
||||
dup_count := func(list []string) map[string]int {
|
||||
|
||||
duplicate_frequency := make(map[string]int)
|
||||
@@ -731,7 +814,22 @@ func TestGetMentionKeywords(t *testing.T) {
|
||||
user3.Id: user3,
|
||||
user4.Id: user4,
|
||||
}
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true)
|
||||
// Channel-wide mentions are not ignored on channel level for all users
|
||||
channelMemberNotifyPropsMap5Off := map[string]model.StringMap{
|
||||
user1.Id: {
|
||||
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
|
||||
},
|
||||
user2.Id: {
|
||||
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
|
||||
},
|
||||
user3.Id: {
|
||||
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
|
||||
},
|
||||
user4.Id: {
|
||||
"ignore_channel_mentions": model.IGNORE_CHANNEL_MENTIONS_OFF,
|
||||
},
|
||||
}
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap5Off)
|
||||
if len(mentions) != 6 {
|
||||
t.Fatal("should've returned six mention keywords")
|
||||
} else if ids, ok := mentions["user"]; !ok || len(ids) != 2 || (ids[0] != user1.Id && ids[1] != user1.Id) || (ids[0] != user4.Id && ids[1] != user4.Id) {
|
||||
@@ -750,7 +848,7 @@ func TestGetMentionKeywords(t *testing.T) {
|
||||
|
||||
// multiple users and more than MaxNotificationsPerChannel
|
||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.MaxNotificationsPerChannel = 3 })
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true)
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, true, channelMemberNotifyPropsMap4Off)
|
||||
if len(mentions) != 4 {
|
||||
t.Fatal("should've returned four mention keywords")
|
||||
} else if _, ok := mentions["@channel"]; ok {
|
||||
@@ -765,7 +863,7 @@ func TestGetMentionKeywords(t *testing.T) {
|
||||
profiles = map[string]*model.User{
|
||||
user1.Id: user1,
|
||||
}
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, false)
|
||||
mentions = th.App.GetMentionKeywordsInChannel(profiles, false, channelMemberNotifyPropsMap4Off)
|
||||
if len(mentions) != 3 {
|
||||
t.Fatal("should've returned three mention keywords")
|
||||
} else if ids, ok := mentions["user"]; !ok || len(ids) != 1 || ids[0] != user1.Id {
|
||||
|
||||
@@ -601,7 +601,7 @@ func (a *App) CompleteSwitchWithOAuth(service string, userData io.ReadCloser, em
|
||||
return nil, result.Err
|
||||
}
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.SendSignInChangeEmail(user.Email, strings.Title(service)+" SSO", user.Locale, a.GetSiteURL()); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
@@ -859,7 +859,7 @@ func (a *App) SwitchOAuthToEmail(email, password, requesterId string) (string, *
|
||||
|
||||
T := utils.GetUserTranslations(user.Locale)
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.SendSignInChangeEmail(user.Email, T("api.templates.signin_change_email.body.method_email"), user.Locale, a.GetSiteURL()); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ func StoreOverride(override interface{}) Option {
|
||||
return func(a *App) {
|
||||
switch o := override.(type) {
|
||||
case store.Store:
|
||||
a.newStore = func() store.Store {
|
||||
a.Srv.newStore = func() store.Store {
|
||||
return o
|
||||
}
|
||||
case func(*App) store.Store:
|
||||
a.newStore = func() store.Store {
|
||||
a.Srv.newStore = func() store.Store {
|
||||
return o(a)
|
||||
}
|
||||
default:
|
||||
@@ -32,10 +32,10 @@ func StoreOverride(override interface{}) Option {
|
||||
|
||||
func ConfigFile(file string) Option {
|
||||
return func(a *App) {
|
||||
a.configFile = file
|
||||
a.Srv.configFile = file
|
||||
}
|
||||
}
|
||||
|
||||
func DisableConfigWatch(a *App) {
|
||||
a.disableConfigWatch = true
|
||||
a.Srv.disableConfigWatch = true
|
||||
}
|
||||
|
||||
@@ -16,21 +16,21 @@ import (
|
||||
)
|
||||
|
||||
func (a *App) SyncPluginsActiveState() {
|
||||
if a.Plugins == nil {
|
||||
if a.Srv.Plugins == nil {
|
||||
return
|
||||
}
|
||||
|
||||
config := a.Config().PluginSettings
|
||||
|
||||
if *config.Enable {
|
||||
availablePlugins, err := a.Plugins.Available()
|
||||
availablePlugins, err := a.Srv.Plugins.Available()
|
||||
if err != nil {
|
||||
a.Log.Error("Unable to get available plugins", mlog.Err(err))
|
||||
return
|
||||
}
|
||||
|
||||
// Deactivate any plugins that have been disabled.
|
||||
for _, plugin := range a.Plugins.Active() {
|
||||
for _, plugin := range a.Srv.Plugins.Active() {
|
||||
// Determine if plugin is enabled
|
||||
pluginId := plugin.Manifest.Id
|
||||
pluginEnabled := false
|
||||
@@ -40,7 +40,7 @@ func (a *App) SyncPluginsActiveState() {
|
||||
|
||||
// If it's not enabled we need to deactivate it
|
||||
if !pluginEnabled {
|
||||
deactivated := a.Plugins.Deactivate(pluginId)
|
||||
deactivated := a.Srv.Plugins.Deactivate(pluginId)
|
||||
if deactivated && plugin.Manifest.HasClient() {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PLUGIN_DISABLED, "", "", "", nil)
|
||||
message.Add("manifest", plugin.Manifest.ClientManifest())
|
||||
@@ -65,7 +65,7 @@ func (a *App) SyncPluginsActiveState() {
|
||||
|
||||
// Activate plugin if enabled
|
||||
if pluginEnabled {
|
||||
updatedManifest, activated, err := a.Plugins.Activate(pluginId)
|
||||
updatedManifest, activated, err := a.Srv.Plugins.Activate(pluginId)
|
||||
if err != nil {
|
||||
plugin.WrapLogger(a.Log).Error("Unable to activate plugin", mlog.Err(err))
|
||||
continue
|
||||
@@ -79,7 +79,7 @@ func (a *App) SyncPluginsActiveState() {
|
||||
}
|
||||
}
|
||||
} else { // If plugins are disabled, shutdown plugins.
|
||||
a.Plugins.Shutdown()
|
||||
a.Srv.Plugins.Shutdown()
|
||||
}
|
||||
|
||||
if err := a.notifyPluginStatusesChanged(); err != nil {
|
||||
@@ -92,7 +92,7 @@ func (a *App) NewPluginAPI(manifest *model.Manifest) plugin.API {
|
||||
}
|
||||
|
||||
func (a *App) InitPlugins(pluginDir, webappPluginDir string) {
|
||||
if a.Plugins != nil || !*a.Config().PluginSettings.Enable {
|
||||
if a.Srv.Plugins != nil || !*a.Config().PluginSettings.Enable {
|
||||
a.SyncPluginsActiveState()
|
||||
return
|
||||
}
|
||||
@@ -113,7 +113,7 @@ func (a *App) InitPlugins(pluginDir, webappPluginDir string) {
|
||||
mlog.Error("Failed to start up plugins", mlog.Err(err))
|
||||
return
|
||||
} else {
|
||||
a.Plugins = env
|
||||
a.Srv.Plugins = env
|
||||
}
|
||||
|
||||
prepackagedPluginsDir, found := utils.FindDir("prepackaged_plugins")
|
||||
@@ -136,10 +136,10 @@ func (a *App) InitPlugins(pluginDir, webappPluginDir string) {
|
||||
}
|
||||
|
||||
// Sync plugin active state when config changes. Also notify plugins.
|
||||
a.RemoveConfigListener(a.PluginConfigListenerId)
|
||||
a.PluginConfigListenerId = a.AddConfigListener(func(*model.Config, *model.Config) {
|
||||
a.RemoveConfigListener(a.Srv.PluginConfigListenerId)
|
||||
a.Srv.PluginConfigListenerId = a.AddConfigListener(func(*model.Config, *model.Config) {
|
||||
a.SyncPluginsActiveState()
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.OnConfigurationChange()
|
||||
return true
|
||||
}, plugin.OnConfigurationChangeId)
|
||||
@@ -150,25 +150,25 @@ func (a *App) InitPlugins(pluginDir, webappPluginDir string) {
|
||||
}
|
||||
|
||||
func (a *App) ShutDownPlugins() {
|
||||
if a.Plugins == nil {
|
||||
if a.Srv.Plugins == nil {
|
||||
return
|
||||
}
|
||||
|
||||
mlog.Info("Shutting down plugins")
|
||||
|
||||
a.Plugins.Shutdown()
|
||||
a.Srv.Plugins.Shutdown()
|
||||
|
||||
a.RemoveConfigListener(a.PluginConfigListenerId)
|
||||
a.PluginConfigListenerId = ""
|
||||
a.Plugins = nil
|
||||
a.RemoveConfigListener(a.Srv.PluginConfigListenerId)
|
||||
a.Srv.PluginConfigListenerId = ""
|
||||
a.Srv.Plugins = nil
|
||||
}
|
||||
|
||||
func (a *App) GetActivePluginManifests() ([]*model.Manifest, *model.AppError) {
|
||||
if a.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
if a.Srv.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
return nil, model.NewAppError("GetActivePluginManifests", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
plugins := a.Plugins.Active()
|
||||
plugins := a.Srv.Plugins.Active()
|
||||
|
||||
manifests := make([]*model.Manifest, len(plugins))
|
||||
for i, plugin := range plugins {
|
||||
@@ -181,11 +181,11 @@ func (a *App) GetActivePluginManifests() ([]*model.Manifest, *model.AppError) {
|
||||
// EnablePlugin will set the config for an installed plugin to enabled, triggering asynchronous
|
||||
// activation if inactive anywhere in the cluster.
|
||||
func (a *App) EnablePlugin(id string) *model.AppError {
|
||||
if a.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
if a.Srv.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
return model.NewAppError("EnablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
plugins, err := a.Plugins.Available()
|
||||
plugins, err := a.Srv.Plugins.Available()
|
||||
if err != nil {
|
||||
return model.NewAppError("EnablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -221,11 +221,11 @@ func (a *App) EnablePlugin(id string) *model.AppError {
|
||||
|
||||
// DisablePlugin will set the config for an installed plugin to disabled, triggering deactivation if active.
|
||||
func (a *App) DisablePlugin(id string) *model.AppError {
|
||||
if a.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
if a.Srv.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
return model.NewAppError("DisablePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
plugins, err := a.Plugins.Available()
|
||||
plugins, err := a.Srv.Plugins.Available()
|
||||
if err != nil {
|
||||
return model.NewAppError("DisablePlugin", "app.plugin.config.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -256,7 +256,7 @@ func (a *App) DisablePlugin(id string) *model.AppError {
|
||||
}
|
||||
|
||||
func (a *App) PluginsReady() bool {
|
||||
return a.Plugins != nil && *a.Config().PluginSettings.Enable
|
||||
return a.Srv.Plugins != nil && *a.Config().PluginSettings.Enable
|
||||
}
|
||||
|
||||
func (a *App) GetPlugins() (*model.PluginsResponse, *model.AppError) {
|
||||
@@ -264,7 +264,7 @@ func (a *App) GetPlugins() (*model.PluginsResponse, *model.AppError) {
|
||||
return nil, model.NewAppError("GetPlugins", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
availablePlugins, err := a.Plugins.Available()
|
||||
availablePlugins, err := a.Srv.Plugins.Available()
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetPlugins", "app.plugin.get_plugins.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -278,7 +278,7 @@ func (a *App) GetPlugins() (*model.PluginsResponse, *model.AppError) {
|
||||
Manifest: *plugin.Manifest,
|
||||
}
|
||||
|
||||
if a.Plugins.IsActive(plugin.Manifest.Id) {
|
||||
if a.Srv.Plugins.IsActive(plugin.Manifest.Id) {
|
||||
resp.Active = append(resp.Active, info)
|
||||
} else {
|
||||
resp.Inactive = append(resp.Inactive, info)
|
||||
|
||||
@@ -107,6 +107,10 @@ func (api *PluginAPI) GetTeamByName(name string) (*model.Team, *model.AppError)
|
||||
return api.app.GetTeamByName(name)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) GetTeamsUnreadForUser(userId string) ([]*model.TeamUnread, *model.AppError) {
|
||||
return api.app.GetTeamsUnreadForUser("", userId)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) {
|
||||
return api.app.UpdateTeam(team)
|
||||
}
|
||||
@@ -385,6 +389,10 @@ func (api *PluginAPI) GetProfileImage(userId string) ([]byte, *model.AppError) {
|
||||
return data, err
|
||||
}
|
||||
|
||||
func (api *PluginAPI) GetEmojiList(sortBy string, page, perPage int) ([]*model.Emoji, *model.AppError) {
|
||||
return api.app.GetEmojiList(page, perPage, sortBy)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) GetEmojiByName(name string) (*model.Emoji, *model.AppError) {
|
||||
return api.app.GetEmojiByName(name)
|
||||
}
|
||||
@@ -422,10 +430,49 @@ func (api *PluginAPI) ReadFile(path string) ([]byte, *model.AppError) {
|
||||
return api.app.ReadFile(path)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) UploadFile(data []byte, channelId string, filename string) (*model.FileInfo, *model.AppError) {
|
||||
return api.app.UploadFile(data, channelId, filename)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) GetEmojiImage(emojiId string) ([]byte, string, *model.AppError) {
|
||||
return api.app.GetEmojiImage(emojiId)
|
||||
}
|
||||
|
||||
// Plugin Section
|
||||
|
||||
func (api *PluginAPI) GetPlugins() ([]*model.Manifest, *model.AppError) {
|
||||
plugins, err := api.app.GetPlugins()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var manifests []*model.Manifest
|
||||
for _, manifest := range plugins.Active {
|
||||
manifests = append(manifests, &manifest.Manifest)
|
||||
}
|
||||
for _, manifest := range plugins.Inactive {
|
||||
manifests = append(manifests, &manifest.Manifest)
|
||||
}
|
||||
return manifests, nil
|
||||
}
|
||||
|
||||
func (api *PluginAPI) EnablePlugin(id string) *model.AppError {
|
||||
return api.app.EnablePlugin(id)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) DisablePlugin(id string) *model.AppError {
|
||||
return api.app.DisablePlugin(id)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) RemovePlugin(id string) *model.AppError {
|
||||
return api.app.RemovePlugin(id)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) {
|
||||
return api.app.GetPluginStatus(id)
|
||||
}
|
||||
|
||||
// KV Store Section
|
||||
|
||||
func (api *PluginAPI) KVSet(key string, value []byte) *model.AppError {
|
||||
return api.app.SetPluginKey(api.id, key, value)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -31,9 +32,12 @@ func setupPluginApiTest(t *testing.T, pluginCode string, pluginManifest string,
|
||||
compileGo(t, pluginCode, backend)
|
||||
|
||||
ioutil.WriteFile(filepath.Join(pluginDir, pluginId, "plugin.json"), []byte(pluginManifest), 0600)
|
||||
env.Activate(pluginId)
|
||||
manifest, activated, reterr := env.Activate(pluginId)
|
||||
require.Nil(t, reterr)
|
||||
require.NotNil(t, manifest)
|
||||
require.True(t, activated)
|
||||
|
||||
app.Plugins = env
|
||||
app.Srv.Plugins = env
|
||||
}
|
||||
|
||||
func TestPluginAPIUpdateUserStatus(t *testing.T) {
|
||||
@@ -120,7 +124,7 @@ func TestPluginAPILoadPluginConfiguration(t *testing.T) {
|
||||
}
|
||||
]
|
||||
}}`, "testloadpluginconfig", th.App)
|
||||
hooks, err := th.App.Plugins.HooksForPlugin("testloadpluginconfig")
|
||||
hooks, err := th.App.Srv.Plugins.HooksForPlugin("testloadpluginconfig")
|
||||
assert.NoError(t, err)
|
||||
_, ret := hooks.MessageWillBePosted(nil, nil)
|
||||
assert.Equal(t, "str32true", ret)
|
||||
@@ -194,7 +198,7 @@ func TestPluginAPILoadPluginConfigurationDefaults(t *testing.T) {
|
||||
}
|
||||
]
|
||||
}}`, "testloadpluginconfig", th.App)
|
||||
hooks, err := th.App.Plugins.HooksForPlugin("testloadpluginconfig")
|
||||
hooks, err := th.App.Srv.Plugins.HooksForPlugin("testloadpluginconfig")
|
||||
assert.NoError(t, err)
|
||||
_, ret := hooks.MessageWillBePosted(nil, nil)
|
||||
assert.Equal(t, "override35true", ret)
|
||||
@@ -215,3 +219,61 @@ func TestPluginAPIGetProfileImage(t *testing.T) {
|
||||
require.NotNil(t, err)
|
||||
require.Nil(t, data)
|
||||
}
|
||||
|
||||
func TestPluginAPIGetPlugins(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
api := th.SetupPluginAPI()
|
||||
|
||||
pluginCode := `
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/plugin"
|
||||
)
|
||||
|
||||
type MyPlugin struct {
|
||||
plugin.MattermostPlugin
|
||||
}
|
||||
|
||||
func main() {
|
||||
plugin.ClientMain(&MyPlugin{})
|
||||
}
|
||||
`
|
||||
|
||||
pluginDir, err := ioutil.TempDir("", "")
|
||||
require.NoError(t, err)
|
||||
webappPluginDir, err := ioutil.TempDir("", "")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(pluginDir)
|
||||
defer os.RemoveAll(webappPluginDir)
|
||||
|
||||
env, err := plugin.NewEnvironment(th.App.NewPluginAPI, pluginDir, webappPluginDir, th.App.Log)
|
||||
require.NoError(t, err)
|
||||
|
||||
pluginIDs := []string{"pluginid1", "pluginid2", "pluginid3"}
|
||||
var pluginManifests []*model.Manifest
|
||||
for _, pluginID := range pluginIDs {
|
||||
backend := filepath.Join(pluginDir, pluginID, "backend.exe")
|
||||
compileGo(t, pluginCode, backend)
|
||||
|
||||
ioutil.WriteFile(filepath.Join(pluginDir, pluginID, "plugin.json"), []byte(fmt.Sprintf(`{"id": "%s", "server": {"executable": "backend.exe"}}`, pluginID)), 0600)
|
||||
manifest, activated, reterr := env.Activate(pluginID)
|
||||
|
||||
require.Nil(t, reterr)
|
||||
require.NotNil(t, manifest)
|
||||
require.True(t, activated)
|
||||
pluginManifests = append(pluginManifests, manifest)
|
||||
}
|
||||
th.App.Srv.Plugins = env
|
||||
|
||||
// Decative the last one for testing
|
||||
sucess := env.Deactivate(pluginIDs[len(pluginIDs)-1])
|
||||
require.True(t, sucess)
|
||||
|
||||
// check existing user first
|
||||
plugins, err := api.GetPlugins()
|
||||
assert.Nil(t, err)
|
||||
assert.NotEmpty(t, plugins)
|
||||
assert.Equal(t, pluginManifests, plugins)
|
||||
}
|
||||
|
||||
@@ -31,10 +31,10 @@ func (a *App) RegisterPluginCommand(pluginId string, command *model.Command) err
|
||||
DisplayName: command.DisplayName,
|
||||
}
|
||||
|
||||
a.pluginCommandsLock.Lock()
|
||||
defer a.pluginCommandsLock.Unlock()
|
||||
a.Srv.pluginCommandsLock.Lock()
|
||||
defer a.Srv.pluginCommandsLock.Unlock()
|
||||
|
||||
for _, pc := range a.pluginCommands {
|
||||
for _, pc := range a.Srv.pluginCommands {
|
||||
if pc.Command.Trigger == command.Trigger && pc.Command.TeamId == command.TeamId {
|
||||
if pc.PluginId == pluginId {
|
||||
pc.Command = command
|
||||
@@ -43,7 +43,7 @@ func (a *App) RegisterPluginCommand(pluginId string, command *model.Command) err
|
||||
}
|
||||
}
|
||||
|
||||
a.pluginCommands = append(a.pluginCommands, &PluginCommand{
|
||||
a.Srv.pluginCommands = append(a.Srv.pluginCommands, &PluginCommand{
|
||||
Command: command,
|
||||
PluginId: pluginId,
|
||||
})
|
||||
@@ -53,37 +53,37 @@ func (a *App) RegisterPluginCommand(pluginId string, command *model.Command) err
|
||||
func (a *App) UnregisterPluginCommand(pluginId, teamId, trigger string) {
|
||||
trigger = strings.ToLower(trigger)
|
||||
|
||||
a.pluginCommandsLock.Lock()
|
||||
defer a.pluginCommandsLock.Unlock()
|
||||
a.Srv.pluginCommandsLock.Lock()
|
||||
defer a.Srv.pluginCommandsLock.Unlock()
|
||||
|
||||
var remaining []*PluginCommand
|
||||
for _, pc := range a.pluginCommands {
|
||||
for _, pc := range a.Srv.pluginCommands {
|
||||
if pc.Command.TeamId != teamId || pc.Command.Trigger != trigger {
|
||||
remaining = append(remaining, pc)
|
||||
}
|
||||
}
|
||||
a.pluginCommands = remaining
|
||||
a.Srv.pluginCommands = remaining
|
||||
}
|
||||
|
||||
func (a *App) UnregisterPluginCommands(pluginId string) {
|
||||
a.pluginCommandsLock.Lock()
|
||||
defer a.pluginCommandsLock.Unlock()
|
||||
a.Srv.pluginCommandsLock.Lock()
|
||||
defer a.Srv.pluginCommandsLock.Unlock()
|
||||
|
||||
var remaining []*PluginCommand
|
||||
for _, pc := range a.pluginCommands {
|
||||
for _, pc := range a.Srv.pluginCommands {
|
||||
if pc.PluginId != pluginId {
|
||||
remaining = append(remaining, pc)
|
||||
}
|
||||
}
|
||||
a.pluginCommands = remaining
|
||||
a.Srv.pluginCommands = remaining
|
||||
}
|
||||
|
||||
func (a *App) PluginCommandsForTeam(teamId string) []*model.Command {
|
||||
a.pluginCommandsLock.RLock()
|
||||
defer a.pluginCommandsLock.RUnlock()
|
||||
a.Srv.pluginCommandsLock.RLock()
|
||||
defer a.Srv.pluginCommandsLock.RUnlock()
|
||||
|
||||
var commands []*model.Command
|
||||
for _, pc := range a.pluginCommands {
|
||||
for _, pc := range a.Srv.pluginCommands {
|
||||
if pc.Command.TeamId == "" || pc.Command.TeamId == teamId {
|
||||
commands = append(commands, pc.Command)
|
||||
}
|
||||
@@ -96,12 +96,12 @@ func (a *App) ExecutePluginCommand(args *model.CommandArgs) (*model.Command, *mo
|
||||
trigger := parts[0][1:]
|
||||
trigger = strings.ToLower(trigger)
|
||||
|
||||
a.pluginCommandsLock.RLock()
|
||||
defer a.pluginCommandsLock.RUnlock()
|
||||
a.Srv.pluginCommandsLock.RLock()
|
||||
defer a.Srv.pluginCommandsLock.RUnlock()
|
||||
|
||||
for _, pc := range a.pluginCommands {
|
||||
for _, pc := range a.Srv.pluginCommands {
|
||||
if (pc.Command.TeamId == "" || pc.Command.TeamId == args.TeamId) && pc.Command.Trigger == trigger {
|
||||
pluginHooks, err := a.Plugins.HooksForPlugin(pc.PluginId)
|
||||
pluginHooks, err := a.Srv.Plugins.HooksForPlugin(pc.PluginId)
|
||||
if err != nil {
|
||||
return pc.Command, nil, model.NewAppError("ExecutePluginCommand", "model.plugin_command.error.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func SetAppEnvironmentWithPlugins(t *testing.T, pluginCode []string, app *App, a
|
||||
env, err := plugin.NewEnvironment(apiFunc, pluginDir, webappPluginDir, app.Log)
|
||||
require.NoError(t, err)
|
||||
|
||||
app.Plugins = env
|
||||
app.Srv.Plugins = env
|
||||
pluginIds := []string{}
|
||||
activationErrors := []error{}
|
||||
for _, code := range pluginCode {
|
||||
|
||||
@@ -22,7 +22,7 @@ func (a *App) InstallPlugin(pluginFile io.Reader, replace bool) (*model.Manifest
|
||||
}
|
||||
|
||||
func (a *App) installPlugin(pluginFile io.Reader, replace bool) (*model.Manifest, *model.AppError) {
|
||||
if a.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
if a.Srv.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
return nil, model.NewAppError("installPlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func (a *App) installPlugin(pluginFile io.Reader, replace bool) (*model.Manifest
|
||||
return nil, model.NewAppError("installPlugin", "app.plugin.invalid_id.app_error", map[string]interface{}{"Min": plugin.MinIdLength, "Max": plugin.MaxIdLength, "Regex": plugin.ValidIdRegex}, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
bundles, err := a.Plugins.Available()
|
||||
bundles, err := a.Srv.Plugins.Available()
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("installPlugin", "app.plugin.install.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -91,11 +91,11 @@ func (a *App) RemovePlugin(id string) *model.AppError {
|
||||
}
|
||||
|
||||
func (a *App) removePlugin(id string) *model.AppError {
|
||||
if a.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
if a.Srv.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
return model.NewAppError("removePlugin", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
plugins, err := a.Plugins.Available()
|
||||
plugins, err := a.Srv.Plugins.Available()
|
||||
if err != nil {
|
||||
return model.NewAppError("removePlugin", "app.plugin.deactivate.app_error", nil, err.Error(), http.StatusBadRequest)
|
||||
}
|
||||
@@ -114,13 +114,13 @@ func (a *App) removePlugin(id string) *model.AppError {
|
||||
return model.NewAppError("removePlugin", "app.plugin.not_installed.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
if a.Plugins.IsActive(id) && manifest.HasClient() {
|
||||
if a.Srv.Plugins.IsActive(id) && manifest.HasClient() {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PLUGIN_DISABLED, "", "", "", nil)
|
||||
message.Add("manifest", manifest.ClientManifest())
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
a.Plugins.Deactivate(id)
|
||||
a.Srv.Plugins.Deactivate(id)
|
||||
a.UnregisterPluginCommands(id)
|
||||
|
||||
err = os.RemoveAll(pluginPath)
|
||||
|
||||
@@ -9,16 +9,17 @@ import (
|
||||
"strings"
|
||||
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/plugin"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func (a *App) ServePluginRequest(w http.ResponseWriter, r *http.Request) {
|
||||
if a.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
if a.Srv.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
err := model.NewAppError("ServePluginRequest", "app.plugin.disabled.app_error", nil, "Enable plugins to serve plugin requests", http.StatusNotImplemented)
|
||||
a.Log.Error(err.Error())
|
||||
w.WriteHeader(err.StatusCode)
|
||||
@@ -28,7 +29,7 @@ func (a *App) ServePluginRequest(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
params := mux.Vars(r)
|
||||
hooks, err := a.Plugins.HooksForPlugin(params["plugin_id"])
|
||||
hooks, err := a.Srv.Plugins.HooksForPlugin(params["plugin_id"])
|
||||
if err != nil {
|
||||
a.Log.Error("Access to route for non-existent plugin", mlog.String("missing_plugin_id", params["plugin_id"]), mlog.Err(err))
|
||||
http.NotFound(w, r)
|
||||
|
||||
@@ -9,13 +9,34 @@ import (
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
// GetPluginStatus returns the status for a plugin installed on this server.
|
||||
func (a *App) GetPluginStatus(id string) (*model.PluginStatus, *model.AppError) {
|
||||
if a.Srv.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
return nil, model.NewAppError("GetPluginStatus", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
pluginStatuses, err := a.Srv.Plugins.Statuses()
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetPluginStatus", "app.plugin.get_statuses.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
// Add our cluster ID
|
||||
for _, status := range pluginStatuses {
|
||||
if status.PluginId == id {
|
||||
status.ClusterId = a.GetClusterId()
|
||||
return status, nil
|
||||
}
|
||||
}
|
||||
return nil, model.NewAppError("GetPluginStatus", "app.plugin.not_installed.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// GetPluginStatuses returns the status for plugins installed on this server.
|
||||
func (a *App) GetPluginStatuses() (model.PluginStatuses, *model.AppError) {
|
||||
if a.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
if a.Srv.Plugins == nil || !*a.Config().PluginSettings.Enable {
|
||||
return nil, model.NewAppError("GetPluginStatuses", "app.plugin.disabled.app_error", nil, "", http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
pluginStatuses, err := a.Plugins.Statuses()
|
||||
pluginStatuses, err := a.Srv.Plugins.Statuses()
|
||||
if err != nil {
|
||||
return nil, model.NewAppError("GetPluginStatuses", "app.plugin.get_statuses.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
24
app/post.go
24
app/post.go
@@ -146,7 +146,7 @@ func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhoo
|
||||
if a.PluginsReady() {
|
||||
var rejectionError *model.AppError
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
replacementPost, rejectionReason := hooks.MessageWillBePosted(pluginContext, post)
|
||||
if rejectionReason != "" {
|
||||
rejectionError = model.NewAppError("createPost", "Post rejected by plugin. "+rejectionReason, nil, "", http.StatusBadRequest)
|
||||
@@ -170,9 +170,9 @@ func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhoo
|
||||
rpost := result.Data.(*model.Post)
|
||||
|
||||
if a.PluginsReady() {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.MessageHasBeenPosted(pluginContext, rpost)
|
||||
return true
|
||||
}, plugin.MessageHasBeenPostedId)
|
||||
@@ -181,7 +181,7 @@ func (a *App) CreatePost(post *model.Post, channel *model.Channel, triggerWebhoo
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
esInterface.IndexPost(rpost, channel.TeamId)
|
||||
})
|
||||
}
|
||||
@@ -273,7 +273,7 @@ func (a *App) handlePostEvents(post *model.Post, user *model.User, channel *mode
|
||||
}
|
||||
|
||||
if triggerWebhooks {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.handleWebhookEvents(post, team, channel, user); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
@@ -363,7 +363,7 @@ func (a *App) UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model
|
||||
if a.PluginsReady() {
|
||||
var rejectionReason string
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
newPost, rejectionReason = hooks.MessageWillBeUpdated(pluginContext, newPost, oldPost)
|
||||
return post != nil
|
||||
}, plugin.MessageWillBeUpdatedId)
|
||||
@@ -379,9 +379,9 @@ func (a *App) UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model
|
||||
rpost := result.Data.(*model.Post)
|
||||
|
||||
if a.PluginsReady() {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.MessageHasBeenUpdated(pluginContext, newPost, oldPost)
|
||||
return true
|
||||
}, plugin.MessageHasBeenUpdatedId)
|
||||
@@ -390,7 +390,7 @@ func (a *App) UpdatePost(post *model.Post, safeUpdate bool) (*model.Post, *model
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
rchannel := <-a.Srv.Store.Channel().GetForPost(rpost.Id)
|
||||
if rchannel.Err != nil {
|
||||
mlog.Error(fmt.Sprintf("Couldn't get channel %v for post %v for Elasticsearch indexing.", rpost.ChannelId, rpost.Id))
|
||||
@@ -578,16 +578,16 @@ func (a *App) DeletePost(postId, deleteByID string) (*model.Post, *model.AppErro
|
||||
message.Add("post", clientPost.ToJson())
|
||||
a.Publish(message)
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
a.DeletePostFiles(post)
|
||||
})
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
a.DeleteFlaggedPosts(post.Id)
|
||||
})
|
||||
|
||||
esInterface := a.Elasticsearch
|
||||
if esInterface != nil && *a.Config().ElasticsearchSettings.EnableIndexing {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
esInterface.DeletePost(post)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -528,9 +528,9 @@ func TestMaxPostSize(t *testing.T) {
|
||||
|
||||
app := App{
|
||||
Srv: &Server{
|
||||
Store: mockStore,
|
||||
Store: mockStore,
|
||||
config: atomic.Value{},
|
||||
},
|
||||
config: atomic.Value{},
|
||||
}
|
||||
|
||||
assert.Equal(t, testCase.ExpectedMaxPostSize, app.MaxPostSize())
|
||||
|
||||
@@ -46,7 +46,7 @@ func (a *App) SaveReactionForPost(reaction *model.Reaction) (*model.Reaction, *m
|
||||
// The post is always modified since the UpdateAt always changes
|
||||
a.InvalidateCacheForChannelPosts(post.ChannelId)
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
a.sendReactionEvent(model.WEBSOCKET_EVENT_REACTION_ADDED, reaction, post, true)
|
||||
})
|
||||
|
||||
@@ -99,7 +99,7 @@ func (a *App) DeleteReactionForPost(reaction *model.Reaction) *model.AppError {
|
||||
// The post is always modified since the UpdateAt always changes
|
||||
a.InvalidateCacheForChannelPosts(post.ChannelId)
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
a.sendReactionEvent(model.WEBSOCKET_EVENT_REACTION_REMOVED, reaction, post, hasReactions)
|
||||
})
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ func (a *App) sendUpdatedRoleEvent(role *model.Role) {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_ROLE_UPDATED, "", "", "", nil)
|
||||
message.Add("role", role.ToJson())
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ func (a *App) GetChannelsForScheme(scheme *model.Scheme, offset int, limit int)
|
||||
}
|
||||
|
||||
func (a *App) IsPhase2MigrationCompleted() *model.AppError {
|
||||
if a.phase2PermissionsMigrationComplete {
|
||||
if a.Srv.phase2PermissionsMigrationComplete {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ func (a *App) IsPhase2MigrationCompleted() *model.AppError {
|
||||
return model.NewAppError("App.IsPhase2MigrationCompleted", "app.schemes.is_phase_2_migration_completed.not_completed.app_error", nil, result.Err.Error(), http.StatusNotImplemented)
|
||||
}
|
||||
|
||||
a.phase2PermissionsMigrationComplete = true
|
||||
a.Srv.phase2PermissionsMigrationComplete = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -14,16 +15,21 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/cors"
|
||||
"github.com/throttled/throttled"
|
||||
"golang.org/x/crypto/acme/autocert"
|
||||
|
||||
"github.com/mattermost/mattermost-server/jobs"
|
||||
"github.com/mattermost/mattermost-server/mlog"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/plugin"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
@@ -44,6 +50,79 @@ type Server struct {
|
||||
RateLimiter *RateLimiter
|
||||
|
||||
didFinishListen chan struct{}
|
||||
|
||||
goroutineCount int32
|
||||
goroutineExitSignal chan struct{}
|
||||
|
||||
Plugins *plugin.Environment
|
||||
PluginConfigListenerId string
|
||||
|
||||
EmailBatching *EmailBatchingJob
|
||||
EmailRateLimiter *throttled.GCRARateLimiter
|
||||
|
||||
Hubs []*Hub
|
||||
HubsStopCheckingForDeadlock chan bool
|
||||
|
||||
PushNotificationsHub PushNotificationsHub
|
||||
|
||||
Jobs *jobs.JobServer
|
||||
|
||||
config atomic.Value
|
||||
envConfig map[string]interface{}
|
||||
configFile string
|
||||
configListeners map[string]func(*model.Config, *model.Config)
|
||||
clusterLeaderListeners sync.Map
|
||||
|
||||
licenseValue atomic.Value
|
||||
clientLicenseValue atomic.Value
|
||||
licenseListeners map[string]func()
|
||||
|
||||
timezones atomic.Value
|
||||
|
||||
newStore func() store.Store
|
||||
|
||||
htmlTemplateWatcher *utils.HTMLTemplateWatcher
|
||||
sessionCache *utils.Cache
|
||||
configListenerId string
|
||||
licenseListenerId string
|
||||
logListenerId string
|
||||
clusterLeaderListenerId string
|
||||
disableConfigWatch bool
|
||||
configWatcher *utils.ConfigWatcher
|
||||
asymmetricSigningKey *ecdsa.PrivateKey
|
||||
|
||||
pluginCommands []*PluginCommand
|
||||
pluginCommandsLock sync.RWMutex
|
||||
|
||||
clientConfig map[string]string
|
||||
clientConfigHash string
|
||||
limitedClientConfig map[string]string
|
||||
diagnosticId string
|
||||
|
||||
phase2PermissionsMigrationComplete bool
|
||||
}
|
||||
|
||||
// Go creates a goroutine, but maintains a record of it to ensure that execution completes before
|
||||
// the app is destroyed.
|
||||
func (s *Server) Go(f func()) {
|
||||
atomic.AddInt32(&s.goroutineCount, 1)
|
||||
|
||||
go func() {
|
||||
f()
|
||||
|
||||
atomic.AddInt32(&s.goroutineCount, -1)
|
||||
select {
|
||||
case s.goroutineExitSignal <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// WaitForGoroutines blocks until all goroutines created by App.Go exit.
|
||||
func (s *Server) WaitForGoroutines() {
|
||||
for atomic.LoadInt32(&s.goroutineCount) != 0 {
|
||||
<-s.goroutineExitSignal
|
||||
}
|
||||
}
|
||||
|
||||
var corsAllowedMethods = []string{
|
||||
|
||||
@@ -5,13 +5,14 @@ package app
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -24,7 +25,7 @@ func TestStartServerSuccess(t *testing.T) {
|
||||
serverErr := a.StartServer()
|
||||
|
||||
client := &http.Client{}
|
||||
checkEndpoint(t, client, "http://localhost:" + strconv.Itoa(a.Srv.ListenAddr.Port) + "/", http.StatusNotFound)
|
||||
checkEndpoint(t, client, "http://localhost:"+strconv.Itoa(a.Srv.ListenAddr.Port)+"/", http.StatusNotFound)
|
||||
|
||||
a.Shutdown()
|
||||
require.NoError(t, serverErr)
|
||||
@@ -77,7 +78,7 @@ func TestStartServerTLSSuccess(t *testing.T) {
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: tr}
|
||||
checkEndpoint(t, client, "https://localhost:" + strconv.Itoa(a.Srv.ListenAddr.Port) + "/", http.StatusNotFound)
|
||||
checkEndpoint(t, client, "https://localhost:"+strconv.Itoa(a.Srv.ListenAddr.Port)+"/", http.StatusNotFound)
|
||||
|
||||
a.Shutdown()
|
||||
require.NoError(t, serverErr)
|
||||
@@ -100,12 +101,12 @@ func TestStartServerTLSVersion(t *testing.T) {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
MaxVersion: tls.VersionTLS11,
|
||||
MaxVersion: tls.VersionTLS11,
|
||||
},
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: tr}
|
||||
err = checkEndpoint(t, client, "https://localhost:" + strconv.Itoa(a.Srv.ListenAddr.Port) + "/", http.StatusNotFound)
|
||||
err = checkEndpoint(t, client, "https://localhost:"+strconv.Itoa(a.Srv.ListenAddr.Port)+"/", http.StatusNotFound)
|
||||
|
||||
if !strings.Contains(err.Error(), "remote error: tls: protocol version not supported") {
|
||||
t.Errorf("Expected protocol version error, got %s", err)
|
||||
@@ -117,7 +118,7 @@ func TestStartServerTLSVersion(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
err = checkEndpoint(t, client, "https://localhost:" + strconv.Itoa(a.Srv.ListenAddr.Port) + "/", http.StatusNotFound)
|
||||
err = checkEndpoint(t, client, "https://localhost:"+strconv.Itoa(a.Srv.ListenAddr.Port)+"/", http.StatusNotFound)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil, got %s", err)
|
||||
@@ -154,7 +155,7 @@ func TestStartServerTLSOverwriteCipher(t *testing.T) {
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: tr}
|
||||
err = checkEndpoint(t, client, "https://localhost:" + strconv.Itoa(a.Srv.ListenAddr.Port) + "/", http.StatusNotFound)
|
||||
err = checkEndpoint(t, client, "https://localhost:"+strconv.Itoa(a.Srv.ListenAddr.Port)+"/", http.StatusNotFound)
|
||||
|
||||
if !strings.Contains(err.Error(), "remote error: tls: handshake failure") {
|
||||
t.Errorf("Expected protocol version error, got %s", err)
|
||||
@@ -170,7 +171,7 @@ func TestStartServerTLSOverwriteCipher(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
err = checkEndpoint(t, client, "https://localhost:" + strconv.Itoa(a.Srv.ListenAddr.Port) + "/", http.StatusNotFound)
|
||||
err = checkEndpoint(t, client, "https://localhost:"+strconv.Itoa(a.Srv.ListenAddr.Port)+"/", http.StatusNotFound)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Expected nil, got %s", err)
|
||||
|
||||
@@ -29,7 +29,7 @@ func (a *App) GetSession(token string) (*model.Session, *model.AppError) {
|
||||
metrics := a.Metrics
|
||||
|
||||
var session *model.Session
|
||||
if ts, ok := a.sessionCache.Get(token); ok {
|
||||
if ts, ok := a.Srv.sessionCache.Get(token); ok {
|
||||
session = ts.(*model.Session)
|
||||
if metrics != nil {
|
||||
metrics.IncrementMemCacheHitCounterSession()
|
||||
@@ -137,13 +137,13 @@ func (a *App) ClearSessionCacheForUser(userId string) {
|
||||
}
|
||||
|
||||
func (a *App) ClearSessionCacheForUserSkipClusterSend(userId string) {
|
||||
keys := a.sessionCache.Keys()
|
||||
keys := a.Srv.sessionCache.Keys()
|
||||
|
||||
for _, key := range keys {
|
||||
if ts, ok := a.sessionCache.Get(key); ok {
|
||||
if ts, ok := a.Srv.sessionCache.Get(key); ok {
|
||||
session := ts.(*model.Session)
|
||||
if session.UserId == userId {
|
||||
a.sessionCache.Remove(key)
|
||||
a.Srv.sessionCache.Remove(key)
|
||||
if a.Metrics != nil {
|
||||
a.Metrics.IncrementMemCacheInvalidationCounterSession()
|
||||
}
|
||||
@@ -155,11 +155,11 @@ func (a *App) ClearSessionCacheForUserSkipClusterSend(userId string) {
|
||||
}
|
||||
|
||||
func (a *App) AddSessionToCache(session *model.Session) {
|
||||
a.sessionCache.AddWithExpiresInSecs(session.Token, session, int64(*a.Config().ServiceSettings.SessionCacheInMinutes*60))
|
||||
a.Srv.sessionCache.AddWithExpiresInSecs(session.Token, session, int64(*a.Config().ServiceSettings.SessionCacheInMinutes*60))
|
||||
}
|
||||
|
||||
func (a *App) SessionCacheLength() int {
|
||||
return a.sessionCache.Len()
|
||||
return a.Srv.sessionCache.Len()
|
||||
}
|
||||
|
||||
func (a *App) RevokeSessionsForDeviceId(userId string, deviceId string, currentSessionId string) *model.AppError {
|
||||
|
||||
@@ -22,16 +22,16 @@ func TestCache(t *testing.T) {
|
||||
UserId: model.NewId(),
|
||||
}
|
||||
|
||||
th.App.sessionCache.AddWithExpiresInSecs(session.Token, session, 5*60)
|
||||
th.App.Srv.sessionCache.AddWithExpiresInSecs(session.Token, session, 5*60)
|
||||
|
||||
keys := th.App.sessionCache.Keys()
|
||||
keys := th.App.Srv.sessionCache.Keys()
|
||||
if len(keys) <= 0 {
|
||||
t.Fatal("should have items")
|
||||
}
|
||||
|
||||
th.App.ClearSessionCacheForUser(session.UserId)
|
||||
|
||||
rkeys := th.App.sessionCache.Keys()
|
||||
rkeys := th.App.Srv.sessionCache.Keys()
|
||||
if len(rkeys) != len(keys)-1 {
|
||||
t.Fatal("should have one less")
|
||||
}
|
||||
|
||||
@@ -466,9 +466,9 @@ func (a *App) JoinUserToTeam(team *model.Team, user *model.User, userRequestorId
|
||||
actor, _ = a.GetUser(userRequestorId)
|
||||
}
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasJoinedTeam(pluginContext, tm, actor)
|
||||
return true
|
||||
}, plugin.UserHasJoinedTeamId)
|
||||
@@ -789,9 +789,9 @@ func (a *App) LeaveTeam(team *model.Team, user *model.User, requestorId string)
|
||||
actor, _ = a.GetUser(requestorId)
|
||||
}
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
pluginContext := &plugin.Context{}
|
||||
a.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
a.Srv.Plugins.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
|
||||
hooks.UserHasLeftTeam(pluginContext, teamMember, actor)
|
||||
return true
|
||||
}, plugin.UserHasLeftTeamId)
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func (a *App) Timezones() model.SupportedTimezones {
|
||||
if cfg := a.timezones.Load(); cfg != nil {
|
||||
if cfg := a.Srv.timezones.Load(); cfg != nil {
|
||||
return cfg.(model.SupportedTimezones)
|
||||
}
|
||||
return model.SupportedTimezones{}
|
||||
@@ -24,5 +24,5 @@ func (a *App) LoadTimezones() {
|
||||
|
||||
timezoneCfg := utils.LoadTimezones(timezonePath)
|
||||
|
||||
a.timezones.Store(timezoneCfg)
|
||||
a.Srv.timezones.Store(timezoneCfg)
|
||||
}
|
||||
|
||||
29
app/user.go
29
app/user.go
@@ -1034,14 +1034,14 @@ func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User,
|
||||
|
||||
if sendNotifications {
|
||||
if rusers[0].Email != rusers[1].Email {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.SendEmailChangeEmail(rusers[1].Email, rusers[0].Email, rusers[0].Locale, a.GetSiteURL()); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
})
|
||||
|
||||
if a.Config().EmailSettings.RequireEmailVerification {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.SendEmailVerification(rusers[0]); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
@@ -1050,7 +1050,7 @@ func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User,
|
||||
}
|
||||
|
||||
if rusers[0].Username != rusers[1].Username {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.SendChangeUsernameEmail(rusers[1].Username, rusers[0].Username, rusers[0].Email, rusers[0].Locale, a.GetSiteURL()); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
@@ -1090,7 +1090,7 @@ func (a *App) UpdateMfa(activate bool, userId, token string) *model.AppError {
|
||||
}
|
||||
}
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
user, err := a.GetUser(userId)
|
||||
if err != nil {
|
||||
mlog.Error(err.Error())
|
||||
@@ -1133,7 +1133,7 @@ func (a *App) UpdatePasswordSendEmail(user *model.User, newPassword, method stri
|
||||
return err
|
||||
}
|
||||
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
if err := a.SendPasswordChangeEmail(user.Email, method, user.Locale, a.GetSiteURL()); err != nil {
|
||||
mlog.Error(err.Error())
|
||||
}
|
||||
@@ -1622,22 +1622,3 @@ func (a *App) UpdateOAuthUserAttrs(userData io.Reader, user *model.User, provide
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) RecordUserTermsOfServiceAction(userId, termsOfServiceId string, accepted bool) *model.AppError {
|
||||
user, err := a.GetUser(userId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if accepted {
|
||||
user.AcceptedTermsOfServiceId = termsOfServiceId
|
||||
} else {
|
||||
user.AcceptedTermsOfServiceId = ""
|
||||
}
|
||||
_, err = a.UpdateUser(user, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
33
app/user_terms_of_service.go
Обычный файл
33
app/user_terms_of_service.go
Обычный файл
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import "github.com/mattermost/mattermost-server/model"
|
||||
|
||||
func (a *App) GetUserTermsOfService(userId string) (*model.UserTermsOfService, *model.AppError) {
|
||||
if result := <-a.Srv.Store.UserTermsOfService().GetByUser(userId); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
return result.Data.(*model.UserTermsOfService), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) SaveUserTermsOfService(userId, termsOfServiceId string, accepted bool) *model.AppError {
|
||||
if accepted {
|
||||
userTermsOfService := &model.UserTermsOfService{
|
||||
UserId: userId,
|
||||
TermsOfServiceId: termsOfServiceId,
|
||||
}
|
||||
|
||||
if result := <-a.Srv.Store.UserTermsOfService().Save(userTermsOfService); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
} else {
|
||||
if result := <-a.Srv.Store.UserTermsOfService().Delete(userId, termsOfServiceId); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
34
app/user_terms_of_service_test.go
Обычный файл
34
app/user_terms_of_service_test.go
Обычный файл
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUserTermsOfService(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
userTermsOfService, err := th.App.GetUserTermsOfService(th.BasicUser.Id)
|
||||
checkError(t, err)
|
||||
assert.Nil(t, userTermsOfService)
|
||||
assert.Equal(t, "store.sql_user_terms_of_service.get_by_user.no_rows.app_error", err.Id)
|
||||
|
||||
termsOfService, err := th.App.CreateTermsOfService("terms of service", th.BasicUser.Id)
|
||||
checkNoError(t, err)
|
||||
|
||||
err = th.App.SaveUserTermsOfService(th.BasicUser.Id, termsOfService.Id, true)
|
||||
checkNoError(t, err)
|
||||
|
||||
userTermsOfService, err = th.App.GetUserTermsOfService(th.BasicUser.Id)
|
||||
checkNoError(t, err)
|
||||
assert.NotNil(t, userTermsOfService)
|
||||
assert.NotEmpty(t, userTermsOfService)
|
||||
|
||||
assert.Equal(t, th.BasicUser.Id, userTermsOfService.UserId)
|
||||
assert.Equal(t, termsOfService.Id, userTermsOfService.TermsOfServiceId)
|
||||
assert.NotEmpty(t, userTermsOfService.CreateAt)
|
||||
}
|
||||
@@ -544,43 +544,3 @@ func TestPermanentDeleteUser(t *testing.T) {
|
||||
t.Fatal("GetFileInfo after DeleteUser is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordUserTermsOfServiceAction(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
user := &model.User{
|
||||
Email: strings.ToLower(model.NewId()) + "success+test@example.com",
|
||||
Nickname: "Luke Skywalker", // trying to bring balance to the "Force", one test user at a time
|
||||
Username: "luke" + model.NewId(),
|
||||
Password: "passwd1",
|
||||
AuthService: "",
|
||||
}
|
||||
user, err := th.App.CreateUser(user)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create user: %v", err)
|
||||
}
|
||||
|
||||
defer th.App.PermanentDeleteUser(user)
|
||||
|
||||
termsOfService, err := th.App.CreateTermsOfService("text", user.Id)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create terms of service: %v", err)
|
||||
}
|
||||
|
||||
err = th.App.RecordUserTermsOfServiceAction(user.Id, termsOfService.Id, true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to record user action: %v", err)
|
||||
}
|
||||
|
||||
nuser, err := th.App.GetUser(user.Id)
|
||||
assert.Equal(t, termsOfService.Id, nuser.AcceptedTermsOfServiceId)
|
||||
|
||||
err = th.App.RecordUserTermsOfServiceAction(user.Id, termsOfService.Id, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to record user action: %v", err)
|
||||
}
|
||||
|
||||
nuser, err = th.App.GetUser(user.Id)
|
||||
assert.Empty(t, nuser.AcceptedTermsOfServiceId)
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ type WebConn struct {
|
||||
|
||||
func (a *App) NewWebConn(ws *websocket.Conn, session model.Session, t goi18n.TranslateFunc, locale string) *WebConn {
|
||||
if len(session.UserId) > 0 {
|
||||
a.Go(func() {
|
||||
a.Srv.Go(func() {
|
||||
a.SetStatusOnline(session.UserId, false)
|
||||
a.UpdateLastActivityAtIfNeeded(session)
|
||||
})
|
||||
@@ -126,7 +126,7 @@ func (c *WebConn) readPump() {
|
||||
c.WebSocket.SetPongHandler(func(string) error {
|
||||
c.WebSocket.SetReadDeadline(time.Now().Add(PONG_WAIT))
|
||||
if c.IsAuthenticated() {
|
||||
c.App.Go(func() {
|
||||
c.App.Srv.Go(func() {
|
||||
c.App.SetStatusAwayIfNeeded(c.UserId, false)
|
||||
})
|
||||
}
|
||||
@@ -212,7 +212,7 @@ func (c *WebConn) writePump() {
|
||||
}
|
||||
|
||||
if c.App.Metrics != nil {
|
||||
c.App.Go(func() {
|
||||
c.App.Srv.Go(func() {
|
||||
c.App.Metrics.IncrementWebSocketBroadcast(msg.EventType())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func (a *App) NewWebHub() *Hub {
|
||||
|
||||
func (a *App) TotalWebsocketConnections() int {
|
||||
count := int64(0)
|
||||
for _, hub := range a.Hubs {
|
||||
for _, hub := range a.Srv.Hubs {
|
||||
count = count + atomic.LoadInt64(&hub.connectionCount)
|
||||
}
|
||||
|
||||
@@ -74,13 +74,13 @@ func (a *App) HubStart() {
|
||||
numberOfHubs := runtime.NumCPU() * 2
|
||||
mlog.Info(fmt.Sprintf("Starting %v websocket hubs", numberOfHubs))
|
||||
|
||||
a.Hubs = make([]*Hub, numberOfHubs)
|
||||
a.HubsStopCheckingForDeadlock = make(chan bool, 1)
|
||||
a.Srv.Hubs = make([]*Hub, numberOfHubs)
|
||||
a.Srv.HubsStopCheckingForDeadlock = make(chan bool, 1)
|
||||
|
||||
for i := 0; i < len(a.Hubs); i++ {
|
||||
a.Hubs[i] = a.NewWebHub()
|
||||
a.Hubs[i].connectionIndex = i
|
||||
a.Hubs[i].Start()
|
||||
for i := 0; i < len(a.Srv.Hubs); i++ {
|
||||
a.Srv.Hubs[i] = a.NewWebHub()
|
||||
a.Srv.Hubs[i].connectionIndex = i
|
||||
a.Srv.Hubs[i].Start()
|
||||
}
|
||||
|
||||
go func() {
|
||||
@@ -93,7 +93,7 @@ func (a *App) HubStart() {
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
for _, hub := range a.Hubs {
|
||||
for _, hub := range a.Srv.Hubs {
|
||||
if len(hub.broadcast) >= DEADLOCK_WARN {
|
||||
mlog.Error(fmt.Sprintf("Hub processing might be deadlock on hub %v goroutine %v with %v events in the buffer", hub.connectionIndex, hub.goroutineId, len(hub.broadcast)))
|
||||
buf := make([]byte, 1<<16)
|
||||
@@ -109,7 +109,7 @@ func (a *App) HubStart() {
|
||||
}
|
||||
}
|
||||
|
||||
case <-a.HubsStopCheckingForDeadlock:
|
||||
case <-a.Srv.HubsStopCheckingForDeadlock:
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -120,27 +120,27 @@ func (a *App) HubStop() {
|
||||
mlog.Info("stopping websocket hub connections")
|
||||
|
||||
select {
|
||||
case a.HubsStopCheckingForDeadlock <- true:
|
||||
case a.Srv.HubsStopCheckingForDeadlock <- true:
|
||||
default:
|
||||
mlog.Warn("We appear to have already sent the stop checking for deadlocks command")
|
||||
}
|
||||
|
||||
for _, hub := range a.Hubs {
|
||||
for _, hub := range a.Srv.Hubs {
|
||||
hub.Stop()
|
||||
}
|
||||
|
||||
a.Hubs = []*Hub{}
|
||||
a.Srv.Hubs = []*Hub{}
|
||||
}
|
||||
|
||||
func (a *App) GetHubForUserId(userId string) *Hub {
|
||||
if len(a.Hubs) == 0 {
|
||||
if len(a.Srv.Hubs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
hash := fnv.New32a()
|
||||
hash.Write([]byte(userId))
|
||||
index := hash.Sum32() % uint32(len(a.Hubs))
|
||||
return a.Hubs[index]
|
||||
index := hash.Sum32() % uint32(len(a.Srv.Hubs))
|
||||
return a.Srv.Hubs[index]
|
||||
}
|
||||
|
||||
func (a *App) HubRegister(webConn *WebConn) {
|
||||
@@ -190,7 +190,7 @@ func (a *App) PublishSkipClusterSend(message *model.WebSocketEvent) {
|
||||
hub.Broadcast(message)
|
||||
}
|
||||
} else {
|
||||
for _, hub := range a.Hubs {
|
||||
for _, hub := range a.Srv.Hubs {
|
||||
hub.Broadcast(message)
|
||||
}
|
||||
}
|
||||
@@ -416,7 +416,7 @@ func (h *Hub) Start() {
|
||||
|
||||
conns := connections.ForUser(webCon.UserId)
|
||||
if len(conns) == 0 {
|
||||
h.app.Go(func() {
|
||||
h.app.Srv.Go(func() {
|
||||
h.app.SetStatusOffline(webCon.UserId, false)
|
||||
})
|
||||
} else {
|
||||
@@ -427,7 +427,7 @@ func (h *Hub) Start() {
|
||||
}
|
||||
}
|
||||
if h.app.IsUserAway(latestActivity) {
|
||||
h.app.Go(func() {
|
||||
h.app.Srv.Go(func() {
|
||||
h.app.SetStatusLastActivityAt(webCon.UserId, latestActivity)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ func (a *App) handleWebhookEvents(post *model.Post, team *model.Team, channel *m
|
||||
TriggerWord: triggerWord,
|
||||
FileIds: strings.Join(post.FileIds, ","),
|
||||
}
|
||||
a.Go(func(hook *model.OutgoingWebhook) func() {
|
||||
a.Srv.Go(func(hook *model.OutgoingWebhook) func() {
|
||||
return func() {
|
||||
a.TriggerWebhook(payload, hook, post, channel)
|
||||
}
|
||||
@@ -102,7 +102,7 @@ func (a *App) TriggerWebhook(payload *model.OutgoingWebhookPayload, hook *model.
|
||||
}
|
||||
|
||||
for _, url := range hook.CallbackURLs {
|
||||
a.Go(func(url string) func() {
|
||||
a.Srv.Go(func(url string) func() {
|
||||
return func() {
|
||||
req, _ := http.NewRequest("POST", url, body)
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
|
||||
@@ -10,10 +10,11 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestCreateIncomingWebhookForChannel(t *testing.T) {
|
||||
|
||||
@@ -55,7 +55,7 @@ func (wr *WebSocketRouter) ServeWebSocket(conn *WebConn, r *model.WebSocketReque
|
||||
return
|
||||
}
|
||||
|
||||
wr.app.Go(func() {
|
||||
wr.app.Srv.Go(func() {
|
||||
wr.app.SetStatusOnline(session.UserId, false)
|
||||
wr.app.UpdateLastActivityAtIfNeeded(*session)
|
||||
})
|
||||
|
||||
Ссылка в новой задаче
Block a user