Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MM-133] Added a feature of restricting bot messages to private channels and DM/GMs #337

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d3a7688
[MM-133] Work on zoom feature of restricting bot messages to private …
ayusht2810 Jan 17, 2024
672f29a
[MM-133] Add role on channel-settings slash command
ayusht2810 Jan 17, 2024
1fb2e67
[MM-133] Update readme and help text slash command on basis of user role
ayusht2810 Jan 17, 2024
0c51cfb
[MM-133] Fix lint issues
ayusht2810 Jan 17, 2024
fdc98fc
[MM-133] Update dialog text
ayusht2810 Jan 18, 2024
6837578
[MM-133] Review fixes: Update user statements and update if else cond…
ayusht2810 Jan 22, 2024
ecc3cf9
Merge branch 'master' into MM-133
ayusht2810 Feb 1, 2024
6c05f40
[MM-133] Update system console and some review fixes
ayusht2810 Feb 2, 2024
417a123
[MM-133] Add some test cases
ayusht2810 Feb 7, 2024
fe29a06
[MM-133] Updated a test case
ayusht2810 Feb 7, 2024
645f26f
Merge branch 'master' of github.com:mattermost/mattermost-plugin-zoom…
ayusht2810 Feb 27, 2024
2549a1e
[MM-133] Remove unused variable
ayusht2810 Feb 27, 2024
eb5981d
Merge branch 'master' of github.com:mattermost/mattermost-plugin-zoom…
ayusht2810 Apr 25, 2024
4c013a8
[MM-133]: review fixes
Kshitij-Katiyar Jun 6, 2024
ebc3bca
[MM-133]: fixed server testcases
Kshitij-Katiyar Jun 6, 2024
66fc89c
[MM-133]: updated the channel setting list values
Kshitij-Katiyar Jun 11, 2024
5ff77f7
[MM-133]: resolved conflict
Kshitij-Katiyar Jun 21, 2024
ad2e197
Merge branch 'master' of github.com:mattermost/mattermost-plugin-zoom…
raghavaggarwal2308 Jul 2, 2024
df05023
[MM-542] Review fixes:
raghavaggarwal2308 Jul 2, 2024
8583cac
[MM-542] Fixed confusion in channel settings modal
raghavaggarwal2308 Jul 2, 2024
5f0a6bf
[MM-546] Remove bin folder
raghavaggarwal2308 Jul 4, 2024
6eb844d
[MM-133] Fix issue of list empty incase of no setting is present
ayusht2810 Jul 15, 2024
8304f0a
Merge branch 'master' of github.com:mattermost/mattermost-plugin-zoom…
ayusht2810 Jul 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,15 @@
"regenerate_help_text": "",
"placeholder": "",
"default": null
},
{
"key": "RestrictMeetingCreation",
"display_name": "Restrict Meeting Creation:",
"type": "bool",
"help_text": "Restrict user from creating meetings in public channels.",
"regenerate_help_text": "",
"placeholder": "",
"default": false
}
]
}
Expand Down
189 changes: 169 additions & 20 deletions server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,29 @@ const (
helpText = `* |/zoom start| - Start a zoom meeting`
oAuthHelpText = `* |/zoom connect| - Connect to Zoom
* |/zoom disconnect| - Disconnect from Zoom`
settingHelpText = `* |/zoom settings| - Update your preferences`
alreadyConnectedText = "Already connected"
zoomPreferenceCategory = "plugin:zoom"
zoomPMISettingName = "use-pmi"
zoomPMISettingValueAsk = "ask"
settingHelpText = `* |/zoom settings| - Update your preferences`
channelPreferenceHelpText = `* |/zoom channel-settings| - Update your current channel preference`
listChannelPreferenceHelpText = `* |/zoom channel-settings list| - List all channel preferences`
alreadyConnectedText = "Already connected"
zoomPreferenceCategory = "plugin:zoom"
zoomPMISettingName = "use-pmi"
zoomPMISettingValueAsk = "ask"
)

const (
actionConnect = "connect"
actionStart = "start"
actionDisconnect = "disconnect"
actionHelp = "help"
settings = "settings"
actionConnect = "connect"
actionStart = "start"
actionDisconnect = "disconnect"
actionHelp = "help"
settings = "settings"
actionChannelSettings = "channel-settings"
channelSettingsActionList = "list"

actionUnkown = "Unknown Action"
)

const channelPreferenceListErr = "Unable to list channel preferences"

func (p *Plugin) getCommand() (*model.Command, error) {
iconData, err := command.GetIconData(p.API, "assets/profile.svg")
if err != nil {
Expand All @@ -40,9 +48,9 @@ func (p *Plugin) getCommand() (*model.Command, error) {

canConnect := !p.configuration.AccountLevelApp

autoCompleteDesc := "Available commands: start, help, settings"
autoCompleteDesc := "Available commands: start, help, settings, channel-settings"
if canConnect {
autoCompleteDesc = "Available commands: start, connect, disconnect, help, settings"
autoCompleteDesc = "Available commands: start, connect, disconnect, help, settings, channel-settings"
}

return &model.Command{
Expand Down Expand Up @@ -104,8 +112,10 @@ func (p *Plugin) executeCommand(c *plugin.Context, args *model.CommandArgs) (str
return p.runHelpCommand(user)
case settings:
return p.runSettingCommand(args, strings.Fields(args.Command)[2:], user)
case actionChannelSettings:
return p.runChannelSettingsCommand(args, strings.Fields(args.Command)[2:], user)
default:
return fmt.Sprintf("Unknown action %v", action), nil
return fmt.Sprintf("%s %v", actionUnkown, action), nil
}
}

Expand All @@ -126,6 +136,16 @@ func (p *Plugin) ExecuteCommand(c *plugin.Context, args *model.CommandArgs) (*mo

// runStartCommand runs command to start a Zoom meeting.
func (p *Plugin) runStartCommand(args *model.CommandArgs, user *model.User, topic string) (string, error) {
restrict, err := p.isChannelRestrictedForMeetings(args.ChannelId)
if err != nil {
p.client.Log.Error("Unable to check channel preference", "ChannelID", args.ChannelId, "Error", err.Error())
return "Error occurred while starting meeting", nil
}

if restrict {
return "Creating zoom meeting is disabled for this channel.", nil
}

if _, appErr := p.API.GetChannelMember(args.ChannelId, user.Id); appErr != nil {
return fmt.Sprintf("We could not get the channel members (channelId: %v)", args.ChannelId), nil
}
Expand Down Expand Up @@ -167,14 +187,14 @@ func (p *Plugin) runStartCommand(args *model.CommandArgs, user *model.User, topi
meetingID = zoomUser.Pmi

if meetingID <= 0 {
meetingID, createMeetingErr = p.createMeetingWithoutPMI(user, zoomUser, args.ChannelId, topic)
meetingID, createMeetingErr = p.createMeetingWithoutPMI(user, zoomUser, topic)
if createMeetingErr != nil {
return "", errors.Wrap(createMeetingErr, "failed to create the meeting")
}
p.sendEnableZoomPMISettingMessage(user.Id, args.ChannelId, args.RootId)
}
default:
meetingID, createMeetingErr = p.createMeetingWithoutPMI(user, zoomUser, args.ChannelId, topic)
meetingID, createMeetingErr = p.createMeetingWithoutPMI(user, zoomUser, topic)
if createMeetingErr != nil {
return "", errors.Wrap(createMeetingErr, "failed to create the meeting")
}
Expand All @@ -191,7 +211,7 @@ func (p *Plugin) runStartCommand(args *model.CommandArgs, user *model.User, topi

func (p *Plugin) runConnectCommand(user *model.User, extra *model.CommandArgs) (string, error) {
if !p.canConnect(user) {
return "Unknown action `connect`", nil
return fmt.Sprintf("%s `%s`", actionUnkown, actionConnect), nil
}

oauthMsg := fmt.Sprintf(
Expand Down Expand Up @@ -228,7 +248,7 @@ func (p *Plugin) runConnectCommand(user *model.User, extra *model.CommandArgs) (
// runDisconnectCommand runs command to disconnect from Zoom. Will fail if user cannot connect.
func (p *Plugin) runDisconnectCommand(user *model.User) (string, error) {
if !p.canConnect(user) {
return "Unknown action `disconnect`", nil
return fmt.Sprintf("%s `%s`", actionUnkown, actionDisconnect), nil
}

if p.configuration.AccountLevelApp {
Expand All @@ -253,6 +273,10 @@ func (p *Plugin) runDisconnectCommand(user *model.User) (string, error) {
// runHelpCommand runs command to display help text.
func (p *Plugin) runHelpCommand(user *model.User) (string, error) {
text := starterText + strings.ReplaceAll(helpText+"\n"+settingHelpText, "|", "`")
if p.API.HasPermissionTo(user.Id, model.PermissionManageSystem) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit to use p.client instead of p.API. I wonder if we can make a golint rule for this. It doesn't make a huge difference, but it helps with references to AppError and error to avoid weird concrete vs interface things there. This HasPermissionTo method returns only a boolean so it's not any difference here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can make a seperate for adding golint rule for not using p.API since it will include a lot changes through the code once the new lint rule is implemented

text += "\n" + strings.ReplaceAll(channelPreferenceHelpText+"\n"+listChannelPreferenceHelpText, "|", "`")
}

if p.canConnect(user) {
text += "\n" + strings.ReplaceAll(oAuthHelpText, "|", "`")
}
Expand All @@ -275,7 +299,125 @@ func (p *Plugin) runSettingCommand(args *model.CommandArgs, params []string, use
}
return "", nil
}
return fmt.Sprintf("Unknown Action %v", ""), nil
return actionUnkown, nil
}

func (p *Plugin) runChannelSettingsCommand(args *model.CommandArgs, params []string, user *model.User) (string, error) {
if len(params) == 0 {
return p.runEditChannelSettingsCommand(args, user)
} else if params[0] == channelSettingsActionList {
return p.runChannelSettingsListCommand(args)
}

return actionUnkown, nil
}

func (p *Plugin) runEditChannelSettingsCommand(args *model.CommandArgs, user *model.User) (string, error) {
if !p.client.User.HasPermissionTo(args.UserId, model.PermissionManageChannelRoles) {
return "Unable to execute the command, only channel admins have access to execute this command.", nil
}

channel, appErr := p.client.Channel.Get(args.ChannelId)
if appErr != nil {
p.client.Log.Error("Unable to get channel", "ChannelID", args.ChannelId, "Error", appErr.Error())
return "Error occurred while fetching channel information", nil
}

if channel.Type == model.ChannelTypeDirect || channel.Type == model.ChannelTypeGroup {
return "Preference not allowed to set for DM/GM.", nil
}

requestBody := model.OpenDialogRequest{
TriggerId: args.TriggerId,
URL: fmt.Sprintf("%s/plugins/%s%s", p.siteURL, manifest.Id, pathChannelPreference),
Dialog: model.Dialog{
Title: "Set Channel Preference",
SubmitLabel: "Submit",
CallbackId: channel.DisplayName,
Elements: []model.DialogElement{
{
DisplayName: fmt.Sprintf("Select your channel preference for ~%s", channel.DisplayName),
HelpText: "Disable to restrict creating meetings in this channel.",
Name: "preference",
Type: "radio",
Options: []*model.PostActionOptions{
{
Text: "Enable Zoom Meetings in this channel",
Value: "allow",
},
{
Text: "Disable Zoom Meetings in this channel",
Value: "restrict",
},
{
Text: fmt.Sprintf("Default to plugin-wide settings (%t)", p.getConfiguration().RestrictMeetingCreation),
Value: "default",
},
},
},
},
},
}

client, _, err := p.getActiveClient(user)
if err != nil {
p.client.Log.Error("Unable to get the client", "Error", err.Error())
return "Unable to send request to open preference dialog", nil
}

if err := client.OpenDialogRequest(&requestBody); err != nil {
p.client.Log.Error("Failed to fulfill the request to open preference dialog", "Error", err.Error())
return "Unable to open the dialog for setting preference", nil
}

return "", nil
}

func (p *Plugin) runChannelSettingsListCommand(args *model.CommandArgs) (string, error) {
if !p.client.User.HasPermissionTo(args.UserId, model.PermissionManageSystem) {
return "Unable to execute the command, only system admins have access to execute this command.", nil
}

zoomChannelSettingsMap, err := p.listZoomChannelSettings()
if err != nil {
p.client.Log.Error(channelPreferenceListErr, "Error", err.Error())
return channelPreferenceListErr, nil
}

if len(zoomChannelSettingsMap) == 0 {
return "No channel preference present", nil
}

var sb strings.Builder
sb.WriteString("#### Channel preferences\n")
config := p.getConfiguration()
if config.RestrictMeetingCreation {
sb.WriteString("Default: Allow meetings only in private channels and DMs/GMs\n\n")
} else {
sb.WriteString("Default: Allow meetings in public channels, private channels, and DMs/GMs\n\n")
}

listChannelHeading := true
for key, value := range zoomChannelSettingsMap {
preference := value.Preference
channel, err := p.client.Channel.Get(key)
if err != nil {
p.client.Log.Error(channelPreferenceListErr, "Error", err.Error())
return channelPreferenceListErr, nil
}
if value.Preference == ZoomChannelPreferences[DefaultChannelRestrictionPreference] {
continue
}

if listChannelHeading {
sb.WriteString("| Channel ID | Channel Name | Preference |\n| :---- | :-------- | :-------- |")
listChannelHeading = false
}

sb.WriteString(fmt.Sprintf("\n|%s|%s|%s|", key, channel.DisplayName, preference))
}

return sb.String(), nil
}

func (p *Plugin) updateUserPersonalSettings(usePMIValue, userID string) *model.AppError {
Expand All @@ -293,9 +435,9 @@ func (p *Plugin) updateUserPersonalSettings(usePMIValue, userID string) *model.A
func (p *Plugin) getAutocompleteData() *model.AutocompleteData {
canConnect := !p.configuration.AccountLevelApp

available := "start, help, settings"
available := "start, help, settings, channel-settings"
if canConnect {
available = "start, connect, disconnect, help, settings"
available = "start, connect, disconnect, help, settings, channel-settings"
}

zoom := model.NewAutocompleteData("zoom", "[command]", fmt.Sprintf("Available commands: %s", available))
Expand All @@ -314,6 +456,13 @@ func (p *Plugin) getAutocompleteData() *model.AutocompleteData {
setting := model.NewAutocompleteData("settings", "", "Update your meeting ID preferences")
zoom.AddCommand(setting)

// channel-settings to update channel preferences
channelSettings := model.NewAutocompleteData("channel-settings", "", "Update current channel preference")
channelSettingsList := model.NewAutocompleteData("list", "", "List all the channel preferences")
channelSettings.AddCommand(channelSettingsList)
channelSettings.RoleID = model.SystemAdminRoleId
zoom.AddCommand(channelSettings)

help := model.NewAutocompleteData("help", "", "Display usage")
zoom.AddCommand(help)

Expand Down
4 changes: 4 additions & 0 deletions server/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ type configuration struct {

// ZoomWebhookSecret is the `Secret Token` taken from Zoom's webhook configuration page
ZoomWebhookSecret string

// RestrictMeetingCreation allows the admin to by default restrict zoom meetings to only private channels.
// The admin can also edit each channel's behavior with the `/zoom channel-settings` command
RestrictMeetingCreation bool
mickmister marked this conversation as resolved.
Show resolved Hide resolved
}

// Clone shallow copies the configuration. Your implementation may require a deep copy if
Expand Down
Loading
Loading