Add support for membership policy, featured channels
This commit is contained in:
@@ -110,6 +110,13 @@
|
||||
"Client ID",
|
||||
"Session ID"
|
||||
],
|
||||
"struct": [
|
||||
{
|
||||
"imports": [
|
||||
"github.com/cortezaproject/corteza-server/messaging/types"
|
||||
]
|
||||
}
|
||||
],
|
||||
"apis": [
|
||||
{
|
||||
"name": "list",
|
||||
@@ -152,6 +159,12 @@
|
||||
"required": false,
|
||||
"title": "Channel type"
|
||||
},
|
||||
{
|
||||
"type": "types.ChannelMembershipPolicy",
|
||||
"name": "membershipPolicy",
|
||||
"required": false,
|
||||
"title": "Membership policy (eg: featured, forced)?"
|
||||
},
|
||||
{
|
||||
"type": "[]string",
|
||||
"name": "members",
|
||||
@@ -188,6 +201,12 @@
|
||||
"required": false,
|
||||
"title": "Subject of Channel"
|
||||
},
|
||||
{
|
||||
"type": "types.ChannelMembershipPolicy",
|
||||
"name": "membershipPolicy",
|
||||
"required": false,
|
||||
"title": "Membership policy (eg: featured, forced)?"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"name": "type",
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
"Title": "Channels",
|
||||
"Description": "A channel is a representation of a sequence of messages. It has meta data like channel subject. Channels may be public, private or group.",
|
||||
"Interface": "Channel",
|
||||
"Struct": null,
|
||||
"Struct": [
|
||||
{
|
||||
"imports": [
|
||||
"github.com/cortezaproject/corteza-server/messaging/types"
|
||||
]
|
||||
}
|
||||
],
|
||||
"Parameters": null,
|
||||
"Protocol": "",
|
||||
"Authentication": [
|
||||
@@ -52,6 +58,12 @@
|
||||
"title": "Channel type",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "membershipPolicy",
|
||||
"required": false,
|
||||
"title": "Membership policy (eg: featured, forced)?",
|
||||
"type": "types.ChannelMembershipPolicy"
|
||||
},
|
||||
{
|
||||
"name": "members",
|
||||
"required": false,
|
||||
@@ -88,6 +100,12 @@
|
||||
"title": "Subject of Channel",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"name": "membershipPolicy",
|
||||
"required": false,
|
||||
"title": "Membership policy (eg: featured, forced)?",
|
||||
"type": "types.ChannelMembershipPolicy"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"required": false,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -120,6 +120,7 @@ A channel is a representation of a sequence of messages. It has meta data like c
|
||||
| name | string | POST | Name of Channel | N/A | NO |
|
||||
| topic | string | POST | Subject of Channel | N/A | NO |
|
||||
| type | string | POST | Channel type | N/A | NO |
|
||||
| membershipPolicy | types.ChannelMembershipPolicy | POST | Membership policy (eg: featured, forced)? | N/A | NO |
|
||||
| members | []string | POST | Initial members of the channel | N/A | NO |
|
||||
|
||||
## Update channel details
|
||||
@@ -137,6 +138,7 @@ A channel is a representation of a sequence of messages. It has meta data like c
|
||||
| channelID | uint64 | PATH | Channel ID | N/A | YES |
|
||||
| name | string | POST | Name of Channel | N/A | NO |
|
||||
| topic | string | POST | Subject of Channel | N/A | NO |
|
||||
| membershipPolicy | types.ChannelMembershipPolicy | POST | Membership policy (eg: featured, forced)? | N/A | NO |
|
||||
| type | string | POST | Channel type | N/A | NO |
|
||||
| organisationID | uint64 | POST | Move channel to different organisation | N/A | NO |
|
||||
|
||||
|
||||
@@ -138,24 +138,26 @@ func Channel(ch *messagingTypes.Channel) *outgoing.Channel {
|
||||
}
|
||||
|
||||
return &outgoing.Channel{
|
||||
ID: Uint64toa(ch.ID),
|
||||
Name: ch.Name,
|
||||
LastMessageID: Uint64toa(ch.LastMessageID),
|
||||
Topic: ch.Topic,
|
||||
Type: string(ch.Type),
|
||||
MembershipFlag: string(flag),
|
||||
Members: Uint64stoa(ch.Members),
|
||||
Unread: ChannelUnread(ch.Unread),
|
||||
ID: Uint64toa(ch.ID),
|
||||
Name: ch.Name,
|
||||
LastMessageID: Uint64toa(ch.LastMessageID),
|
||||
Topic: ch.Topic,
|
||||
Type: string(ch.Type),
|
||||
MembershipFlag: string(flag),
|
||||
MembershipPolicy: string(ch.MembershipPolicy),
|
||||
Members: Uint64stoa(ch.Members),
|
||||
Unread: ChannelUnread(ch.Unread),
|
||||
|
||||
CanJoin: ch.CanJoin,
|
||||
CanPart: ch.CanPart,
|
||||
CanObserve: ch.CanObserve,
|
||||
CanSendMessages: ch.CanSendMessages,
|
||||
CanDeleteMessages: ch.CanDeleteMessages,
|
||||
CanChangeMembers: ch.CanChangeMembers,
|
||||
CanUpdate: ch.CanUpdate,
|
||||
CanArchive: ch.CanArchive,
|
||||
CanDelete: ch.CanDelete,
|
||||
CanJoin: ch.CanJoin,
|
||||
CanPart: ch.CanPart,
|
||||
CanObserve: ch.CanObserve,
|
||||
CanSendMessages: ch.CanSendMessages,
|
||||
CanDeleteMessages: ch.CanDeleteMessages,
|
||||
CanChangeMembers: ch.CanChangeMembers,
|
||||
CanChangeMembershipPolicy: ch.CanChangeMembershipPolicy,
|
||||
CanUpdate: ch.CanUpdate,
|
||||
CanArchive: ch.CanArchive,
|
||||
CanDelete: ch.CanDelete,
|
||||
|
||||
CreatedAt: ch.CreatedAt,
|
||||
UpdatedAt: ch.UpdatedAt,
|
||||
|
||||
@@ -24,24 +24,26 @@ type (
|
||||
|
||||
Channel struct {
|
||||
// Channel to part (nil) for ALL channels
|
||||
ID string `json:"channelID"`
|
||||
Name string `json:"name"`
|
||||
Topic string `json:"topic"`
|
||||
Type string `json:"type"`
|
||||
LastMessageID string `json:"lastMessageID"`
|
||||
Unread *Unread `json:"unread,omitempty"`
|
||||
Members []string `json:"members,omitempty"`
|
||||
MembershipFlag string `json:"membershipFlag"`
|
||||
ID string `json:"channelID"`
|
||||
Name string `json:"name"`
|
||||
Topic string `json:"topic"`
|
||||
Type string `json:"type"`
|
||||
MembershipPolicy string `json:"membershipPolicy"`
|
||||
LastMessageID string `json:"lastMessageID"`
|
||||
Unread *Unread `json:"unread,omitempty"`
|
||||
Members []string `json:"members,omitempty"`
|
||||
MembershipFlag string `json:"membershipFlag"`
|
||||
|
||||
CanJoin bool `json:"canJoin"`
|
||||
CanPart bool `json:"canPart"`
|
||||
CanObserve bool `json:"canObserve"`
|
||||
CanSendMessages bool `json:"canSendMessages"`
|
||||
CanDeleteMessages bool `json:"canDeleteMessages"`
|
||||
CanChangeMembers bool `json:"canChangeMembers"`
|
||||
CanUpdate bool `json:"canUpdate"`
|
||||
CanArchive bool `json:"canArchive"`
|
||||
CanDelete bool `json:"canDelete"`
|
||||
CanJoin bool `json:"canJoin"`
|
||||
CanPart bool `json:"canPart"`
|
||||
CanObserve bool `json:"canObserve"`
|
||||
CanSendMessages bool `json:"canSendMessages"`
|
||||
CanDeleteMessages bool `json:"canDeleteMessages"`
|
||||
CanChangeMembers bool `json:"canChangeMembers"`
|
||||
CanChangeMembershipPolicy bool `json:"canChangeMembershipPolicy"`
|
||||
CanUpdate bool `json:"canUpdate"`
|
||||
CanArchive bool `json:"canArchive"`
|
||||
CanDelete bool `json:"canDelete"`
|
||||
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
ALTER TABLE `messaging_channel` ADD `membership_policy` ENUM ('featured', 'forced', '') NOT NULL DEFAULT '' AFTER `type`;
|
||||
@@ -40,6 +40,7 @@ const (
|
||||
sqlChannelColumns = " id," +
|
||||
"name, " +
|
||||
"meta, " +
|
||||
"membership_policy, " +
|
||||
"created_at, " +
|
||||
"updated_at, " +
|
||||
"archived_at, " +
|
||||
@@ -159,7 +160,7 @@ func (r *channel) Update(mod *types.Channel) (*types.Channel, error) {
|
||||
mod.Type = types.ChannelTypePublic
|
||||
}
|
||||
|
||||
whitelist := []string{"id", "name", "type", "topic", "meta", "updated_at"}
|
||||
whitelist := []string{"id", "name", "type", "membership_policy", "topic", "meta", "updated_at"}
|
||||
|
||||
return mod, r.db().UpdatePartial("messaging_channel", mod, whitelist, "id")
|
||||
}
|
||||
|
||||
@@ -115,6 +115,11 @@ func (svc accessControl) CanManageChannelMembers(ctx context.Context, ch *types.
|
||||
return svc.can(ctx, ch, "members.manage", svc.isChannelOwnerFallback(ctx, ch))
|
||||
}
|
||||
|
||||
func (svc accessControl) CanChangeChannelMembershipPolicy(ctx context.Context, ch *types.Channel) bool {
|
||||
// @todo introduce dedicated channel op. for this.
|
||||
return svc.can(ctx, types.MessagingPermissionResource, "grant")
|
||||
}
|
||||
|
||||
func (svc accessControl) CanManageChannelAttachments(ctx context.Context, ch *types.Channel) bool {
|
||||
return svc.can(ctx, ch, "attachments.manage")
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ type (
|
||||
CanArchiveChannel(context.Context, *types.Channel) bool
|
||||
CanUnarchiveChannel(context.Context, *types.Channel) bool
|
||||
CanManageChannelMembers(context.Context, *types.Channel) bool
|
||||
CanChangeChannelMembershipPolicy(context.Context, *types.Channel) bool
|
||||
CanSendMessage(context.Context, *types.Channel) bool
|
||||
CanUpdateOwnMessages(context.Context, *types.Channel) bool
|
||||
CanUpdateMessages(context.Context, *types.Channel) bool
|
||||
@@ -268,13 +269,23 @@ func (svc *channel) Create(in *types.Channel) (out *types.Channel, err error) {
|
||||
return ErrNoPermissions.withStack()
|
||||
}
|
||||
|
||||
if !in.MembershipPolicy.IsValid() {
|
||||
// Reset invalid membership flag to default
|
||||
in.MembershipPolicy = types.ChannelMembershipPolicyDefault
|
||||
}
|
||||
|
||||
if in.MembershipPolicy != types.ChannelMembershipPolicyDefault && !svc.ac.CanChangeChannelMembershipPolicy(svc.ctx, in) {
|
||||
return ErrNoPermissions.withStack()
|
||||
}
|
||||
|
||||
// This is a fresh channel, just copy values
|
||||
out = &types.Channel{
|
||||
Name: in.Name,
|
||||
Topic: in.Topic,
|
||||
Type: in.Type,
|
||||
OrganisationID: organisationID,
|
||||
CreatorID: chCreatorID,
|
||||
Name: in.Name,
|
||||
Topic: in.Topic,
|
||||
Type: in.Type,
|
||||
MembershipPolicy: in.MembershipPolicy,
|
||||
OrganisationID: organisationID,
|
||||
CreatorID: chCreatorID,
|
||||
}
|
||||
|
||||
// Save the channel
|
||||
@@ -427,6 +438,13 @@ func (svc *channel) Update(in *types.Channel) (ch *types.Channel, err error) {
|
||||
changed = true
|
||||
}
|
||||
|
||||
if ch.MembershipPolicy != in.MembershipPolicy && !svc.ac.CanChangeChannelMembershipPolicy(svc.ctx, ch) {
|
||||
return ErrNoPermissions.withStack()
|
||||
} else {
|
||||
ch.MembershipPolicy = in.MembershipPolicy
|
||||
changed = true
|
||||
}
|
||||
|
||||
if !changed {
|
||||
return nil
|
||||
}
|
||||
@@ -891,6 +909,8 @@ func (svc *channel) setPermissionFlags(ch *types.Channel) (err error) {
|
||||
ch.CanUpdateMessages = svc.ac.CanUpdateMessages(svc.ctx, ch)
|
||||
ch.CanUpdateOwnMessages = svc.ac.CanUpdateOwnMessages(svc.ctx, ch)
|
||||
ch.CanChangeMembers = svc.ac.CanManageChannelMembers(svc.ctx, ch)
|
||||
// @todo migrate to proper change-membership-policy action check
|
||||
ch.CanChangeMembershipPolicy = svc.ac.CanChangeChannelMembershipPolicy(svc.ctx, ch)
|
||||
|
||||
ch.CanUpdate = svc.ac.CanUpdateChannel(svc.ctx, ch)
|
||||
ch.CanArchive = svc.ac.CanArchiveChannel(svc.ctx, ch)
|
||||
|
||||
@@ -41,6 +41,8 @@ func (ctrl *Channel) Create(ctx context.Context, r *request.ChannelCreate) (inte
|
||||
// string input for members (for now)
|
||||
// https://github.com/golang/go/issues/15624
|
||||
Members: payload.ParseUInt64s(r.Members),
|
||||
|
||||
MembershipPolicy: r.MembershipPolicy,
|
||||
}
|
||||
|
||||
return ctrl.wrap(ctrl.svc.ch.With(ctx).Create(channel))
|
||||
@@ -52,6 +54,8 @@ func (ctrl *Channel) Update(ctx context.Context, r *request.ChannelUpdate) (inte
|
||||
Name: r.Name,
|
||||
Topic: r.Topic,
|
||||
Type: types.ChannelType(r.Type),
|
||||
|
||||
MembershipPolicy: r.MembershipPolicy,
|
||||
}
|
||||
|
||||
return ctrl.wrap(ctrl.svc.ch.With(ctx).Update(channel))
|
||||
|
||||
@@ -25,6 +25,8 @@ import (
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/messaging/types"
|
||||
)
|
||||
|
||||
var _ = chi.URLParam
|
||||
@@ -85,10 +87,11 @@ var _ RequestFiller = NewChannelList()
|
||||
|
||||
// Channel create request parameters
|
||||
type ChannelCreate struct {
|
||||
Name string
|
||||
Topic string
|
||||
Type string
|
||||
Members []string
|
||||
Name string
|
||||
Topic string
|
||||
Type string
|
||||
MembershipPolicy types.ChannelMembershipPolicy
|
||||
Members []string
|
||||
}
|
||||
|
||||
func NewChannelCreate() *ChannelCreate {
|
||||
@@ -101,6 +104,7 @@ func (r ChannelCreate) Auditable() map[string]interface{} {
|
||||
out["name"] = r.Name
|
||||
out["topic"] = r.Topic
|
||||
out["type"] = r.Type
|
||||
out["membershipPolicy"] = r.MembershipPolicy
|
||||
out["members"] = r.Members
|
||||
|
||||
return out
|
||||
@@ -142,6 +146,9 @@ func (r *ChannelCreate) Fill(req *http.Request) (err error) {
|
||||
if val, ok := post["type"]; ok {
|
||||
r.Type = val
|
||||
}
|
||||
if val, ok := post["membershipPolicy"]; ok {
|
||||
r.MembershipPolicy = types.ChannelMembershipPolicy(val)
|
||||
}
|
||||
|
||||
if val, ok := req.Form["members"]; ok {
|
||||
r.Members = parseStrings(val)
|
||||
@@ -154,11 +161,12 @@ var _ RequestFiller = NewChannelCreate()
|
||||
|
||||
// Channel update request parameters
|
||||
type ChannelUpdate struct {
|
||||
ChannelID uint64 `json:",string"`
|
||||
Name string
|
||||
Topic string
|
||||
Type string
|
||||
OrganisationID uint64 `json:",string"`
|
||||
ChannelID uint64 `json:",string"`
|
||||
Name string
|
||||
Topic string
|
||||
MembershipPolicy types.ChannelMembershipPolicy
|
||||
Type string
|
||||
OrganisationID uint64 `json:",string"`
|
||||
}
|
||||
|
||||
func NewChannelUpdate() *ChannelUpdate {
|
||||
@@ -171,6 +179,7 @@ func (r ChannelUpdate) Auditable() map[string]interface{} {
|
||||
out["channelID"] = r.ChannelID
|
||||
out["name"] = r.Name
|
||||
out["topic"] = r.Topic
|
||||
out["membershipPolicy"] = r.MembershipPolicy
|
||||
out["type"] = r.Type
|
||||
out["organisationID"] = r.OrganisationID
|
||||
|
||||
@@ -211,6 +220,9 @@ func (r *ChannelUpdate) Fill(req *http.Request) (err error) {
|
||||
if val, ok := post["topic"]; ok {
|
||||
r.Topic = val
|
||||
}
|
||||
if val, ok := post["membershipPolicy"]; ok {
|
||||
r.MembershipPolicy = types.ChannelMembershipPolicy(val)
|
||||
}
|
||||
if val, ok := post["type"]; ok {
|
||||
r.Type = val
|
||||
}
|
||||
|
||||
+38
-15
@@ -15,6 +15,8 @@ type (
|
||||
Type ChannelType `json:"type" db:"type"`
|
||||
Meta types.JSONText `json:"-" db:"meta"`
|
||||
|
||||
MembershipPolicy ChannelMembershipPolicy `json:"membershipPolicy" db:"membership_policy""`
|
||||
|
||||
CreatorID uint64 `json:"creatorId" db:"rel_creator"`
|
||||
OrganisationID uint64 `json:"organisationId" db:"rel_organisation"`
|
||||
|
||||
@@ -26,20 +28,21 @@ type (
|
||||
|
||||
LastMessageID uint64 `json:",omitempty" db:"rel_last_message"`
|
||||
|
||||
CanJoin bool `json:"-" db:"-"`
|
||||
CanPart bool `json:"-" db:"-"`
|
||||
CanObserve bool `json:"-" db:"-"`
|
||||
CanSendMessages bool `json:"-" db:"-"`
|
||||
CanDeleteMessages bool `json:"-" db:"-"`
|
||||
CanDeleteOwnMessages bool `json:"-" db:"-"`
|
||||
CanUpdateMessages bool `json:"-" db:"-"`
|
||||
CanUpdateOwnMessages bool `json:"-" db:"-"`
|
||||
CanChangeMembers bool `json:"-" db:"-"`
|
||||
CanUpdate bool `json:"-" db:"-"`
|
||||
CanArchive bool `json:"-" db:"-"`
|
||||
CanUnarchive bool `json:"-" db:"-"`
|
||||
CanDelete bool `json:"-" db:"-"`
|
||||
CanUndelete bool `json:"-" db:"-"`
|
||||
CanJoin bool `json:"-" db:"-"`
|
||||
CanPart bool `json:"-" db:"-"`
|
||||
CanObserve bool `json:"-" db:"-"`
|
||||
CanSendMessages bool `json:"-" db:"-"`
|
||||
CanDeleteMessages bool `json:"-" db:"-"`
|
||||
CanDeleteOwnMessages bool `json:"-" db:"-"`
|
||||
CanUpdateMessages bool `json:"-" db:"-"`
|
||||
CanUpdateOwnMessages bool `json:"-" db:"-"`
|
||||
CanChangeMembers bool `json:"-" db:"-"`
|
||||
CanChangeMembershipPolicy bool `json:"-" db:"-"`
|
||||
CanUpdate bool `json:"-" db:"-"`
|
||||
CanArchive bool `json:"-" db:"-"`
|
||||
CanUnarchive bool `json:"-" db:"-"`
|
||||
CanDelete bool `json:"-" db:"-"`
|
||||
CanUndelete bool `json:"-" db:"-"`
|
||||
|
||||
Member *ChannelMember `json:"-" db:"-"`
|
||||
Members []uint64 `json:"-" db:"-"`
|
||||
@@ -56,7 +59,8 @@ type (
|
||||
IncludeDeleted bool
|
||||
}
|
||||
|
||||
ChannelType string
|
||||
ChannelMembershipPolicy string
|
||||
ChannelType string
|
||||
)
|
||||
|
||||
// Resource returns a system resource ID for this type
|
||||
@@ -72,6 +76,10 @@ const (
|
||||
ChannelTypePublic ChannelType = "public"
|
||||
ChannelTypePrivate ChannelType = "private"
|
||||
ChannelTypeGroup ChannelType = "group"
|
||||
|
||||
ChannelMembershipPolicyFeatured ChannelMembershipPolicy = "featured"
|
||||
ChannelMembershipPolicyForced ChannelMembershipPolicy = "forced"
|
||||
ChannelMembershipPolicyDefault ChannelMembershipPolicy = ""
|
||||
)
|
||||
|
||||
func (mtype ChannelType) String() string {
|
||||
@@ -88,3 +96,18 @@ func (mtype ChannelType) IsValid() bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (cm ChannelMembershipPolicy) String() string {
|
||||
return string(cm)
|
||||
}
|
||||
|
||||
func (cm ChannelMembershipPolicy) IsValid() bool {
|
||||
switch cm {
|
||||
case ChannelMembershipPolicyFeatured,
|
||||
ChannelMembershipPolicyForced,
|
||||
ChannelMembershipPolicyDefault:
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user