(feature) New MessagesWillBeConsumed Server Plugin Hook (#23305)
* feature: implemented basic MessageWillBeConsumed hook and applied on GetSinglePost method. * use hook in get methods * chore: refactored hook usage and created utils functions to apply hook * bugfix: single post not updating * chore: adjusted hook to return post * chore: reverted some uneeded changes * chore: updated hook to accept slice of posts * bugfix: slice filled with niil values * chore: MessageWillBeConsumed ranamed to MessagesWillBeConsumed * Update plugin/hooks.go Co-authored-by: Jesse Hallam <jesse@thehallams.ca> * Add feature flag * Update min version * update tests to account for feature flag * fix linting issues --------- Co-authored-by: Matej Topolovac <> Co-authored-by: mtopolovac <43346061+mtopolovac@users.noreply.github.com> Co-authored-by: Jesse Hallam <jesse@thehallams.ca> Co-authored-by: Kevin Hsieh <kevinh@qrypt.com> Co-authored-by: Jesse Hallam <jesse.hallam@gmail.com>
Этот коммит содержится в:
@@ -1550,3 +1550,82 @@ func TestHookNotificationWillBePushed(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHookMessagesWillBeConsumed(t *testing.T) {
|
||||||
|
setupPlugin := func(t *testing.T, th *TestHelper) {
|
||||||
|
var mockAPI plugintest.API
|
||||||
|
mockAPI.On("LoadPluginConfiguration", mock.Anything).Return(nil)
|
||||||
|
mockAPI.On("LogDebug", "message").Return(nil)
|
||||||
|
|
||||||
|
tearDown, _, _ := SetAppEnvironmentWithPlugins(t, []string{`
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/mattermost/server/public/plugin"
|
||||||
|
"github.com/mattermost/mattermost/server/public/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyPlugin struct {
|
||||||
|
plugin.MattermostPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MyPlugin) MessagesWillBeConsumed(posts []*model.Post) []*model.Post {
|
||||||
|
for _, post := range posts {
|
||||||
|
post.Message = "mwbc_plugin:" + post.Message
|
||||||
|
}
|
||||||
|
return posts
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
plugin.ClientMain(&MyPlugin{})
|
||||||
|
}
|
||||||
|
`}, th.App, func(*model.Manifest) plugin.API { return &mockAPI })
|
||||||
|
t.Cleanup(tearDown)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("feature flag disabled", func(t *testing.T) {
|
||||||
|
os.Setenv("MM_FEATUREFLAGS_CONSUMEPOSTHOOK", "false")
|
||||||
|
defer os.Unsetenv("MM_FEATUREFLAGS_CONSUMEPOSTHOOK")
|
||||||
|
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
t.Cleanup(th.TearDown)
|
||||||
|
|
||||||
|
setupPlugin(t, th)
|
||||||
|
|
||||||
|
newPost := &model.Post{
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
ChannelId: th.BasicChannel.Id,
|
||||||
|
Message: "message",
|
||||||
|
CreateAt: model.GetMillis() - 10000,
|
||||||
|
}
|
||||||
|
_, err := th.App.CreatePost(th.Context, newPost, th.BasicChannel, false, true)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
post, err := th.App.GetSinglePost(newPost.Id, true)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assert.Equal(t, "message", post.Message)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("feature flag enabled", func(t *testing.T) {
|
||||||
|
os.Setenv("MM_FEATUREFLAGS_CONSUMEPOSTHOOK", "true")
|
||||||
|
defer os.Unsetenv("MM_FEATUREFLAGS_CONSUMEPOSTHOOK")
|
||||||
|
|
||||||
|
th := Setup(t).InitBasic()
|
||||||
|
t.Cleanup(th.TearDown)
|
||||||
|
|
||||||
|
setupPlugin(t, th)
|
||||||
|
|
||||||
|
newPost := &model.Post{
|
||||||
|
UserId: th.BasicUser.Id,
|
||||||
|
ChannelId: th.BasicChannel.Id,
|
||||||
|
Message: "message",
|
||||||
|
CreateAt: model.GetMillis() - 10000,
|
||||||
|
}
|
||||||
|
_, err := th.App.CreatePost(th.Context, newPost, th.BasicChannel, false, true)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
post, err := th.App.GetSinglePost(newPost.Id, true)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assert.Equal(t, "mwbc_plugin:message", post.Message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -386,6 +386,8 @@ func (a *App) CreatePost(c request.CTX, post *model.Post, channel *model.Channel
|
|||||||
// so we just return the one that was passed with post
|
// so we just return the one that was passed with post
|
||||||
rpost = a.PreparePostForClient(c, rpost, true, false, false)
|
rpost = a.PreparePostForClient(c, rpost, true, false, false)
|
||||||
|
|
||||||
|
a.applyPostWillBeConsumedHook(&rpost)
|
||||||
|
|
||||||
if rpost.RootId != "" {
|
if rpost.RootId != "" {
|
||||||
if appErr := a.ResolvePersistentNotification(c, parentPostList.Posts[post.RootId], rpost.UserId); appErr != nil {
|
if appErr := a.ResolvePersistentNotification(c, parentPostList.Posts[post.RootId], rpost.UserId); appErr != nil {
|
||||||
return nil, appErr
|
return nil, appErr
|
||||||
@@ -870,6 +872,8 @@ func (a *App) GetPostsPage(options model.GetPostsOptions) (*model.PostList, *mod
|
|||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostsWillBeConsumedHook(postList.Posts)
|
||||||
|
|
||||||
return postList, nil
|
return postList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -889,6 +893,8 @@ func (a *App) GetPosts(channelID string, offset int, limit int) (*model.PostList
|
|||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostsWillBeConsumedHook(postList.Posts)
|
||||||
|
|
||||||
return postList, nil
|
return postList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -906,6 +912,8 @@ func (a *App) GetPostsSince(options model.GetPostsSinceOptions) (*model.PostList
|
|||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostsWillBeConsumedHook(postList.Posts)
|
||||||
|
|
||||||
return postList, nil
|
return postList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -929,6 +937,8 @@ func (a *App) GetSinglePost(postID string, includeDeleted bool) (*model.Post, *m
|
|||||||
return nil, model.NewAppError("GetSinglePost", "app.post.cloud.get.app_error", nil, "", http.StatusForbidden)
|
return nil, model.NewAppError("GetSinglePost", "app.post.cloud.get.app_error", nil, "", http.StatusForbidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostWillBeConsumedHook(&post)
|
||||||
|
|
||||||
return post, nil
|
return post, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -959,6 +969,8 @@ func (a *App) GetPostThread(postID string, opts model.GetPostsOptions, userID st
|
|||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostsWillBeConsumedHook(posts.Posts)
|
||||||
|
|
||||||
return posts, nil
|
return posts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -972,6 +984,8 @@ func (a *App) GetFlaggedPosts(userID string, offset int, limit int) (*model.Post
|
|||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostsWillBeConsumedHook(postList.Posts)
|
||||||
|
|
||||||
return postList, nil
|
return postList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -985,6 +999,8 @@ func (a *App) GetFlaggedPostsForTeam(userID, teamID string, offset int, limit in
|
|||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostsWillBeConsumedHook(postList.Posts)
|
||||||
|
|
||||||
return postList, nil
|
return postList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -998,6 +1014,8 @@ func (a *App) GetFlaggedPostsForChannel(userID, channelID string, offset int, li
|
|||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostsWillBeConsumedHook(postList.Posts)
|
||||||
|
|
||||||
return postList, nil
|
return postList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1034,6 +1052,8 @@ func (a *App) GetPermalinkPost(c request.CTX, postID string, userID string) (*mo
|
|||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostsWillBeConsumedHook(list.Posts)
|
||||||
|
|
||||||
return list, nil
|
return list, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1062,6 +1082,8 @@ func (a *App) GetPostsBeforePost(options model.GetPostsOptions) (*model.PostList
|
|||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostsWillBeConsumedHook(postList.Posts)
|
||||||
|
|
||||||
return postList, nil
|
return postList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1090,6 +1112,8 @@ func (a *App) GetPostsAfterPost(options model.GetPostsOptions) (*model.PostList,
|
|||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostsWillBeConsumedHook(postList.Posts)
|
||||||
|
|
||||||
return postList, nil
|
return postList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1126,6 +1150,8 @@ func (a *App) GetPostsAroundPost(before bool, options model.GetPostsOptions) (*m
|
|||||||
return nil, appErr
|
return nil, appErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostsWillBeConsumedHook(postList.Posts)
|
||||||
|
|
||||||
return postList, nil
|
return postList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1135,6 +1161,8 @@ func (a *App) GetPostAfterTime(channelID string, time int64, collapsedThreads bo
|
|||||||
return nil, model.NewAppError("GetPostAfterTime", "app.post.get_post_after_time.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
return nil, model.NewAppError("GetPostAfterTime", "app.post.get_post_after_time.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.applyPostWillBeConsumedHook(&post)
|
||||||
|
|
||||||
return post, nil
|
return post, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2249,3 +2277,37 @@ func (a *App) GetPostInfo(c request.CTX, postID string) (*model.PostInfo, *model
|
|||||||
}
|
}
|
||||||
return &info, nil
|
return &info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) applyPostsWillBeConsumedHook(posts map[string]*model.Post) {
|
||||||
|
if !a.Config().FeatureFlags.ConsumePostHook {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
postsSlice := make([]*model.Post, 0, len(posts))
|
||||||
|
|
||||||
|
for _, post := range posts {
|
||||||
|
postsSlice = append(postsSlice, post.ForPlugin())
|
||||||
|
}
|
||||||
|
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||||
|
postReplacements := hooks.MessagesWillBeConsumed(postsSlice)
|
||||||
|
for _, postReplacement := range postReplacements {
|
||||||
|
posts[postReplacement.Id] = postReplacement
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}, plugin.MessagesWillBeConsumedID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) applyPostWillBeConsumedHook(post **model.Post) {
|
||||||
|
if !a.Config().FeatureFlags.ConsumePostHook {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ps := []*model.Post{*post}
|
||||||
|
a.ch.RunMultiHook(func(hooks plugin.Hooks) bool {
|
||||||
|
rp := hooks.MessagesWillBeConsumed(ps)
|
||||||
|
if len(rp) > 0 {
|
||||||
|
(*post) = rp[0]
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}, plugin.MessagesWillBeConsumedID)
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ type FeatureFlags struct {
|
|||||||
EnableExportDirectDownload bool
|
EnableExportDirectDownload bool
|
||||||
|
|
||||||
StreamlinedMarketplace bool
|
StreamlinedMarketplace bool
|
||||||
|
|
||||||
|
ConsumePostHook bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FeatureFlags) SetDefaults() {
|
func (f *FeatureFlags) SetDefaults() {
|
||||||
@@ -58,6 +60,7 @@ func (f *FeatureFlags) SetDefaults() {
|
|||||||
f.CloudReverseTrial = false
|
f.CloudReverseTrial = false
|
||||||
f.EnableExportDirectDownload = false
|
f.EnableExportDirectDownload = false
|
||||||
f.StreamlinedMarketplace = true
|
f.StreamlinedMarketplace = true
|
||||||
|
f.ConsumePostHook = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToMap returns the feature flags as a map[string]string
|
// ToMap returns the feature flags as a map[string]string
|
||||||
|
|||||||
@@ -698,6 +698,43 @@ func (s *hooksRPCServer) MessageWillBeUpdated(args *Z_MessageWillBeUpdatedArgs,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MessagesWillBeConsumed is in this file because of the difficulty of identifying which fields need special behaviour.
|
||||||
|
// The special behaviour needed is decoding the returned post into the original one to avoid the unintentional removal
|
||||||
|
// of fields by older plugins.
|
||||||
|
func init() {
|
||||||
|
hookNameToId["MessagesWillBeConsumed"] = MessagesWillBeConsumedID
|
||||||
|
}
|
||||||
|
|
||||||
|
type Z_MessagesWillBeConsumedArgs struct {
|
||||||
|
A []*model.Post
|
||||||
|
}
|
||||||
|
|
||||||
|
type Z_MessagesWillBeConsumedReturns struct {
|
||||||
|
A []*model.Post
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *hooksRPCClient) MessagesWillBeConsumed(posts []*model.Post) []*model.Post {
|
||||||
|
_args := &Z_MessagesWillBeConsumedArgs{posts}
|
||||||
|
_returns := &Z_MessagesWillBeConsumedReturns{}
|
||||||
|
if g.implemented[MessagesWillBeConsumedID] {
|
||||||
|
if err := g.client.Call("Plugin.MessagesWillBeConsumed", _args, _returns); err != nil {
|
||||||
|
g.log.Error("RPC call MessagesWillBeConsumed to plugin failed.", mlog.Err(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _returns.A
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *hooksRPCServer) MessagesWillBeConsumed(args *Z_MessagesWillBeConsumedArgs, returns *Z_MessagesWillBeConsumedReturns) error {
|
||||||
|
if hook, ok := s.impl.(interface {
|
||||||
|
MessagesWillBeConsumed(posts []*model.Post) []*model.Post
|
||||||
|
}); ok {
|
||||||
|
returns.A = hook.MessagesWillBeConsumed(args.A)
|
||||||
|
} else {
|
||||||
|
return encodableError(fmt.Errorf("hook MessagesWillBeConsumed called but not implemented"))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Z_LogDebugArgs struct {
|
type Z_LogDebugArgs struct {
|
||||||
A string
|
A string
|
||||||
B []any
|
B []any
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ const (
|
|||||||
NotificationWillBePushedID = 35
|
NotificationWillBePushedID = 35
|
||||||
UserHasBeenDeactivatedID = 36
|
UserHasBeenDeactivatedID = 36
|
||||||
MessageHasBeenDeletedID = 37
|
MessageHasBeenDeletedID = 37
|
||||||
|
MessagesWillBeConsumedID = 38
|
||||||
TotalHooksID = iota
|
TotalHooksID = iota
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -170,6 +171,15 @@ type Hooks interface {
|
|||||||
// Minimum server version: 5.2
|
// Minimum server version: 5.2
|
||||||
MessageHasBeenUpdated(c *Context, newPost, oldPost *model.Post)
|
MessageHasBeenUpdated(c *Context, newPost, oldPost *model.Post)
|
||||||
|
|
||||||
|
// MessagesWillBeConsumed is invoked when a message is requested by a client before it is returned
|
||||||
|
// to the client
|
||||||
|
//
|
||||||
|
// Note that this method will be called for posts created by plugins, including the plugin that
|
||||||
|
// created the post.
|
||||||
|
//
|
||||||
|
// Minimum server version: 9.3
|
||||||
|
MessagesWillBeConsumed(posts []*model.Post) []*model.Post
|
||||||
|
|
||||||
// MessageHasBeenDeleted is invoked after the message has been deleted from the database.
|
// MessageHasBeenDeleted is invoked after the message has been deleted from the database.
|
||||||
// Note that this method will be called for posts deleted by plugins, including the plugin that
|
// Note that this method will be called for posts deleted by plugins, including the plugin that
|
||||||
// deleted the post.
|
// deleted the post.
|
||||||
|
|||||||
@@ -113,6 +113,13 @@ func (hooks *hooksTimerLayer) MessageHasBeenUpdated(c *Context, newPost, oldPost
|
|||||||
hooks.recordTime(startTime, "MessageHasBeenUpdated", true)
|
hooks.recordTime(startTime, "MessageHasBeenUpdated", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (hooks *hooksTimerLayer) MessagesWillBeConsumed(posts []*model.Post) []*model.Post {
|
||||||
|
startTime := timePkg.Now()
|
||||||
|
_returnsA := hooks.hooksImpl.MessagesWillBeConsumed(posts)
|
||||||
|
hooks.recordTime(startTime, "MessagesWillBeConsumed", true)
|
||||||
|
return _returnsA
|
||||||
|
}
|
||||||
|
|
||||||
func (hooks *hooksTimerLayer) MessageHasBeenDeleted(c *Context, post *model.Post) {
|
func (hooks *hooksTimerLayer) MessageHasBeenDeleted(c *Context, post *model.Post) {
|
||||||
startTime := timePkg.Now()
|
startTime := timePkg.Now()
|
||||||
hooks.hooksImpl.MessageHasBeenDeleted(c, post)
|
hooks.hooksImpl.MessageHasBeenDeleted(c, post)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ var excludedPluginHooks = []string{
|
|||||||
"LogWarn",
|
"LogWarn",
|
||||||
"MessageWillBePosted",
|
"MessageWillBePosted",
|
||||||
"MessageWillBeUpdated",
|
"MessageWillBeUpdated",
|
||||||
|
"MessagesWillBeConsumed",
|
||||||
"OnActivate",
|
"OnActivate",
|
||||||
"PluginHTTP",
|
"PluginHTTP",
|
||||||
"ServeHTTP",
|
"ServeHTTP",
|
||||||
|
|||||||
@@ -198,6 +198,22 @@ func (_m *Hooks) MessageWillBeUpdated(c *plugin.Context, newPost *model.Post, ol
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MessagesWillBeConsumed provides a mock function with given fields: posts
|
||||||
|
func (_m *Hooks) MessagesWillBeConsumed(posts []*model.Post) []*model.Post {
|
||||||
|
ret := _m.Called(posts)
|
||||||
|
|
||||||
|
var r0 []*model.Post
|
||||||
|
if rf, ok := ret.Get(0).(func([]*model.Post) []*model.Post); ok {
|
||||||
|
r0 = rf(posts)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*model.Post)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// NotificationWillBePushed provides a mock function with given fields: pushNotification, userID
|
// NotificationWillBePushed provides a mock function with given fields: pushNotification, userID
|
||||||
func (_m *Hooks) NotificationWillBePushed(pushNotification *model.PushNotification, userID string) (*model.PushNotification, string) {
|
func (_m *Hooks) NotificationWillBePushed(pushNotification *model.PushNotification, userID string) (*model.PushNotification, string) {
|
||||||
ret := _m.Called(pushNotification, userID)
|
ret := _m.Called(pushNotification, userID)
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ type MessageHasBeenUpdatedIFace interface {
|
|||||||
MessageHasBeenUpdated(c *Context, newPost, oldPost *model.Post)
|
MessageHasBeenUpdated(c *Context, newPost, oldPost *model.Post)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MessagesWillBeConsumedIFace interface {
|
||||||
|
MessagesWillBeConsumed(posts []*model.Post) []*model.Post
|
||||||
|
}
|
||||||
|
|
||||||
type MessageHasBeenDeletedIFace interface {
|
type MessageHasBeenDeletedIFace interface {
|
||||||
MessageHasBeenDeleted(c *Context, post *model.Post)
|
MessageHasBeenDeleted(c *Context, post *model.Post)
|
||||||
}
|
}
|
||||||
@@ -224,6 +228,15 @@ func NewAdapter(productHooks any) (*HooksAdapter, error) {
|
|||||||
return nil, errors.New("hook has MessageHasBeenUpdated method but does not implement plugin.MessageHasBeenUpdated interface")
|
return nil, errors.New("hook has MessageHasBeenUpdated method but does not implement plugin.MessageHasBeenUpdated interface")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assessing the type of the productHooks if it individually implements MessagesWillBeConsumed interface.
|
||||||
|
tt = reflect.TypeOf((*MessagesWillBeConsumedIFace)(nil)).Elem()
|
||||||
|
|
||||||
|
if ft.Implements(tt) {
|
||||||
|
a.implemented[MessagesWillBeConsumedID] = struct{}{}
|
||||||
|
} else if _, ok := ft.MethodByName("MessagesWillBeConsumed"); ok {
|
||||||
|
return nil, errors.New("hook has MessagesWillBeConsumed method but does not implement plugin.MessagesWillBeConsumed interface")
|
||||||
|
}
|
||||||
|
|
||||||
// Assessing the type of the productHooks if it individually implements MessageHasBeenDeleted interface.
|
// Assessing the type of the productHooks if it individually implements MessageHasBeenDeleted interface.
|
||||||
tt = reflect.TypeOf((*MessageHasBeenDeletedIFace)(nil)).Elem()
|
tt = reflect.TypeOf((*MessageHasBeenDeletedIFace)(nil)).Elem()
|
||||||
|
|
||||||
@@ -488,6 +501,15 @@ func (a *HooksAdapter) MessageHasBeenUpdated(c *Context, newPost, oldPost *model
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *HooksAdapter) MessagesWillBeConsumed(posts []*model.Post) []*model.Post {
|
||||||
|
if _, ok := a.implemented[MessagesWillBeConsumedID]; !ok {
|
||||||
|
panic("product hooks must implement MessagesWillBeConsumed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.productHooks.(MessagesWillBeConsumedIFace).MessagesWillBeConsumed(posts)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (a *HooksAdapter) MessageHasBeenDeleted(c *Context, post *model.Post) {
|
func (a *HooksAdapter) MessageHasBeenDeleted(c *Context, post *model.Post) {
|
||||||
if _, ok := a.implemented[MessageHasBeenDeletedID]; !ok {
|
if _, ok := a.implemented[MessageHasBeenDeletedID]; !ok {
|
||||||
panic("product hooks must implement MessageHasBeenDeleted")
|
panic("product hooks must implement MessageHasBeenDeleted")
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user