ff020d85e3
* Don't notify non-board members of card changes - include permissions service in notifications backends - use permissions service to ensure @mentions are to users on team - use permissions service to ensure subscribers are members of board
138 lines
3.3 KiB
Go
138 lines
3.3 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package notifymentions
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/mattermost/focalboard/server/model"
|
|
"github.com/mattermost/focalboard/server/services/notify"
|
|
"github.com/mattermost/focalboard/server/services/permissions"
|
|
"github.com/wiggin77/merror"
|
|
|
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
)
|
|
|
|
const (
|
|
backendName = "notifyMentions"
|
|
)
|
|
|
|
type MentionListener interface {
|
|
OnMention(userID string, evt notify.BlockChangeEvent)
|
|
}
|
|
|
|
// Backend provides the notification backend for @mentions.
|
|
type Backend struct {
|
|
delivery MentionDelivery
|
|
permissions permissions.PermissionsService
|
|
logger *mlog.Logger
|
|
|
|
mux sync.RWMutex
|
|
listeners []MentionListener
|
|
}
|
|
|
|
func New(delivery MentionDelivery, permissions permissions.PermissionsService, logger *mlog.Logger) *Backend {
|
|
return &Backend{
|
|
delivery: delivery,
|
|
permissions: permissions,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (b *Backend) Start() error {
|
|
return nil
|
|
}
|
|
|
|
func (b *Backend) ShutDown() error {
|
|
_ = b.logger.Flush()
|
|
return nil
|
|
}
|
|
|
|
func (b *Backend) Name() string {
|
|
return backendName
|
|
}
|
|
|
|
func (b *Backend) AddListener(l MentionListener) {
|
|
b.mux.Lock()
|
|
defer b.mux.Unlock()
|
|
b.listeners = append(b.listeners, l)
|
|
b.logger.Debug("Mention listener added.", mlog.Int("listener_count", len(b.listeners)))
|
|
}
|
|
|
|
func (b *Backend) RemoveListener(l MentionListener) {
|
|
b.mux.Lock()
|
|
defer b.mux.Unlock()
|
|
list := make([]MentionListener, 0, len(b.listeners))
|
|
for _, listener := range b.listeners {
|
|
if listener != l {
|
|
list = append(list, listener)
|
|
}
|
|
}
|
|
b.listeners = list
|
|
b.logger.Debug("Mention listener removed.", mlog.Int("listener_count", len(b.listeners)))
|
|
}
|
|
|
|
func (b *Backend) BlockChanged(evt notify.BlockChangeEvent) error {
|
|
if evt.Board == nil || evt.Card == nil {
|
|
return nil
|
|
}
|
|
|
|
if evt.Action == notify.Delete {
|
|
return nil
|
|
}
|
|
|
|
switch evt.BlockChanged.Type {
|
|
case model.TypeText, model.TypeComment, model.TypeImage:
|
|
default:
|
|
return nil
|
|
}
|
|
|
|
mentions := extractMentions(evt.BlockChanged)
|
|
if len(mentions) == 0 {
|
|
return nil
|
|
}
|
|
|
|
oldMentions := extractMentions(evt.BlockOld)
|
|
merr := merror.New()
|
|
|
|
b.mux.RLock()
|
|
listeners := make([]MentionListener, len(b.listeners))
|
|
copy(listeners, b.listeners)
|
|
b.mux.RUnlock()
|
|
|
|
for username := range mentions {
|
|
if _, exists := oldMentions[username]; exists {
|
|
// the mention already existed; no need to notify again
|
|
continue
|
|
}
|
|
|
|
extract := extractText(evt.BlockChanged.Title, username, newLimits())
|
|
|
|
userID, err := b.delivery.MentionDeliver(username, extract, evt)
|
|
if err != nil {
|
|
merr.Append(fmt.Errorf("cannot deliver notification for @%s: %w", username, err))
|
|
}
|
|
|
|
b.logger.Debug("Mention notification delivered",
|
|
mlog.String("user", username),
|
|
mlog.Int("listener_count", len(listeners)),
|
|
)
|
|
|
|
for _, listener := range listeners {
|
|
safeCallListener(listener, userID, evt, b.logger)
|
|
}
|
|
}
|
|
return merr.ErrorOrNil()
|
|
}
|
|
|
|
func safeCallListener(listener MentionListener, userID string, evt notify.BlockChangeEvent, logger *mlog.Logger) {
|
|
// don't let panicky listeners stop notifications
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
logger.Error("panic calling @mention notification listener", mlog.Any("err", r))
|
|
}
|
|
}()
|
|
listener.OnMention(userID, evt)
|
|
}
|