Merge branch 'master' into PLT-1851
Этот коммит содержится в:
953
api/command.go
953
api/command.go
@@ -4,12 +4,11 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"io"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
"github.com/gorilla/mux"
|
||||
@@ -17,630 +16,386 @@ import (
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
type commandHandler func(c *Context, command *model.Command) bool
|
||||
type CommandProvider interface {
|
||||
GetTrigger() string
|
||||
GetCommand(c *Context) *model.Command
|
||||
DoCommand(c *Context, channelId string, message string) *model.CommandResponse
|
||||
}
|
||||
|
||||
var (
|
||||
cmds = map[string]string{
|
||||
"logoutCommand": "/logout",
|
||||
"joinCommand": "/join",
|
||||
"loadTestCommand": "/loadtest",
|
||||
"echoCommand": "/echo",
|
||||
"shrugCommand": "/shrug",
|
||||
"meCommand": "/me",
|
||||
var commandProviders = make(map[string]CommandProvider)
|
||||
|
||||
func RegisterCommandProvider(newProvider CommandProvider) {
|
||||
commandProviders[newProvider.GetTrigger()] = newProvider
|
||||
}
|
||||
|
||||
func GetCommandProvider(name string) CommandProvider {
|
||||
provider, ok := commandProviders[name]
|
||||
if ok {
|
||||
return provider
|
||||
}
|
||||
commands = []commandHandler{
|
||||
logoutCommand,
|
||||
joinCommand,
|
||||
loadTestCommand,
|
||||
echoCommand,
|
||||
shrugCommand,
|
||||
meCommand,
|
||||
}
|
||||
commandNotImplementedErr = model.NewLocAppError("checkCommand", "api.command.no_implemented.app_error", nil, "")
|
||||
)
|
||||
var echoSem chan bool
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitCommand(r *mux.Router) {
|
||||
l4g.Debug(utils.T("api.command.init.debug"))
|
||||
r.Handle("/command", ApiUserRequired(command)).Methods("POST")
|
||||
|
||||
sr := r.PathPrefix("/commands").Subrouter()
|
||||
|
||||
sr.Handle("/execute", ApiUserRequired(executeCommand)).Methods("POST")
|
||||
sr.Handle("/list", ApiUserRequired(listCommands)).Methods("GET")
|
||||
|
||||
sr.Handle("/create", ApiUserRequired(createCommand)).Methods("POST")
|
||||
sr.Handle("/list_team_commands", ApiUserRequired(listTeamCommands)).Methods("GET")
|
||||
sr.Handle("/regen_token", ApiUserRequired(regenCommandToken)).Methods("POST")
|
||||
sr.Handle("/delete", ApiUserRequired(deleteCommand)).Methods("POST")
|
||||
|
||||
sr.Handle("/test", ApiAppHandler(testCommand)).Methods("POST")
|
||||
sr.Handle("/test", ApiAppHandler(testCommand)).Methods("GET")
|
||||
}
|
||||
|
||||
func command(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
func listCommands(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
commands := make([]*model.Command, 0, 32)
|
||||
seen := make(map[string]bool)
|
||||
for _, value := range commandProviders {
|
||||
cpy := *value.GetCommand(c)
|
||||
if cpy.AutoComplete && !seen[cpy.Id] {
|
||||
cpy.Sanitize()
|
||||
seen[cpy.Trigger] = true
|
||||
commands = append(commands, &cpy)
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Command().GetByTeam(c.Session.TeamId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
teamCmds := result.Data.([]*model.Command)
|
||||
for _, cmd := range teamCmds {
|
||||
if cmd.AutoComplete && !seen[cmd.Id] {
|
||||
cmd.Sanitize()
|
||||
seen[cmd.Trigger] = true
|
||||
commands = append(commands, cmd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.Write([]byte(model.CommandListToJson(commands)))
|
||||
}
|
||||
|
||||
func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJson(r.Body)
|
||||
command := strings.TrimSpace(props["command"])
|
||||
channelId := strings.TrimSpace(props["channelId"])
|
||||
|
||||
if len(command) <= 1 || strings.Index(command, "/") != 0 {
|
||||
c.Err = model.NewLocAppError("executeCommand", "api.command.execute_command.start.app_error", nil, "")
|
||||
return
|
||||
}
|
||||
|
||||
if len(channelId) > 0 {
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "checkCommand") {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
parts := strings.Split(command, " ")
|
||||
trigger := parts[0][1:]
|
||||
message := strings.Join(parts[1:], " ")
|
||||
provider := GetCommandProvider(trigger)
|
||||
|
||||
if provider != nil {
|
||||
|
||||
response := provider.DoCommand(c, channelId, message)
|
||||
handleResponse(c, w, response, channelId)
|
||||
return
|
||||
} else {
|
||||
chanChan := Srv.Store.Channel().Get(channelId)
|
||||
teamChan := Srv.Store.Team().Get(c.Session.TeamId)
|
||||
userChan := Srv.Store.User().Get(c.Session.UserId)
|
||||
|
||||
if result := <-Srv.Store.Command().GetByTeam(c.Session.TeamId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
|
||||
var team *model.Team
|
||||
if tr := <-teamChan; tr.Err != nil {
|
||||
c.Err = tr.Err
|
||||
return
|
||||
} else {
|
||||
team = tr.Data.(*model.Team)
|
||||
|
||||
}
|
||||
|
||||
var user *model.User
|
||||
if ur := <-userChan; ur.Err != nil {
|
||||
c.Err = ur.Err
|
||||
return
|
||||
} else {
|
||||
user = ur.Data.(*model.User)
|
||||
}
|
||||
|
||||
var channel *model.Channel
|
||||
if cr := <-chanChan; cr.Err != nil {
|
||||
c.Err = cr.Err
|
||||
return
|
||||
} else {
|
||||
channel = cr.Data.(*model.Channel)
|
||||
}
|
||||
|
||||
teamCmds := result.Data.([]*model.Command)
|
||||
for _, cmd := range teamCmds {
|
||||
if trigger == cmd.Trigger {
|
||||
l4g.Debug(fmt.Sprintf(utils.T("api.command.execute_command.debug"), trigger, c.Session.UserId))
|
||||
|
||||
p := url.Values{}
|
||||
p.Set("token", cmd.Token)
|
||||
|
||||
p.Set("team_id", cmd.TeamId)
|
||||
p.Set("team_domain", team.Name)
|
||||
|
||||
p.Set("channel_id", channelId)
|
||||
p.Set("channel_name", channel.Name)
|
||||
|
||||
p.Set("user_id", c.Session.UserId)
|
||||
p.Set("user_name", user.Username)
|
||||
|
||||
p.Set("command", "/"+trigger)
|
||||
p.Set("text", message)
|
||||
p.Set("response_url", "not supported yet")
|
||||
|
||||
method := "POST"
|
||||
if cmd.Method == model.COMMAND_METHOD_GET {
|
||||
method = "GET"
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
req, _ := http.NewRequest(method, cmd.URL, strings.NewReader(p.Encode()))
|
||||
req.Header.Set("Accept", "application/json")
|
||||
if cmd.Method == model.COMMAND_METHOD_POST {
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
}
|
||||
|
||||
if resp, err := client.Do(req); err != nil {
|
||||
c.Err = model.NewLocAppError("command", "api.command.execute_command.failed.app_error", map[string]interface{}{"Trigger": trigger}, err.Error())
|
||||
} else {
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
response := model.CommandResponseFromJson(resp.Body)
|
||||
if response == nil {
|
||||
c.Err = model.NewLocAppError("command", "api.command.execute_command.failed_empty.app_error", map[string]interface{}{"Trigger": trigger}, "")
|
||||
} else {
|
||||
handleResponse(c, w, response, channelId)
|
||||
}
|
||||
} else {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
c.Err = model.NewLocAppError("command", "api.command.execute_command.failed_resp.app_error", map[string]interface{}{"Trigger": trigger, "Status": resp.Status}, string(body))
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
c.Err = model.NewLocAppError("command", "api.command.execute_command.not_found.app_error", map[string]interface{}{"Trigger": trigger}, "")
|
||||
}
|
||||
|
||||
func handleResponse(c *Context, w http.ResponseWriter, response *model.CommandResponse, channelId string) {
|
||||
if response.ResponseType == model.COMMAND_RESPONSE_TYPE_IN_CHANNEL {
|
||||
post := &model.Post{}
|
||||
post.ChannelId = channelId
|
||||
post.Message = response.Text
|
||||
if _, err := CreatePost(c, post, true); err != nil {
|
||||
c.Err = model.NewLocAppError("command", "api.command.execute_command.save.app_error", nil, "")
|
||||
}
|
||||
} else if response.ResponseType == model.COMMAND_RESPONSE_TYPE_EPHEMERAL {
|
||||
// post := &model.Post{}
|
||||
// post.ChannelId = channelId
|
||||
// post.Message = "TODO_EPHEMERAL: " + response.Text
|
||||
// if _, err := CreatePost(c, post, true); err != nil {
|
||||
// c.Err = model.NewLocAppError("command", "api.command.execute_command.save.app_error", nil, "")
|
||||
// }
|
||||
}
|
||||
|
||||
w.Write([]byte(response.ToJson()))
|
||||
}
|
||||
|
||||
func createCommand(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !*utils.Cfg.ServiceSettings.EnableCommands {
|
||||
c.Err = model.NewLocAppError("createCommand", "api.command.disabled.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
|
||||
if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
|
||||
c.Err = model.NewLocAppError("createCommand", "api.command.admin_only.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
cmd := model.CommandFromJson(r.Body)
|
||||
|
||||
if cmd == nil {
|
||||
c.SetInvalidParam("createCommand", "command")
|
||||
return
|
||||
}
|
||||
|
||||
cmd.CreatorId = c.Session.UserId
|
||||
cmd.TeamId = c.Session.TeamId
|
||||
|
||||
if result := <-Srv.Store.Command().Save(cmd); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
c.LogAudit("success")
|
||||
rcmd := result.Data.(*model.Command)
|
||||
w.Write([]byte(rcmd.ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func listTeamCommands(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !*utils.Cfg.ServiceSettings.EnableCommands {
|
||||
c.Err = model.NewLocAppError("listTeamCommands", "api.command.disabled.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
|
||||
if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
|
||||
c.Err = model.NewLocAppError("listTeamCommands", "api.command.admin_only.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Command().GetByTeam(c.Session.TeamId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
cmds := result.Data.([]*model.Command)
|
||||
w.Write([]byte(model.CommandListToJson(cmds)))
|
||||
}
|
||||
}
|
||||
|
||||
func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !*utils.Cfg.ServiceSettings.EnableCommands {
|
||||
c.Err = model.NewLocAppError("regenCommandToken", "api.command.disabled.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
|
||||
if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
|
||||
c.Err = model.NewLocAppError("regenCommandToken", "api.command.admin_only.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
command := &model.Command{
|
||||
Command: strings.TrimSpace(props["command"]),
|
||||
ChannelId: strings.TrimSpace(props["channelId"]),
|
||||
Suggest: props["suggest"] == "true",
|
||||
Suggestions: make([]*model.SuggestCommand, 0, 128),
|
||||
id := props["id"]
|
||||
if len(id) == 0 {
|
||||
c.SetInvalidParam("regenCommandToken", "id")
|
||||
return
|
||||
}
|
||||
|
||||
checkCommand(c, command)
|
||||
if c.Err != nil {
|
||||
if c.Err != commandNotImplementedErr {
|
||||
return
|
||||
} else {
|
||||
c.Err = nil
|
||||
command.Response = model.RESP_NOT_IMPLEMENTED
|
||||
w.Write([]byte(command.ToJson()))
|
||||
return
|
||||
}
|
||||
var cmd *model.Command
|
||||
if result := <-Srv.Store.Command().Get(id); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(command.ToJson()))
|
||||
cmd = result.Data.(*model.Command)
|
||||
|
||||
if c.Session.TeamId != cmd.TeamId || (c.Session.UserId != cmd.CreatorId && !c.IsTeamAdmin()) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.Err = model.NewLocAppError("regenToken", "api.command.regen.app_error", nil, "user_id="+c.Session.UserId)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
cmd.Token = model.NewId()
|
||||
|
||||
if result := <-Srv.Store.Command().Update(cmd); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
w.Write([]byte(result.Data.(*model.Command).ToJson()))
|
||||
}
|
||||
}
|
||||
|
||||
func checkCommand(c *Context, command *model.Command) bool {
|
||||
|
||||
if len(command.Command) == 0 || strings.Index(command.Command, "/") != 0 {
|
||||
c.Err = model.NewLocAppError("checkCommand", "api.command.check_command.start.app_error", nil, "")
|
||||
return false
|
||||
func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if !*utils.Cfg.ServiceSettings.EnableCommands {
|
||||
c.Err = model.NewLocAppError("deleteCommand", "api.command.disabled.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusNotImplemented
|
||||
return
|
||||
}
|
||||
|
||||
if len(command.ChannelId) > 0 {
|
||||
cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, command.ChannelId, c.Session.UserId)
|
||||
|
||||
if !c.HasPermissionsToChannel(cchan, "checkCommand") {
|
||||
return true
|
||||
if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
|
||||
if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
|
||||
c.Err = model.NewLocAppError("deleteCommand", "api.command.admin_only.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !command.Suggest {
|
||||
implemented := false
|
||||
for _, cmd := range cmds {
|
||||
bounds := len(cmd)
|
||||
if len(command.Command) < bounds {
|
||||
continue
|
||||
}
|
||||
if command.Command[:bounds] == cmd {
|
||||
implemented = true
|
||||
}
|
||||
}
|
||||
if !implemented {
|
||||
c.Err = commandNotImplementedErr
|
||||
return false
|
||||
c.LogAudit("attempt")
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
|
||||
id := props["id"]
|
||||
if len(id) == 0 {
|
||||
c.SetInvalidParam("deleteCommand", "id")
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Command().Get(id); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
if c.Session.TeamId != result.Data.(*model.Command).TeamId || (c.Session.UserId != result.Data.(*model.Command).CreatorId && !c.IsTeamAdmin()) {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.Err = model.NewLocAppError("deleteCommand", "api.command.delete.app_error", nil, "user_id="+c.Session.UserId)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range commands {
|
||||
|
||||
if v(c, command) || c.Err != nil {
|
||||
return true
|
||||
}
|
||||
if err := (<-Srv.Store.Command().Delete(id, model.GetMillis())).Err; err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
return false
|
||||
c.LogAudit("success")
|
||||
w.Write([]byte(model.MapToJson(props)))
|
||||
}
|
||||
|
||||
func logoutCommand(c *Context, command *model.Command) bool {
|
||||
func testCommand(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
|
||||
cmd := cmds["logoutCommand"]
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.logout_command.description")})
|
||||
|
||||
if !command.Suggest {
|
||||
command.GotoLocation = "/logout"
|
||||
command.Response = model.RESP_EXECUTED
|
||||
return true
|
||||
}
|
||||
|
||||
} else if strings.Index(cmd, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.logout_command.description")})
|
||||
msg := ""
|
||||
if r.Method == "POST" {
|
||||
msg = msg + "\ntoken=" + r.FormValue("token")
|
||||
msg = msg + "\nteam_domain=" + r.FormValue("team_domain")
|
||||
} else {
|
||||
body, _ := ioutil.ReadAll(r.Body)
|
||||
msg = string(body)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func echoCommand(c *Context, command *model.Command) bool {
|
||||
cmd := cmds["echoCommand"]
|
||||
maxThreads := 100
|
||||
|
||||
if !command.Suggest && strings.Index(command.Command, cmd) == 0 {
|
||||
parameters := strings.SplitN(command.Command, " ", 2)
|
||||
if len(parameters) != 2 || len(parameters[1]) == 0 {
|
||||
return false
|
||||
}
|
||||
message := strings.Trim(parameters[1], " ")
|
||||
delay := 0
|
||||
if endMsg := strings.LastIndex(message, "\""); string(message[0]) == "\"" && endMsg > 1 {
|
||||
if checkDelay, err := strconv.Atoi(strings.Trim(message[endMsg:], " \"")); err == nil {
|
||||
delay = checkDelay
|
||||
}
|
||||
message = message[1:endMsg]
|
||||
} else if strings.Index(message, " ") > -1 {
|
||||
delayIdx := strings.LastIndex(message, " ")
|
||||
delayStr := strings.Trim(message[delayIdx:], " ")
|
||||
|
||||
if checkDelay, err := strconv.Atoi(delayStr); err == nil {
|
||||
delay = checkDelay
|
||||
message = message[:delayIdx]
|
||||
}
|
||||
}
|
||||
|
||||
if delay > 10000 {
|
||||
c.Err = model.NewLocAppError("echoCommand", "api.command.echo_command.under.app_error", nil, "")
|
||||
return false
|
||||
}
|
||||
|
||||
if echoSem == nil {
|
||||
// We want one additional thread allowed so we never reach channel lockup
|
||||
echoSem = make(chan bool, maxThreads+1)
|
||||
}
|
||||
|
||||
if len(echoSem) >= maxThreads {
|
||||
c.Err = model.NewLocAppError("echoCommand", "api.command.echo_command.high_volume.app_error", nil, "")
|
||||
return false
|
||||
}
|
||||
|
||||
echoSem <- true
|
||||
go func() {
|
||||
defer func() { <-echoSem }()
|
||||
post := &model.Post{}
|
||||
post.ChannelId = command.ChannelId
|
||||
post.Message = message
|
||||
|
||||
time.Sleep(time.Duration(delay) * time.Second)
|
||||
|
||||
if _, err := CreatePost(c, post, true); err != nil {
|
||||
l4g.Error(utils.T("api.command.echo_command.create.error"), err)
|
||||
}
|
||||
}()
|
||||
|
||||
command.Response = model.RESP_EXECUTED
|
||||
return true
|
||||
|
||||
} else if strings.Index(cmd, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.echo_command.description")})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func meCommand(c *Context, command *model.Command) bool {
|
||||
cmd := cmds["meCommand"]
|
||||
|
||||
if !command.Suggest && strings.Index(command.Command, cmd) == 0 {
|
||||
message := ""
|
||||
|
||||
parameters := strings.SplitN(command.Command, " ", 2)
|
||||
if len(parameters) > 1 {
|
||||
message += "*" + parameters[1] + "*"
|
||||
}
|
||||
|
||||
post := &model.Post{}
|
||||
post.Message = message
|
||||
post.ChannelId = command.ChannelId
|
||||
if _, err := CreatePost(c, post, false); err != nil {
|
||||
l4g.Error(utils.T("api.command.me_command.create.error"), err)
|
||||
return false
|
||||
}
|
||||
command.Response = model.RESP_EXECUTED
|
||||
return true
|
||||
|
||||
} else if strings.Index(cmd, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.me_command.description")})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func shrugCommand(c *Context, command *model.Command) bool {
|
||||
cmd := cmds["shrugCommand"]
|
||||
|
||||
if !command.Suggest && strings.Index(command.Command, cmd) == 0 {
|
||||
message := `¯\\\_(ツ)_/¯`
|
||||
|
||||
parameters := strings.SplitN(command.Command, " ", 2)
|
||||
if len(parameters) > 1 {
|
||||
message += " " + parameters[1]
|
||||
}
|
||||
|
||||
post := &model.Post{}
|
||||
post.Message = message
|
||||
post.ChannelId = command.ChannelId
|
||||
if _, err := CreatePost(c, post, false); err != nil {
|
||||
l4g.Error(utils.T("api.command.shrug_command.create.error"), err)
|
||||
return false
|
||||
}
|
||||
command.Response = model.RESP_EXECUTED
|
||||
return true
|
||||
|
||||
} else if strings.Index(cmd, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.shrug_command.description")})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func joinCommand(c *Context, command *model.Command) bool {
|
||||
|
||||
// looks for "/join channel-name"
|
||||
cmd := cmds["joinCommand"]
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 {
|
||||
|
||||
parts := strings.Split(command.Command, " ")
|
||||
|
||||
startsWith := ""
|
||||
|
||||
if len(parts) == 2 {
|
||||
startsWith = parts[1]
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Channel().GetMoreChannels(c.Session.TeamId, c.Session.UserId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return false
|
||||
} else {
|
||||
channels := result.Data.(*model.ChannelList)
|
||||
|
||||
for _, v := range channels.Channels {
|
||||
|
||||
if v.Name == startsWith && !command.Suggest {
|
||||
|
||||
if v.Type == model.CHANNEL_DIRECT {
|
||||
return false
|
||||
}
|
||||
|
||||
JoinChannel(c, v.Id, "")
|
||||
|
||||
if c.Err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
command.GotoLocation = c.GetTeamURL() + "/channels/" + v.Name
|
||||
command.Response = model.RESP_EXECUTED
|
||||
return true
|
||||
}
|
||||
|
||||
if len(startsWith) == 0 || strings.Index(v.Name, startsWith) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd + " " + v.Name, Description: c.T("api.commmand.join_command.description")})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if strings.Index(cmd, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.commmand.join_command.description")})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func loadTestCommand(c *Context, command *model.Command) bool {
|
||||
cmd := cmds["loadTestCommand"]
|
||||
|
||||
// This command is only available when EnableTesting is true
|
||||
if !utils.Cfg.ServiceSettings.EnableTesting {
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 {
|
||||
if loadTestSetupCommand(c, command) {
|
||||
return true
|
||||
}
|
||||
if loadTestUsersCommand(c, command) {
|
||||
return true
|
||||
}
|
||||
if loadTestChannelsCommand(c, command) {
|
||||
return true
|
||||
}
|
||||
if loadTestPostsCommand(c, command) {
|
||||
return true
|
||||
}
|
||||
if loadTestUrlCommand(c, command) {
|
||||
return true
|
||||
}
|
||||
} else if strings.Index(cmd, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.load_test_command.description")})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func parseRange(command string, cmd string) (utils.Range, bool) {
|
||||
tokens := strings.Fields(strings.TrimPrefix(command, cmd))
|
||||
var begin int
|
||||
var end int
|
||||
var err1 error
|
||||
var err2 error
|
||||
switch {
|
||||
case len(tokens) == 1:
|
||||
begin, err1 = strconv.Atoi(tokens[0])
|
||||
end = begin
|
||||
if err1 != nil {
|
||||
return utils.Range{0, 0}, false
|
||||
}
|
||||
case len(tokens) >= 2:
|
||||
begin, err1 = strconv.Atoi(tokens[0])
|
||||
end, err2 = strconv.Atoi(tokens[1])
|
||||
if err1 != nil || err2 != nil {
|
||||
return utils.Range{0, 0}, false
|
||||
}
|
||||
default:
|
||||
return utils.Range{0, 0}, false
|
||||
}
|
||||
return utils.Range{begin, end}, true
|
||||
}
|
||||
|
||||
func contains(items []string, token string) bool {
|
||||
for _, elem := range items {
|
||||
if elem == token {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func loadTestSetupCommand(c *Context, command *model.Command) bool {
|
||||
cmd := cmds["loadTestCommand"] + " setup"
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 && !command.Suggest {
|
||||
tokens := strings.Fields(strings.TrimPrefix(command.Command, cmd))
|
||||
doTeams := contains(tokens, "teams")
|
||||
doFuzz := contains(tokens, "fuzz")
|
||||
|
||||
numArgs := 0
|
||||
if doTeams {
|
||||
numArgs++
|
||||
}
|
||||
if doFuzz {
|
||||
numArgs++
|
||||
}
|
||||
|
||||
var numTeams int
|
||||
var numChannels int
|
||||
var numUsers int
|
||||
var numPosts int
|
||||
|
||||
// Defaults
|
||||
numTeams = 10
|
||||
numChannels = 10
|
||||
numUsers = 10
|
||||
numPosts = 10
|
||||
|
||||
if doTeams {
|
||||
if (len(tokens) - numArgs) >= 4 {
|
||||
numTeams, _ = strconv.Atoi(tokens[numArgs+0])
|
||||
numChannels, _ = strconv.Atoi(tokens[numArgs+1])
|
||||
numUsers, _ = strconv.Atoi(tokens[numArgs+2])
|
||||
numPosts, _ = strconv.Atoi(tokens[numArgs+3])
|
||||
}
|
||||
} else {
|
||||
if (len(tokens) - numArgs) >= 3 {
|
||||
numChannels, _ = strconv.Atoi(tokens[numArgs+0])
|
||||
numUsers, _ = strconv.Atoi(tokens[numArgs+1])
|
||||
numPosts, _ = strconv.Atoi(tokens[numArgs+2])
|
||||
}
|
||||
}
|
||||
client := model.NewClient(c.GetSiteURL())
|
||||
|
||||
if doTeams {
|
||||
if err := CreateBasicUser(client); err != nil {
|
||||
l4g.Error(utils.T("api.command.load_test_setup_command.create.error"))
|
||||
return true
|
||||
}
|
||||
client.LoginByEmail(BTEST_TEAM_NAME, BTEST_USER_EMAIL, BTEST_USER_PASSWORD)
|
||||
environment, err := CreateTestEnvironmentWithTeams(
|
||||
client,
|
||||
utils.Range{numTeams, numTeams},
|
||||
utils.Range{numChannels, numChannels},
|
||||
utils.Range{numUsers, numUsers},
|
||||
utils.Range{numPosts, numPosts},
|
||||
doFuzz)
|
||||
if err != true {
|
||||
l4g.Error(utils.T("api.command.load_test_setup_command.create.error"))
|
||||
return true
|
||||
} else {
|
||||
l4g.Info("Testing environment created")
|
||||
for i := 0; i < len(environment.Teams); i++ {
|
||||
l4g.Info(utils.T("api.command.load_test_setup_command.created.info"), environment.Teams[i].Name)
|
||||
l4g.Info(utils.T("api.command.load_test_setup_command.login.info"), environment.Environments[i].Users[0].Email, USER_PASSWORD)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client.MockSession(c.Session.Token)
|
||||
CreateTestEnvironmentInTeam(
|
||||
client,
|
||||
c.Session.TeamId,
|
||||
utils.Range{numChannels, numChannels},
|
||||
utils.Range{numUsers, numUsers},
|
||||
utils.Range{numPosts, numPosts},
|
||||
doFuzz)
|
||||
}
|
||||
return true
|
||||
} else if strings.Index(cmd, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{
|
||||
Suggestion: cmd,
|
||||
Description: c.T("api.command.load_test_setup_command.description")})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func loadTestUsersCommand(c *Context, command *model.Command) bool {
|
||||
cmd1 := cmds["loadTestCommand"] + " users"
|
||||
cmd2 := cmds["loadTestCommand"] + " users fuzz"
|
||||
|
||||
if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
|
||||
cmd := cmd1
|
||||
doFuzz := false
|
||||
if strings.Index(command.Command, cmd2) == 0 {
|
||||
doFuzz = true
|
||||
cmd = cmd2
|
||||
}
|
||||
usersr, err := parseRange(command.Command, cmd)
|
||||
if err == false {
|
||||
usersr = utils.Range{10, 15}
|
||||
}
|
||||
client := model.NewClient(c.GetSiteURL())
|
||||
userCreator := NewAutoUserCreator(client, c.Session.TeamId)
|
||||
userCreator.Fuzzy = doFuzz
|
||||
userCreator.CreateTestUsers(usersr)
|
||||
return true
|
||||
} else if strings.Index(cmd1, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd1, Description: c.T("api.command.load_test_users_command.users.description")})
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: c.T("api.command.load_test_users_command.fuzz.description")})
|
||||
} else if strings.Index(cmd2, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: c.T("api.command.load_test_users_command.fuzz.description")})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func loadTestChannelsCommand(c *Context, command *model.Command) bool {
|
||||
cmd1 := cmds["loadTestCommand"] + " channels"
|
||||
cmd2 := cmds["loadTestCommand"] + " channels fuzz"
|
||||
|
||||
if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
|
||||
cmd := cmd1
|
||||
doFuzz := false
|
||||
if strings.Index(command.Command, cmd2) == 0 {
|
||||
doFuzz = true
|
||||
cmd = cmd2
|
||||
}
|
||||
channelsr, err := parseRange(command.Command, cmd)
|
||||
if err == false {
|
||||
channelsr = utils.Range{20, 30}
|
||||
}
|
||||
client := model.NewClient(c.GetSiteURL())
|
||||
client.MockSession(c.Session.Token)
|
||||
channelCreator := NewAutoChannelCreator(client, c.Session.TeamId)
|
||||
channelCreator.Fuzzy = doFuzz
|
||||
channelCreator.CreateTestChannels(channelsr)
|
||||
return true
|
||||
} else if strings.Index(cmd1, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd1, Description: c.T("api.command.load_test_channels_command.channel.description")})
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: c.T("api.command.load_test_channels_command.fuzz.description")})
|
||||
} else if strings.Index(cmd2, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: c.T("api.command.load_test_channels_command.fuzz.description")})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func loadTestPostsCommand(c *Context, command *model.Command) bool {
|
||||
cmd1 := cmds["loadTestCommand"] + " posts"
|
||||
cmd2 := cmds["loadTestCommand"] + " posts fuzz"
|
||||
|
||||
if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
|
||||
cmd := cmd1
|
||||
doFuzz := false
|
||||
if strings.Index(command.Command, cmd2) == 0 {
|
||||
cmd = cmd2
|
||||
doFuzz = true
|
||||
}
|
||||
|
||||
postsr, err := parseRange(command.Command, cmd)
|
||||
if err == false {
|
||||
postsr = utils.Range{20, 30}
|
||||
}
|
||||
|
||||
tokens := strings.Fields(strings.TrimPrefix(command.Command, cmd))
|
||||
rimages := utils.Range{0, 0}
|
||||
if len(tokens) >= 3 {
|
||||
if numImages, err := strconv.Atoi(tokens[2]); err == nil {
|
||||
rimages = utils.Range{numImages, numImages}
|
||||
}
|
||||
}
|
||||
|
||||
var usernames []string
|
||||
if result := <-Srv.Store.User().GetProfiles(c.Session.TeamId); result.Err == nil {
|
||||
profileUsers := result.Data.(map[string]*model.User)
|
||||
usernames = make([]string, len(profileUsers))
|
||||
i := 0
|
||||
for _, userprof := range profileUsers {
|
||||
usernames[i] = userprof.Username
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
client := model.NewClient(c.GetSiteURL())
|
||||
client.MockSession(c.Session.Token)
|
||||
testPoster := NewAutoPostCreator(client, command.ChannelId)
|
||||
testPoster.Fuzzy = doFuzz
|
||||
testPoster.Users = usernames
|
||||
|
||||
numImages := utils.RandIntFromRange(rimages)
|
||||
numPosts := utils.RandIntFromRange(postsr)
|
||||
for i := 0; i < numPosts; i++ {
|
||||
testPoster.HasImage = (i < numImages)
|
||||
testPoster.CreateRandomPost()
|
||||
}
|
||||
return true
|
||||
} else if strings.Index(cmd1, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd1, Description: c.T("api.command.load_test_posts_command.posts.description")})
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: c.T("api.command.load_test_posts_command.fuzz.description")})
|
||||
} else if strings.Index(cmd2, command.Command) == 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: c.T("api.command.load_test_posts_command.fuzz.description")})
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func loadTestUrlCommand(c *Context, command *model.Command) bool {
|
||||
cmd := cmds["loadTestCommand"] + " url"
|
||||
|
||||
if strings.Index(command.Command, cmd) == 0 && !command.Suggest {
|
||||
url := ""
|
||||
|
||||
parameters := strings.SplitN(command.Command, " ", 3)
|
||||
if len(parameters) != 3 {
|
||||
c.Err = model.NewLocAppError("loadTestUrlCommand", "api.command.load_test_url_command.url.app_error", nil, "")
|
||||
return true
|
||||
} else {
|
||||
url = parameters[2]
|
||||
}
|
||||
|
||||
// provide a shortcut to easily access tests stored in doc/developer/tests
|
||||
if !strings.HasPrefix(url, "http") {
|
||||
url = "https://raw.githubusercontent.com/mattermost/platform/master/doc/developer/tests/" + url
|
||||
|
||||
if path.Ext(url) == "" {
|
||||
url += ".md"
|
||||
}
|
||||
}
|
||||
|
||||
var contents io.ReadCloser
|
||||
if r, err := http.Get(url); err != nil {
|
||||
c.Err = model.NewLocAppError("loadTestUrlCommand", "api.command.load_test_url_command.file.app_error", nil, err.Error())
|
||||
return false
|
||||
} else if r.StatusCode > 400 {
|
||||
c.Err = model.NewLocAppError("loadTestUrlCommand", "api.command.load_test_url_command.file.app_error", nil, r.Status)
|
||||
return false
|
||||
} else {
|
||||
contents = r.Body
|
||||
}
|
||||
|
||||
bytes := make([]byte, 4000)
|
||||
|
||||
// break contents into 4000 byte posts
|
||||
for {
|
||||
length, err := contents.Read(bytes)
|
||||
if err != nil && err != io.EOF {
|
||||
c.Err = model.NewLocAppError("loadTestUrlCommand", "api.command.load_test_url_command.reading.app_error", nil, err.Error())
|
||||
return false
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
post := &model.Post{}
|
||||
post.Message = string(bytes[:length])
|
||||
post.ChannelId = command.ChannelId
|
||||
|
||||
if _, err := CreatePost(c, post, false); err != nil {
|
||||
l4g.Error(utils.T("api.command.load_test_url_command.create.error"), err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
command.Response = model.RESP_EXECUTED
|
||||
|
||||
return true
|
||||
} else if strings.Index(cmd, command.Command) == 0 && strings.Index(command.Command, "/loadtest posts") != 0 {
|
||||
command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.load_test_url_command.description")})
|
||||
}
|
||||
|
||||
return false
|
||||
rc := &model.CommandResponse{
|
||||
Text: "test command response " + msg,
|
||||
ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
|
||||
}
|
||||
|
||||
w.Write([]byte(rc.ToJson()))
|
||||
}
|
||||
|
||||
89
api/command_echo.go
Обычный файл
89
api/command_echo.go
Обычный файл
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
var echoSem chan bool
|
||||
|
||||
type EchoProvider struct {
|
||||
}
|
||||
|
||||
const (
|
||||
CMD_ECHO = "echo"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCommandProvider(&EchoProvider{})
|
||||
}
|
||||
|
||||
func (me *EchoProvider) GetTrigger() string {
|
||||
return CMD_ECHO
|
||||
}
|
||||
|
||||
func (me *EchoProvider) GetCommand(c *Context) *model.Command {
|
||||
return &model.Command{
|
||||
Trigger: CMD_ECHO,
|
||||
AutoComplete: true,
|
||||
AutoCompleteDesc: c.T("api.command_echo.desc"),
|
||||
AutoCompleteHint: c.T("api.command_echo.hint"),
|
||||
DisplayName: c.T("api.command_echo.name"),
|
||||
}
|
||||
}
|
||||
|
||||
func (me *EchoProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
maxThreads := 100
|
||||
|
||||
delay := 0
|
||||
if endMsg := strings.LastIndex(message, "\""); string(message[0]) == "\"" && endMsg > 1 {
|
||||
if checkDelay, err := strconv.Atoi(strings.Trim(message[endMsg:], " \"")); err == nil {
|
||||
delay = checkDelay
|
||||
}
|
||||
message = message[1:endMsg]
|
||||
} else if strings.Index(message, " ") > -1 {
|
||||
delayIdx := strings.LastIndex(message, " ")
|
||||
delayStr := strings.Trim(message[delayIdx:], " ")
|
||||
|
||||
if checkDelay, err := strconv.Atoi(delayStr); err == nil {
|
||||
delay = checkDelay
|
||||
message = message[:delayIdx]
|
||||
}
|
||||
}
|
||||
|
||||
if delay > 10000 {
|
||||
return &model.CommandResponse{Text: c.T("api.command_echo.delay.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
if echoSem == nil {
|
||||
// We want one additional thread allowed so we never reach channel lockup
|
||||
echoSem = make(chan bool, maxThreads+1)
|
||||
}
|
||||
|
||||
if len(echoSem) >= maxThreads {
|
||||
return &model.CommandResponse{Text: c.T("api.command_echo.high_volume.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
echoSem <- true
|
||||
go func() {
|
||||
defer func() { <-echoSem }()
|
||||
post := &model.Post{}
|
||||
post.ChannelId = channelId
|
||||
post.Message = message
|
||||
|
||||
time.Sleep(time.Duration(delay) * time.Second)
|
||||
|
||||
if _, err := CreatePost(c, post, true); err != nil {
|
||||
l4g.Error(c.T("api.command_echo.create.app_error"), err)
|
||||
}
|
||||
}()
|
||||
|
||||
return &model.CommandResponse{}
|
||||
}
|
||||
42
api/command_echo_test.go
Обычный файл
42
api/command_echo_test.go
Обычный файл
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
)
|
||||
|
||||
func TestEchoCommand(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
echoTestString := "/echo test"
|
||||
|
||||
r1 := Client.Must(Client.Command(channel1.Id, echoTestString, false)).Data.(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Echo command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
|
||||
if len(p1.Order) != 1 {
|
||||
t.Fatal("Echo command failed to send")
|
||||
}
|
||||
}
|
||||
62
api/command_join.go
Обычный файл
62
api/command_join.go
Обычный файл
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type JoinProvider struct {
|
||||
}
|
||||
|
||||
const (
|
||||
CMD_JOIN = "join"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCommandProvider(&JoinProvider{})
|
||||
}
|
||||
|
||||
func (me *JoinProvider) GetTrigger() string {
|
||||
return CMD_JOIN
|
||||
}
|
||||
|
||||
func (me *JoinProvider) GetCommand(c *Context) *model.Command {
|
||||
return &model.Command{
|
||||
Trigger: CMD_JOIN,
|
||||
AutoComplete: true,
|
||||
AutoCompleteDesc: c.T("api.command_join.desc"),
|
||||
AutoCompleteHint: c.T("api.command_join.hint"),
|
||||
DisplayName: c.T("api.command_join.name"),
|
||||
}
|
||||
}
|
||||
|
||||
func (me *JoinProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
if result := <-Srv.Store.Channel().GetMoreChannels(c.Session.TeamId, c.Session.UserId); result.Err != nil {
|
||||
return &model.CommandResponse{Text: c.T("api.command_join.list.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
} else {
|
||||
channels := result.Data.(*model.ChannelList)
|
||||
|
||||
for _, v := range channels.Channels {
|
||||
|
||||
if v.Name == message {
|
||||
|
||||
if v.Type == model.CHANNEL_DIRECT {
|
||||
return &model.CommandResponse{Text: c.T("api.command_join.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
JoinChannel(c, v.Id, "")
|
||||
|
||||
if c.Err != nil {
|
||||
c.Err = nil
|
||||
return &model.CommandResponse{Text: c.T("api.command_join.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
return &model.CommandResponse{GotoLocation: c.GetTeamURL() + "/channels/" + v.Name, Text: c.T("api.command_join.success"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: c.T("api.command_join.missing.app_error")}
|
||||
}
|
||||
71
api/command_join_test.go
Обычный файл
71
api/command_join_test.go
Обычный файл
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
)
|
||||
|
||||
func TestJoinCommands(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel0 := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel0 = Client.Must(Client.CreateChannel(channel0)).Data.(*model.Channel)
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
Client.Must(Client.LeaveChannel(channel1.Id))
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "BB", Name: "bb" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
Client.Must(Client.LeaveChannel(channel2.Id))
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = user2.Id
|
||||
channel3 := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel)
|
||||
|
||||
rs5 := Client.Must(Client.Command(channel0.Id, "/join "+channel2.Name, false)).Data.(*model.CommandResponse)
|
||||
if !strings.HasSuffix(rs5.GotoLocation, "/"+team.Name+"/channels/"+channel2.Name) {
|
||||
t.Fatal("failed to join channel")
|
||||
}
|
||||
|
||||
rs6 := Client.Must(Client.Command(channel0.Id, "/join "+channel3.Name, false)).Data.(*model.CommandResponse)
|
||||
if strings.HasSuffix(rs6.GotoLocation, "/"+team.Name+"/channels/"+channel3.Name) {
|
||||
t.Fatal("should not have joined direct message channel")
|
||||
}
|
||||
|
||||
c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
|
||||
|
||||
if len(c1.Channels) != 5 { // 4 because of town-square, off-topic and direct
|
||||
t.Fatal("didn't join channel")
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, c := range c1.Channels {
|
||||
if c.Name == channel2.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("didn't join channel")
|
||||
}
|
||||
}
|
||||
365
api/command_loadtest.go
Обычный файл
365
api/command_loadtest.go
Обычный файл
@@ -0,0 +1,365 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
var usage = `Mattermost load testing commands to help configure the system
|
||||
|
||||
COMMANDS:
|
||||
|
||||
Setup - Creates a testing environment in current team.
|
||||
/loadtest setup [teams] [fuzz] <Num Channels> <Num Users> <NumPosts>
|
||||
|
||||
Example:
|
||||
/loadtest setup teams fuzz 10 20 50
|
||||
|
||||
Users - Add a specified number of random users with fuzz text to current team.
|
||||
/loadtest users [fuzz] <Min Users> <Max Users>
|
||||
|
||||
Example:
|
||||
/loadtest users fuzz 5 10
|
||||
|
||||
Channels - Add a specified number of random channels with fuzz text to current team.
|
||||
/loadtest channels [fuzz] <Min Channels> <Max Channels>
|
||||
|
||||
Example:
|
||||
/loadtest channels fuzz 5 10
|
||||
|
||||
Posts - Add some random posts with fuzz text to current channel.
|
||||
/loadtest posts [fuzz] <Min Posts> <Max Posts> <Max Images>
|
||||
|
||||
Example:
|
||||
/loadtest posts fuzz 5 10 3
|
||||
|
||||
Url - Add a post containing the text from a given url to current channel.
|
||||
/loadtest url
|
||||
|
||||
Example:
|
||||
/loadtest http://www.example.com/sample_file.md
|
||||
|
||||
|
||||
`
|
||||
|
||||
const (
|
||||
CMD_LOADTEST = "loadtest"
|
||||
)
|
||||
|
||||
type LoadTestProvider struct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
if !utils.Cfg.ServiceSettings.EnableTesting {
|
||||
RegisterCommandProvider(&LoadTestProvider{})
|
||||
}
|
||||
}
|
||||
|
||||
func (me *LoadTestProvider) GetTrigger() string {
|
||||
return CMD_LOADTEST
|
||||
}
|
||||
|
||||
func (me *LoadTestProvider) GetCommand(c *Context) *model.Command {
|
||||
return &model.Command{
|
||||
Trigger: CMD_LOADTEST,
|
||||
AutoComplete: false,
|
||||
AutoCompleteDesc: "Debug Load Testing",
|
||||
AutoCompleteHint: "help",
|
||||
DisplayName: "loadtest",
|
||||
}
|
||||
}
|
||||
|
||||
func (me *LoadTestProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
|
||||
//This command is only available when EnableTesting is true
|
||||
if !utils.Cfg.ServiceSettings.EnableTesting {
|
||||
return &model.CommandResponse{}
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "setup") {
|
||||
return me.SetupCommand(c, channelId, message)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "users") {
|
||||
return me.UsersCommand(c, channelId, message)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "channels") {
|
||||
return me.ChannelsCommand(c, channelId, message)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "posts") {
|
||||
return me.PostsCommand(c, channelId, message)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(message, "url") {
|
||||
return me.UrlCommand(c, channelId, message)
|
||||
}
|
||||
|
||||
return me.HelpCommand(c, channelId, message)
|
||||
}
|
||||
|
||||
func (me *LoadTestProvider) HelpCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
return &model.CommandResponse{Text: usage, ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
func (me *LoadTestProvider) SetupCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
tokens := strings.Fields(strings.TrimPrefix(message, "setup"))
|
||||
doTeams := contains(tokens, "teams")
|
||||
doFuzz := contains(tokens, "fuzz")
|
||||
|
||||
numArgs := 0
|
||||
if doTeams {
|
||||
numArgs++
|
||||
}
|
||||
if doFuzz {
|
||||
numArgs++
|
||||
}
|
||||
|
||||
var numTeams int
|
||||
var numChannels int
|
||||
var numUsers int
|
||||
var numPosts int
|
||||
|
||||
// Defaults
|
||||
numTeams = 10
|
||||
numChannels = 10
|
||||
numUsers = 10
|
||||
numPosts = 10
|
||||
|
||||
if doTeams {
|
||||
if (len(tokens) - numArgs) >= 4 {
|
||||
numTeams, _ = strconv.Atoi(tokens[numArgs+0])
|
||||
numChannels, _ = strconv.Atoi(tokens[numArgs+1])
|
||||
numUsers, _ = strconv.Atoi(tokens[numArgs+2])
|
||||
numPosts, _ = strconv.Atoi(tokens[numArgs+3])
|
||||
}
|
||||
} else {
|
||||
if (len(tokens) - numArgs) >= 3 {
|
||||
numChannels, _ = strconv.Atoi(tokens[numArgs+0])
|
||||
numUsers, _ = strconv.Atoi(tokens[numArgs+1])
|
||||
numPosts, _ = strconv.Atoi(tokens[numArgs+2])
|
||||
}
|
||||
}
|
||||
client := model.NewClient(c.GetSiteURL())
|
||||
|
||||
if doTeams {
|
||||
if err := CreateBasicUser(client); err != nil {
|
||||
return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
client.LoginByEmail(BTEST_TEAM_NAME, BTEST_USER_EMAIL, BTEST_USER_PASSWORD)
|
||||
environment, err := CreateTestEnvironmentWithTeams(
|
||||
client,
|
||||
utils.Range{numTeams, numTeams},
|
||||
utils.Range{numChannels, numChannels},
|
||||
utils.Range{numUsers, numUsers},
|
||||
utils.Range{numPosts, numPosts},
|
||||
doFuzz)
|
||||
if err != true {
|
||||
return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
} else {
|
||||
l4g.Info("Testing environment created")
|
||||
for i := 0; i < len(environment.Teams); i++ {
|
||||
l4g.Info("Team Created: " + environment.Teams[i].Name)
|
||||
l4g.Info("\t User to login: " + environment.Environments[i].Users[0].Email + ", " + USER_PASSWORD)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
client.MockSession(c.Session.Token)
|
||||
CreateTestEnvironmentInTeam(
|
||||
client,
|
||||
c.Session.TeamId,
|
||||
utils.Range{numChannels, numChannels},
|
||||
utils.Range{numUsers, numUsers},
|
||||
utils.Range{numPosts, numPosts},
|
||||
doFuzz)
|
||||
}
|
||||
|
||||
return &model.CommandResponse{Text: "Creating enviroment...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
func (me *LoadTestProvider) UsersCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
cmd := strings.TrimSpace(strings.TrimPrefix(message, "users"))
|
||||
|
||||
doFuzz := false
|
||||
if strings.Index(cmd, "fuzz") == 0 {
|
||||
doFuzz = true
|
||||
cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
|
||||
}
|
||||
|
||||
usersr, err := parseRange(cmd, "")
|
||||
if err == false {
|
||||
usersr = utils.Range{2, 5}
|
||||
}
|
||||
|
||||
client := model.NewClient(c.GetSiteURL())
|
||||
userCreator := NewAutoUserCreator(client, c.Session.TeamId)
|
||||
userCreator.Fuzzy = doFuzz
|
||||
userCreator.CreateTestUsers(usersr)
|
||||
|
||||
return &model.CommandResponse{Text: "Adding users...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
func (me *LoadTestProvider) ChannelsCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
cmd := strings.TrimSpace(strings.TrimPrefix(message, "channels"))
|
||||
|
||||
doFuzz := false
|
||||
if strings.Index(cmd, "fuzz") == 0 {
|
||||
doFuzz = true
|
||||
cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
|
||||
}
|
||||
|
||||
channelsr, err := parseRange(cmd, "")
|
||||
if err == false {
|
||||
channelsr = utils.Range{2, 5}
|
||||
}
|
||||
client := model.NewClient(c.GetSiteURL())
|
||||
client.MockSession(c.Session.Token)
|
||||
channelCreator := NewAutoChannelCreator(client, c.Session.TeamId)
|
||||
channelCreator.Fuzzy = doFuzz
|
||||
channelCreator.CreateTestChannels(channelsr)
|
||||
|
||||
return &model.CommandResponse{Text: "Adding channels...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
func (me *LoadTestProvider) PostsCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
cmd := strings.TrimSpace(strings.TrimPrefix(message, "posts"))
|
||||
|
||||
doFuzz := false
|
||||
if strings.Index(cmd, "fuzz") == 0 {
|
||||
doFuzz = true
|
||||
cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
|
||||
}
|
||||
|
||||
postsr, err := parseRange(cmd, "")
|
||||
if err == false {
|
||||
postsr = utils.Range{20, 30}
|
||||
}
|
||||
|
||||
tokens := strings.Fields(cmd)
|
||||
rimages := utils.Range{0, 0}
|
||||
if len(tokens) >= 3 {
|
||||
if numImages, err := strconv.Atoi(tokens[2]); err == nil {
|
||||
rimages = utils.Range{numImages, numImages}
|
||||
}
|
||||
}
|
||||
|
||||
var usernames []string
|
||||
if result := <-Srv.Store.User().GetProfiles(c.Session.TeamId); result.Err == nil {
|
||||
profileUsers := result.Data.(map[string]*model.User)
|
||||
usernames = make([]string, len(profileUsers))
|
||||
i := 0
|
||||
for _, userprof := range profileUsers {
|
||||
usernames[i] = userprof.Username
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
client := model.NewClient(c.GetSiteURL())
|
||||
client.MockSession(c.Session.Token)
|
||||
testPoster := NewAutoPostCreator(client, channelId)
|
||||
testPoster.Fuzzy = doFuzz
|
||||
testPoster.Users = usernames
|
||||
|
||||
numImages := utils.RandIntFromRange(rimages)
|
||||
numPosts := utils.RandIntFromRange(postsr)
|
||||
for i := 0; i < numPosts; i++ {
|
||||
testPoster.HasImage = (i < numImages)
|
||||
testPoster.CreateRandomPost()
|
||||
}
|
||||
|
||||
return &model.CommandResponse{Text: "Adding posts...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
func (me *LoadTestProvider) UrlCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
url := strings.TrimSpace(strings.TrimPrefix(message, "url"))
|
||||
if len(url) == 0 {
|
||||
return &model.CommandResponse{Text: "Command must contain a url", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
// provide a shortcut to easily access tests stored in doc/developer/tests
|
||||
if !strings.HasPrefix(url, "http") {
|
||||
url = "https://raw.githubusercontent.com/mattermost/platform/master/doc/developer/tests/" + url
|
||||
|
||||
if path.Ext(url) == "" {
|
||||
url += ".md"
|
||||
}
|
||||
}
|
||||
|
||||
var contents io.ReadCloser
|
||||
if r, err := http.Get(url); err != nil {
|
||||
return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
} else if r.StatusCode > 400 {
|
||||
return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
} else {
|
||||
contents = r.Body
|
||||
}
|
||||
|
||||
bytes := make([]byte, 4000)
|
||||
|
||||
// break contents into 4000 byte posts
|
||||
for {
|
||||
length, err := contents.Read(bytes)
|
||||
if err != nil && err != io.EOF {
|
||||
return &model.CommandResponse{Text: "Encountered error reading file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
if length == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
post := &model.Post{}
|
||||
post.Message = string(bytes[:length])
|
||||
post.ChannelId = channelId
|
||||
|
||||
if _, err := CreatePost(c, post, false); err != nil {
|
||||
return &model.CommandResponse{Text: "Unable to create post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
}
|
||||
|
||||
return &model.CommandResponse{Text: "Loading data...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
|
||||
}
|
||||
|
||||
func parseRange(command string, cmd string) (utils.Range, bool) {
|
||||
tokens := strings.Fields(strings.TrimPrefix(command, cmd))
|
||||
var begin int
|
||||
var end int
|
||||
var err1 error
|
||||
var err2 error
|
||||
switch {
|
||||
case len(tokens) == 1:
|
||||
begin, err1 = strconv.Atoi(tokens[0])
|
||||
end = begin
|
||||
if err1 != nil {
|
||||
return utils.Range{0, 0}, false
|
||||
}
|
||||
case len(tokens) >= 2:
|
||||
begin, err1 = strconv.Atoi(tokens[0])
|
||||
end, err2 = strconv.Atoi(tokens[1])
|
||||
if err1 != nil || err2 != nil {
|
||||
return utils.Range{0, 0}, false
|
||||
}
|
||||
default:
|
||||
return utils.Range{0, 0}, false
|
||||
}
|
||||
return utils.Range{begin, end}, true
|
||||
}
|
||||
|
||||
func contains(items []string, token string) bool {
|
||||
for _, elem := range items {
|
||||
if elem == token {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
221
api/command_loadtest_test.go
Обычный файл
221
api/command_loadtest_test.go
Обычный файл
@@ -0,0 +1,221 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
func TestLoadTestHelpCommands(t *testing.T) {
|
||||
Setup()
|
||||
// enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
|
||||
enableTesting := utils.Cfg.ServiceSettings.EnableTesting
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableTesting = enableTesting
|
||||
}()
|
||||
|
||||
utils.Cfg.ServiceSettings.EnableTesting = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
|
||||
|
||||
rs := Client.Must(Client.Command(channel.Id, "/loadtest help", false)).Data.(*model.CommandResponse)
|
||||
if !strings.Contains(rs.Text, "Mattermost load testing commands to help") {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestSetupCommands(t *testing.T) {
|
||||
Setup()
|
||||
// enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
|
||||
enableTesting := utils.Cfg.ServiceSettings.EnableTesting
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableTesting = enableTesting
|
||||
}()
|
||||
|
||||
utils.Cfg.ServiceSettings.EnableTesting = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
|
||||
|
||||
rs := Client.Must(Client.Command(channel.Id, "/loadtest setup fuzz 1 1 1", false)).Data.(*model.CommandResponse)
|
||||
if rs.Text != "Creating enviroment..." {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestUsersCommands(t *testing.T) {
|
||||
Setup()
|
||||
// enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
|
||||
enableTesting := utils.Cfg.ServiceSettings.EnableTesting
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableTesting = enableTesting
|
||||
}()
|
||||
|
||||
utils.Cfg.ServiceSettings.EnableTesting = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
|
||||
|
||||
rs := Client.Must(Client.Command(channel.Id, "/loadtest users fuzz 1 2", false)).Data.(*model.CommandResponse)
|
||||
if rs.Text != "Adding users..." {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestChannelsCommands(t *testing.T) {
|
||||
Setup()
|
||||
// enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
|
||||
enableTesting := utils.Cfg.ServiceSettings.EnableTesting
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableTesting = enableTesting
|
||||
}()
|
||||
|
||||
utils.Cfg.ServiceSettings.EnableTesting = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
|
||||
|
||||
rs := Client.Must(Client.Command(channel.Id, "/loadtest channels fuzz 1 2", false)).Data.(*model.CommandResponse)
|
||||
if rs.Text != "Adding channels..." {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestPostsCommands(t *testing.T) {
|
||||
Setup()
|
||||
// enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
|
||||
enableTesting := utils.Cfg.ServiceSettings.EnableTesting
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableTesting = enableTesting
|
||||
}()
|
||||
|
||||
utils.Cfg.ServiceSettings.EnableTesting = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
|
||||
|
||||
rs := Client.Must(Client.Command(channel.Id, "/loadtest posts fuzz 2 3 2", false)).Data.(*model.CommandResponse)
|
||||
if rs.Text != "Adding posts..." {
|
||||
t.Fatal(rs.Text)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
func TestLoadTestUrlCommands(t *testing.T) {
|
||||
Setup()
|
||||
// enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
|
||||
enableTesting := utils.Cfg.ServiceSettings.EnableTesting
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableTesting = enableTesting
|
||||
}()
|
||||
|
||||
utils.Cfg.ServiceSettings.EnableTesting = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
|
||||
|
||||
command := "/loadtest url "
|
||||
if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Command must contain a url" {
|
||||
t.Fatal("/loadtest url with no url should've failed")
|
||||
}
|
||||
|
||||
command = "/loadtest url http://missingfiletonwhere/path/asdf/qwerty"
|
||||
if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Unable to get file" {
|
||||
t.Log(r.Text)
|
||||
t.Fatal("/loadtest url with invalid url should've failed")
|
||||
}
|
||||
|
||||
command = "/loadtest url https://raw.githubusercontent.com/mattermost/platform/master/README.md"
|
||||
if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Loading data..." {
|
||||
t.Fatal("/loadtest url for README.md should've executed")
|
||||
}
|
||||
|
||||
command = "/loadtest url test-emoticons.md"
|
||||
if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Loading data..." {
|
||||
t.Fatal("/loadtest url for test-emoticons.md should've executed")
|
||||
}
|
||||
|
||||
command = "/loadtest url test-emoticons"
|
||||
if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Loading data..." {
|
||||
t.Fatal("/loadtest url for test-emoticons should've executed")
|
||||
}
|
||||
|
||||
posts := Client.Must(Client.GetPosts(channel.Id, 0, 5, "")).Data.(*model.PostList)
|
||||
// note that this may make more than 3 posts if files are too long to fit in an individual post
|
||||
if len(posts.Order) < 3 {
|
||||
t.Fatal("/loadtest url made too few posts, perhaps there needs to be a delay before GetPosts in the test?")
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
37
api/command_logout.go
Обычный файл
37
api/command_logout.go
Обычный файл
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type LogoutProvider struct {
|
||||
}
|
||||
|
||||
const (
|
||||
CMD_LOGOUT = "logout"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCommandProvider(&LogoutProvider{})
|
||||
}
|
||||
|
||||
func (me *LogoutProvider) GetTrigger() string {
|
||||
return CMD_LOGOUT
|
||||
}
|
||||
|
||||
func (me *LogoutProvider) GetCommand(c *Context) *model.Command {
|
||||
return &model.Command{
|
||||
Trigger: CMD_LOGOUT,
|
||||
AutoComplete: true,
|
||||
AutoCompleteDesc: c.T("api.command_logout.desc"),
|
||||
AutoCompleteHint: "",
|
||||
DisplayName: c.T("api.command_logout.name"),
|
||||
}
|
||||
}
|
||||
|
||||
func (me *LogoutProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
return &model.CommandResponse{GotoLocation: "/logout", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: c.T("api.command_logout.success_message")}
|
||||
}
|
||||
32
api/command_logout_test.go
Обычный файл
32
api/command_logout_test.go
Обычный файл
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
)
|
||||
|
||||
func TestLogoutTestCommand(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
rs1 := Client.Must(Client.Command(channel1.Id, "/logout", false)).Data.(*model.CommandResponse)
|
||||
if rs1.GotoLocation != "/logout" {
|
||||
t.Fatal("failed to logout")
|
||||
}
|
||||
}
|
||||
37
api/command_me.go
Обычный файл
37
api/command_me.go
Обычный файл
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type MeProvider struct {
|
||||
}
|
||||
|
||||
const (
|
||||
CMD_ME = "me"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCommandProvider(&MeProvider{})
|
||||
}
|
||||
|
||||
func (me *MeProvider) GetTrigger() string {
|
||||
return CMD_ME
|
||||
}
|
||||
|
||||
func (me *MeProvider) GetCommand(c *Context) *model.Command {
|
||||
return &model.Command{
|
||||
Trigger: CMD_ME,
|
||||
AutoComplete: true,
|
||||
AutoCompleteDesc: c.T("api.command_me.desc"),
|
||||
AutoCompleteHint: c.T("api.command_me.hint"),
|
||||
DisplayName: c.T("api.command_me.name"),
|
||||
}
|
||||
}
|
||||
|
||||
func (me *MeProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL, Text: "*" + message + "*"}
|
||||
}
|
||||
47
api/command_me_test.go
Обычный файл
47
api/command_me_test.go
Обычный файл
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
)
|
||||
|
||||
func TestMeCommand(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
testString := "/me hello"
|
||||
|
||||
r1 := Client.Must(Client.Command(channel1.Id, testString, false)).Data.(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
|
||||
if len(p1.Order) != 1 {
|
||||
t.Fatal("Command failed to send")
|
||||
} else {
|
||||
if p1.Posts[p1.Order[0]].Message != `*hello*` {
|
||||
t.Log(p1.Posts[p1.Order[0]].Message)
|
||||
t.Fatal("invalid shrug reponse")
|
||||
}
|
||||
}
|
||||
}
|
||||
42
api/command_shrug.go
Обычный файл
42
api/command_shrug.go
Обычный файл
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type ShrugProvider struct {
|
||||
}
|
||||
|
||||
const (
|
||||
CMD_SHRUG = "shrug"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCommandProvider(&ShrugProvider{})
|
||||
}
|
||||
|
||||
func (me *ShrugProvider) GetTrigger() string {
|
||||
return CMD_SHRUG
|
||||
}
|
||||
|
||||
func (me *ShrugProvider) GetCommand(c *Context) *model.Command {
|
||||
return &model.Command{
|
||||
Trigger: CMD_SHRUG,
|
||||
AutoComplete: true,
|
||||
AutoCompleteDesc: c.T("api.command_shrug.desc"),
|
||||
AutoCompleteHint: c.T("api.command_shrug.hint"),
|
||||
DisplayName: c.T("api.command_shrug.name"),
|
||||
}
|
||||
}
|
||||
|
||||
func (me *ShrugProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
rmsg := `¯\\\_(ツ)\_/¯`
|
||||
if len(message) > 0 {
|
||||
rmsg = message + " " + rmsg
|
||||
}
|
||||
|
||||
return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL, Text: rmsg}
|
||||
}
|
||||
47
api/command_shrug_test.go
Обычный файл
47
api/command_shrug_test.go
Обычный файл
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
"github.com/mattermost/platform/store"
|
||||
)
|
||||
|
||||
func TestShrugCommand(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
testString := "/shrug"
|
||||
|
||||
r1 := Client.Must(Client.Command(channel1.Id, testString, false)).Data.(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
|
||||
if len(p1.Order) != 1 {
|
||||
t.Fatal("Command failed to send")
|
||||
} else {
|
||||
if p1.Posts[p1.Order[0]].Message != `¯\\\_(ツ)\_/¯` {
|
||||
t.Log(p1.Posts[p1.Order[0]].Message)
|
||||
t.Fatal("invalid shrug reponse")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
"github.com/mattermost/platform/utils"
|
||||
)
|
||||
|
||||
func TestSuggestRootCommands(t *testing.T) {
|
||||
func TestListCommands(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
@@ -25,177 +24,89 @@ func TestSuggestRootCommands(t *testing.T) {
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
if _, err := Client.Command("", "", true); err == nil {
|
||||
t.Fatal("Should fail")
|
||||
}
|
||||
if results, err := Client.ListCommands(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
commands := results.Data.([]*model.Command)
|
||||
foundEcho := false
|
||||
|
||||
rs1 := Client.Must(Client.Command("", "/", true)).Data.(*model.Command)
|
||||
for _, command := range commands {
|
||||
if command.Trigger == "echo" {
|
||||
foundEcho = true
|
||||
}
|
||||
}
|
||||
|
||||
hasLogout := false
|
||||
for _, v := range rs1.Suggestions {
|
||||
if v.Suggestion == "/logout" {
|
||||
hasLogout = true
|
||||
if !foundEcho {
|
||||
t.Fatal("Couldn't find echo command")
|
||||
}
|
||||
}
|
||||
|
||||
if !hasLogout {
|
||||
t.Log(rs1.Suggestions)
|
||||
t.Fatal("should have logout cmd")
|
||||
}
|
||||
|
||||
rs2 := Client.Must(Client.Command("", "/log", true)).Data.(*model.Command)
|
||||
|
||||
if rs2.Suggestions[0].Suggestion != "/logout" {
|
||||
t.Fatal("should have logout cmd")
|
||||
}
|
||||
|
||||
rs3 := Client.Must(Client.Command("", "/joi", true)).Data.(*model.Command)
|
||||
|
||||
if rs3.Suggestions[0].Suggestion != "/join" {
|
||||
t.Fatal("should have join cmd")
|
||||
}
|
||||
|
||||
rs4 := Client.Must(Client.Command("", "/ech", true)).Data.(*model.Command)
|
||||
|
||||
if rs4.Suggestions[0].Suggestion != "/echo" {
|
||||
t.Fatal("should have echo cmd")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogoutCommands(t *testing.T) {
|
||||
func TestCreateCommand(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
rs1 := Client.Must(Client.Command("", "/logout", false)).Data.(*model.Command)
|
||||
if rs1.GotoLocation != "/logout" {
|
||||
t.Fatal("failed to logout")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinCommands(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
Client.Must(Client.LeaveChannel(channel1.Id))
|
||||
|
||||
channel2 := &model.Channel{DisplayName: "BB", Name: "bb" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
|
||||
Client.Must(Client.LeaveChannel(channel2.Id))
|
||||
|
||||
user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
data := make(map[string]string)
|
||||
data["user_id"] = user2.Id
|
||||
channel3 := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel)
|
||||
|
||||
rs1 := Client.Must(Client.Command("", "/join aa", true)).Data.(*model.Command)
|
||||
if rs1.Suggestions[0].Suggestion != "/join "+channel1.Name {
|
||||
t.Fatal("should have join cmd")
|
||||
}
|
||||
|
||||
rs2 := Client.Must(Client.Command("", "/join bb", true)).Data.(*model.Command)
|
||||
if rs2.Suggestions[0].Suggestion != "/join "+channel2.Name {
|
||||
t.Fatal("should have join cmd")
|
||||
}
|
||||
|
||||
rs3 := Client.Must(Client.Command("", "/join", true)).Data.(*model.Command)
|
||||
if len(rs3.Suggestions) != 2 {
|
||||
t.Fatal("should have 2 join cmd")
|
||||
}
|
||||
|
||||
rs4 := Client.Must(Client.Command("", "/join ", true)).Data.(*model.Command)
|
||||
if len(rs4.Suggestions) != 2 {
|
||||
t.Fatal("should have 2 join cmd")
|
||||
}
|
||||
|
||||
rs5 := Client.Must(Client.Command("", "/join "+channel2.Name, false)).Data.(*model.Command)
|
||||
if !strings.HasSuffix(rs5.GotoLocation, "/"+team.Name+"/channels/"+channel2.Name) {
|
||||
t.Fatal("failed to join channel")
|
||||
}
|
||||
|
||||
rs6 := Client.Must(Client.Command("", "/join "+channel3.Name, false)).Data.(*model.Command)
|
||||
if strings.HasSuffix(rs6.GotoLocation, "/"+team.Name+"/channels/"+channel3.Name) {
|
||||
t.Fatal("should not have joined direct message channel")
|
||||
}
|
||||
|
||||
c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
|
||||
|
||||
if len(c1.Channels) != 4 { // 4 because of town-square, off-topic and direct
|
||||
t.Fatal("didn't join channel")
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, c := range c1.Channels {
|
||||
if c.Name == channel2.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("didn't join channel")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEchoCommand(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user1.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
echoTestString := "/echo test"
|
||||
|
||||
r1 := Client.Must(Client.Command(channel1.Id, echoTestString, false)).Data.(*model.Command)
|
||||
if r1.Response != model.RESP_EXECUTED {
|
||||
t.Fatal("Echo command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
|
||||
if len(p1.Order) != 1 {
|
||||
t.Fatal("Echo command failed to send")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadTestUrlCommand(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
// enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
|
||||
enableTesting := utils.Cfg.ServiceSettings.EnableTesting
|
||||
enableCommands := *utils.Cfg.ServiceSettings.EnableCommands
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableTesting = enableTesting
|
||||
utils.Cfg.ServiceSettings.EnableCommands = &enableCommands
|
||||
}()
|
||||
*utils.Cfg.ServiceSettings.EnableCommands = true
|
||||
|
||||
utils.Cfg.ServiceSettings.EnableTesting = true
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
|
||||
|
||||
if _, err := Client.CreateCommand(cmd); err == nil {
|
||||
t.Fatal("should have failed because not admin")
|
||||
}
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
var rcmd *model.Command
|
||||
if result, err := Client.CreateCommand(cmd); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
rcmd = result.Data.(*model.Command)
|
||||
}
|
||||
|
||||
if rcmd.CreatorId != user.Id {
|
||||
t.Fatal("user ids didn't match")
|
||||
}
|
||||
|
||||
if rcmd.TeamId != team.Id {
|
||||
t.Fatal("team ids didn't match")
|
||||
}
|
||||
|
||||
cmd = &model.Command{CreatorId: "123", TeamId: "456", URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
|
||||
if result, err := Client.CreateCommand(cmd); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.Command).CreatorId != user.Id {
|
||||
t.Fatal("bad user id wasn't overwritten")
|
||||
}
|
||||
if result.Data.(*model.Command).TeamId != team.Id {
|
||||
t.Fatal("bad team id wasn't overwritten")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestListTeamCommands(t *testing.T) {
|
||||
Setup()
|
||||
enableCommands := *utils.Cfg.ServiceSettings.EnableCommands
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableCommands = &enableCommands
|
||||
}()
|
||||
*utils.Cfg.ServiceSettings.EnableCommands = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
@@ -204,39 +115,160 @@ func TestLoadTestUrlCommand(t *testing.T) {
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
channel := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
|
||||
cmd1 := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
|
||||
cmd1 = Client.Must(Client.CreateCommand(cmd1)).Data.(*model.Command)
|
||||
|
||||
command := "/loadtest url "
|
||||
if _, err := Client.Command(channel.Id, command, false); err == nil {
|
||||
t.Fatal("/loadtest url with no url should've failed")
|
||||
}
|
||||
if result, err := Client.ListTeamCommands(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
cmds := result.Data.([]*model.Command)
|
||||
|
||||
// command = "/loadtest url http://www.hopefullynonexistent.file/path/asdf/qwerty"
|
||||
// if _, err := Client.Command(channel.Id, command, false); err == nil {
|
||||
// t.Fatal("/loadtest url with invalid url should've failed")
|
||||
// }
|
||||
|
||||
command = "/loadtest url https://raw.githubusercontent.com/mattermost/platform/master/README.md"
|
||||
if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
|
||||
t.Fatal("/loadtest url for README.md should've executed")
|
||||
}
|
||||
|
||||
command = "/loadtest url test-emoticons.md"
|
||||
if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
|
||||
t.Fatal("/loadtest url for test-emoticons.md should've executed")
|
||||
}
|
||||
|
||||
command = "/loadtest url test-emoticons"
|
||||
if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
|
||||
t.Fatal("/loadtest url for test-emoticons should've executed")
|
||||
}
|
||||
|
||||
posts := Client.Must(Client.GetPosts(channel.Id, 0, 5, "")).Data.(*model.PostList)
|
||||
// note that this may make more than 3 posts if files are too long to fit in an individual post
|
||||
if len(posts.Order) < 3 {
|
||||
t.Fatal("/loadtest url made too few posts, perhaps there needs to be a delay before GetPosts in the test?")
|
||||
if len(cmds) != 1 {
|
||||
t.Fatal("incorrect number of cmd")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegenToken(t *testing.T) {
|
||||
Setup()
|
||||
enableCommands := *utils.Cfg.ServiceSettings.EnableCommands
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableCommands = &enableCommands
|
||||
}()
|
||||
*utils.Cfg.ServiceSettings.EnableCommands = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
|
||||
cmd = Client.Must(Client.CreateCommand(cmd)).Data.(*model.Command)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["id"] = cmd.Id
|
||||
|
||||
if result, err := Client.RegenCommandToken(data); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
if result.Data.(*model.Command).Token == cmd.Token {
|
||||
t.Fatal("regen didn't work properly")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteCommand(t *testing.T) {
|
||||
Setup()
|
||||
enableCommands := *utils.Cfg.ServiceSettings.EnableCommands
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableCommands = &enableCommands
|
||||
}()
|
||||
*utils.Cfg.ServiceSettings.EnableCommands = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
|
||||
cmd = Client.Must(Client.CreateCommand(cmd)).Data.(*model.Command)
|
||||
|
||||
data := make(map[string]string)
|
||||
data["id"] = cmd.Id
|
||||
|
||||
if _, err := Client.DeleteCommand(data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmds := Client.Must(Client.ListTeamCommands()).Data.([]*model.Command)
|
||||
if len(cmds) != 0 {
|
||||
t.Fatal("delete didn't work properly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestCommand(t *testing.T) {
|
||||
Setup()
|
||||
enableCommands := *utils.Cfg.ServiceSettings.EnableCommands
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableCommands = &enableCommands
|
||||
}()
|
||||
*utils.Cfg.ServiceSettings.EnableCommands = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
|
||||
user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
|
||||
|
||||
cmd1 := &model.Command{
|
||||
URL: "http://localhost" + utils.Cfg.ServiceSettings.ListenAddress + "/api/v1/commands/test",
|
||||
Method: model.COMMAND_METHOD_POST,
|
||||
Trigger: "test",
|
||||
}
|
||||
|
||||
cmd1 = Client.Must(Client.CreateCommand(cmd1)).Data.(*model.Command)
|
||||
|
||||
r1 := Client.Must(Client.Command(channel1.Id, "/test", false)).Data.(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Test command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
|
||||
if len(p1.Order) != 1 {
|
||||
t.Fatal("Test command failed to send")
|
||||
}
|
||||
|
||||
cmd2 := &model.Command{
|
||||
URL: "http://localhost" + utils.Cfg.ServiceSettings.ListenAddress + "/api/v1/commands/test",
|
||||
Method: model.COMMAND_METHOD_GET,
|
||||
Trigger: "test2",
|
||||
}
|
||||
|
||||
cmd2 = Client.Must(Client.CreateCommand(cmd2)).Data.(*model.Command)
|
||||
|
||||
r2 := Client.Must(Client.Command(channel1.Id, "/test2", false)).Data.(*model.CommandResponse)
|
||||
if r2 == nil {
|
||||
t.Fatal("Test2 command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
p2 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
|
||||
if len(p2.Order) != 2 {
|
||||
t.Fatal("Test command failed to send")
|
||||
}
|
||||
}
|
||||
|
||||
11
api/user.go
11
api/user.go
@@ -1542,6 +1542,10 @@ func PermanentDeleteUser(c *Context, user *model.User) *model.AppError {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Command().PermanentDeleteByUser(user.Id); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Preference().PermanentDeleteByUser(user.Id); result.Err != nil {
|
||||
return result.Err
|
||||
}
|
||||
@@ -2087,13 +2091,16 @@ func switchToSSO(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func CompleteSwitchWithOAuth(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.ReadCloser, team *model.Team, email string) {
|
||||
authData := ""
|
||||
ssoEmail := ""
|
||||
provider := einterfaces.GetOauthProvider(service)
|
||||
if provider == nil {
|
||||
c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.unavailable.app_error",
|
||||
map[string]interface{}{"Service": service}, "")
|
||||
return
|
||||
} else {
|
||||
authData = provider.GetAuthDataFromJson(userData)
|
||||
ssoUser := provider.GetUserFromJson(userData)
|
||||
authData = ssoUser.AuthData
|
||||
ssoEmail = ssoUser.Email
|
||||
}
|
||||
|
||||
if len(authData) == 0 {
|
||||
@@ -2120,7 +2127,7 @@ func CompleteSwitchWithOAuth(c *Context, w http.ResponseWriter, r *http.Request,
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.User().UpdateAuthData(user.Id, service, authData); result.Err != nil {
|
||||
if result := <-Srv.Store.User().UpdateAuthData(user.Id, service, authData, ssoEmail); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
}
|
||||
|
||||
@@ -32,6 +32,14 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
|
||||
if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
|
||||
c.Err = model.NewLocAppError("createIncomingHook", "api.command.admin_only.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
hook := model.IncomingWebhookFromJson(r.Body)
|
||||
@@ -79,6 +87,14 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
|
||||
if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
|
||||
c.Err = model.NewLocAppError("deleteIncomingHook", "api.command.admin_only.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
@@ -116,7 +132,15 @@ func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Webhook().GetIncomingByUser(c.Session.UserId); result.Err != nil {
|
||||
if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
|
||||
if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
|
||||
c.Err = model.NewLocAppError("getIncomingHooks", "api.command.admin_only.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Webhook().GetIncomingByTeam(c.Session.TeamId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
@@ -132,6 +156,14 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
|
||||
if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
|
||||
c.Err = model.NewLocAppError("createOutgoingHook", "api.command.admin_only.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
hook := model.OutgoingWebhookFromJson(r.Body)
|
||||
@@ -188,7 +220,15 @@ func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Webhook().GetOutgoingByCreator(c.Session.UserId); result.Err != nil {
|
||||
if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
|
||||
if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
|
||||
c.Err = model.NewLocAppError("getOutgoingHooks", "api.command.admin_only.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-Srv.Store.Webhook().GetOutgoingByTeam(c.Session.TeamId); result.Err != nil {
|
||||
c.Err = result.Err
|
||||
return
|
||||
} else {
|
||||
@@ -204,6 +244,14 @@ func deleteOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
|
||||
if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
|
||||
c.Err = model.NewLocAppError("deleteOutgoingHook", "api.command.admin_only.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
@@ -241,6 +289,14 @@ func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
|
||||
if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
|
||||
c.Err = model.NewLocAppError("regenOutgoingHookToken", "api.command.admin_only.app_error", nil, "")
|
||||
c.Err.StatusCode = http.StatusForbidden
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.LogAudit("attempt")
|
||||
|
||||
props := model.MapFromJson(r.Body)
|
||||
@@ -258,7 +314,7 @@ func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request)
|
||||
} else {
|
||||
hook = result.Data.(*model.OutgoingWebhook)
|
||||
|
||||
if c.Session.UserId != hook.CreatorId && !c.IsTeamAdmin() {
|
||||
if c.Session.TeamId != hook.TeamId && c.Session.UserId != hook.CreatorId && !c.IsTeamAdmin() {
|
||||
c.LogAudit("fail - inappropriate permissions")
|
||||
c.Err = model.NewLocAppError("regenOutgoingHookToken", "api.webhook.regen_outgoing_token.permissions.app_error", nil, "user_id="+c.Session.UserId)
|
||||
return
|
||||
|
||||
@@ -13,6 +13,14 @@ import (
|
||||
|
||||
func TestCreateIncomingHook(t *testing.T) {
|
||||
Setup()
|
||||
enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
|
||||
enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
|
||||
}()
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
@@ -21,6 +29,10 @@ func TestCreateIncomingHook(t *testing.T) {
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
@@ -76,6 +88,14 @@ func TestCreateIncomingHook(t *testing.T) {
|
||||
|
||||
func TestListIncomingHooks(t *testing.T) {
|
||||
Setup()
|
||||
enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
|
||||
enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
|
||||
}()
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
@@ -84,6 +104,10 @@ func TestListIncomingHooks(t *testing.T) {
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
@@ -114,6 +138,14 @@ func TestListIncomingHooks(t *testing.T) {
|
||||
|
||||
func TestDeleteIncomingHook(t *testing.T) {
|
||||
Setup()
|
||||
enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
|
||||
enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
|
||||
}()
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
@@ -122,6 +154,10 @@ func TestDeleteIncomingHook(t *testing.T) {
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
@@ -154,6 +190,14 @@ func TestDeleteIncomingHook(t *testing.T) {
|
||||
|
||||
func TestCreateOutgoingHook(t *testing.T) {
|
||||
Setup()
|
||||
enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
|
||||
enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
|
||||
}()
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
@@ -162,6 +206,10 @@ func TestCreateOutgoingHook(t *testing.T) {
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
@@ -217,6 +265,14 @@ func TestCreateOutgoingHook(t *testing.T) {
|
||||
|
||||
func TestListOutgoingHooks(t *testing.T) {
|
||||
Setup()
|
||||
enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
|
||||
enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
|
||||
}()
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
@@ -225,6 +281,10 @@ func TestListOutgoingHooks(t *testing.T) {
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
@@ -255,6 +315,14 @@ func TestListOutgoingHooks(t *testing.T) {
|
||||
|
||||
func TestDeleteOutgoingHook(t *testing.T) {
|
||||
Setup()
|
||||
enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
|
||||
enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
|
||||
}()
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
@@ -263,6 +331,10 @@ func TestDeleteOutgoingHook(t *testing.T) {
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
@@ -295,6 +367,14 @@ func TestDeleteOutgoingHook(t *testing.T) {
|
||||
|
||||
func TestRegenOutgoingHookToken(t *testing.T) {
|
||||
Setup()
|
||||
enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
|
||||
enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
|
||||
defer func() {
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
|
||||
}()
|
||||
utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
|
||||
utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
|
||||
|
||||
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
|
||||
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
|
||||
@@ -303,6 +383,10 @@ func TestRegenOutgoingHookToken(t *testing.T) {
|
||||
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
c := &Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
Client.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
"EnableOAuthServiceProvider": false,
|
||||
"EnableIncomingWebhooks": false,
|
||||
"EnableOutgoingWebhooks": false,
|
||||
"EnableCommands": false,
|
||||
"EnableOnlyAdminIntegrations": true,
|
||||
"EnablePostUsernameOverride": false,
|
||||
"EnablePostIconOverride": false,
|
||||
"EnableTesting": false,
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
"EnableOAuthServiceProvider": false,
|
||||
"EnableIncomingWebhooks": false,
|
||||
"EnableOutgoingWebhooks": false,
|
||||
"EnableCommands": false,
|
||||
"EnableOnlyAdminIntegrations": true,
|
||||
"EnablePostUsernameOverride": false,
|
||||
"EnablePostIconOverride": false,
|
||||
"EnableTesting": false,
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
"EnableOAuthServiceProvider": false,
|
||||
"EnableIncomingWebhooks": false,
|
||||
"EnableOutgoingWebhooks": false,
|
||||
"EnableCommands": false,
|
||||
"EnableOnlyAdminIntegrations": true,
|
||||
"EnablePostUsernameOverride": false,
|
||||
"EnablePostIconOverride": false,
|
||||
"EnableTesting": false,
|
||||
|
||||
278
i18n/en.json
278
i18n/en.json
@@ -244,121 +244,141 @@
|
||||
"translation": "Tried to perform an invalid update of the default channel {{.Channel}}"
|
||||
},
|
||||
{
|
||||
"id": "api.command.check_command.start.app_error",
|
||||
"translation": "Command must start with /"
|
||||
"id": "api.command.admin_only.app_error",
|
||||
"translation": "Integrations have been limited to admins only."
|
||||
},
|
||||
{
|
||||
"id": "api.command.echo_command.create.error",
|
||||
"translation": "Unable to create /echo post, err=%v"
|
||||
"id": "api.command.delete.app_error",
|
||||
"translation": "Inappropriate permissions to delete command"
|
||||
},
|
||||
{
|
||||
"id": "api.command.echo_command.description",
|
||||
"translation": "Echo back text from your account, /echo \"message\" [delay in seconds]"
|
||||
"id": "api.command.disabled.app_error",
|
||||
"translation": "Commands have been disabled by the system admin."
|
||||
},
|
||||
{
|
||||
"id": "api.command.echo_command.high_volume.app_error",
|
||||
"translation": "High volume of echo request, cannot process request"
|
||||
"id": "api.command.execute_command.debug",
|
||||
"translation": "Executing cmd=%v userId=%v"
|
||||
},
|
||||
{
|
||||
"id": "api.command.echo_command.under.app_error",
|
||||
"translation": "Delays must be under 10000 seconds"
|
||||
"id": "api.command.execute_command.failed.app_error",
|
||||
"translation": "Command with a trigger of '{{.Trigger}}' failed"
|
||||
},
|
||||
{
|
||||
"id": "api.command.execute_command.failed_empty.app_error",
|
||||
"translation": "Command with a trigger of '{{.Trigger}}' returned an empty response"
|
||||
},
|
||||
{
|
||||
"id": "api.command.execute_command.failed_resp.app_error",
|
||||
"translation": "Command with a trigger of '{{.Trigger}}' returned response {{.Status}}"
|
||||
},
|
||||
{
|
||||
"id": "api.command.execute_command.not_found.app_error",
|
||||
"translation": "Command with a trigger of '{{.Trigger}}' not found"
|
||||
},
|
||||
{
|
||||
"id": "api.command.execute_command.save.app_error",
|
||||
"translation": "An error while saving the command response to the channel"
|
||||
},
|
||||
{
|
||||
"id": "api.command.execute_command.start.app_error",
|
||||
"translation": "No command trigger found"
|
||||
},
|
||||
{
|
||||
"id": "api.command.init.debug",
|
||||
"translation": "Initializing command api routes"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_channels_command.channel.description",
|
||||
"translation": "Add a specified number of random channels to current team <MinChannels> <MaxChannels>"
|
||||
"id": "api.command.regen.app_error",
|
||||
"translation": "Inappropriate permissions to regenerate command token"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_channels_command.fuzz.description",
|
||||
"translation": "Add a specified number of random channels with fuzz text to current team <Min Channels> <Max Channels>"
|
||||
"id": "api.command_echo.create.app_error",
|
||||
"translation": "Unable to create /echo post, err=%v"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_command.description",
|
||||
"translation": "Debug Load Testing"
|
||||
"id": "api.command_echo.delay.app_error",
|
||||
"translation": "Delays must be under 10000 seconds"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_posts_command.fuzz.description",
|
||||
"translation": "Add some random posts with fuzz text to current channel <Min Posts> <Max Posts> <Min Images> <Max Images>"
|
||||
"id": "api.command_echo.desc",
|
||||
"translation": "Echo back text from your account"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_posts_command.posts.description",
|
||||
"translation": "Add some random posts to current channel <Min Posts> <Max Posts> <Min Images> <Max Images>"
|
||||
"id": "api.command_echo.high_volume.app_error",
|
||||
"translation": "High volume of echo request, cannot process request"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_setup_command.create.error",
|
||||
"translation": "Failed to create testing environment"
|
||||
"id": "api.command_echo.hint",
|
||||
"translation": "'message' [delay in seconds]"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_setup_command.created.info",
|
||||
"translation": "Team Created: %v"
|
||||
"id": "api.command_echo.name",
|
||||
"translation": "echo"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_setup_command.description",
|
||||
"translation": "Creates a testing environment in current team. [teams] [fuzz] <Num Channels> <Num Users> <NumPosts>"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_setup_command.login.info",
|
||||
"translation": "\t User to login: %v, %v"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_url_command.create.error",
|
||||
"translation": "Unable to create post, err=%v"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_url_command.description",
|
||||
"translation": "Add a post containing the text from a given url to current channel <Url>"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_url_command.file.app_error",
|
||||
"translation": "Unable to get file"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_url_command.reading.app_error",
|
||||
"translation": "Encountered error reading file"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_url_command.url.app_error",
|
||||
"translation": "Command must contain a url"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_users_command.fuzz.description",
|
||||
"translation": "Add a specified number of random users with fuzz text to current team <Min Users> <Max Users>"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_users_command.users.description",
|
||||
"translation": "Add a specified number of random users to current team <Min Users> <Max Users>"
|
||||
},
|
||||
{
|
||||
"id": "api.command.logout_command.description",
|
||||
"translation": "Logout"
|
||||
},
|
||||
{
|
||||
"id": "api.command.me_command.create.error",
|
||||
"translation": "Unable to create /me post post, err=%v"
|
||||
},
|
||||
{
|
||||
"id": "api.command.me_command.description",
|
||||
"translation": "Do an action, /me [message]"
|
||||
},
|
||||
{
|
||||
"id": "api.command.no_implemented.app_error",
|
||||
"translation": "Command not implemented"
|
||||
},
|
||||
{
|
||||
"id": "api.command.shrug_command.create.error",
|
||||
"translation": "Unable to create /shrug post post, err=%v"
|
||||
},
|
||||
{
|
||||
"id": "api.command.shrug_command.description",
|
||||
"translation": "Adds ¯\\_(ツ)_/¯ to your message, /shrug [message]"
|
||||
},
|
||||
{
|
||||
"id": "api.commmand.join_command.description",
|
||||
"id": "api.command_join.desc",
|
||||
"translation": "Join the open channel"
|
||||
},
|
||||
{
|
||||
"id": "api.command_join.fail.app_error",
|
||||
"translation": "An error occured while joining the channel."
|
||||
},
|
||||
{
|
||||
"id": "api.command_join.hint",
|
||||
"translation": "[channel-name]"
|
||||
},
|
||||
{
|
||||
"id": "api.command_join.list.app_error",
|
||||
"translation": "An error occured while listing channels."
|
||||
},
|
||||
{
|
||||
"id": "api.command_join.missing.app_error",
|
||||
"translation": "We couldn't find the channel"
|
||||
},
|
||||
{
|
||||
"id": "api.command_join.name",
|
||||
"translation": "join"
|
||||
},
|
||||
{
|
||||
"id": "api.command_join.success",
|
||||
"translation": "Joined channel."
|
||||
},
|
||||
{
|
||||
"id": "api.command_logout.desc",
|
||||
"translation": "Logout of Mattermost"
|
||||
},
|
||||
{
|
||||
"id": "api.command_logout.name",
|
||||
"translation": "logout"
|
||||
},
|
||||
{
|
||||
"id": "api.command_logout.success_message",
|
||||
"translation": "Logging out..."
|
||||
},
|
||||
{
|
||||
"id": "api.command_me.desc",
|
||||
"translation": "Do an action"
|
||||
},
|
||||
{
|
||||
"id": "api.command_me.hint",
|
||||
"translation": "[message]"
|
||||
},
|
||||
{
|
||||
"id": "api.command_me.name",
|
||||
"translation": "me"
|
||||
},
|
||||
{
|
||||
"id": "api.command_shrug.desc",
|
||||
"translation": "Adds ¯\\_(ツ)_/¯ to your message"
|
||||
},
|
||||
{
|
||||
"id": "api.command_shrug.hint",
|
||||
"translation": "[message]"
|
||||
},
|
||||
{
|
||||
"id": "api.command_shrug.name",
|
||||
"translation": "shrug"
|
||||
},
|
||||
{
|
||||
"id": "api.context.404.app_error",
|
||||
"translation": "Sorry, we could not find the page."
|
||||
@@ -1871,6 +1891,46 @@
|
||||
"id": "model.client.login.app_error",
|
||||
"translation": "Authentication tokens didn't match"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.create_at.app_error",
|
||||
"translation": "Create at must be a valid time"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.id.app_error",
|
||||
"translation": "Invalid Id"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.method.app_error",
|
||||
"translation": "Invalid Method"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.team_id.app_error",
|
||||
"translation": "Invalid team id"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.token.app_error",
|
||||
"translation": "Invalid token"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.trigger.app_error",
|
||||
"translation": "Invalid trigger"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.update_at.app_error",
|
||||
"translation": "Update at must be a valid time"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.url.app_error",
|
||||
"translation": "Invalid url"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.url_http.app_error",
|
||||
"translation": "Invalid URL. Must be a valid URL and start with http:// or https://"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.user_id.app_error",
|
||||
"translation": "Invalid user id"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.email_reset_salt.app_error",
|
||||
"translation": "Invalid password reset salt for email settings. Must be 32 chars or more."
|
||||
@@ -2555,6 +2615,34 @@
|
||||
"id": "store.sql_channel.update_member.app_error",
|
||||
"translation": "We encountered an error updating the channel member"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.delete.app_error",
|
||||
"translation": "We couldn't delete the command"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.delete_perm.app_error",
|
||||
"translation": "We couldn't delete the command"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.get.app_error",
|
||||
"translation": "We couldn't get the command"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.get_team.app_error",
|
||||
"translation": "We couldn't get the commands"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.saving.app_error",
|
||||
"translation": "We couldn't save the Command"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.saving_overwrite.app_error",
|
||||
"translation": "You cannot overwrite an existing Command"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.update.app_error",
|
||||
"translation": "We couldn't update the command"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_oauth.get_access_data.app_error",
|
||||
"translation": "We encountered an error finding the access token"
|
||||
@@ -3015,6 +3103,14 @@
|
||||
"id": "store.sql_user.verify_email.app_error",
|
||||
"translation": "Unable to update verify email field"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_webhooks.analytics_incoming_count.app_error",
|
||||
"translation": "We couldn't count the incoming webhooks"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_webhooks.analytics_outgoing_count.app_error",
|
||||
"translation": "We couldn't count the outgoing webhooks"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_webhooks.delete_incoming.app_error",
|
||||
"translation": "We couldn't delete the webhook"
|
||||
@@ -3043,10 +3139,6 @@
|
||||
"id": "store.sql_webhooks.get_outgoing_by_channel.app_error",
|
||||
"translation": "We couldn't get the webhooks"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_webhooks.get_outgoing_by_creator.app_error",
|
||||
"translation": "We couldn't get the webhooks"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_webhooks.get_outgoing_by_team.app_error",
|
||||
"translation": "We couldn't get the webhooks"
|
||||
@@ -3079,14 +3171,6 @@
|
||||
"id": "store.sql_webhooks.update_outgoing.app_error",
|
||||
"translation": "We couldn't update the webhook"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_webhooks.analytics_incoming_count.app_error",
|
||||
"translation": "We couldn't count the incoming webhooks"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_webhooks.analytics_outgoing_count.app_error",
|
||||
"translation": "We couldn't count the outgoing webhooks"
|
||||
},
|
||||
{
|
||||
"id": "utils.config.load_config.decoding.panic",
|
||||
"translation": "Error decoding config file={{.Filename}}, err={{.Error}}"
|
||||
|
||||
204
i18n/es.json
204
i18n/es.json
@@ -244,120 +244,140 @@
|
||||
"translation": "Intento de realizar una actualización inválida al canal predeterminado {{.Channel}}"
|
||||
},
|
||||
{
|
||||
"id": "api.command.check_command.start.app_error",
|
||||
"translation": "El comando debe comenzar con /"
|
||||
"id": "api.command.admin_only.app_error",
|
||||
"translation": "Las ingtegraciones solo pueden ser utilizadas por adminitradores."
|
||||
},
|
||||
{
|
||||
"id": "api.command.echo_command.create.error",
|
||||
"translation": "No se puede crear /echo mensaje, err=%v"
|
||||
"id": "api.command.delete.app_error",
|
||||
"translation": "Permisos inapropiados para utilizar el comando de borrar"
|
||||
},
|
||||
{
|
||||
"id": "api.command.echo_command.description",
|
||||
"translation": "Echo del texto desde tu cuenta, /echo \"mensaje\" [retraso en segundos]"
|
||||
"id": "api.command.disabled.app_error",
|
||||
"translation": "Los comandos han sido deshabilitados por el administrador de sistema."
|
||||
},
|
||||
{
|
||||
"id": "api.command.echo_command.high_volume.app_error",
|
||||
"translation": "Volumen alto de solicitudes echo, no se puede procesar la solicitud"
|
||||
"id": "api.command.execute_command.debug",
|
||||
"translation": "Ejecutando cmd=%v userId=%v"
|
||||
},
|
||||
{
|
||||
"id": "api.command.echo_command.under.app_error",
|
||||
"translation": "El retraso debe ser menor a 10000 segundos"
|
||||
"id": "api.command.execute_command.failed.app_error",
|
||||
"translation": "El Comando con el gatillador '{{.Trigger}}' falló"
|
||||
},
|
||||
{
|
||||
"id": "api.command.execute_command.failed_empty.app_error",
|
||||
"translation": "El Comando con el gatillador '{{.Trigger}}' retorno una respuesta vacía"
|
||||
},
|
||||
{
|
||||
"id": "api.command.execute_command.failed_resp.app_error",
|
||||
"translation": "El Comando con el gatillador '{{.Trigger}}' retorno la respuesta {{.Status}}"
|
||||
},
|
||||
{
|
||||
"id": "api.command.execute_command.not_found.app_error",
|
||||
"translation": "No se encontró el Comando con el gatillador '{{.Trigger}}'"
|
||||
},
|
||||
{
|
||||
"id": "api.command.execute_command.save.app_error",
|
||||
"translation": "Ocurrió un error mientras se guardaba la respuesta del comando en el canal"
|
||||
},
|
||||
{
|
||||
"id": "api.command.execute_command.start.app_error",
|
||||
"translation": "No se encontró un gatillador para el comando"
|
||||
},
|
||||
{
|
||||
"id": "api.command.init.debug",
|
||||
"translation": "Inicializando rutas del API para los comandos"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_channels_command.channel.description",
|
||||
"translation": "Agrega un número específico de canales aleatorios al equipo actual <MinChannels> <MaxChannels>"
|
||||
"id": "api.command.regen.app_error",
|
||||
"translation": "Permisos inapropiados para utilizar le comando de regenerar token"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_channels_command.fuzz.description",
|
||||
"translation": "Agrega un número específico de canales aleatorios con el texto fuzz al equipo actual <Min Channels> <Max Channels>"
|
||||
"id": "api.command_echo.create.app_error",
|
||||
"translation": "No se pudo crear el /echo mensaje, err=%v"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_command.description",
|
||||
"translation": "Depurar pruebas de carga"
|
||||
"id": "api.command_echo.delay.app_error",
|
||||
"translation": "El retraso debe ser menor a 10000 segundos"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_posts_command.fuzz.description",
|
||||
"translation": "Agrega algunos mensajes aleatorios con el texto fuzz al canal actual <Min Mensajes> <Max Mensajes> <Min Images> <Max Imágenes>"
|
||||
"id": "api.command_echo.desc",
|
||||
"translation": "Eco de vuelta un texto utilizando tu cuenta"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_posts_command.posts.description",
|
||||
"translation": "Agrega algunos mensajes aleatorios al canal actual <Min Mensajes> <Max Mensajes> <Min Images> <Max Imágenes>"
|
||||
"id": "api.command_echo.high_volume.app_error",
|
||||
"translation": "Demasiadas solicitudes de echo, no pudimos procesar la solicitud"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_setup_command.create.error",
|
||||
"translation": "Falla al crear el entorno de pruebas"
|
||||
"id": "api.command_echo.hint",
|
||||
"translation": "'mensaje' [retraso en segundos]"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_setup_command.created.info",
|
||||
"translation": "Equipo Creado: %v"
|
||||
"id": "api.command_echo.name",
|
||||
"translation": "echo"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_setup_command.description",
|
||||
"translation": "Crea un entorno de prueba para el equipo actual. [equipos] [fuzz] <Cant Canales> <Cant Usuarios> <Num Mensajes>"
|
||||
"id": "api.command_join.desc",
|
||||
"translation": "Unirte a un canal público"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_setup_command.login.info",
|
||||
"translation": "\t Usuario que inicia sesión: %v, %v"
|
||||
"id": "api.command_join.fail.app_error",
|
||||
"translation": "Ocurrió un error al unirse al canal."
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_url_command.create.error",
|
||||
"translation": "No se pudo crear el mensaje, err=%v"
|
||||
"id": "api.command_join.hint",
|
||||
"translation": "[nombre del canal]"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_url_command.description",
|
||||
"translation": "Agraga un mensaje que contiene el texto de un url dado al canal actual <Url>"
|
||||
"id": "api.command_join.list.app_error",
|
||||
"translation": "Ocurrió un error al listar los canales."
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_url_command.file.app_error",
|
||||
"translation": "No se puede obtener el archivo"
|
||||
"id": "api.command_join.missing.app_error",
|
||||
"translation": "No pudimos encontrar el canal"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_url_command.reading.app_error",
|
||||
"translation": "Se encontró un error leyendo el archivo"
|
||||
"id": "api.command_join.name",
|
||||
"translation": "unir"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_url_command.url.app_error",
|
||||
"translation": "El comando debe contener un url"
|
||||
"id": "api.command_join.success",
|
||||
"translation": "Te uniste al canal."
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_users_command.fuzz.description",
|
||||
"translation": "Agraga un número específico de usuarios aleatorios con el texto fuzz al equipo actual <Min Users> <Max Users>"
|
||||
"id": "api.command_logout.desc",
|
||||
"translation": "Salir de Mattermost"
|
||||
},
|
||||
{
|
||||
"id": "api.command.load_test_users_command.users.description",
|
||||
"translation": "Agrega un número específico de usuarios aleatorios al equipo actual <Min Users> <Max Users>"
|
||||
"id": "api.command_logout.name",
|
||||
"translation": "salir"
|
||||
},
|
||||
{
|
||||
"id": "api.command.logout_command.description",
|
||||
"translation": "Cerrar sesión"
|
||||
"id": "api.command_logout.success_message",
|
||||
"translation": "Cerrando sesión..."
|
||||
},
|
||||
{
|
||||
"id": "api.command.me_command.create.error",
|
||||
"translation": "No se pudo crear el mensaje /me mensaje, err=%v"
|
||||
"id": "api.command_me.desc",
|
||||
"translation": "Hacer una acción"
|
||||
},
|
||||
{
|
||||
"id": "api.command.me_command.description",
|
||||
"translation": "Realiza una acción, /me [mensaje]"
|
||||
"id": "api.command_me.hint",
|
||||
"translation": "[mensaje]"
|
||||
},
|
||||
{
|
||||
"id": "api.command.no_implemented.app_error",
|
||||
"translation": "Comando no implementado"
|
||||
"id": "api.command_me.name",
|
||||
"translation": "yo"
|
||||
},
|
||||
{
|
||||
"id": "api.command.shrug_command.create.error",
|
||||
"translation": "No se pudo crear el mensaje /shrug mensaje, err=%v"
|
||||
"id": "api.command_shrug.desc",
|
||||
"translation": "Agrega ¯\\_(ツ)_/¯ a tu mensaje"
|
||||
},
|
||||
{
|
||||
"id": "api.command.shrug_command.description",
|
||||
"translation": "Agrega ¯\\_(ツ)_/¯ a tu mensaje, /shrug [mensaje]"
|
||||
"id": "api.command_shrug.hint",
|
||||
"translation": "[mensaje]"
|
||||
},
|
||||
{
|
||||
"id": "api.commmand.join_command.description",
|
||||
"translation": "Unirme al canal abierto"
|
||||
"id": "api.command_shrug.name",
|
||||
"translation": "npi"
|
||||
},
|
||||
{
|
||||
"id": "api.context.404.app_error",
|
||||
@@ -1871,6 +1891,46 @@
|
||||
"id": "model.client.login.app_error",
|
||||
"translation": "Token de autenticación no coincidió"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.create_at.app_error",
|
||||
"translation": "Fecha de Creación debe ser válida"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.id.app_error",
|
||||
"translation": "Id inválido"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.method.app_error",
|
||||
"translation": "Método Inválido"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.team_id.app_error",
|
||||
"translation": "Id del equipo inválido"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.token.app_error",
|
||||
"translation": "Token inválido"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.trigger.app_error",
|
||||
"translation": "gatillador inválido"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.update_at.app_error",
|
||||
"translation": "Fecha de Actualización debe ser válida"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.url.app_error",
|
||||
"translation": "URL inválido"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.url_http.app_error",
|
||||
"translation": "URL inválido. Debe ser un URL válido y comenzar con http:// o https://"
|
||||
},
|
||||
{
|
||||
"id": "model.command.is_valid.user_id.app_error",
|
||||
"translation": "Id de usuario inválido"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.email_reset_salt.app_error",
|
||||
"translation": "Salt para restablecer contraseñas en la configuración de correos es inválido. Debe ser de 32 caracteres o más."
|
||||
@@ -2555,6 +2615,34 @@
|
||||
"id": "store.sql_channel.update_member.app_error",
|
||||
"translation": "Encontramos un error actualizando el miembro del canal"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.delete.app_error",
|
||||
"translation": "No pudimos eliminar el comando"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.delete_perm.app_error",
|
||||
"translation": "No pudimos eliminar el comando"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.get.app_error",
|
||||
"translation": "No pudimos obtener el comando"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.get_team.app_error",
|
||||
"translation": "No pudimos obtener los comandos"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.saving.app_error",
|
||||
"translation": "No pudimos guardar el Comando"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.saving_overwrite.app_error",
|
||||
"translation": "No puedes sobreescribir un comando existente"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_command.save.update.app_error",
|
||||
"translation": "No pudimos actualizar el comando"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_oauth.get_access_data.app_error",
|
||||
"translation": "Encontramos un error buscando el token de acceso"
|
||||
@@ -3051,10 +3139,6 @@
|
||||
"id": "store.sql_webhooks.get_outgoing_by_channel.app_error",
|
||||
"translation": "No pudimos obtener los webhooks"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_webhooks.get_outgoing_by_creator.app_error",
|
||||
"translation": "No pudimos obtener los webhooks"
|
||||
},
|
||||
{
|
||||
"id": "store.sql_webhooks.get_outgoing_by_team.app_error",
|
||||
"translation": "No pudimos obtener los webhooks"
|
||||
|
||||
@@ -380,7 +380,34 @@ func (c *Client) Command(channelId string, command string, suggest bool) (*Resul
|
||||
m["command"] = command
|
||||
m["channelId"] = channelId
|
||||
m["suggest"] = strconv.FormatBool(suggest)
|
||||
if r, err := c.DoApiPost("/command", MapToJson(m)); err != nil {
|
||||
if r, err := c.DoApiPost("/commands/execute", MapToJson(m)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), CommandResponseFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) ListCommands() (*Result, *AppError) {
|
||||
if r, err := c.DoApiGet("/commands/list", "", ""); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), CommandListFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) ListTeamCommands() (*Result, *AppError) {
|
||||
if r, err := c.DoApiGet("/commands/list_team_commands", "", ""); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), CommandListFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) CreateCommand(cmd *Command) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost("/commands/create", cmd.ToJson()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
@@ -388,6 +415,24 @@ func (c *Client) Command(channelId string, command string, suggest bool) (*Resul
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) RegenCommandToken(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost("/commands/regen_token", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), CommandFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) DeleteCommand(data map[string]string) (*Result, *AppError) {
|
||||
if r, err := c.DoApiPost("/commands/delete", MapToJson(data)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return &Result{r.Header.Get(HEADER_REQUEST_ID),
|
||||
r.Header.Get(HEADER_ETAG_SERVER), MapFromJson(r.Body)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) GetAudits(id string, etag string) (*Result, *AppError) {
|
||||
if r, err := c.DoApiGet("/users/"+id+"/audits", "", etag); err != nil {
|
||||
return nil, err
|
||||
|
||||
130
model/command.go
130
model/command.go
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
@@ -9,28 +9,27 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
RESP_EXECUTED = "executed"
|
||||
RESP_NOT_IMPLEMENTED = "not implemented"
|
||||
COMMAND_METHOD_POST = "P"
|
||||
COMMAND_METHOD_GET = "G"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
Command string `json:"command"`
|
||||
Response string `json:"response"`
|
||||
GotoLocation string `json:"goto_location"`
|
||||
ChannelId string `json:"channel_id"`
|
||||
Suggest bool `json:"-"`
|
||||
Suggestions []*SuggestCommand `json:"suggestions"`
|
||||
}
|
||||
|
||||
func (o *Command) AddSuggestion(suggest *SuggestCommand) {
|
||||
|
||||
if o.Suggest {
|
||||
if o.Suggestions == nil {
|
||||
o.Suggestions = make([]*SuggestCommand, 0, 128)
|
||||
}
|
||||
|
||||
o.Suggestions = append(o.Suggestions, suggest)
|
||||
}
|
||||
Id string `json:"id"`
|
||||
Token string `json:"token"`
|
||||
CreateAt int64 `json:"create_at"`
|
||||
UpdateAt int64 `json:"update_at"`
|
||||
DeleteAt int64 `json:"delete_at"`
|
||||
CreatorId string `json:"creator_id"`
|
||||
TeamId string `json:"team_id"`
|
||||
Trigger string `json:"trigger"`
|
||||
Method string `json:"method"`
|
||||
Username string `json:"username"`
|
||||
IconURL string `json:"icon_url"`
|
||||
AutoComplete bool `json:"auto_complete"`
|
||||
AutoCompleteDesc string `json:"auto_complete_desc"`
|
||||
AutoCompleteHint string `json:"auto_complete_hint"`
|
||||
DisplayName string `json:"display_name"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
func (o *Command) ToJson() string {
|
||||
@@ -52,3 +51,94 @@ func CommandFromJson(data io.Reader) *Command {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func CommandListToJson(l []*Command) string {
|
||||
b, err := json.Marshal(l)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func CommandListFromJson(data io.Reader) []*Command {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o []*Command
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Command) IsValid() *AppError {
|
||||
|
||||
if len(o.Id) != 26 {
|
||||
return NewLocAppError("Command.IsValid", "model.command.is_valid.id.app_error", nil, "")
|
||||
}
|
||||
|
||||
if len(o.Token) != 26 {
|
||||
return NewLocAppError("Command.IsValid", "model.command.is_valid.token.app_error", nil, "")
|
||||
}
|
||||
|
||||
if o.CreateAt == 0 {
|
||||
return NewLocAppError("Command.IsValid", "model.command.is_valid.create_at.app_error", nil, "")
|
||||
}
|
||||
|
||||
if o.UpdateAt == 0 {
|
||||
return NewLocAppError("Command.IsValid", "model.command.is_valid.update_at.app_error", nil, "")
|
||||
}
|
||||
|
||||
if len(o.CreatorId) != 26 {
|
||||
return NewLocAppError("Command.IsValid", "model.command.is_valid.user_id.app_error", nil, "")
|
||||
}
|
||||
|
||||
if len(o.TeamId) != 26 {
|
||||
return NewLocAppError("Command.IsValid", "model.command.is_valid.team_id.app_error", nil, "")
|
||||
}
|
||||
|
||||
if len(o.Trigger) > 1024 {
|
||||
return NewLocAppError("Command.IsValid", "model.command.is_valid.trigger.app_error", nil, "")
|
||||
}
|
||||
|
||||
if len(o.URL) == 0 || len(o.URL) > 1024 {
|
||||
return NewLocAppError("Command.IsValid", "model.command.is_valid.url.app_error", nil, "")
|
||||
}
|
||||
|
||||
if !IsValidHttpUrl(o.URL) {
|
||||
return NewLocAppError("Command.IsValid", "model.command.is_valid.url_http.app_error", nil, "")
|
||||
}
|
||||
|
||||
if !(o.Method == COMMAND_METHOD_GET || o.Method == COMMAND_METHOD_POST) {
|
||||
return NewLocAppError("Command.IsValid", "model.command.is_valid.method.app_error", nil, "")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Command) PreSave() {
|
||||
if o.Id == "" {
|
||||
o.Id = NewId()
|
||||
}
|
||||
|
||||
if o.Token == "" {
|
||||
o.Token = NewId()
|
||||
}
|
||||
|
||||
o.CreateAt = GetMillis()
|
||||
o.UpdateAt = o.CreateAt
|
||||
}
|
||||
|
||||
func (o *Command) PreUpdate() {
|
||||
o.UpdateAt = GetMillis()
|
||||
}
|
||||
|
||||
func (o *Command) Sanitize() {
|
||||
o.Token = ""
|
||||
o.CreatorId = ""
|
||||
o.Method = ""
|
||||
o.URL = ""
|
||||
o.Username = ""
|
||||
o.IconURL = ""
|
||||
}
|
||||
|
||||
41
model/command_response.go
Обычный файл
41
model/command_response.go
Обычный файл
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
COMMAND_RESPONSE_TYPE_IN_CHANNEL = "in_channel"
|
||||
COMMAND_RESPONSE_TYPE_EPHEMERAL = "ephemeral"
|
||||
)
|
||||
|
||||
type CommandResponse struct {
|
||||
ResponseType string `json:"response_type"`
|
||||
Text string `json:"text"`
|
||||
GotoLocation string `json:"goto_location"`
|
||||
Attachments interface{} `json:"attachments"`
|
||||
}
|
||||
|
||||
func (o *CommandResponse) ToJson() string {
|
||||
b, err := json.Marshal(o)
|
||||
if err != nil {
|
||||
return ""
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func CommandResponseFromJson(data io.Reader) *CommandResponse {
|
||||
decoder := json.NewDecoder(data)
|
||||
var o CommandResponse
|
||||
err := decoder.Decode(&o)
|
||||
if err == nil {
|
||||
return &o
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
19
model/command_response_test.go
Обычный файл
19
model/command_response_test.go
Обычный файл
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandResponseJson(t *testing.T) {
|
||||
o := CommandResponse{Text: "test"}
|
||||
json := o.ToJson()
|
||||
ro := CommandResponseFromJson(strings.NewReader(json))
|
||||
|
||||
if o.Text != ro.Text {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package model
|
||||
@@ -9,17 +9,89 @@ import (
|
||||
)
|
||||
|
||||
func TestCommandJson(t *testing.T) {
|
||||
o := Command{Id: NewId()}
|
||||
json := o.ToJson()
|
||||
ro := CommandFromJson(strings.NewReader(json))
|
||||
|
||||
command := &Command{Command: NewId(), Suggest: true}
|
||||
command.AddSuggestion(&SuggestCommand{Suggestion: NewId()})
|
||||
json := command.ToJson()
|
||||
result := CommandFromJson(strings.NewReader(json))
|
||||
|
||||
if command.Command != result.Command {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
|
||||
if command.Suggestions[0].Suggestion != result.Suggestions[0].Suggestion {
|
||||
if o.Id != ro.Id {
|
||||
t.Fatal("Ids do not match")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandIsValid(t *testing.T) {
|
||||
o := Command{}
|
||||
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Id = NewId()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.CreateAt = GetMillis()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.UpdateAt = GetMillis()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.CreatorId = "123"
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.CreatorId = NewId()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Token = "123"
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Token = NewId()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.TeamId = "123"
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.TeamId = NewId()
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.URL = "nowhere.com/"
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.URL = "http://nowhere.com/"
|
||||
if err := o.IsValid(); err == nil {
|
||||
t.Fatal("should be invalid")
|
||||
}
|
||||
|
||||
o.Method = COMMAND_METHOD_GET
|
||||
if err := o.IsValid(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandPreSave(t *testing.T) {
|
||||
o := Command{}
|
||||
o.PreSave()
|
||||
}
|
||||
|
||||
func TestCommandPreUpdate(t *testing.T) {
|
||||
o := Command{}
|
||||
o.PreUpdate()
|
||||
}
|
||||
|
||||
@@ -24,24 +24,26 @@ const (
|
||||
)
|
||||
|
||||
type ServiceSettings struct {
|
||||
ListenAddress string
|
||||
MaximumLoginAttempts int
|
||||
SegmentDeveloperKey string
|
||||
GoogleDeveloperKey string
|
||||
EnableOAuthServiceProvider bool
|
||||
EnableIncomingWebhooks bool
|
||||
EnableOutgoingWebhooks bool
|
||||
EnablePostUsernameOverride bool
|
||||
EnablePostIconOverride bool
|
||||
EnableTesting bool
|
||||
EnableDeveloper *bool
|
||||
EnableSecurityFixAlert *bool
|
||||
SessionLengthWebInDays *int
|
||||
SessionLengthMobileInDays *int
|
||||
SessionLengthSSOInDays *int
|
||||
SessionCacheInMinutes *int
|
||||
WebsocketSecurePort *int
|
||||
WebsocketPort *int
|
||||
ListenAddress string
|
||||
MaximumLoginAttempts int
|
||||
SegmentDeveloperKey string
|
||||
GoogleDeveloperKey string
|
||||
EnableOAuthServiceProvider bool
|
||||
EnableIncomingWebhooks bool
|
||||
EnableOutgoingWebhooks bool
|
||||
EnableCommands *bool
|
||||
EnableOnlyAdminIntegrations *bool
|
||||
EnablePostUsernameOverride bool
|
||||
EnablePostIconOverride bool
|
||||
EnableTesting bool
|
||||
EnableDeveloper *bool
|
||||
EnableSecurityFixAlert *bool
|
||||
SessionLengthWebInDays *int
|
||||
SessionLengthMobileInDays *int
|
||||
SessionLengthSSOInDays *int
|
||||
SessionCacheInMinutes *int
|
||||
WebsocketSecurePort *int
|
||||
WebsocketPort *int
|
||||
}
|
||||
|
||||
type SSOSettings struct {
|
||||
@@ -349,10 +351,22 @@ func (o *Config) SetDefaults() {
|
||||
o.ServiceSettings.SessionCacheInMinutes = new(int)
|
||||
*o.ServiceSettings.SessionCacheInMinutes = 10
|
||||
}
|
||||
|
||||
if o.ServiceSettings.EnableCommands == nil {
|
||||
o.ServiceSettings.EnableCommands = new(bool)
|
||||
*o.ServiceSettings.EnableCommands = false
|
||||
}
|
||||
|
||||
if o.ServiceSettings.EnableOnlyAdminIntegrations == nil {
|
||||
o.ServiceSettings.EnableOnlyAdminIntegrations = new(bool)
|
||||
*o.ServiceSettings.EnableOnlyAdminIntegrations = true
|
||||
}
|
||||
|
||||
if o.ServiceSettings.WebsocketPort == nil {
|
||||
o.ServiceSettings.WebsocketPort = new(int)
|
||||
*o.ServiceSettings.WebsocketPort = 80
|
||||
}
|
||||
|
||||
if o.ServiceSettings.WebsocketSecurePort == nil {
|
||||
o.ServiceSettings.WebsocketSecurePort = new(int)
|
||||
*o.ServiceSettings.WebsocketSecurePort = 443
|
||||
|
||||
173
store/sql_command_store.go
Обычный файл
173
store/sql_command_store.go
Обычный файл
@@ -0,0 +1,173 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type SqlCommandStore struct {
|
||||
*SqlStore
|
||||
}
|
||||
|
||||
func NewSqlCommandStore(sqlStore *SqlStore) CommandStore {
|
||||
s := &SqlCommandStore{sqlStore}
|
||||
|
||||
for _, db := range sqlStore.GetAllConns() {
|
||||
tableo := db.AddTableWithName(model.Command{}, "Commands").SetKeys(false, "Id")
|
||||
tableo.ColMap("Id").SetMaxSize(26)
|
||||
tableo.ColMap("Token").SetMaxSize(26)
|
||||
tableo.ColMap("CreatorId").SetMaxSize(26)
|
||||
tableo.ColMap("TeamId").SetMaxSize(26)
|
||||
tableo.ColMap("Trigger").SetMaxSize(128)
|
||||
tableo.ColMap("URL").SetMaxSize(1024)
|
||||
tableo.ColMap("Method").SetMaxSize(1)
|
||||
tableo.ColMap("Username").SetMaxSize(64)
|
||||
tableo.ColMap("IconURL").SetMaxSize(1024)
|
||||
tableo.ColMap("AutoCompleteDesc").SetMaxSize(1024)
|
||||
tableo.ColMap("AutoCompleteHint").SetMaxSize(1024)
|
||||
tableo.ColMap("DisplayName").SetMaxSize(64)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) UpgradeSchemaIfNeeded() {
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) CreateIndexesIfNotExists() {
|
||||
s.CreateIndexIfNotExists("idx_command_team_id", "Commands", "TeamId")
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) Save(command *model.Command) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
if len(command.Id) > 0 {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.Save", "store.sql_command.save.saving_overwrite.app_error", nil, "id="+command.Id)
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
command.PreSave()
|
||||
if result.Err = command.IsValid(); result.Err != nil {
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.GetMaster().Insert(command); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.Save", "store.sql_command.save.saving.app_error", nil, "id="+command.Id+", "+err.Error())
|
||||
} else {
|
||||
result.Data = command
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) Get(id string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var command model.Command
|
||||
|
||||
if err := s.GetReplica().SelectOne(&command, "SELECT * FROM Commands WHERE Id = :Id AND DeleteAt = 0", map[string]interface{}{"Id": id}); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.Get", "store.sql_command.save.get.app_error", nil, "id="+id+", err="+err.Error())
|
||||
}
|
||||
|
||||
result.Data = &command
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) GetByTeam(teamId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var commands []*model.Command
|
||||
|
||||
if _, err := s.GetReplica().Select(&commands, "SELECT * FROM Commands WHERE TeamId = :TeamId AND DeleteAt = 0", map[string]interface{}{"TeamId": teamId}); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.GetByTeam", "store.sql_command.save.get_team.app_error", nil, "teamId="+teamId+", err="+err.Error())
|
||||
}
|
||||
|
||||
result.Data = commands
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) Delete(commandId string, time int64) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
_, err := s.GetMaster().Exec("Update Commands SET DeleteAt = :DeleteAt, UpdateAt = :UpdateAt WHERE Id = :Id", map[string]interface{}{"DeleteAt": time, "UpdateAt": time, "Id": commandId})
|
||||
if err != nil {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.Delete", "store.sql_command.save.delete.app_error", nil, "id="+commandId+", err="+err.Error())
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) PermanentDeleteByUser(userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
_, err := s.GetMaster().Exec("DELETE FROM Commands WHERE CreatorId = :UserId", map[string]interface{}{"UserId": userId})
|
||||
if err != nil {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.DeleteByUser", "store.sql_command.save.delete_perm.app_error", nil, "id="+userId+", err="+err.Error())
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlCommandStore) Update(hook *model.Command) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
hook.UpdateAt = model.GetMillis()
|
||||
|
||||
if _, err := s.GetMaster().Update(hook); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlCommandStore.Update", "store.sql_command.save.update.app_error", nil, "id="+hook.Id+", "+err.Error())
|
||||
} else {
|
||||
result.Data = hook
|
||||
}
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
155
store/sql_command_store_test.go
Обычный файл
155
store/sql_command_store_test.go
Обычный файл
@@ -0,0 +1,155 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package store
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandStoreSave(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := model.Command{}
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.Method = model.COMMAND_METHOD_POST
|
||||
o1.TeamId = model.NewId()
|
||||
o1.URL = "http://nowhere.com/"
|
||||
|
||||
if err := (<-store.Command().Save(&o1)).Err; err != nil {
|
||||
t.Fatal("couldn't save item", err)
|
||||
}
|
||||
|
||||
if err := (<-store.Command().Save(&o1)).Err; err == nil {
|
||||
t.Fatal("shouldn't be able to update from save")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandStoreGet(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Command{}
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.Method = model.COMMAND_METHOD_POST
|
||||
o1.TeamId = model.NewId()
|
||||
o1.URL = "http://nowhere.com/"
|
||||
|
||||
o1 = (<-store.Command().Save(o1)).Data.(*model.Command)
|
||||
|
||||
if r1 := <-store.Command().Get(o1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.Command).CreateAt != o1.CreateAt {
|
||||
t.Fatal("invalid returned command")
|
||||
}
|
||||
}
|
||||
|
||||
if err := (<-store.Command().Get("123")).Err; err == nil {
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandStoreGetByTeam(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Command{}
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.Method = model.COMMAND_METHOD_POST
|
||||
o1.TeamId = model.NewId()
|
||||
o1.URL = "http://nowhere.com/"
|
||||
|
||||
o1 = (<-store.Command().Save(o1)).Data.(*model.Command)
|
||||
|
||||
if r1 := <-store.Command().GetByTeam(o1.TeamId); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.([]*model.Command)[0].CreateAt != o1.CreateAt {
|
||||
t.Fatal("invalid returned command")
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-store.Command().GetByTeam("123"); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
} else {
|
||||
if len(result.Data.([]*model.Command)) != 0 {
|
||||
t.Fatal("no commands should have returned")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandStoreDelete(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Command{}
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.Method = model.COMMAND_METHOD_POST
|
||||
o1.TeamId = model.NewId()
|
||||
o1.URL = "http://nowhere.com/"
|
||||
|
||||
o1 = (<-store.Command().Save(o1)).Data.(*model.Command)
|
||||
|
||||
if r1 := <-store.Command().Get(o1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.Command).CreateAt != o1.CreateAt {
|
||||
t.Fatal("invalid returned command")
|
||||
}
|
||||
}
|
||||
|
||||
if r2 := <-store.Command().Delete(o1.Id, model.GetMillis()); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
}
|
||||
|
||||
if r3 := (<-store.Command().Get(o1.Id)); r3.Err == nil {
|
||||
t.Log(r3.Data)
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandStoreDeleteByUser(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Command{}
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.Method = model.COMMAND_METHOD_POST
|
||||
o1.TeamId = model.NewId()
|
||||
o1.URL = "http://nowhere.com/"
|
||||
|
||||
o1 = (<-store.Command().Save(o1)).Data.(*model.Command)
|
||||
|
||||
if r1 := <-store.Command().Get(o1.Id); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.(*model.Command).CreateAt != o1.CreateAt {
|
||||
t.Fatal("invalid returned command")
|
||||
}
|
||||
}
|
||||
|
||||
if r2 := <-store.Command().PermanentDeleteByUser(o1.CreatorId); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
}
|
||||
|
||||
if r3 := (<-store.Command().Get(o1.Id)); r3.Err == nil {
|
||||
t.Log(r3.Data)
|
||||
t.Fatal("Missing id should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandStoreUpdate(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.Command{}
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.Method = model.COMMAND_METHOD_POST
|
||||
o1.TeamId = model.NewId()
|
||||
o1.URL = "http://nowhere.com/"
|
||||
|
||||
o1 = (<-store.Command().Save(o1)).Data.(*model.Command)
|
||||
|
||||
o1.Token = model.NewId()
|
||||
|
||||
if r2 := <-store.Command().Update(o1); r2.Err != nil {
|
||||
t.Fatal(r2.Err)
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@ type SqlStore struct {
|
||||
oauth OAuthStore
|
||||
system SystemStore
|
||||
webhook WebhookStore
|
||||
command CommandStore
|
||||
preference PreferenceStore
|
||||
}
|
||||
|
||||
@@ -100,6 +101,7 @@ func NewSqlStore() Store {
|
||||
sqlStore.oauth = NewSqlOAuthStore(sqlStore)
|
||||
sqlStore.system = NewSqlSystemStore(sqlStore)
|
||||
sqlStore.webhook = NewSqlWebhookStore(sqlStore)
|
||||
sqlStore.command = NewSqlCommandStore(sqlStore)
|
||||
sqlStore.preference = NewSqlPreferenceStore(sqlStore)
|
||||
|
||||
err := sqlStore.master.CreateTablesIfNotExists()
|
||||
@@ -116,6 +118,7 @@ func NewSqlStore() Store {
|
||||
sqlStore.oauth.(*SqlOAuthStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.system.(*SqlSystemStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.webhook.(*SqlWebhookStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.command.(*SqlCommandStore).UpgradeSchemaIfNeeded()
|
||||
sqlStore.preference.(*SqlPreferenceStore).UpgradeSchemaIfNeeded()
|
||||
|
||||
sqlStore.team.(*SqlTeamStore).CreateIndexesIfNotExists()
|
||||
@@ -127,6 +130,7 @@ func NewSqlStore() Store {
|
||||
sqlStore.oauth.(*SqlOAuthStore).CreateIndexesIfNotExists()
|
||||
sqlStore.system.(*SqlSystemStore).CreateIndexesIfNotExists()
|
||||
sqlStore.webhook.(*SqlWebhookStore).CreateIndexesIfNotExists()
|
||||
sqlStore.command.(*SqlCommandStore).CreateIndexesIfNotExists()
|
||||
sqlStore.preference.(*SqlPreferenceStore).CreateIndexesIfNotExists()
|
||||
|
||||
sqlStore.preference.(*SqlPreferenceStore).DeleteUnusedFeatures()
|
||||
@@ -511,6 +515,10 @@ func (ss SqlStore) Webhook() WebhookStore {
|
||||
return ss.webhook
|
||||
}
|
||||
|
||||
func (ss SqlStore) Command() CommandStore {
|
||||
return ss.command
|
||||
}
|
||||
|
||||
func (ss SqlStore) Preference() PreferenceStore {
|
||||
return ss.preference
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ func (us SqlUserStore) UpdateFailedPasswordAttempts(userId string, attempts int)
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (us SqlUserStore) UpdateAuthData(userId, service, authData string) StoreChannel {
|
||||
func (us SqlUserStore) UpdateAuthData(userId, service, authData, email string) StoreChannel {
|
||||
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
@@ -314,7 +314,24 @@ func (us SqlUserStore) UpdateAuthData(userId, service, authData string) StoreCha
|
||||
|
||||
updateAt := model.GetMillis()
|
||||
|
||||
if _, err := us.GetMaster().Exec("UPDATE Users SET Password = '', LastPasswordUpdate = :LastPasswordUpdate, UpdateAt = :UpdateAt, FailedAttempts = 0, AuthService = :AuthService, AuthData = :AuthData WHERE Id = :UserId", map[string]interface{}{"LastPasswordUpdate": updateAt, "UpdateAt": updateAt, "UserId": userId, "AuthService": service, "AuthData": authData}); err != nil {
|
||||
query := `
|
||||
UPDATE
|
||||
Users
|
||||
SET
|
||||
Password = '',
|
||||
LastPasswordUpdate = :LastPasswordUpdate,
|
||||
UpdateAt = :UpdateAt,
|
||||
FailedAttempts = 0,
|
||||
AuthService = :AuthService,
|
||||
AuthData = :AuthData`
|
||||
|
||||
if len(email) != 0 {
|
||||
query += ", Email = :Email"
|
||||
}
|
||||
|
||||
query += " WHERE Id = :UserId"
|
||||
|
||||
if _, err := us.GetMaster().Exec(query, map[string]interface{}{"LastPasswordUpdate": updateAt, "UpdateAt": updateAt, "UserId": userId, "AuthService": service, "AuthData": authData, "Email": email}); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlUserStore.UpdateAuthData", "store.sql_user.update_auth_data.app_error", nil, "id="+userId+", "+err.Error())
|
||||
} else {
|
||||
result.Data = userId
|
||||
|
||||
@@ -402,7 +402,7 @@ func TestUserStoreUpdateAuthData(t *testing.T) {
|
||||
service := "someservice"
|
||||
authData := "1"
|
||||
|
||||
if err := (<-store.User().UpdateAuthData(u1.Id, service, authData)).Err; err != nil {
|
||||
if err := (<-store.User().UpdateAuthData(u1.Id, service, authData, "")).Err; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ func (s SqlWebhookStore) PermanentDeleteIncomingByUser(userId string) StoreChann
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlWebhookStore) GetIncomingByUser(userId string) StoreChannel {
|
||||
func (s SqlWebhookStore) GetIncomingByTeam(teamId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
@@ -142,8 +142,8 @@ func (s SqlWebhookStore) GetIncomingByUser(userId string) StoreChannel {
|
||||
|
||||
var webhooks []*model.IncomingWebhook
|
||||
|
||||
if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE UserId = :UserId AND DeleteAt = 0", map[string]interface{}{"UserId": userId}); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlWebhookStore.GetIncomingByUser", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "userId="+userId+", err="+err.Error())
|
||||
if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM IncomingWebhooks WHERE TeamId = :TeamId AND DeleteAt = 0", map[string]interface{}{"TeamId": teamId}); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlWebhookStore.GetIncomingByUser", "store.sql_webhooks.get_incoming_by_user.app_error", nil, "teamId="+teamId+", err="+err.Error())
|
||||
}
|
||||
|
||||
result.Data = webhooks
|
||||
@@ -231,27 +231,6 @@ func (s SqlWebhookStore) GetOutgoing(id string) StoreChannel {
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlWebhookStore) GetOutgoingByCreator(userId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
go func() {
|
||||
result := StoreResult{}
|
||||
|
||||
var webhooks []*model.OutgoingWebhook
|
||||
|
||||
if _, err := s.GetReplica().Select(&webhooks, "SELECT * FROM OutgoingWebhooks WHERE CreatorId = :UserId AND DeleteAt = 0", map[string]interface{}{"UserId": userId}); err != nil {
|
||||
result.Err = model.NewLocAppError("SqlWebhookStore.GetOutgoingByCreator", "store.sql_webhooks.get_outgoing_by_creator.app_error", nil, "userId="+userId+", err="+err.Error())
|
||||
}
|
||||
|
||||
result.Data = webhooks
|
||||
|
||||
storeChannel <- result
|
||||
close(storeChannel)
|
||||
}()
|
||||
|
||||
return storeChannel
|
||||
}
|
||||
|
||||
func (s SqlWebhookStore) GetOutgoingByChannel(channelId string) StoreChannel {
|
||||
storeChannel := make(StoreChannel)
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestWebhookStoreGetIncoming(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebhookStoreGetIncomingByUser(t *testing.T) {
|
||||
func TestWebhookStoreGetIncomingByTeam(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.IncomingWebhook{}
|
||||
@@ -58,7 +58,7 @@ func TestWebhookStoreGetIncomingByUser(t *testing.T) {
|
||||
|
||||
o1 = (<-store.Webhook().SaveIncoming(o1)).Data.(*model.IncomingWebhook)
|
||||
|
||||
if r1 := <-store.Webhook().GetIncomingByUser(o1.UserId); r1.Err != nil {
|
||||
if r1 := <-store.Webhook().GetIncomingByTeam(o1.TeamId); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.([]*model.IncomingWebhook)[0].CreateAt != o1.CreateAt {
|
||||
@@ -66,7 +66,7 @@ func TestWebhookStoreGetIncomingByUser(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-store.Webhook().GetIncomingByUser("123"); result.Err != nil {
|
||||
if result := <-store.Webhook().GetIncomingByTeam("123"); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
} else {
|
||||
if len(result.Data.([]*model.IncomingWebhook)) != 0 {
|
||||
@@ -201,34 +201,6 @@ func TestWebhookStoreGetOutgoingByChannel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebhookStoreGetOutgoingByCreator(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
o1 := &model.OutgoingWebhook{}
|
||||
o1.ChannelId = model.NewId()
|
||||
o1.CreatorId = model.NewId()
|
||||
o1.TeamId = model.NewId()
|
||||
o1.CallbackURLs = []string{"http://nowhere.com/"}
|
||||
|
||||
o1 = (<-store.Webhook().SaveOutgoing(o1)).Data.(*model.OutgoingWebhook)
|
||||
|
||||
if r1 := <-store.Webhook().GetOutgoingByCreator(o1.CreatorId); r1.Err != nil {
|
||||
t.Fatal(r1.Err)
|
||||
} else {
|
||||
if r1.Data.([]*model.OutgoingWebhook)[0].CreateAt != o1.CreateAt {
|
||||
t.Fatal("invalid returned webhook")
|
||||
}
|
||||
}
|
||||
|
||||
if result := <-store.Webhook().GetOutgoingByCreator("123"); result.Err != nil {
|
||||
t.Fatal(result.Err)
|
||||
} else {
|
||||
if len(result.Data.([]*model.OutgoingWebhook)) != 0 {
|
||||
t.Fatal("no webhooks should have returned")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebhookStoreGetOutgoingByTeam(t *testing.T) {
|
||||
Setup()
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ type Store interface {
|
||||
OAuth() OAuthStore
|
||||
System() SystemStore
|
||||
Webhook() WebhookStore
|
||||
Command() CommandStore
|
||||
Preference() PreferenceStore
|
||||
MarkSystemRanUnitTests()
|
||||
Close()
|
||||
@@ -111,7 +112,7 @@ type UserStore interface {
|
||||
UpdateLastActivityAt(userId string, time int64) StoreChannel
|
||||
UpdateUserAndSessionActivity(userId string, sessionId string, time int64) StoreChannel
|
||||
UpdatePassword(userId, newPassword string) StoreChannel
|
||||
UpdateAuthData(userId, service, authData string) StoreChannel
|
||||
UpdateAuthData(userId, service, authData, email string) StoreChannel
|
||||
Get(id string) StoreChannel
|
||||
GetProfiles(teamId string) StoreChannel
|
||||
GetByEmail(teamId string, email string) StoreChannel
|
||||
@@ -170,13 +171,12 @@ type SystemStore interface {
|
||||
type WebhookStore interface {
|
||||
SaveIncoming(webhook *model.IncomingWebhook) StoreChannel
|
||||
GetIncoming(id string) StoreChannel
|
||||
GetIncomingByUser(userId string) StoreChannel
|
||||
GetIncomingByTeam(teamId string) StoreChannel
|
||||
GetIncomingByChannel(channelId string) StoreChannel
|
||||
DeleteIncoming(webhookId string, time int64) StoreChannel
|
||||
PermanentDeleteIncomingByUser(userId string) StoreChannel
|
||||
SaveOutgoing(webhook *model.OutgoingWebhook) StoreChannel
|
||||
GetOutgoing(id string) StoreChannel
|
||||
GetOutgoingByCreator(userId string) StoreChannel
|
||||
GetOutgoingByChannel(channelId string) StoreChannel
|
||||
GetOutgoingByTeam(teamId string) StoreChannel
|
||||
DeleteOutgoing(webhookId string, time int64) StoreChannel
|
||||
@@ -186,6 +186,15 @@ type WebhookStore interface {
|
||||
AnalyticsOutgoingCount(teamId string) StoreChannel
|
||||
}
|
||||
|
||||
type CommandStore interface {
|
||||
Save(webhook *model.Command) StoreChannel
|
||||
Get(id string) StoreChannel
|
||||
GetByTeam(teamId string) StoreChannel
|
||||
Delete(commandId string, time int64) StoreChannel
|
||||
PermanentDeleteByUser(userId string) StoreChannel
|
||||
Update(hook *model.Command) StoreChannel
|
||||
}
|
||||
|
||||
type PreferenceStore interface {
|
||||
Save(preferences *model.Preferences) StoreChannel
|
||||
Get(userId string, category string, name string) StoreChannel
|
||||
|
||||
@@ -202,6 +202,8 @@ func getClientConfig(c *model.Config) map[string]string {
|
||||
props["GoogleDeveloperKey"] = c.ServiceSettings.GoogleDeveloperKey
|
||||
props["EnableIncomingWebhooks"] = strconv.FormatBool(c.ServiceSettings.EnableIncomingWebhooks)
|
||||
props["EnableOutgoingWebhooks"] = strconv.FormatBool(c.ServiceSettings.EnableOutgoingWebhooks)
|
||||
props["EnableCommands"] = strconv.FormatBool(*c.ServiceSettings.EnableCommands)
|
||||
props["EnableOnlyAdminIntegrations"] = strconv.FormatBool(*c.ServiceSettings.EnableOnlyAdminIntegrations)
|
||||
props["EnablePostUsernameOverride"] = strconv.FormatBool(c.ServiceSettings.EnablePostUsernameOverride)
|
||||
props["EnablePostIconOverride"] = strconv.FormatBool(c.ServiceSettings.EnablePostIconOverride)
|
||||
props["EnableDeveloper"] = strconv.FormatBool(*c.ServiceSettings.EnableDeveloper)
|
||||
|
||||
@@ -75,6 +75,8 @@ class ServiceSettings extends React.Component {
|
||||
config.ServiceSettings.EnableTesting = ReactDOM.findDOMNode(this.refs.EnableTesting).checked;
|
||||
config.ServiceSettings.EnableDeveloper = ReactDOM.findDOMNode(this.refs.EnableDeveloper).checked;
|
||||
config.ServiceSettings.EnableSecurityFixAlert = ReactDOM.findDOMNode(this.refs.EnableSecurityFixAlert).checked;
|
||||
config.ServiceSettings.EnableCommands = ReactDOM.findDOMNode(this.refs.EnableCommands).checked;
|
||||
config.ServiceSettings.EnableOnlyAdminIntegrations = ReactDOM.findDOMNode(this.refs.EnableOnlyAdminIntegrations).checked;
|
||||
|
||||
//config.ServiceSettings.EnableOAuthServiceProvider = ReactDOM.findDOMNode(this.refs.EnableOAuthServiceProvider).checked;
|
||||
|
||||
@@ -386,6 +388,100 @@ class ServiceSettings extends React.Component {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label
|
||||
className='control-label col-sm-4'
|
||||
htmlFor='EnableCommands'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='admin.service.cmdsTitle'
|
||||
defaultMessage='Enable Slash Commands: '
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-8'>
|
||||
<label className='radio-inline'>
|
||||
<input
|
||||
type='radio'
|
||||
name='EnableCommands'
|
||||
value='true'
|
||||
ref='EnableCommands'
|
||||
defaultChecked={this.props.config.ServiceSettings.EnableCommands}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='admin.service.true'
|
||||
defaultMessage='true'
|
||||
/>
|
||||
</label>
|
||||
<label className='radio-inline'>
|
||||
<input
|
||||
type='radio'
|
||||
name='EnableCommands'
|
||||
value='false'
|
||||
defaultChecked={!this.props.config.ServiceSettings.EnableCommands}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='admin.service.false'
|
||||
defaultMessage='false'
|
||||
/>
|
||||
</label>
|
||||
<p className='help-text'>
|
||||
<FormattedMessage
|
||||
id='admin.service.cmdsDesc'
|
||||
defaultMessage='When true, user created slash commands will be allowed.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label
|
||||
className='control-label col-sm-4'
|
||||
htmlFor='EnableOnlyAdminIntegrations'
|
||||
>
|
||||
<FormattedMessage
|
||||
id='admin.service.integrationAdmin'
|
||||
defaultMessage='Enable Integrations for Admin Only: '
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-8'>
|
||||
<label className='radio-inline'>
|
||||
<input
|
||||
type='radio'
|
||||
name='EnableOnlyAdminIntegrations'
|
||||
value='true'
|
||||
ref='EnableOnlyAdminIntegrations'
|
||||
defaultChecked={this.props.config.ServiceSettings.EnableOnlyAdminIntegrations}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='admin.service.true'
|
||||
defaultMessage='true'
|
||||
/>
|
||||
</label>
|
||||
<label className='radio-inline'>
|
||||
<input
|
||||
type='radio'
|
||||
name='EnableOnlyAdminIntegrations'
|
||||
value='false'
|
||||
defaultChecked={!this.props.config.ServiceSettings.EnableOnlyAdminIntegrations}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='admin.service.false'
|
||||
defaultMessage='false'
|
||||
/>
|
||||
</label>
|
||||
<p className='help-text'>
|
||||
<FormattedMessage
|
||||
id='admin.service.integrationAdminDesc'
|
||||
defaultMessage='When true, user created integrations can only be created by admins.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='form-group'>
|
||||
<label
|
||||
className='control-label col-sm-4'
|
||||
@@ -393,7 +489,7 @@ class ServiceSettings extends React.Component {
|
||||
>
|
||||
<FormattedMessage
|
||||
id='admin.service.overrideTitle'
|
||||
defaultMessage='Enable Overriding Usernames from Webhooks: '
|
||||
defaultMessage='Enable Overriding Usernames from Webhooks and Slash Commands: '
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-8'>
|
||||
@@ -427,7 +523,7 @@ class ServiceSettings extends React.Component {
|
||||
<p className='help-text'>
|
||||
<FormattedMessage
|
||||
id='admin.service.overrideDescription'
|
||||
defaultMessage='When true, webhooks will be allowed to change the username they are posting as. Note, combined with allowing icon overriding, this could open users up to phishing attacks.'
|
||||
defaultMessage='When true, webhooks and slash commands will be allowed to change the username they are posting as. Note, combined with allowing icon overriding, this could open users up to phishing attacks.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
@@ -440,7 +536,7 @@ class ServiceSettings extends React.Component {
|
||||
>
|
||||
<FormattedMessage
|
||||
id='admin.service.iconTitle'
|
||||
defaultMessage='Enable Overriding Icon from Webhooks: '
|
||||
defaultMessage='Enable Overriding Icon from Webhooks and Slash Commands: '
|
||||
/>
|
||||
</label>
|
||||
<div className='col-sm-8'>
|
||||
@@ -474,7 +570,7 @@ class ServiceSettings extends React.Component {
|
||||
<p className='help-text'>
|
||||
<FormattedMessage
|
||||
id='admin.service.iconDescription'
|
||||
defaultMessage='When true, webhooks will be allowed to change the icon they post with. Note, combined with allowing username overriding, this could open users up to phishing attacks.'
|
||||
defaultMessage='When true, webhooks and slash commands will be allowed to change the icon they post with. Note, combined with allowing username overriding, this could open users up to phishing attacks.'
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -128,6 +128,16 @@ class ChannelLoader extends React.Component {
|
||||
}
|
||||
});
|
||||
|
||||
$('body').on('mouseenter mouseleave', '.search-item__container .post', function mouseOver(ev) {
|
||||
if (ev.type === 'mouseenter') {
|
||||
$(this).closest('.search-item__container').find('.date-separator').addClass('hovered--after');
|
||||
$(this).closest('.search-item__container').next('div').find('.date-separator').addClass('hovered--before');
|
||||
} else {
|
||||
$(this).closest('.search-item__container').find('.date-separator').removeClass('hovered--after');
|
||||
$(this).closest('.search-item__container').next('div').find('.date-separator').removeClass('hovered--before');
|
||||
}
|
||||
});
|
||||
|
||||
$('body').on('mouseenter mouseleave', '.post.post--comment.same--root', function mouseOver(ev) {
|
||||
if (ev.type === 'mouseenter') {
|
||||
$(this).parent('div').prev('.date-separator, .new-separator').addClass('hovered--comment');
|
||||
|
||||
@@ -202,8 +202,7 @@ class CreateComment extends React.Component {
|
||||
if (e.keyCode === KeyCodes.UP && this.state.messageText === '') {
|
||||
e.preventDefault();
|
||||
|
||||
const channelId = ChannelStore.getCurrentId();
|
||||
const lastPost = PostStore.getCurrentUsersLatestPost(channelId, this.props.rootId);
|
||||
const lastPost = PostStore.getCurrentUsersLatestPost(this.props.channelId, this.props.rootId);
|
||||
if (!lastPost) {
|
||||
return;
|
||||
}
|
||||
@@ -402,4 +401,4 @@ CreateComment.propTypes = {
|
||||
rootId: React.PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(CreateComment);
|
||||
export default injectIntl(CreateComment);
|
||||
|
||||
@@ -155,15 +155,10 @@ class CreatePost extends React.Component {
|
||||
post.message,
|
||||
false,
|
||||
(data) => {
|
||||
if (data.response === 'not implemented') {
|
||||
this.sendMessage(post);
|
||||
return;
|
||||
}
|
||||
|
||||
PostStore.storeDraft(data.channel_id, null);
|
||||
this.setState({messageText: '', submitting: false, postError: null, previews: [], serverError: null});
|
||||
|
||||
if (data.goto_location.length > 0) {
|
||||
if (data.goto_location && data.goto_location.length > 0) {
|
||||
window.location.href = data.goto_location;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -101,9 +101,9 @@ class FileUpload extends React.Component {
|
||||
} else if (tooLargeFiles.length > 1) {
|
||||
var tooLargeFilenames = tooLargeFiles.map((file) => file.name).join(', ');
|
||||
|
||||
this.props.onUploadError(formatMessage(holders.filesAbove, {max: (Constants.MAX_FILE_SIZE / 1000000), files: tooLargeFilenames}));
|
||||
this.props.onUploadError(formatMessage(holders.filesAbove, {max: (Constants.MAX_FILE_SIZE / 1000000), filenames: tooLargeFilenames}));
|
||||
} else if (tooLargeFiles.length > 0) {
|
||||
this.props.onUploadError(formatMessage(holders.fileAbove, {max: (Constants.MAX_FILE_SIZE / 1000000), file: tooLargeFiles[0].name}));
|
||||
this.props.onUploadError(formatMessage(holders.fileAbove, {max: (Constants.MAX_FILE_SIZE / 1000000), filename: tooLargeFiles[0].name}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,4 +329,4 @@ FileUpload.propTypes = {
|
||||
postType: React.PropTypes.string
|
||||
};
|
||||
|
||||
export default injectIntl(FileUpload);
|
||||
export default injectIntl(FileUpload);
|
||||
|
||||
@@ -59,64 +59,74 @@ export default class SearchResultsItem extends React.Component {
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className='search-item-container post'
|
||||
>
|
||||
<div className='search-channel__name'>{channelName}</div>
|
||||
<div className='post__content'>
|
||||
<div className='post__img'>
|
||||
<img
|
||||
src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex()}
|
||||
height='36'
|
||||
width='36'
|
||||
<div className='search-item__container'>
|
||||
<div className='date-separator'>
|
||||
<hr className='separator__hr' />
|
||||
<div className='separator__text'>
|
||||
<FormattedDate
|
||||
value={this.props.post.create_at}
|
||||
day='numeric'
|
||||
month='long'
|
||||
year='numeric'
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<ul className='post__header'>
|
||||
<li className='col__name'><strong><UserProfile userId={this.props.post.user_id} /></strong></li>
|
||||
<li className='col'>
|
||||
<time className='search-item-time'>
|
||||
<FormattedDate
|
||||
value={this.props.post.create_at}
|
||||
day='numeric'
|
||||
month='long'
|
||||
year='numeric'
|
||||
hour12={true}
|
||||
hour='2-digit'
|
||||
minute='2-digit'
|
||||
/>
|
||||
</time>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href='#'
|
||||
className='search-item__jump'
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='search_item.jump'
|
||||
defaultMessage='Jump'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href='#'
|
||||
className='comment-icon__container search-item__comment'
|
||||
onClick={this.handleFocusRHSClick}
|
||||
>
|
||||
<span
|
||||
className='comment-icon'
|
||||
dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div className='search-item-snippet'>
|
||||
<span
|
||||
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, formattingOptions)}}
|
||||
</div>
|
||||
<div
|
||||
className='post'
|
||||
>
|
||||
<div className='search-channel__name'>{channelName}</div>
|
||||
<div className='post__content'>
|
||||
<div className='post__img'>
|
||||
<img
|
||||
src={'/api/v1/users/' + this.props.post.user_id + '/image?time=' + timestamp + '&' + utils.getSessionIndex()}
|
||||
height='36'
|
||||
width='36'
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<ul className='post__header'>
|
||||
<li className='col__name'><strong><UserProfile userId={this.props.post.user_id} /></strong></li>
|
||||
<li className='col'>
|
||||
<time className='search-item-time'>
|
||||
<FormattedDate
|
||||
value={this.props.post.create_at}
|
||||
hour12={true}
|
||||
hour='2-digit'
|
||||
minute='2-digit'
|
||||
/>
|
||||
</time>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href='#'
|
||||
className='search-item__jump'
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='search_item.jump'
|
||||
defaultMessage='Jump'
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href='#'
|
||||
className='comment-icon__container search-item__comment'
|
||||
onClick={this.handleFocusRHSClick}
|
||||
>
|
||||
<span
|
||||
className='comment-icon'
|
||||
dangerouslySetInnerHTML={{__html: Constants.REPLY_ICON}}
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div className='search-item-snippet'>
|
||||
<span
|
||||
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(this.props.post.message, formattingOptions)}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
652
web/react/components/user_settings/manage_command_hooks.jsx
Обычный файл
652
web/react/components/user_settings/manage_command_hooks.jsx
Обычный файл
@@ -0,0 +1,652 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import LoadingScreen from '../loading_screen.jsx';
|
||||
|
||||
import * as Client from '../../utils/client.jsx';
|
||||
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedHTMLMessage} from 'mm-intl';
|
||||
|
||||
const holders = defineMessages({
|
||||
requestTypePost: {
|
||||
id: 'user.settings.cmds.request_type_post',
|
||||
defaultMessage: 'POST'
|
||||
},
|
||||
requestTypeGet: {
|
||||
id: 'user.settings.cmds.request_type_get',
|
||||
defaultMessage: 'GET'
|
||||
},
|
||||
addDisplayNamePlaceholder: {
|
||||
id: 'user.settings.cmds.add_display_name.placeholder',
|
||||
defaultMessage: 'Display Name'
|
||||
},
|
||||
addUsernamePlaceholder: {
|
||||
id: 'user.settings.cmds.add_username.placeholder',
|
||||
defaultMessage: 'Username'
|
||||
},
|
||||
addTriggerPlaceholder: {
|
||||
id: 'user.settings.cmds.add_trigger.placeholder',
|
||||
defaultMessage: 'Command trigger e.g. "hello" not including the slash'
|
||||
},
|
||||
addAutoCompleteDescPlaceholder: {
|
||||
id: 'user.settings.cmds.auto_complete_desc.placeholder',
|
||||
defaultMessage: 'A short description of what this commands does.'
|
||||
},
|
||||
addAutoCompleteHintPlaceholder: {
|
||||
id: 'user.settings.cmds.auto_complete_hint.placeholder',
|
||||
defaultMessage: '[zipcode]'
|
||||
},
|
||||
adUrlPlaceholder: {
|
||||
id: 'user.settings.cmds.url.placeholder',
|
||||
defaultMessage: 'Must start with http:// or https://'
|
||||
}
|
||||
});
|
||||
|
||||
export default class ManageCommandCmds extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.getCmds = this.getCmds.bind(this);
|
||||
this.addNewCmd = this.addNewCmd.bind(this);
|
||||
this.emptyCmd = this.emptyCmd.bind(this);
|
||||
this.updateTrigger = this.updateTrigger.bind(this);
|
||||
this.updateURL = this.updateURL.bind(this);
|
||||
this.updateMethod = this.updateMethod.bind(this);
|
||||
this.updateUsername = this.updateUsername.bind(this);
|
||||
this.updateIconURL = this.updateIconURL.bind(this);
|
||||
this.updateDisplayName = this.updateDisplayName.bind(this);
|
||||
this.updateAutoComplete = this.updateAutoComplete.bind(this);
|
||||
this.updateAutoCompleteDesc = this.updateAutoCompleteDesc.bind(this);
|
||||
this.updateAutoCompleteHint = this.updateAutoCompleteHint.bind(this);
|
||||
|
||||
this.state = {cmds: [], cmd: this.emptyCmd(), getCmdsComplete: false};
|
||||
}
|
||||
|
||||
static propTypes() {
|
||||
return {
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
}
|
||||
|
||||
emptyCmd() {
|
||||
var cmd = {};
|
||||
cmd.url = '';
|
||||
cmd.trigger = '';
|
||||
cmd.method = 'P';
|
||||
cmd.username = '';
|
||||
cmd.icon_url = '';
|
||||
cmd.auto_complete = false;
|
||||
cmd.auto_complete_desc = '';
|
||||
cmd.auto_complete_hint = '';
|
||||
cmd.display_name = '';
|
||||
return cmd;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.getCmds();
|
||||
}
|
||||
|
||||
addNewCmd(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (this.state.cmd.trigger === '' || this.state.cmd.url === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
var cmd = this.state.cmd;
|
||||
if (cmd.trigger.length !== 0) {
|
||||
cmd.trigger = cmd.trigger.trim();
|
||||
}
|
||||
cmd.url = cmd.url.trim();
|
||||
|
||||
Client.addCommand(
|
||||
cmd,
|
||||
(data) => {
|
||||
let cmds = Object.assign([], this.state.cmds);
|
||||
if (!cmds) {
|
||||
cmds = [];
|
||||
}
|
||||
cmds.push(data);
|
||||
this.setState({cmds, addError: null, cmd: this.emptyCmd()});
|
||||
},
|
||||
(err) => {
|
||||
this.setState({addError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
removeCmd(id) {
|
||||
const data = {};
|
||||
data.id = id;
|
||||
|
||||
Client.deleteCommand(
|
||||
data,
|
||||
() => {
|
||||
const cmds = this.state.cmds;
|
||||
let index = -1;
|
||||
for (let i = 0; i < cmds.length; i++) {
|
||||
if (cmds[i].id === id) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index !== -1) {
|
||||
cmds.splice(index, 1);
|
||||
}
|
||||
|
||||
this.setState({cmds});
|
||||
},
|
||||
(err) => {
|
||||
this.setState({editError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
regenToken(id) {
|
||||
const regenData = {};
|
||||
regenData.id = id;
|
||||
|
||||
Client.regenCommandToken(
|
||||
regenData,
|
||||
(data) => {
|
||||
const cmds = Object.assign([], this.state.cmds);
|
||||
for (let i = 0; i < cmds.length; i++) {
|
||||
if (cmds[i].id === id) {
|
||||
cmds[i] = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({cmds, editError: null});
|
||||
},
|
||||
(err) => {
|
||||
this.setState({editError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getCmds() {
|
||||
Client.listTeamCommands(
|
||||
(data) => {
|
||||
if (data) {
|
||||
this.setState({cmds: data, getCmdsComplete: true, editError: null});
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
this.setState({editError: err.message});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
updateTrigger(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.trigger = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateURL(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.url = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateMethod(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.method = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateUsername(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.username = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateIconURL(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.icon_url = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateDisplayName(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.display_name = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateAutoComplete(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.auto_complete = e.target.checked;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateAutoCompleteDesc(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.auto_complete_desc = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
updateAutoCompleteHint(e) {
|
||||
var cmd = this.state.cmd;
|
||||
cmd.auto_complete_hint = e.target.value;
|
||||
this.setState(cmd);
|
||||
}
|
||||
|
||||
render() {
|
||||
let addError;
|
||||
if (this.state.addError) {
|
||||
addError = <label className='has-error'>{this.state.addError}</label>;
|
||||
}
|
||||
|
||||
let editError;
|
||||
if (this.state.editError) {
|
||||
addError = <label className='has-error'>{this.state.editError}</label>;
|
||||
}
|
||||
|
||||
const cmds = [];
|
||||
this.state.cmds.forEach((cmd) => {
|
||||
let triggerDiv;
|
||||
if (cmd.trigger && cmd.trigger.length !== 0) {
|
||||
triggerDiv = (
|
||||
<div className='padding-top'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.trigger'
|
||||
defaultMessage='Trigger: '
|
||||
/>
|
||||
</strong>{cmd.trigger}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
cmds.push(
|
||||
<div
|
||||
key={cmd.id}
|
||||
className='webcmd__item'
|
||||
>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.display_name'
|
||||
defaultMessage='Display Name: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.display_name}</span>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.username'
|
||||
defaultMessage='Username: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.username}</span>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.icon_url'
|
||||
defaultMessage='Icon URL: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.icon_url}</span>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete'
|
||||
defaultMessage='Auto Complete: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.auto_complete ? 'yes' : 'no'}</span>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_desc'
|
||||
defaultMessage='Auto Complete Description: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.auto_complete_desc}</span>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_hint'
|
||||
defaultMessage='Auto Complete Hint: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.auto_complete_hint}</span>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.request_type'
|
||||
defaultMessage='Request Type: '
|
||||
/>
|
||||
</strong>
|
||||
<span className='word-break--all'>
|
||||
{
|
||||
cmd.method === 'P' ?
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.request_type_post'
|
||||
defaultMessage='POST'
|
||||
/> :
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.request_type_get'
|
||||
defaultMessage='GET'
|
||||
/>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
<div className='padding-top x2 webcmd__url'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.url'
|
||||
defaultMessage='URL: '
|
||||
/>
|
||||
</strong><span className='word-break--all'>{cmd.url}</span>
|
||||
</div>
|
||||
{triggerDiv}
|
||||
<div className='padding-top'>
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.token'
|
||||
defaultMessage='Token: '
|
||||
/>
|
||||
</strong>{cmd.token}
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<a
|
||||
className='text-danger'
|
||||
href='#'
|
||||
onClick={this.regenToken.bind(this, cmd.id)}
|
||||
>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.regen'
|
||||
defaultMessage='Regen Token'
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
className='webcmd__remove'
|
||||
href='#'
|
||||
onClick={this.removeCmd.bind(this, cmd.id)}
|
||||
>
|
||||
<span aria-hidden='true'>{'×'}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div className='padding-top x2 divider-light'></div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
let displayCmds;
|
||||
if (!this.state.getCmdsComplete) {
|
||||
displayCmds = <LoadingScreen/>;
|
||||
} else if (cmds.length > 0) {
|
||||
displayCmds = cmds;
|
||||
} else {
|
||||
displayCmds = (
|
||||
<div className='padding-top x2'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.none'
|
||||
defaultMessage='None'
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const existingCmds = (
|
||||
<div className='webcmds__container'>
|
||||
<label className='control-label padding-top x2'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.existing'
|
||||
defaultMessage='Existing commands'
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top divider-light'></div>
|
||||
<div className='webcmds__list'>
|
||||
{displayCmds}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const disableButton = this.state.cmd.trigger === '' || this.state.cmd.url === '';
|
||||
|
||||
return (
|
||||
<div key='addCommandCmd'>
|
||||
<FormattedHTMLMessage
|
||||
id='user.settings.cmds.add_desc'
|
||||
defaultMessage='Create commands to send message events to an external integration. Please see <a href="http://mattermost.org/commands">http://mattermost.org/commands</a> to learn more.'
|
||||
/>
|
||||
<div><label className='control-label padding-top x2'>{'Add a new command'}</label></div>
|
||||
<div className='padding-top divider-light'></div>
|
||||
<div className='padding-top'>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.display_name'
|
||||
defaultMessage='Display Name: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='displayName'
|
||||
className='form-control'
|
||||
value={this.state.cmd.display_name}
|
||||
onChange={this.updateDisplayName}
|
||||
placeholder={this.props.intl.formatMessage(holders.addDisplayNamePlaceholder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>{'Command display name.'}</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.username'
|
||||
defaultMessage='Username: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='username'
|
||||
className='form-control'
|
||||
value={this.state.cmd.username}
|
||||
onChange={this.updateUsername}
|
||||
placeholder={this.props.intl.formatMessage(holders.addUsernamePlaceholder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.username_desc'
|
||||
defaultMessage='The username to use when overriding the post.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.icon_url'
|
||||
defaultMessage='Icon URL: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='iconURL'
|
||||
className='form-control'
|
||||
value={this.state.cmd.icon_url}
|
||||
onChange={this.updateIconURL}
|
||||
placeholder='https://www.example.com/myicon.png'
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.icon_url_desc'
|
||||
defaultMessage='URL to an icon'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.trigger'
|
||||
defaultMessage='Trigger: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='trigger'
|
||||
className='form-control'
|
||||
value={this.state.cmd.trigger}
|
||||
onChange={this.updateTrigger}
|
||||
placeholder={this.props.intl.formatMessage(holders.addTriggerPlaceholder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.trigger_desc'
|
||||
defaultMessage='Word to trigger on'
|
||||
/>
|
||||
{''}</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete'
|
||||
defaultMessage='Auto Complete: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<label>
|
||||
<input
|
||||
type='checkbox'
|
||||
checked={this.state.cmd.auto_complete}
|
||||
onChange={this.updateAutoComplete}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_desc_desc'
|
||||
defaultMessage='A short description of what this commands does'
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_help'
|
||||
defaultMessage='Show this command in autocomplete list.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_desc'
|
||||
defaultMessage='Auto Complete Description: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='autoCompleteDesc'
|
||||
className='form-control'
|
||||
value={this.state.cmd.auto_complete_desc}
|
||||
onChange={this.updateAutoCompleteDesc}
|
||||
placeholder={this.props.intl.formatMessage(holders.addAutoCompleteDescPlaceholder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_desc_desc'
|
||||
defaultMessage='A short description of what this commands does'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_hint'
|
||||
defaultMessage='Auto Complete Hint: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='autoCompleteHint'
|
||||
className='form-control'
|
||||
value={this.state.cmd.auto_complete_hint}
|
||||
onChange={this.updateAutoCompleteHint}
|
||||
placeholder={this.props.intl.formatMessage(holders.addAutoCompleteHintPlaceholder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.auto_complete_hint_desc'
|
||||
defaultMessage='List parameters to be passed to the command.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.request_type'
|
||||
defaultMessage='Request Type: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<select
|
||||
ref='method'
|
||||
className='form-control'
|
||||
value={this.state.cmd.method}
|
||||
onChange={this.updateMethod}
|
||||
>
|
||||
<option value='P'>
|
||||
{this.props.intl.formatMessage(holders.requestTypePost)}
|
||||
</option>
|
||||
<option value='G'>
|
||||
{this.props.intl.formatMessage(holders.requestTypeGet)}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.request_type_desc'
|
||||
defaultMessage='Command request type issued to the callback URL.'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='padding-top x2'>
|
||||
<label className='control-label'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.url'
|
||||
defaultMessage='URL: '
|
||||
/>
|
||||
</label>
|
||||
<div className='padding-top'>
|
||||
<input
|
||||
ref='URL'
|
||||
className='form-control'
|
||||
value={this.state.cmd.url}
|
||||
rows={1}
|
||||
onChange={this.updateURL}
|
||||
placeholder={this.props.intl.formatMessage(holders.adUrlPlaceholder)}
|
||||
/>
|
||||
</div>
|
||||
<div className='padding-top'>
|
||||
<FormattedMessage
|
||||
id='user.settings.cmds.url_desc'
|
||||
defaultMessage='URL that will receive the HTTP POST or GET event'
|
||||
/>
|
||||
</div>
|
||||
{addError}
|
||||
</div>
|
||||
<div className='padding-top padding-bottom'>
|
||||
<a
|
||||
className={'btn btn-sm btn-primary'}
|
||||
href='#'
|
||||
disabled={disableButton}
|
||||
onClick={this.addNewCmd}
|
||||
>
|
||||
{'Add'}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{existingCmds}
|
||||
{editError}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectIntl(ManageCommandCmds);
|
||||
@@ -297,7 +297,7 @@ class UserSettingsDisplay extends React.Component {
|
||||
if (this.state.nameFormat === 'username') {
|
||||
describe = formatMessage(holders.showUsername);
|
||||
} else if (this.state.nameFormat === 'full_name') {
|
||||
describe = formatMessage(holders.showFullName);
|
||||
describe = formatMessage(holders.showFullname);
|
||||
} else {
|
||||
describe = formatMessage(holders.showNickname);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import SettingItemMin from '../setting_item_min.jsx';
|
||||
import SettingItemMax from '../setting_item_max.jsx';
|
||||
import ManageIncomingHooks from './manage_incoming_hooks.jsx';
|
||||
import ManageOutgoingHooks from './manage_outgoing_hooks.jsx';
|
||||
import ManageCommandHooks from './manage_command_hooks.jsx';
|
||||
|
||||
import {intlShape, injectIntl, defineMessages, FormattedMessage} from 'mm-intl';
|
||||
|
||||
@@ -24,6 +25,14 @@ const holders = defineMessages({
|
||||
outDesc: {
|
||||
id: 'user.settings.integrations.outWebhooksDescription',
|
||||
defaultMessage: 'Manage your outgoing webhooks'
|
||||
},
|
||||
cmdName: {
|
||||
id: 'user.settings.integrations.commands',
|
||||
defaultMessage: 'Commands'
|
||||
},
|
||||
cmdDesc: {
|
||||
id: 'user.settings.integrations.commandsDescription',
|
||||
defaultMessage: 'Manage your commands'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -41,6 +50,7 @@ class UserSettingsIntegrationsTab extends React.Component {
|
||||
render() {
|
||||
let incomingHooksSection;
|
||||
let outgoingHooksSection;
|
||||
let commandHooksSection;
|
||||
var inputs = [];
|
||||
const {formatMessage} = this.props.intl;
|
||||
|
||||
@@ -106,6 +116,37 @@ class UserSettingsIntegrationsTab extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
if (global.window.mm_config.EnableCommands === 'true') {
|
||||
if (this.props.activeSection === 'command-hooks') {
|
||||
inputs.push(
|
||||
<ManageCommandHooks key='command-hook-ui' />
|
||||
);
|
||||
|
||||
commandHooksSection = (
|
||||
<SettingItemMax
|
||||
title={formatMessage(holders.cmdName)}
|
||||
width='medium'
|
||||
inputs={inputs}
|
||||
updateSection={(e) => {
|
||||
this.updateSection('');
|
||||
e.preventDefault();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
commandHooksSection = (
|
||||
<SettingItemMin
|
||||
title={formatMessage(holders.cmdName)}
|
||||
width='medium'
|
||||
describe={formatMessage(holders.cmdDesc)}
|
||||
updateSection={() => {
|
||||
this.updateSection('command-hooks');
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='modal-header'>
|
||||
@@ -144,6 +185,8 @@ class UserSettingsIntegrationsTab extends React.Component {
|
||||
<div className='divider-light'/>
|
||||
{outgoingHooksSection}
|
||||
<div className='divider-dark'/>
|
||||
{commandHooksSection}
|
||||
<div className='divider-dark'/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -774,20 +774,27 @@ export function savePreferences(preferences, success, error) {
|
||||
}
|
||||
|
||||
export function getSuggestedCommands(command, suggestionId, component) {
|
||||
client.executeCommand(
|
||||
'',
|
||||
command,
|
||||
true,
|
||||
client.listCommands(
|
||||
(data) => {
|
||||
var matches = [];
|
||||
data.forEach((cmd) => {
|
||||
if (('/' + cmd.trigger).indexOf(command) === 0) {
|
||||
matches.push({
|
||||
suggestion: '/' + cmd.trigger + ' ' + cmd.auto_complete_hint,
|
||||
description: cmd.auto_complete_desc
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// pull out the suggested commands from the returned data
|
||||
const terms = data.suggestions.map((suggestion) => suggestion.suggestion);
|
||||
const terms = matches.map((suggestion) => suggestion.suggestion);
|
||||
|
||||
AppDispatcher.handleServerAction({
|
||||
type: ActionTypes.SUGGESTION_RECEIVED_SUGGESTIONS,
|
||||
id: suggestionId,
|
||||
matchedPretext: command,
|
||||
terms,
|
||||
items: data.suggestions,
|
||||
items: matches,
|
||||
component
|
||||
});
|
||||
},
|
||||
|
||||
@@ -908,11 +908,11 @@ export function getChannelExtraInfo(id, memberLimit, success, error) {
|
||||
|
||||
export function executeCommand(channelId, command, suggest, success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/command',
|
||||
url: '/api/v1/commands/execute',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'POST',
|
||||
data: JSON.stringify({channelId: channelId, command: command, suggest: '' + suggest}),
|
||||
data: JSON.stringify({channelId, command, suggest: '' + suggest}),
|
||||
success,
|
||||
error: function onError(xhr, status, err) {
|
||||
var e = handleError('executeCommand', xhr, status, err);
|
||||
@@ -921,6 +921,78 @@ export function executeCommand(channelId, command, suggest, success, error) {
|
||||
});
|
||||
}
|
||||
|
||||
export function addCommand(cmd, success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/commands/create',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(cmd),
|
||||
success,
|
||||
error: (xhr, status, err) => {
|
||||
var e = handleError('addCommand', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteCommand(data, success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/commands/delete',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(data),
|
||||
success,
|
||||
error: (xhr, status, err) => {
|
||||
var e = handleError('deleteCommand', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function listTeamCommands(success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/commands/list_team_commands',
|
||||
dataType: 'json',
|
||||
type: 'GET',
|
||||
success,
|
||||
error: (xhr, status, err) => {
|
||||
var e = handleError('listTeamCommands', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function regenCommandToken(data, success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/commands/regen_token',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'POST',
|
||||
data: JSON.stringify(data),
|
||||
success,
|
||||
error: (xhr, status, err) => {
|
||||
var e = handleError('regenCommandToken', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function listCommands(success, error) {
|
||||
$.ajax({
|
||||
url: '/api/v1/commands/list',
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
type: 'GET',
|
||||
success,
|
||||
error: function onError(xhr, status, err) {
|
||||
var e = handleError('listCommands', xhr, status, err);
|
||||
error(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function getPostsPage(channelId, offset, limit, success, error, complete) {
|
||||
$.ajax({
|
||||
cache: false,
|
||||
|
||||
@@ -173,6 +173,7 @@
|
||||
.team__name {
|
||||
line-height: 22px;
|
||||
margin-top: -2px;
|
||||
float: left;
|
||||
}
|
||||
.user__name {
|
||||
@include single-transition(all, 0.1s, linear);
|
||||
|
||||
@@ -764,12 +764,13 @@ body.ios {
|
||||
}
|
||||
|
||||
svg {
|
||||
height: 17px;
|
||||
width: 17px;
|
||||
}
|
||||
|
||||
.comment-icon {
|
||||
display: inline-block;
|
||||
top: 3px;
|
||||
top: 2px;
|
||||
position: relative;
|
||||
margin-right: 3px;
|
||||
fill: inherit;
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
-webkit-overflow-scrolling: touch;
|
||||
@include flex(1 1 auto);
|
||||
height: calc(100% - 56px);
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.search-results-header {
|
||||
@@ -104,19 +105,22 @@
|
||||
border-bottom: $border-gray;
|
||||
}
|
||||
|
||||
.search-item-container {
|
||||
border-top: $border-gray;
|
||||
padding: 10px 1em;
|
||||
margin: 0;
|
||||
.search-item__container {
|
||||
|
||||
&:first-child {
|
||||
border: none;
|
||||
.post {
|
||||
padding: 0 1em 1em;
|
||||
margin: 0;
|
||||
|
||||
&:first-child {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.search-channel__name {
|
||||
font-weight: 600;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.search-channel__name {
|
||||
font-weight: 600;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.search-item__jump {
|
||||
|
||||
@@ -351,10 +351,14 @@
|
||||
"admin.service.webhooksDescription": "When true, incoming webhooks will be allowed. To help combat phishing attacks, all posts from webhooks will be labelled by a BOT tag.",
|
||||
"admin.service.outWebhooksTitle": "Enable Outgoing Webhooks: ",
|
||||
"admin.service.outWebhooksDesc": "When true, outgoing webhooks will be allowed.",
|
||||
"admin.service.overrideTitle": "Enable Overriding Usernames from Webhooks: ",
|
||||
"admin.service.overrideDescription": "When true, webhooks will be allowed to change the username they are posting as. Note, combined with allowing icon overriding, this could open users up to phishing attacks.",
|
||||
"admin.service.iconTitle": "Enable Overriding Icon from Webhooks: ",
|
||||
"admin.service.iconDescription": "When true, webhooks will be allowed to change the icon they post with. Note, combined with allowing username overriding, this could open users up to phishing attacks.",
|
||||
"admin.service.cmdsTitle": "Enable Slash Commands: ",
|
||||
"admin.service.cmdsDesc": "When true, user created slash commands will be allowed.",
|
||||
"admin.service.integrationAdmin": "Enable Integrations for Admin Only: ",
|
||||
"admin.service.integrationAdminDesc": "When true, user created integrations can only be created by admins.",
|
||||
"admin.service.overrideTitle": "Enable Overriding Usernames from Webhooks and Salsh Commands: ",
|
||||
"admin.service.overrideDescription": "When true, webhooks and slash commands will be allowed to change the username they are posting as. Note, combined with allowing icon overriding, this could open users up to phishing attacks.",
|
||||
"admin.service.iconTitle": "Enable Overriding Icon from Webhooks and Slash Commands: ",
|
||||
"admin.service.iconDescription": "When true, webhooks and slash commands will be allowed to change the icon they post with. Note, combined with allowing username overriding, this could open users up to phishing attacks.",
|
||||
"admin.service.testingTitle": "Enable Testing: ",
|
||||
"admin.service.testingDescription": "(Developer Option) When true, /loadtest slash command is enabled to load test accounts and test data. Changing this will require a server restart before taking effect.",
|
||||
"admin.service.developerTitle": "Enable Developer Mode: ",
|
||||
@@ -1148,7 +1152,40 @@
|
||||
"user.settings.integrations.incomingWebhooksDescription": "Manage your incoming webhooks",
|
||||
"user.settings.integrations.outWebhooks": "Outgoing Webhooks",
|
||||
"user.settings.integrations.outWebhooksDescription": "Manage your outgoing webhooks",
|
||||
"user.settings.integrations.commands": "Commands",
|
||||
"user.settings.integrations.commandsDescription": "Manage your commands",
|
||||
"user.settings.integrations.title": "Integration Settings",
|
||||
"user.settings.cmds.trigger": "Trigger: ",
|
||||
"user.settings.cmds.display_name": "Display Name: ",
|
||||
"user.settings.cmds.username": "Username: ",
|
||||
"user.settings.cmds.icon_url": "Icon URL: ",
|
||||
"user.settings.cmds.auto_complete": "Auto Complete: ",
|
||||
"user.settings.cmds.auto_complete_desc": "Auto Complete Description: ",
|
||||
"user.settings.cmds.auto_complete_hint": "Auto Complete Hint: ",
|
||||
"user.settings.cmds.request_type": "Request Type: ",
|
||||
"user.settings.cmds.request_type_post": "POST",
|
||||
"user.settings.cmds.request_type_get": "GET",
|
||||
"user.settings.cmds.url": "URL: ",
|
||||
"user.settings.cmds.token": "Token: ",
|
||||
"user.settings.cmds.regen": "Regen Token",
|
||||
"user.settings.cmds.none": "None",
|
||||
"Existing commands": "Existing commands",
|
||||
"user.settings.cmds.add_desc": "Create commands to send message events to an external integration. Please see <a href=\"http://mattermost.org/commands\">http://mattermost.org/commands</a> to learn more.",
|
||||
"user.settings.cmds.add_display_name.placeholder": "Display Name",
|
||||
"user.settings.cmds.existing": "Existing commands",
|
||||
"user.settings.cmds.add_username.placeholder": "Username",
|
||||
"user.settings.cmds.username_desc": "The username to use when overriding the post.",
|
||||
"user.settings.cmds.icon_url_desc": "URL to an icon",
|
||||
"user.settings.cmds.trigger_desc": "Word to trigger on",
|
||||
"user.settings.cmds.add_trigger.placeholder": "Command trigger e.g. \"hello\" not including the slash",
|
||||
"user.settings.cmds.auto_complete_desc_desc": "A short description of what this commands does",
|
||||
"user.settings.cmds.auto_complete_help": "Show this command in autocomplete list.",
|
||||
"user.settings.cmds.auto_complete_desc.placeholder": "A short description of what this commands does.",
|
||||
"user.settings.cmds.auto_complete_hint.placeholder": "[zipcode]",
|
||||
"user.settings.cmds.auto_complete_hint_desc": "List parameters to be passed to the command.",
|
||||
"user.settings.cmds.request_type_desc": "Command request type issued to the callback URL.",
|
||||
"user.settings.cmds.url_desc": "URL that will receive the HTTP POST or GET event",
|
||||
"user.settings.cmds.url.placeholder": "Must start with http:// or https://",
|
||||
"user.settings.modal.general": "General",
|
||||
"user.settings.modal.security": "Security",
|
||||
"user.settings.modal.notifications": "Notifications",
|
||||
|
||||
@@ -299,8 +299,6 @@
|
||||
"admin.service.googleDescription": "Asigna una llave a este campo para habilitar la previsualización de videos de YouTube tomados de los enlaces que aparecen en los mensajes o comentarios. Las instrucciones de como obtener una llave está disponible en <a href=\"https://www.youtube.com/watch?v=Im69kzhpR3I\" target=\"_blank\">https://www.youtube.com/watch?v=Im69kzhpR3I</a>. Al dejar este campo en blanco deshabilita la generación de previsualizaciones de videos de YouTube desde los enlaces.",
|
||||
"admin.service.googleExample": "Ej \"7rAh6iwQCkV4cA1Gsg3fgGOXJAQ43QV\"",
|
||||
"admin.service.googleTitle": "Llave de desarrolador Google:",
|
||||
"admin.service.iconDescription": "Cuando es verdadero, se le permitirá cambiar el icono del mensaje desde webhooks. Nota, en combinación con permitir el cambio de nombre de usuario, podría exponer a los usuarios a sufrir ataques de phishing.",
|
||||
"admin.service.iconTitle": "Habilitar el cambio de icono desde los Webhooks: ",
|
||||
"admin.service.listenAddress": "Dirección de escucha:",
|
||||
"admin.service.listenDescription": "La dirección a la que se unirá y escuchará. Ingresar \":8065\" se podrá unir a todas las interfaces o podrá seleccionar una como ej: \"127.0.0.1:8065\". Cambiando este valor es necesario reiniciar el servidor.",
|
||||
"admin.service.listenExample": "Ej \":8065\"",
|
||||
@@ -308,8 +306,6 @@
|
||||
"admin.service.mobileSessionDaysDesc": "La sesión nativa de los dispositivos moviles expirará luego de transcurrido el numero de días especificado y se solicitará al usuario que inicie sesión nuevamente.",
|
||||
"admin.service.outWebhooksDesc": "Cuando es verdadero, los webhooks de salida serán permitidos.",
|
||||
"admin.service.outWebhooksTitle": "Habilitar Webhooks de Salida: ",
|
||||
"admin.service.overrideDescription": "Cuando es verdadero, se le permitirá cambiar el nombre de usuario desde webhooks. Nota, en conjunto con cambio de icono, podría exponer a los usuarios a sufrir ataques de phishing.",
|
||||
"admin.service.overrideTitle": "Habilitar el cambio de nombres de usuario desde los Webhooks: ",
|
||||
"admin.service.save": "Guardar",
|
||||
"admin.service.saving": "Guardando....",
|
||||
"admin.service.securityDesc": "Cuando es verdadero, Los Administradores del Sistema serán notificados por correo electrónico se han anunciado alertas de seguridad relevantes en las últimas 12 horas. Requiere que los correos estén habilitados.",
|
||||
@@ -571,8 +567,8 @@
|
||||
"claim.email_to_sso.pwd": "Contraseña",
|
||||
"claim.email_to_sso.pwdError": "Por favor introduce tu contraseña.",
|
||||
"claim.email_to_sso.ssoType": "Al reclamar tu cuenta, sólo podrás iniciar sesión con {type} SSO",
|
||||
"claim.email_to_sso.switchTo": "Cambiar cuenta a ",
|
||||
"claim.email_to_sso.title": "Cambiar Cuenta de Correo/Contraseña a ",
|
||||
"claim.email_to_sso.switchTo": "Cambiar cuenta a {uiType}",
|
||||
"claim.email_to_sso.title": "Cambiar Cuenta de Correo/Contraseña a {uiType}",
|
||||
"claim.sso_to_email.confirm": "Confirmar Contraseña",
|
||||
"claim.sso_to_email.description": "Al cambiar el tipo de cuenta, sólo podrás iniciar sesión con tu correo electrónico y contraseña.",
|
||||
"claim.sso_to_email.enterPwd": "Por favor ingresa una contraseña.",
|
||||
|
||||
@@ -194,6 +194,10 @@ func TestIncomingWebhook(t *testing.T) {
|
||||
user = ApiClient.Must(ApiClient.CreateUser(user, "")).Data.(*model.User)
|
||||
store.Must(api.Srv.Store.User().VerifyEmail(user.Id))
|
||||
|
||||
c := &api.Context{}
|
||||
c.RequestId = model.NewId()
|
||||
c.IpAddress = "cmd_line"
|
||||
api.UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
|
||||
ApiClient.LoginByEmail(team.Name, user.Email, "pwd")
|
||||
|
||||
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
|
||||
|
||||
Ссылка в новой задаче
Block a user