You've already forked focalboard
mirror of
https://github.com/mattermost/focalboard.git
synced 2025-07-15 23:54:29 +02:00
Post message channel change (#3717)
* initial implementation * checking changes * use boards bot * update mocks * linter fixes * linter fixes * clean up * revert manifest change * another lint fix * use common error Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
@ -12,17 +12,9 @@ import (
|
|||||||
"github.com/mattermost/focalboard/server/services/permissions"
|
"github.com/mattermost/focalboard/server/services/permissions"
|
||||||
"github.com/mattermost/focalboard/server/services/store"
|
"github.com/mattermost/focalboard/server/services/store"
|
||||||
|
|
||||||
mm_model "github.com/mattermost/mattermost-server/v6/model"
|
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
botUsername = "boards"
|
|
||||||
botDisplayname = "Boards"
|
|
||||||
botDescription = "Created by Boards plugin."
|
|
||||||
)
|
|
||||||
|
|
||||||
type notifyBackendParams struct {
|
type notifyBackendParams struct {
|
||||||
cfg *config.Configuration
|
cfg *config.Configuration
|
||||||
servicesAPI model.ServicesAPI
|
servicesAPI model.ServicesAPI
|
||||||
@ -71,15 +63,11 @@ func createSubscriptionsNotifyBackend(params notifyBackendParams) (*notifysubscr
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createDelivery(servicesAPI model.ServicesAPI, serverRoot string) (*plugindelivery.PluginDelivery, error) {
|
func createDelivery(servicesAPI model.ServicesAPI, serverRoot string) (*plugindelivery.PluginDelivery, error) {
|
||||||
bot := &mm_model.Bot{
|
bot := model.FocalboardBot
|
||||||
Username: botUsername,
|
|
||||||
DisplayName: botDisplayname,
|
|
||||||
Description: botDescription,
|
|
||||||
OwnerId: model.SystemUserID,
|
|
||||||
}
|
|
||||||
botID, err := servicesAPI.EnsureBot(bot)
|
botID, err := servicesAPI.EnsureBot(bot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to ensure %s bot: %w", botDisplayname, err)
|
return nil, fmt.Errorf("failed to ensure %s bot: %w", bot.DisplayName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return plugindelivery.New(botID, serverRoot, servicesAPI), nil
|
return plugindelivery.New(botID, serverRoot, servicesAPI), nil
|
||||||
|
@ -20,6 +20,9 @@ var (
|
|||||||
ErrInsufficientLicense = errors.New("appropriate license required")
|
ErrInsufficientLicense = errors.New("appropriate license required")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const linkBoardMessage = "@%s linked Board [%s](%s) with this channel"
|
||||||
|
const unlinkBoardMessage = "@%s unlinked Board [%s](%s) with this channel"
|
||||||
|
|
||||||
func (a *App) GetBoard(boardID string) (*model.Board, error) {
|
func (a *App) GetBoard(boardID string) (*model.Board, error) {
|
||||||
board, err := a.store.GetBoard(boardID)
|
board, err := a.store.GetBoard(boardID)
|
||||||
if model.IsErrNotFound(err) {
|
if model.IsErrNotFound(err) {
|
||||||
@ -302,18 +305,54 @@ func (a *App) CreateBoard(board *model.Board, userID string, addMember bool) (*m
|
|||||||
|
|
||||||
func (a *App) PatchBoard(patch *model.BoardPatch, boardID, userID string) (*model.Board, error) {
|
func (a *App) PatchBoard(patch *model.BoardPatch, boardID, userID string) (*model.Board, error) {
|
||||||
var oldMembers []*model.BoardMember
|
var oldMembers []*model.BoardMember
|
||||||
|
var oldChannelID string
|
||||||
if patch.ChannelID != nil && *patch.ChannelID == "" {
|
if patch.ChannelID != nil && *patch.ChannelID == "" {
|
||||||
var err error
|
var err error
|
||||||
oldMembers, err = a.GetMembersForBoard(boardID)
|
oldMembers, err = a.GetMembersForBoard(boardID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.logger.Error("Unable to get the board members", mlog.Err(err))
|
a.logger.Error("Unable to get the board members", mlog.Err(err))
|
||||||
}
|
}
|
||||||
|
board, err := a.store.GetBoard(boardID)
|
||||||
|
if model.IsErrNotFound(err) {
|
||||||
|
return nil, model.NewErrNotFound(boardID)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
oldChannelID = board.ChannelID
|
||||||
}
|
}
|
||||||
updatedBoard, err := a.store.PatchBoard(boardID, patch, userID)
|
updatedBoard, err := a.store.PatchBoard(boardID, patch, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if patch.ChannelID != nil {
|
||||||
|
var username string
|
||||||
|
user, err := a.store.GetUserByID(userID)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Error("Unable to get the board updater", mlog.Err(err))
|
||||||
|
username = "unknown"
|
||||||
|
} else {
|
||||||
|
username = user.Username
|
||||||
|
}
|
||||||
|
|
||||||
|
boardLink := utils.MakeBoardLink(a.config.ServerRoot, updatedBoard.TeamID, updatedBoard.ID)
|
||||||
|
if *patch.ChannelID != "" {
|
||||||
|
// TODO: this needs translated when available on the server
|
||||||
|
message := fmt.Sprintf(linkBoardMessage, username, updatedBoard.Title, boardLink)
|
||||||
|
err := a.store.PostMessage(message, "", *patch.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Error("Unable to post the link message to channel", mlog.Err(err))
|
||||||
|
}
|
||||||
|
} else if *patch.ChannelID == "" {
|
||||||
|
message := fmt.Sprintf(unlinkBoardMessage, username, updatedBoard.Title, boardLink)
|
||||||
|
err := a.store.PostMessage(message, "", oldChannelID)
|
||||||
|
if err != nil {
|
||||||
|
a.logger.Error("Unable to post the link message to channel", mlog.Err(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
a.blockChangeNotifier.Enqueue(func() error {
|
a.blockChangeNotifier.Enqueue(func() error {
|
||||||
a.wsAdapter.BroadcastBoardChange(updatedBoard.TeamID, updatedBoard)
|
a.wsAdapter.BroadcastBoardChange(updatedBoard.TeamID, updatedBoard)
|
||||||
if patch.ChannelID != nil && *patch.ChannelID != "" {
|
if patch.ChannelID != nil && *patch.ChannelID != "" {
|
||||||
|
@ -14,6 +14,19 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
botUsername = "boards"
|
||||||
|
botDisplayname = "Boards"
|
||||||
|
botDescription = "Created by Boards plugin."
|
||||||
|
)
|
||||||
|
|
||||||
|
var FocalboardBot = &mm_model.Bot{
|
||||||
|
Username: botUsername,
|
||||||
|
DisplayName: botDisplayname,
|
||||||
|
Description: botDescription,
|
||||||
|
OwnerId: SystemUserID,
|
||||||
|
}
|
||||||
|
|
||||||
type ServicesAPI interface {
|
type ServicesAPI interface {
|
||||||
// Channels service
|
// Channels service
|
||||||
GetDirectChannel(userID1, userID2 string) (*mm_model.Channel, error)
|
GetDirectChannel(userID1, userID2 string) (*mm_model.Channel, error)
|
||||||
|
@ -18,10 +18,7 @@ import (
|
|||||||
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var systemsBot = &mmModel.Bot{
|
var boardsBotID string
|
||||||
Username: mmModel.BotSystemBotUsername,
|
|
||||||
DisplayName: "System",
|
|
||||||
}
|
|
||||||
|
|
||||||
// servicesAPI is the interface required my the MattermostAuthLayer to interact with
|
// servicesAPI is the interface required my the MattermostAuthLayer to interact with
|
||||||
// the mattermost-server. You can use plugin-api or product-api adapter implementations.
|
// the mattermost-server. You can use plugin-api or product-api adapter implementations.
|
||||||
@ -852,18 +849,18 @@ func (s *MattermostAuthLayer) GetChannel(teamID, channelID string) (*mmModel.Cha
|
|||||||
return channel, nil
|
return channel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MattermostAuthLayer) getSystemBotID() (string, error) {
|
func (s *MattermostAuthLayer) getBoardsBotID() (string, error) {
|
||||||
botID, err := s.servicesAPI.EnsureBot(systemsBot)
|
if boardsBotID == "" {
|
||||||
if err != nil {
|
var err error
|
||||||
s.logger.Error("failed to ensure system bot", mlog.String("username", systemsBot.Username), mlog.Err(err))
|
boardsBotID, err = s.servicesAPI.EnsureBot(model.FocalboardBot)
|
||||||
|
s.logger.Error("failed to ensure boards bot", mlog.Err(err))
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
return boardsBotID, nil
|
||||||
return botID, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MattermostAuthLayer) SendMessage(message, postType string, receipts []string) error {
|
func (s *MattermostAuthLayer) SendMessage(message, postType string, receipts []string) error {
|
||||||
botID, err := s.getSystemBotID()
|
botID, err := s.getBoardsBotID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -880,14 +877,7 @@ func (s *MattermostAuthLayer) SendMessage(message, postType string, receipts []s
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
post := &mmModel.Post{
|
if err := s.PostMessage(message, postType, channel.Id); err != nil {
|
||||||
Message: message,
|
|
||||||
UserId: botID,
|
|
||||||
ChannelId: channel.Id,
|
|
||||||
Type: postType,
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := s.servicesAPI.CreatePost(post); err != nil {
|
|
||||||
s.logger.Error(
|
s.logger.Error(
|
||||||
"failed to send message to receipt from SendMessage",
|
"failed to send message to receipt from SendMessage",
|
||||||
mlog.String("receipt", receipt),
|
mlog.String("receipt", receipt),
|
||||||
@ -896,7 +886,28 @@ func (s *MattermostAuthLayer) SendMessage(message, postType string, receipts []s
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MattermostAuthLayer) PostMessage(message, postType, channelID string) error {
|
||||||
|
botID, err := s.getBoardsBotID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
post := &mmModel.Post{
|
||||||
|
Message: message,
|
||||||
|
UserId: botID,
|
||||||
|
ChannelId: channelID,
|
||||||
|
Type: postType,
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := s.servicesAPI.CreatePost(post); err != nil {
|
||||||
|
s.logger.Error(
|
||||||
|
"failed to send message to receipt from PostMessage",
|
||||||
|
mlog.Err(err),
|
||||||
|
)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1251,6 +1251,20 @@ func (mr *MockStoreMockRecorder) PatchUserProps(arg0, arg1 interface{}) *gomock.
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchUserProps", reflect.TypeOf((*MockStore)(nil).PatchUserProps), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PatchUserProps", reflect.TypeOf((*MockStore)(nil).PatchUserProps), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostMessage mocks base method.
|
||||||
|
func (m *MockStore) PostMessage(arg0, arg1, arg2 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "PostMessage", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostMessage indicates an expected call of PostMessage.
|
||||||
|
func (mr *MockStoreMockRecorder) PostMessage(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PostMessage", reflect.TypeOf((*MockStore)(nil).PostMessage), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
// RefreshSession mocks base method.
|
// RefreshSession mocks base method.
|
||||||
func (m *MockStore) RefreshSession(arg0 *model.Session) error {
|
func (m *MockStore) RefreshSession(arg0 *model.Session) error {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
@ -707,6 +707,11 @@ func (s *SQLStore) PatchUserProps(userID string, patch model.UserPropPatch) erro
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SQLStore) PostMessage(message string, postType string, channelID string) error {
|
||||||
|
return s.postMessage(s.db, message, postType, channelID)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SQLStore) RefreshSession(session *model.Session) error {
|
func (s *SQLStore) RefreshSession(session *model.Session) error {
|
||||||
return s.refreshSession(s.db, session)
|
return s.refreshSession(s.db, session)
|
||||||
|
|
||||||
|
@ -279,6 +279,10 @@ func (s *SQLStore) sendMessage(db sq.BaseRunner, message, postType string, recei
|
|||||||
return errUnsupportedOperation
|
return errUnsupportedOperation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *SQLStore) postMessage(db sq.BaseRunner, message, postType string, channel string) error {
|
||||||
|
return errUnsupportedOperation
|
||||||
|
}
|
||||||
|
|
||||||
func (s *SQLStore) getUserTimezone(_ sq.BaseRunner, _ string) (string, error) {
|
func (s *SQLStore) getUserTimezone(_ sq.BaseRunner, _ string) (string, error) {
|
||||||
return "", errUnsupportedOperation
|
return "", errUnsupportedOperation
|
||||||
}
|
}
|
||||||
|
@ -154,6 +154,7 @@ type Store interface {
|
|||||||
GetCloudLimits() (*mmModel.ProductLimits, error)
|
GetCloudLimits() (*mmModel.ProductLimits, error)
|
||||||
SearchUserChannels(teamID, userID, query string) ([]*mmModel.Channel, error)
|
SearchUserChannels(teamID, userID, query string) ([]*mmModel.Channel, error)
|
||||||
GetChannel(teamID, channelID string) (*mmModel.Channel, error)
|
GetChannel(teamID, channelID string) (*mmModel.Channel, error)
|
||||||
|
PostMessage(message, postType, channelID string) error
|
||||||
SendMessage(message, postType string, receipts []string) error
|
SendMessage(message, postType string, receipts []string) error
|
||||||
|
|
||||||
// Insights
|
// Insights
|
||||||
|
Reference in New Issue
Block a user