[MM-61140] Allow plugins to add Support Packet data without UI elements (#28833)

Co-authored-by: Ibrahim Serdar Acikgoz <serdaracikgoz86@gmail.com>
Этот коммит содержится в:
Ben Schumacher
2024-11-13 11:20:39 +01:00
коммит произвёл GitHub
родитель 9fed818200
Коммит 053d0b5f0a
21 изменённых файлов: 70 добавлений и 58 удалений

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

@@ -294,7 +294,7 @@ func (a *App) CreateChannel(c request.CTX, channel *model.Channel, addMember boo
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.ChannelHasBeenCreated(pluginContext, sc)
return true
}, plugin.ChannelHasBeenCreatedID)
@@ -378,7 +378,7 @@ func (a *App) handleCreationEvent(c request.CTX, userID, otherUserID string, cha
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.ChannelHasBeenCreated(pluginContext, channel)
return true
}, plugin.ChannelHasBeenCreatedID)
@@ -600,7 +600,7 @@ func (a *App) createGroupChannel(c request.CTX, userIDs []string) (*model.Channe
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.ChannelHasBeenCreated(pluginContext, channel)
return true
}, plugin.ChannelHasBeenCreatedID)
@@ -1679,7 +1679,7 @@ func (a *App) AddChannelMember(c request.CTX, userID string, channel *model.Chan
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.UserHasJoinedChannel(pluginContext, cm, userRequestor)
return true
}, plugin.UserHasJoinedChannelID)
@@ -2284,7 +2284,7 @@ func (a *App) JoinChannel(c request.CTX, channel *model.Channel, userID string)
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.UserHasJoinedChannel(pluginContext, cm, nil)
return true
}, plugin.UserHasJoinedChannelID)
@@ -2598,7 +2598,7 @@ func (a *App) removeUserFromChannel(c request.CTX, userIDToRemove string, remove
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.UserHasLeftChannel(pluginContext, cm, actorUser)
return true
}, plugin.UserHasLeftChannelID)

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

@@ -219,7 +219,7 @@ func (ch *Channels) RemoveConfigListener(id string) {
ch.cfgSvc.RemoveConfigListener(id)
}
func (ch *Channels) RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks) bool, hookId int) {
func (ch *Channels) RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks, manifest *model.Manifest) bool, hookId int) {
if env := ch.GetPluginsEnvironment(); env != nil {
env.RunMultiPluginHook(hookRunnerFunc, hookId)
}

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

@@ -1039,7 +1039,7 @@ func (a *App) DoUploadFileExpectModification(c request.CTX, now time.Time, rawTe
var rejectionError *model.AppError
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
var newBytes bytes.Buffer
replacementInfo, rejectionReason := hooks.FileWillBeUploaded(pluginContext, info, bytes.NewReader(data), &newBytes)
if rejectionReason != "" {

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

@@ -159,7 +159,7 @@ func (a *App) GetUserForLogin(c request.CTX, id, loginId string) (*model.User, *
func (a *App) DoLogin(c request.CTX, w http.ResponseWriter, r *http.Request, user *model.User, deviceID string, isMobile, isOAuthUser, isSaml bool) (*model.Session, *model.AppError) {
var rejectionReason string
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
rejectionReason = hooks.UserWillLogIn(pluginContext, user)
return rejectionReason == ""
}, plugin.UserWillLogInID)
@@ -230,7 +230,7 @@ func (a *App) DoLogin(c request.CTX, w http.ResponseWriter, r *http.Request, use
}
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.UserHasLoggedIn(pluginContext, user)
return true
}, plugin.UserHasLoggedInID)

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

@@ -91,7 +91,7 @@ func (a *App) sendPushNotificationSync(c request.CTX, post *model.Post, user *mo
func (a *App) sendPushNotificationToAllSessions(rctx request.CTX, msg *model.PushNotification, userID string, skipSessionId string) *model.AppError {
rejectionReason := ""
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
var replacementNotification *model.PushNotification
replacementNotification, rejectionReason = hooks.NotificationWillBePushed(msg, userID)
if rejectionReason != "" {

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

@@ -73,7 +73,7 @@ func (ps *PlatformService) IsConfigReadOnly() bool {
func (ps *PlatformService) SaveConfig(newCfg *model.Config, sendConfigChangeClusterMessage bool) (*model.Config, *model.Config, *model.AppError) {
if ps.pluginEnv != nil {
var hookErr error
ps.pluginEnv.RunMultiHook(func(hooks plugin.Hooks) bool {
ps.pluginEnv.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
var cfg *model.Config
cfg, hookErr = hooks.ConfigurationWillBeSaved(newCfg)
if hookErr == nil && cfg != nil {

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

@@ -106,7 +106,7 @@ type PlatformService struct {
}
type HookRunner interface {
RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks) bool, hookId int)
RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks, _ *model.Manifest) bool, hookId int)
GetPluginsEnvironment() *plugin.Environment
}

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

@@ -265,7 +265,7 @@ func (ps *PlatformService) NewWebConn(cfg *WebConnConfig, suite SuiteIFace, runn
wc.SetActiveThreadViewThreadChannelID(UnsetPresenceIndicator)
ps.Go(func() {
runner.RunMultiHook(func(hooks plugin.Hooks) bool {
runner.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.OnWebSocketConnect(wc.GetConnectionID(), userID)
return true
}, plugin.OnWebSocketConnectID)
@@ -278,7 +278,7 @@ func (wc *WebConn) pluginPostedConsumer(wg *sync.WaitGroup) {
defer wg.Done()
for msg := range wc.pluginPosted {
wc.HookRunner.RunMultiHook(func(hooks plugin.Hooks) bool {
wc.HookRunner.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.WebSocketMessageHasBeenPosted(msg.connectionID, msg.userID, msg.req)
return true
}, plugin.WebSocketMessageHasBeenPostedID)
@@ -417,7 +417,7 @@ func (wc *WebConn) Pump() {
userID := wc.UserId
wc.Platform.Go(func() {
wc.HookRunner.RunMultiHook(func(hooks plugin.Hooks) bool {
wc.HookRunner.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.OnWebSocketDisconnect(wc.GetConnectionID(), userID)
return true
}, plugin.OnWebSocketDisconnectID)

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

@@ -22,7 +22,7 @@ import (
type hookRunner struct {
}
func (h *hookRunner) RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks) bool, hookId int) {
func (h *hookRunner) RunMultiHook(hookRunnerFunc func(hooks plugin.Hooks, _ *model.Manifest) bool, hookId int) {
}
func (h *hookRunner) HooksForPlugin(id string) (plugin.Hooks, error) {

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

@@ -244,7 +244,7 @@ func (ch *Channels) initPlugins(c request.CTX, pluginDir, webappPluginDir string
ch.syncPluginsActiveState()
}
ch.RunMultiHook(func(hooks plugin.Hooks) bool {
ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
if err := hooks.OnConfigurationChange(); err != nil {
ch.srv.Log().Error("Plugin OnConfigurationChange hook failed", mlog.Err(err))
}

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

@@ -1335,7 +1335,7 @@ func TestHookRunDataRetention(t *testing.T) {
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
hookCalled := false
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
n, _ := hooks.RunDataRetention(0, 0)
// Ensure return it correct
assert.Equal(t, int64(100), n)
@@ -1379,7 +1379,7 @@ func TestHookOnSendDailyTelemetry(t *testing.T) {
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
hookCalled := false
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.OnSendDailyTelemetry()
hookCalled = true
@@ -1423,7 +1423,7 @@ func TestHookOnCloudLimitsUpdated(t *testing.T) {
require.True(t, th.App.GetPluginsEnvironment().IsActive(pluginID))
hookCalled := false
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks) bool {
th.App.Channels().RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.OnCloudLimitsUpdated(nil)
hookCalled = true

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

@@ -308,7 +308,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
}
var rejectionError *model.AppError
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
replacementPost, rejectionReason := hooks.MessageWillBePosted(pluginContext, post.ForPlugin())
if rejectionReason != "" {
id := "Post rejected by plugin. " + rejectionReason
@@ -381,7 +381,7 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
// and to remove the non-GOB-encodable Metadata from it.
pluginPost := rpost.ForPlugin()
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.MessageHasBeenPosted(pluginContext, pluginPost)
return true
}, plugin.MessageHasBeenPostedID)
@@ -738,7 +738,7 @@ func (a *App) UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, safeUpd
var rejectionReason string
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
newPost, rejectionReason = hooks.MessageWillBeUpdated(pluginContext, newPost.ForPlugin(), oldPost.ForPlugin())
return newPost != nil
}, plugin.MessageWillBeUpdatedID)
@@ -762,7 +762,7 @@ func (a *App) UpdatePost(c request.CTX, receivedUpdatedPost *model.Post, safeUpd
pluginOldPost := oldPost.ForPlugin()
pluginNewPost := newPost.ForPlugin()
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.MessageHasBeenUpdated(pluginContext, pluginNewPost, pluginOldPost)
return true
}, plugin.MessageHasBeenUpdatedID)
@@ -2368,7 +2368,7 @@ func (a *App) applyPostsWillBeConsumedHook(posts map[string]*model.Post) {
for _, post := range posts {
postsSlice = append(postsSlice, post.ForPlugin())
}
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
postReplacements := hooks.MessagesWillBeConsumed(postsSlice)
for _, postReplacement := range postReplacements {
posts[postReplacement.Id] = postReplacement
@@ -2383,7 +2383,7 @@ func (a *App) applyPostWillBeConsumedHook(post **model.Post) {
}
ps := []*model.Post{*post}
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
rp := hooks.MessagesWillBeConsumed(ps)
if len(rp) > 0 {
(*post) = rp[0]
@@ -2705,7 +2705,7 @@ func (a *App) CleanUpAfterPostDeletion(c request.CTX, post *model.Post, deleteBy
pluginPost := post.ForPlugin()
pluginContext := pluginContext(c)
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.MessageHasBeenDeleted(pluginContext, pluginPost)
return true
}, plugin.MessageHasBeenDeletedID)

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

@@ -77,7 +77,7 @@ func (a *App) UpdatePreferences(c request.CTX, userID string, preferences model.
pluginContext := pluginContext(c)
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.PreferencesHaveChanged(pluginContext, preferences)
return true
}, plugin.PreferencesHaveChangedID)

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

@@ -84,7 +84,7 @@ func (a *App) SaveReactionForPost(c request.CTX, reaction *model.Reaction) (*mod
pluginContext := pluginContext(c)
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.ReactionHasBeenAdded(pluginContext, reaction)
return true
}, plugin.ReactionHasBeenAddedID)
@@ -155,7 +155,7 @@ func (a *App) DeleteReactionForPost(c request.CTX, reaction *model.Reaction) *mo
pluginContext := pluginContext(c)
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.ReactionHasBeenRemoved(pluginContext, reaction)
return true
}, plugin.ReactionHasBeenRemovedID)

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

@@ -6,6 +6,7 @@ package app
import (
"encoding/json"
"runtime"
"slices"
"sync"
"github.com/hashicorp/go-multierror"
@@ -13,6 +14,7 @@ import (
"gopkg.in/yaml.v2"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/plugin"
"github.com/mattermost/mattermost/server/public/shared/mlog"
"github.com/mattermost/mattermost/server/public/shared/request"
)
@@ -82,26 +84,30 @@ func (a *App) GenerateSupportPacket(c request.CTX, options *model.SupportPacketO
wg.Wait()
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil {
pluginContext := pluginContext(c)
for _, id := range options.PluginPackets {
hooks, err := pluginsEnvironment.HooksForPlugin(id)
if err != nil {
c.Logger().Error("Failed to call hooks for plugin", mlog.Err(err), mlog.String("plugin", id))
warnings = multierror.Append(warnings, err)
continue
}
pluginData, err := hooks.GenerateSupportData(pluginContext)
if err != nil {
c.Logger().Warn("Failed to generate plugin file for Support Packet", mlog.Err(err), mlog.String("plugin", id))
warnings = multierror.Append(warnings, err)
continue
}
for _, data := range pluginData {
fileDatas = append(fileDatas, *data)
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks, manifest *model.Manifest) bool {
// If the plugin defined the support_packet prop it means there is a UI element to include it in the support packet.
// Check if the plugin is in the list of plugins to include in the Support Packet.
if _, ok := manifest.Props["support_packet"]; ok {
if !slices.Contains(options.PluginPackets, manifest.Id) {
return true
}
}
}
// Otherwise, just call the hook as the plugin decided to always include it in the Support Packet.
pluginData, err := hooks.GenerateSupportData(pluginContext)
if err != nil {
c.Logger().Warn("Failed to generate plugin file for Support Packet", mlog.String("plugin", manifest.Id), mlog.Err(err))
warnings = multierror.Append(warnings, err)
return true
}
for _, data := range pluginData {
fileDatas = append(fileDatas, *data)
}
return true
}, plugin.GenerateSupportDataID)
// Adding a warning.txt file to the fileDatas if any warning
if warnings != nil {

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

@@ -803,7 +803,7 @@ func (a *App) JoinUserToTeam(c request.CTX, team *model.Team, user *model.User,
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.UserHasJoinedTeam(pluginContext, teamMember, actor)
return true
}, plugin.UserHasJoinedTeamID)
@@ -1175,7 +1175,7 @@ func (a *App) postProcessTeamMemberLeave(c request.CTX, teamMember *model.TeamMe
a.Srv().Go(func() {
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.UserHasLeftTeam(pluginContext, teamMember, actor)
return true
}, plugin.UserHasLeftTeamID)

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

@@ -62,7 +62,7 @@ func (a *App) runPluginsHook(c request.CTX, info *model.FileInfo, file io.Reader
var rejErr *model.AppError
var once sync.Once
pluginContext := pluginContext(c)
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
once.Do(func() {
hookHasRunCh <- struct{}{}
})

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

@@ -313,7 +313,7 @@ func (a *App) createUserOrGuest(c request.CTX, user *model.User, guest bool) (*m
pluginContext := pluginContext(c)
a.Srv().Go(func() {
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.UserHasBeenCreated(pluginContext, ruser)
return true
}, plugin.UserHasBeenCreatedID)
@@ -1045,10 +1045,10 @@ func (a *App) UpdateActive(c request.CTX, user *model.User, active bool) (*model
a.sendUpdatedUserEvent(ruser)
if pluginsEnvironment := a.GetPluginsEnvironment(); pluginsEnvironment != nil && !active && user.DeleteAt != 0 {
if !active && user.DeleteAt != 0 {
a.Srv().Go(func() {
pluginContext := pluginContext(c)
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
a.ch.RunMultiHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.UserHasBeenDeactivated(pluginContext, user)
return true
}, plugin.UserHasBeenDeactivatedID)

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

@@ -1065,7 +1065,7 @@ func (ts *TelemetryService) trackPlugins() {
"plugins_with_broken_manifests": brokenManifestCount,
})
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks) bool {
pluginsEnvironment.RunMultiPluginHook(func(hooks plugin.Hooks, _ *model.Manifest) bool {
hooks.OnSendDailyTelemetry()
return true
}, plugin.OnSendDailyTelemetryID)

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

@@ -599,7 +599,7 @@ func (env *Environment) HooksForPlugin(id string) (Hooks, error) {
//
// If hookRunnerFunc returns false, iteration will not continue. The iteration order among active
// plugins is not specified.
func (env *Environment) RunMultiPluginHook(hookRunnerFunc func(hooks Hooks) bool, hookId int) {
func (env *Environment) RunMultiPluginHook(hookRunnerFunc func(hooks Hooks, manifest *model.Manifest) bool, hookId int) {
startTime := time.Now()
env.registeredPlugins.Range(func(key, value any) bool {
@@ -610,7 +610,7 @@ func (env *Environment) RunMultiPluginHook(hookRunnerFunc func(hooks Hooks) bool
}
hookStartTime := time.Now()
result := hookRunnerFunc(rp.supervisor.Hooks())
result := hookRunnerFunc(rp.supervisor.Hooks(), rp.BundleInfo.Manifest)
if env.metrics != nil {
elapsedTime := float64(time.Since(hookStartTime)) / float64(time.Second)

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

@@ -387,6 +387,12 @@ type Hooks interface {
// GenerateSupportData is invoked when a Support Packet gets generated.
// It allows plugins to include their own content in the Support Packet.
//
// Plugins may specififes a "support_packet" field in the manifest props with a custom text.
// By doing so, the plugin will be included in the Support Packet UI and the user will be able to select it.
// This hook will only be called, if the user selects the plugin in the Support Packet UI.
//
// If no "support_packet" is specified, this hook will always be called.
//
// Minimum server version: 9.8
GenerateSupportData(c *Context) ([]*model.FileData, error)
}