mirror of
https://github.com/mattermost/focalboard.git
synced 2024-12-24 13:43:12 +02:00
89cc947a21
* Implement permissions specs for mentions. - public boards: admin, editor, commenter can mention team members and auto-add them to board; guests can mention board members - private boards: admin, editor, commenter, guest can mention board members - viewers cannot mention
224 lines
6.8 KiB
Go
224 lines
6.8 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package notifysubscriptions
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/mattermost/focalboard/server/model"
|
|
"github.com/mattermost/focalboard/server/services/notify"
|
|
"github.com/mattermost/focalboard/server/services/permissions"
|
|
"github.com/mattermost/focalboard/server/ws"
|
|
"github.com/wiggin77/merror"
|
|
|
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
)
|
|
|
|
const (
|
|
backendName = "notifySubscriptions"
|
|
)
|
|
|
|
type BackendParams struct {
|
|
ServerRoot string
|
|
Store Store
|
|
Permissions permissions.PermissionsService
|
|
Delivery SubscriptionDelivery
|
|
WSAdapter ws.Adapter
|
|
Logger *mlog.Logger
|
|
NotifyFreqCardSeconds int
|
|
NotifyFreqBoardSeconds int
|
|
}
|
|
|
|
// Backend provides the notification backend for subscriptions.
|
|
type Backend struct {
|
|
store Store
|
|
permissions permissions.PermissionsService
|
|
delivery SubscriptionDelivery
|
|
notifier *notifier
|
|
wsAdapter ws.Adapter
|
|
logger *mlog.Logger
|
|
notifyFreqCardSeconds int
|
|
notifyFreqBoardSeconds int
|
|
}
|
|
|
|
func New(params BackendParams) *Backend {
|
|
return &Backend{
|
|
store: params.Store,
|
|
delivery: params.Delivery,
|
|
permissions: params.Permissions,
|
|
notifier: newNotifier(params),
|
|
wsAdapter: params.WSAdapter,
|
|
logger: params.Logger,
|
|
notifyFreqCardSeconds: params.NotifyFreqCardSeconds,
|
|
notifyFreqBoardSeconds: params.NotifyFreqBoardSeconds,
|
|
}
|
|
}
|
|
|
|
func (b *Backend) Start() error {
|
|
b.logger.Debug("Starting subscriptions backend",
|
|
mlog.Int("freq_card", b.notifyFreqCardSeconds),
|
|
mlog.Int("freq_board", b.notifyFreqBoardSeconds),
|
|
)
|
|
b.notifier.start()
|
|
return nil
|
|
}
|
|
|
|
func (b *Backend) ShutDown() error {
|
|
b.logger.Debug("Stopping subscriptions backend")
|
|
b.notifier.stop()
|
|
_ = b.logger.Flush()
|
|
return nil
|
|
}
|
|
|
|
func (b *Backend) Name() string {
|
|
return backendName
|
|
}
|
|
|
|
func (b *Backend) getBlockUpdateFreq(blockType model.BlockType) time.Duration {
|
|
switch blockType {
|
|
case model.TypeCard:
|
|
return time.Second * time.Duration(b.notifyFreqCardSeconds)
|
|
default:
|
|
return defBlockNotificationFreq
|
|
}
|
|
}
|
|
|
|
func (b *Backend) BlockChanged(evt notify.BlockChangeEvent) error {
|
|
if evt.Board == nil {
|
|
b.logger.Warn("No board found for block, skipping notify",
|
|
mlog.String("block_id", evt.BlockChanged.ID),
|
|
)
|
|
return nil
|
|
}
|
|
|
|
merr := merror.New()
|
|
var err error
|
|
|
|
// if new card added, automatically subscribe the author.
|
|
if evt.Action == notify.Add && evt.BlockChanged.Type == model.TypeCard {
|
|
sub := &model.Subscription{
|
|
BlockType: model.TypeCard,
|
|
BlockID: evt.BlockChanged.ID,
|
|
SubscriberType: model.SubTypeUser,
|
|
SubscriberID: evt.ModifiedBy.UserID,
|
|
}
|
|
|
|
if _, err = b.store.CreateSubscription(sub); err != nil {
|
|
b.logger.Warn("Cannot subscribe card author to card",
|
|
mlog.String("card_id", evt.BlockChanged.ID),
|
|
mlog.Err(err),
|
|
)
|
|
}
|
|
b.wsAdapter.BroadcastSubscriptionChange(evt.TeamID, sub)
|
|
}
|
|
|
|
// notify board subscribers
|
|
subs, err := b.store.GetSubscribersForBlock(evt.Board.ID)
|
|
if err != nil {
|
|
merr.Append(fmt.Errorf("cannot fetch subscribers for board %s: %w", evt.Board.ID, err))
|
|
}
|
|
if err = b.notifySubscribers(subs, evt.Board.ID, model.TypeBoard, evt.ModifiedBy.UserID); err != nil {
|
|
merr.Append(fmt.Errorf("cannot notify board subscribers for board %s: %w", evt.Board.ID, err))
|
|
}
|
|
|
|
if evt.Card == nil {
|
|
return merr.ErrorOrNil()
|
|
}
|
|
|
|
// notify card subscribers
|
|
subs, err = b.store.GetSubscribersForBlock(evt.Card.ID)
|
|
if err != nil {
|
|
merr.Append(fmt.Errorf("cannot fetch subscribers for card %s: %w", evt.Card.ID, err))
|
|
}
|
|
if err = b.notifySubscribers(subs, evt.Card.ID, model.TypeCard, evt.ModifiedBy.UserID); err != nil {
|
|
merr.Append(fmt.Errorf("cannot notify card subscribers for card %s: %w", evt.Card.ID, err))
|
|
}
|
|
|
|
// notify block subscribers (if/when other types can be subscribed to)
|
|
if evt.Board.ID != evt.BlockChanged.ID && evt.Card.ID != evt.BlockChanged.ID {
|
|
subs, err := b.store.GetSubscribersForBlock(evt.BlockChanged.ID)
|
|
if err != nil {
|
|
merr.Append(fmt.Errorf("cannot fetch subscribers for block %s: %w", evt.BlockChanged.ID, err))
|
|
}
|
|
if err := b.notifySubscribers(subs, evt.BlockChanged.ID, evt.BlockChanged.Type, evt.ModifiedBy.UserID); err != nil {
|
|
merr.Append(fmt.Errorf("cannot notify block subscribers for block %s: %w", evt.BlockChanged.ID, err))
|
|
}
|
|
}
|
|
return merr.ErrorOrNil()
|
|
}
|
|
|
|
// notifySubscribers triggers a change notification for subscribers by writing a notification hint to the database.
|
|
func (b *Backend) notifySubscribers(subs []*model.Subscriber, blockID string, idType model.BlockType, modifiedByID string) error {
|
|
if len(subs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
hint := &model.NotificationHint{
|
|
BlockType: idType,
|
|
BlockID: blockID,
|
|
ModifiedByID: modifiedByID,
|
|
}
|
|
|
|
hint, err := b.store.UpsertNotificationHint(hint, b.getBlockUpdateFreq(idType))
|
|
if err != nil {
|
|
return fmt.Errorf("cannot upsert notification hint: %w", err)
|
|
}
|
|
if err := b.notifier.onNotifyHint(hint); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// OnMention satisfies the `MentionListener` interface and is called whenever a @mention notification
|
|
// is sent. Here we create a subscription for the mentioned user to the card.
|
|
func (b *Backend) OnMention(userID string, evt notify.BlockChangeEvent) {
|
|
if evt.Card == nil {
|
|
b.logger.Debug("Cannot subscribe mentioned user to nil card",
|
|
mlog.String("user_id", userID),
|
|
mlog.String("block_id", evt.BlockChanged.ID),
|
|
)
|
|
return
|
|
}
|
|
|
|
// user mentioned must be a board member to subscribe to card.
|
|
if !b.permissions.HasPermissionToBoard(userID, evt.Board.ID, model.PermissionViewBoard) {
|
|
b.logger.Debug("Not subscribing mentioned non-board member to card",
|
|
mlog.String("user_id", userID),
|
|
mlog.String("block_id", evt.BlockChanged.ID),
|
|
)
|
|
return
|
|
}
|
|
|
|
sub := &model.Subscription{
|
|
BlockType: model.TypeCard,
|
|
BlockID: evt.Card.ID,
|
|
SubscriberType: model.SubTypeUser,
|
|
SubscriberID: userID,
|
|
}
|
|
|
|
var err error
|
|
if sub, err = b.store.CreateSubscription(sub); err != nil {
|
|
b.logger.Warn("Cannot subscribe mentioned user to card",
|
|
mlog.String("user_id", userID),
|
|
mlog.String("card_id", evt.Card.ID),
|
|
mlog.Err(err),
|
|
)
|
|
return
|
|
}
|
|
b.wsAdapter.BroadcastSubscriptionChange(evt.TeamID, sub)
|
|
|
|
b.logger.Debug("Subscribed mentioned user to card",
|
|
mlog.String("user_id", userID),
|
|
mlog.String("card_id", evt.Card.ID),
|
|
)
|
|
}
|
|
|
|
// BroadcastSubscriptionChange sends a websocket message with details of the changed subscription to all
|
|
// connected users in the team.
|
|
func (b *Backend) BroadcastSubscriptionChange(teamID string, subscription *model.Subscription) {
|
|
b.wsAdapter.BroadcastSubscriptionChange(teamID, subscription)
|
|
}
|