diff --git a/internal/payload/outgoing.go b/internal/payload/outgoing.go index a5eee2a54..5b812a2a8 100644 --- a/internal/payload/outgoing.go +++ b/internal/payload/outgoing.go @@ -1,12 +1,14 @@ package payload import ( + "context" "fmt" "net/url" - auth "github.com/crusttech/crust/auth/types" + authTypes "github.com/crusttech/crust/auth/types" + "github.com/crusttech/crust/internal/auth" "github.com/crusttech/crust/internal/payload/outgoing" - sam "github.com/crusttech/crust/sam/types" + samTypes "github.com/crusttech/crust/sam/types" ) const ( @@ -14,7 +16,11 @@ const ( attachmentPreviewURL = "/attachment/%d/preview.%s" ) -func Message(msg *sam.Message) *outgoing.Message { +func Message(ctx context.Context, msg *samTypes.Message) *outgoing.Message { + var currentUserID = auth.GetIdentityFromContext(ctx).Identity() + var canEdit = msg.Type.IsEditable() && msg.UserID == currentUserID + var canReply = msg.Type.IsRepliable() && msg.ReplyTo == 0 + return &outgoing.Message{ ID: msg.ID, ChannelID: Uint64toa(msg.ChannelID), @@ -26,22 +32,26 @@ func Message(msg *sam.Message) *outgoing.Message { User: User(msg.User), Attachment: Attachment(msg.Attachment), + CanReply: canReply, + CanEdit: canEdit, + CanDelete: canEdit, + CreatedAt: msg.CreatedAt, UpdatedAt: msg.UpdatedAt, DeletedAt: msg.DeletedAt, } } -func Messages(msg sam.MessageSet) *outgoing.MessageSet { +func Messages(ctx context.Context, msg samTypes.MessageSet) *outgoing.MessageSet { msgs := make([]*outgoing.Message, len(msg)) for k, m := range msg { - msgs[k] = Message(m) + msgs[k] = Message(ctx, m) } retval := outgoing.MessageSet(msgs) return &retval } -func Channel(ch *sam.Channel) *outgoing.Channel { +func Channel(ch *samTypes.Channel) *outgoing.Channel { return &outgoing.Channel{ ID: Uint64toa(ch.ID), Name: ch.Name, @@ -58,7 +68,7 @@ func Channel(ch *sam.Channel) *outgoing.Channel { } } -func Channels(channels sam.ChannelSet) *outgoing.ChannelSet { +func Channels(channels samTypes.ChannelSet) *outgoing.ChannelSet { cc := make([]*outgoing.Channel, len(channels)) for k, c := range channels { cc[k] = Channel(c) @@ -67,7 +77,7 @@ func Channels(channels sam.ChannelSet) *outgoing.ChannelSet { return &retval } -func ChannelMember(m *sam.ChannelMember) *outgoing.ChannelMember { +func ChannelMember(m *samTypes.ChannelMember) *outgoing.ChannelMember { return &outgoing.ChannelMember{ User: User(m.User), Type: string(m.Type), @@ -76,7 +86,7 @@ func ChannelMember(m *sam.ChannelMember) *outgoing.ChannelMember { } } -func ChannelMembers(members sam.ChannelMemberSet) *outgoing.ChannelMemberSet { +func ChannelMembers(members samTypes.ChannelMemberSet) *outgoing.ChannelMemberSet { mm := make([]*outgoing.ChannelMember, len(members)) for k, c := range members { mm[k] = ChannelMember(c) @@ -85,7 +95,7 @@ func ChannelMembers(members sam.ChannelMemberSet) *outgoing.ChannelMemberSet { return &retval } -func ChannelView(v *sam.ChannelView) *outgoing.ChannelView { +func ChannelView(v *samTypes.ChannelView) *outgoing.ChannelView { if v == nil { return nil } @@ -110,7 +120,7 @@ func ChannelPart(channelID, userID uint64) *outgoing.ChannelPart { } } -func User(user *auth.User) *outgoing.User { +func User(user *authTypes.User) *outgoing.User { if user == nil { return nil } @@ -124,7 +134,7 @@ func User(user *auth.User) *outgoing.User { } } -func Users(users []*auth.User) *outgoing.UserSet { +func Users(users []*authTypes.User) *outgoing.UserSet { uu := make([]*outgoing.User, len(users)) for k, u := range users { uu[k] = User(u) @@ -135,7 +145,7 @@ func Users(users []*auth.User) *outgoing.UserSet { return &retval } -func Attachment(in *sam.Attachment) *outgoing.Attachment { +func Attachment(in *samTypes.Attachment) *outgoing.Attachment { if in == nil { return nil } @@ -163,7 +173,7 @@ func Attachment(in *sam.Attachment) *outgoing.Attachment { } } -func Command(cmd *sam.Command) *outgoing.Command { +func Command(cmd *samTypes.Command) *outgoing.Command { if cmd == nil { return nil } @@ -174,7 +184,7 @@ func Command(cmd *sam.Command) *outgoing.Command { } } -func Commands(cc sam.CommandSet) *outgoing.CommandSet { +func Commands(cc samTypes.CommandSet) *outgoing.CommandSet { out := make([]*outgoing.Command, len(cc)) for k, m := range cc { out[k] = Command(m) diff --git a/internal/payload/outgoing/message.go b/internal/payload/outgoing/message.go index fb23f58de..211ff1d20 100644 --- a/internal/payload/outgoing/message.go +++ b/internal/payload/outgoing/message.go @@ -17,6 +17,10 @@ type ( User *User `json:"user"` Attachment *Attachment `json:"att,omitempty"` + CanReply bool `json:"canReply"` + CanEdit bool `json:"canEdit"` + CanDelete bool `json:"canDelete"` + CreatedAt time.Time `json:"createdAt"` UpdatedAt *time.Time `json:"updatedAt,omitempty"` DeletedAt *time.Time `json:"deletedAt,omitempty"` diff --git a/sam/rest/message.go b/sam/rest/message.go index 692558353..e5d157585 100644 --- a/sam/rest/message.go +++ b/sam/rest/message.go @@ -28,14 +28,14 @@ func (Message) New() *Message { } func (ctrl *Message) Create(ctx context.Context, r *request.MessageCreate) (interface{}, error) { - return ctrl.wrap(ctrl.svc.msg.With(ctx).Create(&types.Message{ + return ctrl.wrap(ctx)(ctrl.svc.msg.With(ctx).Create(&types.Message{ ChannelID: r.ChannelID, Message: r.Message, })) } func (ctrl *Message) CreateReply(ctx context.Context, r *request.MessageCreateReply) (interface{}, error) { - return ctrl.wrap(ctrl.svc.msg.With(ctx).Create(&types.Message{ + return ctrl.wrap(ctx)(ctrl.svc.msg.With(ctx).Create(&types.Message{ ChannelID: r.ChannelID, ReplyTo: r.MessageID, Message: r.Message, @@ -43,21 +43,21 @@ func (ctrl *Message) CreateReply(ctx context.Context, r *request.MessageCreateRe } func (ctrl *Message) GetReplies(ctx context.Context, r *request.MessageGetReplies) (interface{}, error) { - return ctrl.wrapSet(ctrl.svc.msg.With(ctx).Find(&types.MessageFilter{ + return ctrl.wrapSet(ctx)(ctrl.svc.msg.With(ctx).Find(&types.MessageFilter{ ChannelID: r.ChannelID, RepliesTo: r.MessageID, })) } func (ctrl *Message) History(ctx context.Context, r *request.MessageHistory) (interface{}, error) { - return ctrl.wrapSet(ctrl.svc.msg.With(ctx).Find(&types.MessageFilter{ + return ctrl.wrapSet(ctx)(ctrl.svc.msg.With(ctx).Find(&types.MessageFilter{ ChannelID: r.ChannelID, FirstID: r.LastMessageID, })) } func (ctrl *Message) Edit(ctx context.Context, r *request.MessageEdit) (interface{}, error) { - return ctrl.wrap(ctrl.svc.msg.With(ctx).Update(&types.Message{ + return ctrl.wrap(ctx)(ctrl.svc.msg.With(ctx).Update(&types.Message{ ID: r.MessageID, ChannelID: r.ChannelID, Message: r.Message, @@ -69,7 +69,7 @@ func (ctrl *Message) Delete(ctx context.Context, r *request.MessageDelete) (inte } func (ctrl *Message) Search(ctx context.Context, r *request.MessageSearch) (interface{}, error) { - return ctrl.wrapSet(ctrl.svc.msg.With(ctx).Find(&types.MessageFilter{ + return ctrl.wrapSet(ctx)(ctrl.svc.msg.With(ctx).Find(&types.MessageFilter{ ChannelID: r.ChannelID, Query: r.Query, })) @@ -98,18 +98,22 @@ func (ctrl *Message) React(ctx context.Context, r *request.MessageReact) (interf func (ctrl *Message) Unreact(ctx context.Context, r *request.MessageUnreact) (interface{}, error) { return nil, ctrl.svc.msg.With(ctx).Unreact(r.MessageID, r.Reaction) } -func (ctrl *Message) wrap(m *types.Message, err error) (*outgoing.Message, error) { - if err != nil { - return nil, err - } else { - return payload.Message(m), nil +func (ctrl *Message) wrap(ctx context.Context) func(m *types.Message, err error) (*outgoing.Message, error) { + return func(m *types.Message, err error) (*outgoing.Message, error) { + if err != nil { + return nil, err + } else { + return payload.Message(ctx, m), nil + } } } -func (ctrl *Message) wrapSet(mm types.MessageSet, err error) (*outgoing.MessageSet, error) { - if err != nil { - return nil, err - } else { - return payload.Messages(mm), nil +func (ctrl *Message) wrapSet(ctx context.Context) func(mm types.MessageSet, err error) (*outgoing.MessageSet, error) { + return func(mm types.MessageSet, err error) (*outgoing.MessageSet, error) { + if err != nil { + return nil, err + } else { + return payload.Messages(ctx, mm), nil + } } } diff --git a/sam/service/events.go b/sam/service/events.go index c34b426cc..f777ba5ae 100644 --- a/sam/service/events.go +++ b/sam/service/events.go @@ -39,7 +39,7 @@ func (svc *event) With(ctx context.Context) EventService { // Message sends message events to subscribers func (svc *event) Message(m *types.Message) error { - return svc.push(payload.Message(m), types.EventQueueItemSubTypeChannel, m.ChannelID) + return svc.push(payload.Message(svc.ctx, m), types.EventQueueItemSubTypeChannel, m.ChannelID) } // Channel notifies subscribers about channel change diff --git a/sam/service/message.go b/sam/service/message.go index 9afd22903..f124b945f 100644 --- a/sam/service/message.go +++ b/sam/service/message.go @@ -106,17 +106,26 @@ func (svc *message) Create(mod *types.Message) (message *types.Message, err erro if mod.ReplyTo > 0 { var original *types.Message - original, err = svc.message.FindMessageByID(mod.ReplyTo) - if err != nil { - return + var replyTo = mod.ReplyTo + + for replyTo > 0 { + // Find original message + original, err = svc.message.FindMessageByID(mod.ReplyTo) + if err != nil { + return + } + + replyTo = original.ReplyTo } - if original.ReplyTo > 0 { - // We do not want to have multi-level threads - // Take original's reply-to and use it - mod.ReplyTo = original.ReplyTo + if !original.Type.IsRepliable() { + return errors.Errorf("Unable to reply on this message (type = %s)", original.Type) } + // We do not want to have multi-level threads + // Take original's reply-to and use it + mod.ReplyTo = original.ID + mod.ChannelID = original.ChannelID // Increment counter, on struct and in repostiry. diff --git a/sam/types/message.go b/sam/types/message.go index b6bd45163..334edcb30 100644 --- a/sam/types/message.go +++ b/sam/types/message.go @@ -89,6 +89,21 @@ func (mtype MessageType) IsValid() bool { return false } +func (mtype MessageType) IsRepliable() bool { + return mtype.IsEditable() +} + +func (mtype MessageType) IsEditable() bool { + switch mtype { + case MessageTypeSimpleMessage, + MessageTypeInlineImage, + MessageTypeAttachment: + return true + } + + return false +} + //func (mtype *MessageType) Scan(value interface{}) error { // switch value.(type) { // case nil: diff --git a/sam/websocket/session_incoming_message.go b/sam/websocket/session_incoming_message.go index 8957c7cb0..f417b3cc4 100644 --- a/sam/websocket/session_incoming_message.go +++ b/sam/websocket/session_incoming_message.go @@ -50,7 +50,7 @@ func (s *Session) messageHistory(ctx context.Context, p *incoming.Messages) erro return err } - err = s.sendReply(payload.Messages(messages)) + err = s.sendReply(payload.Messages(ctx, messages)) if err != nil { return err }