github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/plugin_commands.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"net/http"
     8  	"net/url"
     9  	"strings"
    10  
    11  	"github.com/mattermost/mattermost-server/v5/model"
    12  	"github.com/pkg/errors"
    13  )
    14  
    15  type PluginCommand struct {
    16  	Command  *model.Command
    17  	PluginId string
    18  }
    19  
    20  func (a *App) RegisterPluginCommand(pluginId string, command *model.Command) error {
    21  	if command.Trigger == "" {
    22  		return errors.New("invalid command")
    23  	}
    24  	if command.AutocompleteData != nil {
    25  		if err := command.AutocompleteData.IsValid(); err != nil {
    26  			return errors.Wrap(err, "invalid autocomplete data in command")
    27  		}
    28  	}
    29  
    30  	if command.AutocompleteData == nil {
    31  		command.AutocompleteData = model.NewAutocompleteData(command.Trigger, command.AutoCompleteHint, command.AutoCompleteDesc)
    32  	} else {
    33  		baseURL, err := url.Parse("/plugins/" + pluginId)
    34  		if err != nil {
    35  			return errors.Wrapf(err, "Can't parse url %s", "/plugins/"+pluginId)
    36  		}
    37  		err = command.AutocompleteData.UpdateRelativeURLsForPluginCommands(baseURL)
    38  		if err != nil {
    39  			return errors.Wrap(err, "Can't update relative urls for plugin commands")
    40  		}
    41  	}
    42  
    43  	command = &model.Command{
    44  		Trigger:              strings.ToLower(command.Trigger),
    45  		TeamId:               command.TeamId,
    46  		AutoComplete:         command.AutoComplete,
    47  		AutoCompleteDesc:     command.AutoCompleteDesc,
    48  		AutoCompleteHint:     command.AutoCompleteHint,
    49  		DisplayName:          command.DisplayName,
    50  		AutocompleteData:     command.AutocompleteData,
    51  		AutocompleteIconData: command.AutocompleteIconData,
    52  	}
    53  
    54  	a.Srv().pluginCommandsLock.Lock()
    55  	defer a.Srv().pluginCommandsLock.Unlock()
    56  
    57  	for _, pc := range a.Srv().pluginCommands {
    58  		if pc.Command.Trigger == command.Trigger && pc.Command.TeamId == command.TeamId {
    59  			if pc.PluginId == pluginId {
    60  				pc.Command = command
    61  				return nil
    62  			}
    63  		}
    64  	}
    65  
    66  	a.Srv().pluginCommands = append(a.Srv().pluginCommands, &PluginCommand{
    67  		Command:  command,
    68  		PluginId: pluginId,
    69  	})
    70  	return nil
    71  }
    72  
    73  func (a *App) UnregisterPluginCommand(pluginId, teamId, trigger string) {
    74  	trigger = strings.ToLower(trigger)
    75  
    76  	a.Srv().pluginCommandsLock.Lock()
    77  	defer a.Srv().pluginCommandsLock.Unlock()
    78  
    79  	var remaining []*PluginCommand
    80  	for _, pc := range a.Srv().pluginCommands {
    81  		if pc.Command.TeamId != teamId || pc.Command.Trigger != trigger {
    82  			remaining = append(remaining, pc)
    83  		}
    84  	}
    85  	a.Srv().pluginCommands = remaining
    86  }
    87  
    88  func (a *App) UnregisterPluginCommands(pluginId string) {
    89  	a.Srv().pluginCommandsLock.Lock()
    90  	defer a.Srv().pluginCommandsLock.Unlock()
    91  
    92  	var remaining []*PluginCommand
    93  	for _, pc := range a.Srv().pluginCommands {
    94  		if pc.PluginId != pluginId {
    95  			remaining = append(remaining, pc)
    96  		}
    97  	}
    98  	a.Srv().pluginCommands = remaining
    99  }
   100  
   101  func (a *App) PluginCommandsForTeam(teamId string) []*model.Command {
   102  	a.Srv().pluginCommandsLock.RLock()
   103  	defer a.Srv().pluginCommandsLock.RUnlock()
   104  
   105  	var commands []*model.Command
   106  	for _, pc := range a.Srv().pluginCommands {
   107  		if pc.Command.TeamId == "" || pc.Command.TeamId == teamId {
   108  			commands = append(commands, pc.Command)
   109  		}
   110  	}
   111  	return commands
   112  }
   113  
   114  // tryExecutePluginCommand attempts to run a command provided by a plugin based on the given arguments. If no such
   115  // command can be found, returns nil for all arguments.
   116  func (a *App) tryExecutePluginCommand(args *model.CommandArgs) (*model.Command, *model.CommandResponse, *model.AppError) {
   117  	parts := strings.Split(args.Command, " ")
   118  	trigger := parts[0][1:]
   119  	trigger = strings.ToLower(trigger)
   120  
   121  	var matched *PluginCommand
   122  	a.Srv().pluginCommandsLock.RLock()
   123  	for _, pc := range a.Srv().pluginCommands {
   124  		if (pc.Command.TeamId == "" || pc.Command.TeamId == args.TeamId) && pc.Command.Trigger == trigger {
   125  			matched = pc
   126  			break
   127  		}
   128  	}
   129  	a.Srv().pluginCommandsLock.RUnlock()
   130  	if matched == nil {
   131  		return nil, nil, nil
   132  	}
   133  
   134  	pluginsEnvironment := a.GetPluginsEnvironment()
   135  	if pluginsEnvironment == nil {
   136  		return nil, nil, nil
   137  	}
   138  
   139  	pluginHooks, err := pluginsEnvironment.HooksForPlugin(matched.PluginId)
   140  	if err != nil {
   141  		return matched.Command, nil, model.NewAppError("ExecutePluginCommand", "model.plugin_command.error.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
   142  	}
   143  
   144  	for username, userId := range a.mentionsToTeamMembers(args.Command, args.TeamId) {
   145  		args.AddUserMention(username, userId)
   146  	}
   147  
   148  	for channelName, channelId := range a.mentionsToPublicChannels(args.Command, args.TeamId) {
   149  		args.AddChannelMention(channelName, channelId)
   150  	}
   151  
   152  	response, appErr := pluginHooks.ExecuteCommand(a.PluginContext(), args)
   153  	return matched.Command, response, appErr
   154  }