github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/app/slashcommands/command_share.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package slashcommands 5 6 import ( 7 "errors" 8 "fmt" 9 "strings" 10 11 "github.com/masterhung0112/hk_server/v5/app" 12 "github.com/masterhung0112/hk_server/v5/app/request" 13 "github.com/masterhung0112/hk_server/v5/model" 14 "github.com/masterhung0112/hk_server/v5/shared/i18n" 15 ) 16 17 type ShareProvider struct { 18 } 19 20 const ( 21 CommandTriggerShare = "share-channel" 22 AvailableShareActions = "invite, uninvite, unshare, status" 23 ) 24 25 func init() { 26 app.RegisterCommandProvider(&ShareProvider{}) 27 } 28 29 func (sp *ShareProvider) GetTrigger() string { 30 return CommandTriggerShare 31 } 32 33 func (sp *ShareProvider) GetCommand(a *app.App, T i18n.TranslateFunc) *model.Command { 34 share := model.NewAutocompleteData(CommandTriggerShare, "[action]", T("api.command_share.available_actions", map[string]interface{}{"Actions": AvailableShareActions})) 35 36 inviteRemote := model.NewAutocompleteData("invite", "", T("api.command_share.invite_remote.help")) 37 inviteRemote.AddNamedDynamicListArgument("connectionID", T("api.command_share.remote_id.help"), "builtin:"+CommandTriggerShare, true) 38 inviteRemote.AddNamedTextArgument("readonly", T("api.command_share.share_read_only.help"), T("api.command_share.share_read_only.hint"), "Y|N|y|n", false) 39 40 unInviteRemote := model.NewAutocompleteData("uninvite", "", T("api.command_share.uninvite_remote.help")) 41 unInviteRemote.AddNamedDynamicListArgument("connectionID", T("api.command_share.uninvite_remote_id.help"), "builtin:"+CommandTriggerShare, true) 42 43 unshareChannel := model.NewAutocompleteData("unshare", "", T("api.command_share.unshare_channel.help")) 44 45 status := model.NewAutocompleteData("status", "", T("api.command_share.channel_status.help")) 46 47 share.AddCommand(inviteRemote) 48 share.AddCommand(unInviteRemote) 49 share.AddCommand(unshareChannel) 50 share.AddCommand(status) 51 52 return &model.Command{ 53 Trigger: CommandTriggerShare, 54 AutoComplete: true, 55 AutoCompleteDesc: T("api.command_share.desc"), 56 AutoCompleteHint: T("api.command_share.hint"), 57 DisplayName: T("api.command_share.name"), 58 AutocompleteData: share, 59 } 60 } 61 62 func (sp *ShareProvider) GetAutoCompleteListItems(a *app.App, commandArgs *model.CommandArgs, arg *model.AutocompleteArg, parsed, toBeParsed string) ([]model.AutocompleteListItem, error) { 63 switch { 64 case strings.Contains(parsed, " share "): 65 66 return sp.getAutoCompleteShareChannel(a, commandArgs, arg) 67 68 case strings.Contains(parsed, " invite "): 69 70 return sp.getAutoCompleteInviteRemote(a, commandArgs, arg) 71 72 case strings.Contains(parsed, " uninvite "): 73 74 return sp.getAutoCompleteUnInviteRemote(a, commandArgs, arg) 75 76 } 77 return nil, errors.New("invalid action") 78 } 79 80 func (sp *ShareProvider) getAutoCompleteShareChannel(a *app.App, commandArgs *model.CommandArgs, arg *model.AutocompleteArg) ([]model.AutocompleteListItem, error) { 81 channel, err := a.GetChannel(commandArgs.ChannelId) 82 if err != nil { 83 return nil, err 84 } 85 86 var item model.AutocompleteListItem 87 88 switch arg.Name { 89 case "name": 90 item = model.AutocompleteListItem{ 91 Item: channel.Name, 92 HelpText: channel.DisplayName, 93 } 94 case "displayname": 95 item = model.AutocompleteListItem{ 96 Item: channel.DisplayName, 97 HelpText: channel.Name, 98 } 99 default: 100 return nil, fmt.Errorf("%s not a dynamic argument", arg.Name) 101 } 102 return []model.AutocompleteListItem{item}, nil 103 } 104 105 func (sp *ShareProvider) getAutoCompleteInviteRemote(a *app.App, commandArgs *model.CommandArgs, arg *model.AutocompleteArg) ([]model.AutocompleteListItem, error) { 106 switch arg.Name { 107 case "connectionID": 108 return getRemoteClusterAutocompleteListItemsNotInChannel(a, commandArgs.ChannelId, true) 109 default: 110 return nil, fmt.Errorf("%s not a dynamic argument", arg.Name) 111 } 112 } 113 114 func (sp *ShareProvider) getAutoCompleteUnInviteRemote(a *app.App, _ *model.CommandArgs, arg *model.AutocompleteArg) ([]model.AutocompleteListItem, error) { 115 switch arg.Name { 116 case "connectionID": 117 return getRemoteClusterAutocompleteListItems(a, true) 118 default: 119 return nil, fmt.Errorf("%s not a dynamic argument", arg.Name) 120 } 121 } 122 123 func (sp *ShareProvider) DoCommand(a *app.App, c *request.Context, args *model.CommandArgs, message string) *model.CommandResponse { 124 if !a.HasPermissionTo(args.UserId, model.PERMISSION_MANAGE_SHARED_CHANNELS) { 125 return responsef(args.T("api.command_share.permission_required", map[string]interface{}{"Permission": "manage_shared_channels"})) 126 } 127 128 if a.Srv().GetSharedChannelSyncService() == nil { 129 return responsef(args.T("api.command_share.service_disabled")) 130 } 131 132 if a.Srv().GetRemoteClusterService() == nil { 133 return responsef(args.T("api.command_remote.service_disabled")) 134 } 135 136 margs := parseNamedArgs(args.Command) 137 action, ok := margs[ActionKey] 138 if !ok { 139 return responsef(args.T("api.command_share.missing_action", map[string]interface{}{"Actions": AvailableShareActions})) 140 } 141 142 switch action { 143 case "share": 144 return sp.doShareChannel(a, args, margs) 145 case "unshare": 146 return sp.doUnshareChannel(a, args, margs) 147 case "invite": 148 return sp.doInviteRemote(a, args, margs) 149 case "uninvite": 150 return sp.doUninviteRemote(a, args, margs) 151 case "status": 152 return sp.doStatus(a, args, margs) 153 } 154 return responsef(args.T("api.command_share.unknown_action", map[string]interface{}{"Action": action, "Actions": AvailableShareActions})) 155 } 156 157 func (sp *ShareProvider) doShareChannel(a *app.App, args *model.CommandArgs, margs map[string]string) *model.CommandResponse { 158 // check that channel exists. 159 channel, errApp := a.GetChannel(args.ChannelId) 160 if errApp != nil { 161 return responsef(args.T("api.command_share.share_channel.error", map[string]interface{}{"Error": errApp.Error()})) 162 } 163 164 if name := margs["name"]; name == "" { 165 margs["name"] = channel.Name 166 } 167 if name := margs["displayname"]; name == "" { 168 margs["displayname"] = channel.DisplayName 169 } 170 if name := margs["purpose"]; name == "" { 171 margs["purpose"] = channel.Purpose 172 } 173 if name := margs["header"]; name == "" { 174 margs["header"] = channel.Header 175 } 176 if _, ok := margs["readonly"]; !ok { 177 margs["readonly"] = "N" 178 } 179 180 readonly, err := parseBool(margs["readonly"]) 181 if err != nil { 182 return responsef(args.T("api.command_share.invalid_value.error", map[string]interface{}{"Arg": "readonly", "Error": err.Error()})) 183 } 184 185 sc := &model.SharedChannel{ 186 ChannelId: args.ChannelId, 187 TeamId: args.TeamId, 188 Home: true, 189 ReadOnly: readonly, 190 ShareName: margs["name"], 191 ShareDisplayName: margs["displayname"], 192 SharePurpose: margs["purpose"], 193 ShareHeader: margs["header"], 194 CreatorId: args.UserId, 195 } 196 197 if _, err := a.SaveSharedChannel(sc); err != nil { 198 return responsef(args.T("api.command_share.share_channel.error", map[string]interface{}{"Error": err.Error()})) 199 } 200 201 notifyClientsForChannelUpdate(a, sc) 202 203 return responsef("##### " + args.T("api.command_share.channel_shared")) 204 } 205 206 func (sp *ShareProvider) doUnshareChannel(a *app.App, args *model.CommandArgs, margs map[string]string) *model.CommandResponse { 207 sc, appErr := a.GetSharedChannel(args.ChannelId) 208 if appErr != nil { 209 return responsef(args.T("api.command_share.shared_channel_unshare.error", map[string]interface{}{"Error": appErr.Error()})) 210 } 211 212 deleted, err := a.DeleteSharedChannel(args.ChannelId) 213 if err != nil { 214 return responsef(args.T("api.command_share.shared_channel_unshare.error", map[string]interface{}{"Error": err.Error()})) 215 } 216 if !deleted { 217 return responsef(args.T("api.command_share.not_shared_channel_unshare")) 218 } 219 220 notifyClientsForChannelUpdate(a, sc) 221 222 return responsef("##### " + args.T("api.command_share.shared_channel_unavailable")) 223 } 224 225 func (sp *ShareProvider) doInviteRemote(a *app.App, args *model.CommandArgs, margs map[string]string) (resp *model.CommandResponse) { 226 remoteId, ok := margs["connectionID"] 227 if !ok || remoteId == "" { 228 return responsef(args.T("api.command_share.must_specify_valid_remote")) 229 } 230 231 hasRemote, err := a.HasRemote(args.ChannelId, remoteId) 232 if err != nil { 233 return responsef(args.T("api.command_share.fetch_remote.error", map[string]interface{}{"Error": err.Error()})) 234 } 235 if hasRemote { 236 return responsef(args.T("api.command_share.remote_already_invited")) 237 } 238 239 // Check if channel is shared or not. 240 hasChan, err := a.HasSharedChannel(args.ChannelId) 241 if err != nil { 242 return responsef(args.T("api.command_share.check_channel_exist.error", map[string]interface{}{"Error": err.Error()})) 243 } 244 if !hasChan { 245 // If it doesn't exist, then create it. 246 resp2 := sp.doShareChannel(a, args, margs) 247 // We modify the outgoing response by prepending the text 248 // from the shareChannel response. 249 defer func() { 250 resp.Text = resp2.Text + "\n" + resp.Text 251 }() 252 } 253 254 // don't allow invitation to shared channel originating from remote. 255 // (also blocks cyclic invitations) 256 if err := a.CheckCanInviteToSharedChannel(args.ChannelId); err != nil { 257 return responsef(args.T("api.command_share.channel_invite_not_home.error")) 258 } 259 260 rc, appErr := a.GetRemoteCluster(remoteId) 261 if appErr != nil { 262 return responsef(args.T("api.command_share.remote_id_invalid.error", map[string]interface{}{"Error": appErr.Error()})) 263 } 264 265 channel, errApp := a.GetChannel(args.ChannelId) 266 if errApp != nil { 267 return responsef(args.T("api.command_share.channel_invite.error", map[string]interface{}{"Name": rc.DisplayName, "Error": errApp.Error()})) 268 } 269 // send channel invite to remote cluster 270 if err := a.Srv().GetSharedChannelSyncService().SendChannelInvite(channel, args.UserId, rc); err != nil { 271 return responsef(args.T("api.command_share.channel_invite.error", map[string]interface{}{"Name": rc.DisplayName, "Error": err.Error()})) 272 } 273 274 return responsef("##### " + args.T("api.command_share.invitation_sent", map[string]interface{}{"Name": rc.DisplayName, "SiteURL": rc.SiteURL})) 275 } 276 277 func (sp *ShareProvider) doUninviteRemote(a *app.App, args *model.CommandArgs, margs map[string]string) *model.CommandResponse { 278 remoteId, ok := margs["connectionID"] 279 if !ok || remoteId == "" { 280 return responsef(args.T("api.command_share.remote_not_valid")) 281 } 282 283 scr, err := a.GetSharedChannelRemoteByIds(args.ChannelId, remoteId) 284 if err != nil || scr.ChannelId != args.ChannelId { 285 return responsef(args.T("api.command_share.channel_remote_id_not_exists", map[string]interface{}{"RemoteId": remoteId})) 286 } 287 288 deleted, err := a.DeleteSharedChannelRemote(scr.Id) 289 if err != nil || !deleted { 290 return responsef(args.T("api.command_share.could_not_uninvite.error", map[string]interface{}{"RemoteId": remoteId, "Error": err.Error()})) 291 } 292 return responsef("##### " + args.T("api.command_share.remote_uninvited", map[string]interface{}{"RemoteId": remoteId})) 293 } 294 295 func (sp *ShareProvider) doStatus(a *app.App, args *model.CommandArgs, _ map[string]string) *model.CommandResponse { 296 statuses, err := a.GetSharedChannelRemotesStatus(args.ChannelId) 297 if err != nil { 298 return responsef(args.T("api.command_share.fetch_remote_status.error", map[string]interface{}{"Error": err.Error()})) 299 } 300 if len(statuses) == 0 { 301 return responsef(args.T("api.command_share.no_remote_invited")) 302 } 303 304 var sb strings.Builder 305 306 fmt.Fprintf(&sb, args.T("api.command_share.channel_status_id", map[string]interface{}{"ChannelId": statuses[0].ChannelId})+"\n\n") 307 308 fmt.Fprintf(&sb, args.T("api.command_share.remote_table_header")+" \n") 309 // "| Secure Connection | SiteURL | ReadOnly | InviteAccepted | Online | Last Sync |" 310 fmt.Fprintf(&sb, "| ---- | ---- | ---- | ---- | ---- | ---- | \n") 311 312 for _, status := range statuses { 313 readonly := formatBool(args.T, status.ReadOnly) 314 accepted := formatBool(args.T, status.IsInviteAccepted) 315 online := formatBool(args.T, isOnline(status.LastPingAt)) 316 317 lastSync := formatTimestamp(status.NextSyncAt) 318 319 fmt.Fprintf(&sb, "| %s | %s | %s | %s | %s | %s |\n", 320 status.DisplayName, status.SiteURL, readonly, accepted, online, lastSync) 321 } 322 return responsef(sb.String()) 323 } 324 325 func notifyClientsForChannelUpdate(a *app.App, sharedChannel *model.SharedChannel) { 326 messageWs := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_CONVERTED, sharedChannel.TeamId, "", "", nil) 327 messageWs.Add("channel_id", sharedChannel.ChannelId) 328 a.Publish(messageWs) 329 }