Merge branch 'master' into advanced-permissions-phase-1
Этот коммит содержится в:
4
Makefile
4
Makefile
@@ -56,7 +56,7 @@ GO_LINKER_FLAGS ?= -ldflags \
|
||||
# GOOS/GOARCH of the build host, used to determine whether we're cross-compiling or not
|
||||
BUILDER_GOOS_GOARCH="$(shell $(GO) env GOOS)_$(shell $(GO) env GOARCH)"
|
||||
|
||||
PLATFORM_FILES=$(shell ls -1 ./cmd/platform/*.go | grep -v _test.go)
|
||||
PLATFORM_FILES="./main.go"
|
||||
|
||||
# Output paths
|
||||
DIST_ROOT=dist
|
||||
@@ -118,7 +118,7 @@ start-docker: ## Starts the docker containers for local development.
|
||||
|
||||
@if [ $(shell docker ps -a | grep -ci mattermost-inbucket) -eq 0 ]; then \
|
||||
echo starting mattermost-inbucket; \
|
||||
docker run --name mattermost-inbucket -p 9000:10080 -p 2500:10025 -d jhillyerd/inbucket:latest > /dev/null; \
|
||||
docker run --name mattermost-inbucket -p 9000:10080 -p 2500:10025 -d jhillyerd/inbucket:release-1.2.0 > /dev/null; \
|
||||
elif [ $(shell docker ps | grep -ci mattermost-inbucket) -eq 0 ]; then \
|
||||
echo restarting mattermost-inbucket; \
|
||||
docker start mattermost-inbucket > /dev/null; \
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/avct/uasurfer"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mssola/user_agent"
|
||||
)
|
||||
|
||||
func (api *API) InitAdmin() {
|
||||
@@ -201,12 +201,11 @@ func downloadComplianceReport(c *Context, w http.ResponseWriter, r *http.Request
|
||||
w.Header().Del("Content-Type") // Content-Type will be set automatically by the http writer
|
||||
|
||||
// attach extra headers to trigger a download on IE, Edge, and Safari
|
||||
ua := user_agent.New(r.UserAgent())
|
||||
bname, _ := ua.Browser()
|
||||
ua := uasurfer.Parse(r.UserAgent())
|
||||
|
||||
w.Header().Set("Content-Disposition", "attachment;filename=\""+job.JobName()+".zip\"")
|
||||
|
||||
if bname == "Edge" || bname == "Internet Explorer" || bname == "Safari" {
|
||||
if ua.Browser.Name == uasurfer.BrowserIE || ua.Browser.Name == uasurfer.BrowserSafari {
|
||||
// trim off anything before the final / so we just get the file's name
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
"github.com/mattermost/mattermost-server/store/sqlstore"
|
||||
)
|
||||
|
||||
func TestCreateChannel(t *testing.T) {
|
||||
@@ -345,7 +344,7 @@ func TestUpdateChannel(t *testing.T) {
|
||||
|
||||
th.MakeUserChannelUser(th.BasicUser, channel2)
|
||||
th.MakeUserChannelUser(th.BasicUser, channel3)
|
||||
sqlstore.ClearChannelCaches()
|
||||
th.App.Srv.Store.Channel().ClearCaches()
|
||||
|
||||
if _, err := Client.UpdateChannel(channel2); err == nil {
|
||||
t.Fatal("should have errored not channel admin")
|
||||
@@ -356,7 +355,7 @@ func TestUpdateChannel(t *testing.T) {
|
||||
|
||||
th.MakeUserChannelAdmin(th.BasicUser, channel2)
|
||||
th.MakeUserChannelAdmin(th.BasicUser, channel3)
|
||||
sqlstore.ClearChannelCaches()
|
||||
th.App.Srv.Store.Channel().ClearCaches()
|
||||
|
||||
if _, err := Client.UpdateChannel(channel2); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -508,7 +507,7 @@ func TestUpdateChannelHeader(t *testing.T) {
|
||||
|
||||
th.MakeUserChannelUser(th.BasicUser, channel2)
|
||||
th.MakeUserChannelUser(th.BasicUser, channel3)
|
||||
sqlstore.ClearChannelCaches()
|
||||
th.App.Srv.Store.Channel().ClearCaches()
|
||||
|
||||
if _, err := Client.UpdateChannelHeader(data2); err == nil {
|
||||
t.Fatal("should have errored not channel admin")
|
||||
@@ -519,7 +518,7 @@ func TestUpdateChannelHeader(t *testing.T) {
|
||||
|
||||
th.MakeUserChannelAdmin(th.BasicUser, channel2)
|
||||
th.MakeUserChannelAdmin(th.BasicUser, channel3)
|
||||
sqlstore.ClearChannelCaches()
|
||||
th.App.Srv.Store.Channel().ClearCaches()
|
||||
|
||||
if _, err := Client.UpdateChannelHeader(data2); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -629,7 +628,7 @@ func TestUpdateChannelPurpose(t *testing.T) {
|
||||
|
||||
th.MakeUserChannelUser(th.BasicUser, channel2)
|
||||
th.MakeUserChannelUser(th.BasicUser, channel3)
|
||||
sqlstore.ClearChannelCaches()
|
||||
th.App.Srv.Store.Channel().ClearCaches()
|
||||
|
||||
if _, err := Client.UpdateChannelPurpose(data2); err == nil {
|
||||
t.Fatal("should have errored not channel admin")
|
||||
@@ -640,7 +639,7 @@ func TestUpdateChannelPurpose(t *testing.T) {
|
||||
|
||||
th.MakeUserChannelAdmin(th.BasicUser, channel2)
|
||||
th.MakeUserChannelAdmin(th.BasicUser, channel3)
|
||||
sqlstore.ClearChannelCaches()
|
||||
th.App.Srv.Store.Channel().ClearCaches()
|
||||
|
||||
if _, err := Client.UpdateChannelPurpose(data2); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -1154,7 +1153,7 @@ func TestDeleteChannel(t *testing.T) {
|
||||
|
||||
th.MakeUserChannelAdmin(th.BasicUser, channel2)
|
||||
th.MakeUserChannelAdmin(th.BasicUser, channel3)
|
||||
sqlstore.ClearChannelCaches()
|
||||
th.App.Srv.Store.Channel().ClearCaches()
|
||||
|
||||
if _, err := Client.DeleteChannel(channel2.Id); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -364,10 +364,6 @@ func NewInvalidParamError(where string, name string) *model.AppError {
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Context) SetUnknownError(where string, details string) {
|
||||
c.Err = model.NewAppError(where, "api.context.unknown.app_error", nil, details, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
func (c *Context) SetPermissionError(permission *model.Permission) {
|
||||
c.Err = model.NewAppError("Permissions", "api.context.permissions.app_error", nil, "userId="+c.Session.UserId+", "+"permission="+permission.Id, http.StatusForbidden)
|
||||
}
|
||||
@@ -387,11 +383,6 @@ func (c *Context) SetSiteURLHeader(url string) {
|
||||
c.siteURLHeader = strings.TrimRight(url, "/")
|
||||
}
|
||||
|
||||
// TODO see where these are used
|
||||
func (c *Context) GetTeamURLFromTeam(team *model.Team) string {
|
||||
return c.GetSiteURLHeader() + "/" + team.Name
|
||||
}
|
||||
|
||||
func (c *Context) GetTeamURL() string {
|
||||
if !c.teamURLValid {
|
||||
c.SetTeamURLFromSession()
|
||||
@@ -406,10 +397,6 @@ func (c *Context) GetSiteURLHeader() string {
|
||||
return c.siteURLHeader
|
||||
}
|
||||
|
||||
func (c *Context) GetCurrentTeamMember() *model.TeamMember {
|
||||
return c.Session.GetTeamByTeamId(c.TeamId)
|
||||
}
|
||||
|
||||
func (c *Context) HandleEtag(etag string, routeName string, w http.ResponseWriter, r *http.Request) bool {
|
||||
metrics := c.App.Metrics
|
||||
if et := r.Header.Get(model.HEADER_ETAG_CLIENT); len(etag) > 0 {
|
||||
|
||||
@@ -136,10 +136,7 @@ func saveIsPinnedPost(c *Context, w http.ResponseWriter, r *http.Request, isPinn
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", rpost.ChannelId, "", nil)
|
||||
message.Add("post", c.App.PostWithProxyAddedToImageURLs(rpost).ToJson())
|
||||
|
||||
c.App.Go(func() {
|
||||
c.App.Publish(message)
|
||||
})
|
||||
c.App.Publish(message)
|
||||
|
||||
c.App.InvalidateCacheForChannelPosts(rpost.ChannelId)
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ func TestWebSocketEvent(t *testing.T) {
|
||||
}
|
||||
|
||||
evt2 := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_TYPING, "", "somerandomid", "", nil)
|
||||
go th.App.Publish(evt2)
|
||||
th.App.Publish(evt2)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
eventHit = false
|
||||
|
||||
@@ -468,6 +468,22 @@ func (me *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
member, err := me.App.AddUserToChannel(user, channel)
|
||||
if err != nil {
|
||||
l4g.Error(err.Error())
|
||||
l4g.Close()
|
||||
time.Sleep(time.Second)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
|
||||
return member
|
||||
}
|
||||
|
||||
func (me *TestHelper) GenerateTestEmail() string {
|
||||
if me.App.Config().EmailSettings.SMTPServer != "dockerhost" && os.Getenv("CI_INBUCKET_PORT") == "" {
|
||||
return strings.ToLower("success+" + model.NewId() + "@simulator.amazonses.com")
|
||||
@@ -511,18 +527,6 @@ func CheckUserSanitization(t *testing.T, user *model.User) {
|
||||
}
|
||||
}
|
||||
|
||||
func CheckTeamSanitization(t *testing.T, team *model.Team) {
|
||||
t.Helper()
|
||||
|
||||
if team.Email != "" {
|
||||
t.Fatal("email wasn't blank")
|
||||
}
|
||||
|
||||
if team.AllowedDomains != "" {
|
||||
t.Fatal("'allowed domains' wasn't blank")
|
||||
}
|
||||
}
|
||||
|
||||
func CheckEtag(t *testing.T, data interface{}, resp *model.Response) {
|
||||
t.Helper()
|
||||
|
||||
@@ -670,21 +674,6 @@ func CheckInternalErrorStatus(t *testing.T, resp *model.Response) {
|
||||
}
|
||||
}
|
||||
|
||||
func CheckPayLoadTooLargeStatus(t *testing.T, resp *model.Response) {
|
||||
t.Helper()
|
||||
|
||||
if resp.Error == nil {
|
||||
t.Fatal("should have errored with status:" + strconv.Itoa(http.StatusRequestEntityTooLarge))
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusRequestEntityTooLarge {
|
||||
t.Log("actual: " + strconv.Itoa(resp.StatusCode))
|
||||
t.Log("expected: " + strconv.Itoa(http.StatusRequestEntityTooLarge))
|
||||
t.Fatal("wrong status code")
|
||||
}
|
||||
}
|
||||
|
||||
func readTestFile(name string) ([]byte, error) {
|
||||
path, _ := utils.FindDir("tests")
|
||||
file, err := os.Open(path + "/" + name)
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store/sqlstore"
|
||||
)
|
||||
|
||||
func TestCreateChannel(t *testing.T) {
|
||||
@@ -874,7 +873,7 @@ func TestDeleteChannel(t *testing.T) {
|
||||
// successful delete by channel admin
|
||||
th.MakeUserChannelAdmin(user, publicChannel6)
|
||||
th.MakeUserChannelAdmin(user, privateChannel7)
|
||||
sqlstore.ClearChannelCaches()
|
||||
th.App.Srv.Store.Channel().ClearCaches()
|
||||
|
||||
_, resp = Client.DeleteChannel(publicChannel6.Id)
|
||||
CheckNoError(t, resp)
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/avct/uasurfer"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mssola/user_agent"
|
||||
)
|
||||
|
||||
func (api *API) InitCompliance() {
|
||||
@@ -108,12 +108,11 @@ func downloadComplianceReport(c *Context, w http.ResponseWriter, r *http.Request
|
||||
w.Header().Del("Content-Type") // Content-Type will be set automatically by the http writer
|
||||
|
||||
// attach extra headers to trigger a download on IE, Edge, and Safari
|
||||
ua := user_agent.New(r.UserAgent())
|
||||
bname, _ := ua.Browser()
|
||||
ua := uasurfer.Parse(r.UserAgent())
|
||||
|
||||
w.Header().Set("Content-Disposition", "attachment;filename=\""+job.JobName()+".zip\"")
|
||||
|
||||
if bname == "Edge" || bname == "Internet Explorer" || bname == "Safari" {
|
||||
if ua.Browser.Name == uasurfer.BrowserIE || ua.Browser.Name == uasurfer.BrowserSafari {
|
||||
// trim off anything before the final / so we just get the file's name
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
@@ -247,14 +246,7 @@ func getClientConfig(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
respCfg := map[string]string{}
|
||||
for k, v := range c.App.ClientConfig() {
|
||||
respCfg[k] = v
|
||||
}
|
||||
|
||||
respCfg["NoAccounts"] = strconv.FormatBool(c.App.IsFirstUserAccount())
|
||||
|
||||
w.Write([]byte(model.MapToJson(respCfg)))
|
||||
w.Write([]byte(model.MapToJson(c.App.ClientConfigWithNoAccounts())))
|
||||
}
|
||||
|
||||
func getClientLicense(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
14
api4/user.go
14
api4/user.go
@@ -290,16 +290,21 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if sort != "" && sort != "last_activity_at" && sort != "create_at" {
|
||||
if sort != "" && sort != "last_activity_at" && sort != "create_at" && sort != "status" {
|
||||
c.SetInvalidUrlParam("sort")
|
||||
return
|
||||
}
|
||||
|
||||
// Currently only supports sorting on a team
|
||||
// or sort="status" on inChannelId
|
||||
if (sort == "last_activity_at" || sort == "create_at") && (inTeamId == "" || notInTeamId != "" || inChannelId != "" || notInChannelId != "" || withoutTeam != "") {
|
||||
c.SetInvalidUrlParam("sort")
|
||||
return
|
||||
}
|
||||
if sort == "status" && inChannelId == "" {
|
||||
c.SetInvalidUrlParam("sort")
|
||||
return
|
||||
}
|
||||
|
||||
var profiles []*model.User
|
||||
var err *model.AppError
|
||||
@@ -355,8 +360,11 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.SetPermissionError(model.PERMISSION_READ_CHANNEL)
|
||||
return
|
||||
}
|
||||
|
||||
profiles, err = c.App.GetUsersInChannelPage(inChannelId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin())
|
||||
if sort == "status" {
|
||||
profiles, err = c.App.GetUsersInChannelPageByStatus(inChannelId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin())
|
||||
} else {
|
||||
profiles, err = c.App.GetUsersInChannelPage(inChannelId, c.Params.Page, c.Params.PerPage, c.IsSystemAdmin())
|
||||
}
|
||||
} else {
|
||||
// No permission check required
|
||||
|
||||
|
||||
@@ -2650,3 +2650,146 @@ func TestUserAccessTokenDisableConfig(t *testing.T) {
|
||||
_, resp = Client.GetMe("")
|
||||
CheckNoError(t, resp)
|
||||
}
|
||||
|
||||
func TestGetUsersByStatus(t *testing.T) {
|
||||
th := Setup()
|
||||
defer th.TearDown()
|
||||
|
||||
team, err := th.App.CreateTeam(&model.Team{
|
||||
DisplayName: "dn_" + model.NewId(),
|
||||
Name: GenerateTestTeamName(),
|
||||
Email: th.GenerateTestEmail(),
|
||||
Type: model.TEAM_OPEN,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create team: %v", err)
|
||||
}
|
||||
|
||||
channel, err := th.App.CreateChannel(&model.Channel{
|
||||
DisplayName: "dn_" + model.NewId(),
|
||||
Name: "name_" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
TeamId: team.Id,
|
||||
CreatorId: model.NewId(),
|
||||
}, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create channel: %v", err)
|
||||
}
|
||||
|
||||
createUserWithStatus := func(username string, status string) *model.User {
|
||||
id := model.NewId()
|
||||
|
||||
user, err := th.App.CreateUser(&model.User{
|
||||
Email: "success+" + id + "@simulator.amazonses.com",
|
||||
Username: "un_" + username + "_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
Password: "Password1",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create user: %v", err)
|
||||
}
|
||||
|
||||
th.LinkUserToTeam(user, team)
|
||||
th.AddUserToChannel(user, channel)
|
||||
|
||||
th.App.SaveAndBroadcastStatus(&model.Status{
|
||||
UserId: user.Id,
|
||||
Status: status,
|
||||
Manual: true,
|
||||
})
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
// Creating these out of order in case that affects results
|
||||
offlineUser1 := createUserWithStatus("offline1", model.STATUS_OFFLINE)
|
||||
offlineUser2 := createUserWithStatus("offline2", model.STATUS_OFFLINE)
|
||||
awayUser1 := createUserWithStatus("away1", model.STATUS_AWAY)
|
||||
awayUser2 := createUserWithStatus("away2", model.STATUS_AWAY)
|
||||
onlineUser1 := createUserWithStatus("online1", model.STATUS_ONLINE)
|
||||
onlineUser2 := createUserWithStatus("online2", model.STATUS_ONLINE)
|
||||
dndUser1 := createUserWithStatus("dnd1", model.STATUS_DND)
|
||||
dndUser2 := createUserWithStatus("dnd2", model.STATUS_DND)
|
||||
|
||||
client := th.CreateClient()
|
||||
if _, resp := client.Login(onlineUser2.Username, "Password1"); resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
}
|
||||
|
||||
t.Run("sorting by status then alphabetical", func(t *testing.T) {
|
||||
usersByStatus, resp := client.GetUsersInChannelByStatus(channel.Id, 0, 8, "")
|
||||
if resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
}
|
||||
|
||||
expectedUsersByStatus := []*model.User{
|
||||
onlineUser1,
|
||||
onlineUser2,
|
||||
awayUser1,
|
||||
awayUser2,
|
||||
dndUser1,
|
||||
dndUser2,
|
||||
offlineUser1,
|
||||
offlineUser2,
|
||||
}
|
||||
|
||||
if len(usersByStatus) != len(expectedUsersByStatus) {
|
||||
t.Fatalf("received only %v users, expected %v", len(usersByStatus), len(expectedUsersByStatus))
|
||||
}
|
||||
|
||||
for i := range usersByStatus {
|
||||
if usersByStatus[i].Id != expectedUsersByStatus[i].Id {
|
||||
t.Fatalf("received user %v at index %v, expected %v", usersByStatus[i].Username, i, expectedUsersByStatus[i].Username)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("paging", func(t *testing.T) {
|
||||
usersByStatus, resp := client.GetUsersInChannelByStatus(channel.Id, 0, 3, "")
|
||||
if resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
}
|
||||
|
||||
if len(usersByStatus) != 3 {
|
||||
t.Fatal("received too many users")
|
||||
}
|
||||
|
||||
if usersByStatus[0].Id != onlineUser1.Id && usersByStatus[1].Id != onlineUser2.Id {
|
||||
t.Fatal("expected to receive online users first")
|
||||
}
|
||||
|
||||
if usersByStatus[2].Id != awayUser1.Id {
|
||||
t.Fatal("expected to receive away users second")
|
||||
}
|
||||
|
||||
usersByStatus, resp = client.GetUsersInChannelByStatus(channel.Id, 1, 3, "")
|
||||
if resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
}
|
||||
|
||||
if usersByStatus[0].Id != awayUser2.Id {
|
||||
t.Fatal("expected to receive away users second")
|
||||
}
|
||||
|
||||
if usersByStatus[1].Id != dndUser1.Id && usersByStatus[2].Id != dndUser2.Id {
|
||||
t.Fatal("expected to receive dnd users third")
|
||||
}
|
||||
|
||||
usersByStatus, resp = client.GetUsersInChannelByStatus(channel.Id, 1, 4, "")
|
||||
if resp.Error != nil {
|
||||
t.Fatal(resp.Error)
|
||||
}
|
||||
|
||||
if len(usersByStatus) != 4 {
|
||||
t.Fatal("received too many users")
|
||||
}
|
||||
|
||||
if usersByStatus[0].Id != dndUser1.Id && usersByStatus[1].Id != dndUser2.Id {
|
||||
t.Fatal("expected to receive dnd users third")
|
||||
}
|
||||
|
||||
if usersByStatus[2].Id != offlineUser1.Id && usersByStatus[3].Id != offlineUser2.Id {
|
||||
t.Fatal("expected to receive offline users last")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -522,7 +522,6 @@ func commandWebhook(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func decodePayload(payload io.Reader) (*model.IncomingWebhookRequest, *model.AppError) {
|
||||
decodeError := &model.AppError{}
|
||||
incomingWebhookPayload, decodeError := model.IncomingWebhookRequestFromJson(payload)
|
||||
|
||||
if decodeError != nil {
|
||||
|
||||
10
app/admin.go
10
app/admin.go
@@ -15,7 +15,6 @@ import (
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store/sqlstore"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
@@ -141,10 +140,11 @@ func (a *App) InvalidateAllCachesSkipSend() {
|
||||
l4g.Info(utils.T("api.context.invalidate_all_caches"))
|
||||
a.sessionCache.Purge()
|
||||
ClearStatusCache()
|
||||
sqlstore.ClearChannelCaches()
|
||||
sqlstore.ClearUserCaches()
|
||||
sqlstore.ClearPostCaches()
|
||||
sqlstore.ClearWebhookCaches()
|
||||
a.Srv.Store.Channel().ClearCaches()
|
||||
a.Srv.Store.User().ClearCaches()
|
||||
a.Srv.Store.Post().ClearCaches()
|
||||
a.Srv.Store.FileInfo().ClearCaches()
|
||||
a.Srv.Store.Webhook().ClearCaches()
|
||||
a.LoadLicense()
|
||||
}
|
||||
|
||||
|
||||
18
app/app.go
18
app/app.go
@@ -133,8 +133,24 @@ func New(options ...Option) (outApp *App, outErr error) {
|
||||
|
||||
app.configListenerId = app.AddConfigListener(func(_, _ *model.Config) {
|
||||
app.configOrLicenseListener()
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CONFIG_CHANGED, "", "", "", nil)
|
||||
|
||||
message.Add("config", app.ClientConfigWithNoAccounts())
|
||||
app.Go(func() {
|
||||
app.Publish(message)
|
||||
})
|
||||
})
|
||||
app.licenseListenerId = app.AddLicenseListener(func() {
|
||||
app.configOrLicenseListener()
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_LICENSE_CHANGED, "", "", "", nil)
|
||||
message.Add("license", app.GetSanitizedClientLicense())
|
||||
app.Go(func() {
|
||||
app.Publish(message)
|
||||
})
|
||||
|
||||
})
|
||||
app.licenseListenerId = app.AddLicenseListener(app.configOrLicenseListener)
|
||||
app.regenerateClientConfig()
|
||||
|
||||
l4g.Info(utils.T("api.server.new_server.init.info"))
|
||||
|
||||
@@ -143,10 +143,6 @@ func (me *TestHelper) InitBasic() *TestHelper {
|
||||
return me
|
||||
}
|
||||
|
||||
func (me *TestHelper) MakeUsername() string {
|
||||
return "un_" + model.NewId()
|
||||
}
|
||||
|
||||
func (me *TestHelper) MakeEmail() string {
|
||||
return "success_" + model.NewId() + "@simulator.amazonses.com"
|
||||
}
|
||||
@@ -199,10 +195,6 @@ func (me *TestHelper) CreateChannel(team *model.Team) *model.Channel {
|
||||
return me.createChannel(team, model.CHANNEL_OPEN)
|
||||
}
|
||||
|
||||
func (me *TestHelper) CreatePrivateChannel(team *model.Team) *model.Channel {
|
||||
return me.createChannel(team, model.CHANNEL_PRIVATE)
|
||||
}
|
||||
|
||||
func (me *TestHelper) createChannel(team *model.Team, channelType string) *model.Channel {
|
||||
id := model.NewId()
|
||||
|
||||
@@ -262,6 +254,22 @@ func (me *TestHelper) LinkUserToTeam(user *model.User, team *model.Team) {
|
||||
utils.EnableDebugLogForTest()
|
||||
}
|
||||
|
||||
func (me *TestHelper) AddUserToChannel(user *model.User, channel *model.Channel) *model.ChannelMember {
|
||||
utils.DisableDebugLogForTest()
|
||||
|
||||
member, err := me.App.AddUserToChannel(user, channel)
|
||||
if err != nil {
|
||||
l4g.Error(err.Error())
|
||||
l4g.Close()
|
||||
time.Sleep(time.Second)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
utils.EnableDebugLogForTest()
|
||||
|
||||
return member
|
||||
}
|
||||
|
||||
func (me *TestHelper) TearDown() {
|
||||
me.App.Shutdown()
|
||||
os.Remove(me.tempConfigPath)
|
||||
|
||||
@@ -9,16 +9,15 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
USER_PASSWORD = "passwd"
|
||||
CHANNEL_TYPE = model.CHANNEL_OPEN
|
||||
FUZZ_USER_EMAIL_PREFIX_LEN = 10
|
||||
BTEST_TEAM_DISPLAY_NAME = "TestTeam"
|
||||
BTEST_TEAM_NAME = "z-z-testdomaina"
|
||||
BTEST_TEAM_EMAIL = "test@nowhere.com"
|
||||
BTEST_TEAM_TYPE = model.TEAM_OPEN
|
||||
BTEST_USER_NAME = "Mr. Testing Tester"
|
||||
BTEST_USER_EMAIL = "success+ttester@simulator.amazonses.com"
|
||||
BTEST_USER_PASSWORD = "passwd"
|
||||
USER_PASSWORD = "passwd"
|
||||
CHANNEL_TYPE = model.CHANNEL_OPEN
|
||||
BTEST_TEAM_DISPLAY_NAME = "TestTeam"
|
||||
BTEST_TEAM_NAME = "z-z-testdomaina"
|
||||
BTEST_TEAM_EMAIL = "test@nowhere.com"
|
||||
BTEST_TEAM_TYPE = model.TEAM_OPEN
|
||||
BTEST_USER_NAME = "Mr. Testing Tester"
|
||||
BTEST_USER_EMAIL = "success+ttester@simulator.amazonses.com"
|
||||
BTEST_USER_PASSWORD = "passwd"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -29,8 +28,5 @@ var (
|
||||
USER_EMAIL_LEN = utils.Range{Begin: 15, End: 30}
|
||||
CHANNEL_DISPLAY_NAME_LEN = utils.Range{Begin: 10, End: 20}
|
||||
CHANNEL_NAME_LEN = utils.Range{Begin: 5, End: 20}
|
||||
POST_MESSAGE_LEN = utils.Range{Begin: 100, End: 400}
|
||||
POST_HASHTAGS_NUM = utils.Range{Begin: 5, End: 10}
|
||||
POST_MENTIONS_NUM = utils.Range{Begin: 0, End: 3}
|
||||
TEST_IMAGE_FILENAMES = []string{"test.png", "testjpg.jpg", "testgif.gif"}
|
||||
)
|
||||
|
||||
@@ -90,18 +90,3 @@ func (cfg *AutoPostCreator) CreateRandomPost() (*model.Post, bool) {
|
||||
}
|
||||
return result.Data.(*model.Post), true
|
||||
}
|
||||
|
||||
func (cfg *AutoPostCreator) CreateTestPosts(rangePosts utils.Range) ([]*model.Post, bool) {
|
||||
numPosts := utils.RandIntFromRange(rangePosts)
|
||||
posts := make([]*model.Post, numPosts)
|
||||
|
||||
for i := 0; i < numPosts; i++ {
|
||||
var err bool
|
||||
posts[i], err = cfg.CreateRandomPost()
|
||||
if !err {
|
||||
return posts, false
|
||||
}
|
||||
}
|
||||
|
||||
return posts, true
|
||||
}
|
||||
|
||||
@@ -225,6 +225,14 @@ func (a *App) createDirectChannel(userId string, otherUserId string) (*model.Cha
|
||||
}
|
||||
} else {
|
||||
channel := result.Data.(*model.Channel)
|
||||
|
||||
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(userId, channel.Id, model.GetMillis()); result.Err != nil {
|
||||
l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
|
||||
}
|
||||
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(otherUserId, channel.Id, model.GetMillis()); result.Err != nil {
|
||||
l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
|
||||
}
|
||||
|
||||
return channel, nil
|
||||
}
|
||||
}
|
||||
@@ -369,7 +377,7 @@ func (a *App) postChannelPrivacyMessage(user *model.User, channel *model.Channel
|
||||
})[channel.Type]
|
||||
post := &model.Post{
|
||||
ChannelId: channel.Id,
|
||||
Message: fmt.Sprintf(utils.T("api.channel.change_channel_privacy." + privacy)),
|
||||
Message: utils.T("api.channel.change_channel_privacy." + privacy),
|
||||
Type: model.POST_CHANGE_CHANNEL_PRIVACY,
|
||||
UserId: user.Id,
|
||||
Props: model.StringInterface{
|
||||
@@ -549,7 +557,6 @@ func (a *App) DeleteChannel(channel *model.Channel, userId string) *model.AppErr
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_DELETED, channel.TeamId, "", "", nil)
|
||||
message.Add("channel_id", channel.Id)
|
||||
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
@@ -1059,7 +1066,7 @@ func (a *App) LeaveChannel(channelId string, userId string) *model.AppError {
|
||||
return err
|
||||
}
|
||||
|
||||
if channel.Name == model.DEFAULT_CHANNEL && *a.Config().ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages == false {
|
||||
if channel.Name == model.DEFAULT_CHANNEL && !*a.Config().ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1097,7 +1104,9 @@ func (a *App) PostAddToChannelMessage(user *model.User, addedUser *model.User, c
|
||||
UserId: user.Id,
|
||||
RootId: postRootId,
|
||||
Props: model.StringInterface{
|
||||
"userId": user.Id,
|
||||
"username": user.Username,
|
||||
"addedUserId": addedUser.Id,
|
||||
"addedUsername": addedUser.Username,
|
||||
},
|
||||
}
|
||||
@@ -1117,7 +1126,9 @@ func (a *App) postAddToTeamMessage(user *model.User, addedUser *model.User, chan
|
||||
UserId: user.Id,
|
||||
RootId: postRootId,
|
||||
Props: model.StringInterface{
|
||||
"userId": user.Id,
|
||||
"username": user.Username,
|
||||
"addedUserId": addedUser.Id,
|
||||
"addedUsername": addedUser.Username,
|
||||
},
|
||||
}
|
||||
@@ -1136,6 +1147,7 @@ func (a *App) postRemoveFromChannelMessage(removerUserId string, removedUser *mo
|
||||
Type: model.POST_REMOVE_FROM_CHANNEL,
|
||||
UserId: removerUserId,
|
||||
Props: model.StringInterface{
|
||||
"removedUserId": removedUser.Id,
|
||||
"removedUsername": removedUser.Username,
|
||||
},
|
||||
}
|
||||
@@ -1170,17 +1182,13 @@ func (a *App) removeUserFromChannel(userIdToRemove string, removerUserId string,
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", channel.Id, "", nil)
|
||||
message.Add("user_id", userIdToRemove)
|
||||
message.Add("remover_id", removerUserId)
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
|
||||
// because the removed user no longer belongs to the channel we need to send a separate websocket event
|
||||
userMsg := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_REMOVED, "", "", userIdToRemove, nil)
|
||||
userMsg.Add("channel_id", channel.Id)
|
||||
userMsg.Add("remover_id", removerUserId)
|
||||
a.Go(func() {
|
||||
a.Publish(userMsg)
|
||||
})
|
||||
a.Publish(userMsg)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1250,9 +1258,7 @@ func (a *App) UpdateChannelLastViewedAt(channelIds []string, userId string) *mod
|
||||
for _, channelId := range channelIds {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, "", "", userId, nil)
|
||||
message.Add("channel_id", channelId)
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1329,9 +1335,7 @@ func (a *App) ViewChannel(view *model.ChannelView, userId string, clearPushNotif
|
||||
if *a.Config().ServiceSettings.EnableChannelViewedMessages && model.IsValidId(view.ChannelId) {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, "", "", userId, nil)
|
||||
message.Add("channel_id", view.ChannelId)
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
return times, nil
|
||||
@@ -1434,7 +1438,16 @@ func (a *App) GetDirectChannel(userId1, userId2 string) (*model.Channel, *model.
|
||||
}
|
||||
a.InvalidateCacheForUser(userId1)
|
||||
a.InvalidateCacheForUser(userId2)
|
||||
return result.Data.(*model.Channel), nil
|
||||
|
||||
channel := result.Data.(*model.Channel)
|
||||
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(userId1, channel.Id, model.GetMillis()); result.Err != nil {
|
||||
l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
|
||||
}
|
||||
if result := <-a.Srv.Store.ChannelMemberHistory().LogJoinEvent(userId2, channel.Id, model.GetMillis()); result.Err != nil {
|
||||
l4g.Warn("Failed to update ChannelMemberHistory table %v", result.Err)
|
||||
}
|
||||
|
||||
return channel, nil
|
||||
} else if result.Err != nil {
|
||||
return nil, model.NewAppError("GetOrCreateDMChannel", "web.incoming_webhook.channel.app_error", nil, "err="+result.Err.Message, result.Err.StatusCode)
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ func TestMoveChannel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinDefaultChannelsTownSquare(t *testing.T) {
|
||||
func TestJoinDefaultChannelsCreatesChannelMemberHistoryRecordTownSquare(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -136,7 +136,7 @@ func TestJoinDefaultChannelsTownSquare(t *testing.T) {
|
||||
assert.True(t, found)
|
||||
}
|
||||
|
||||
func TestJoinDefaultChannelsOffTopic(t *testing.T) {
|
||||
func TestJoinDefaultChannelsCreatesChannelMemberHistoryRecordOffTopic(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -162,7 +162,7 @@ func TestJoinDefaultChannelsOffTopic(t *testing.T) {
|
||||
assert.True(t, found)
|
||||
}
|
||||
|
||||
func TestCreateChannelPublic(t *testing.T) {
|
||||
func TestCreateChannelPublicCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -176,7 +176,7 @@ func TestCreateChannelPublic(t *testing.T) {
|
||||
assert.Equal(t, publicChannel.Id, histories[0].ChannelId)
|
||||
}
|
||||
|
||||
func TestCreateChannelPrivate(t *testing.T) {
|
||||
func TestCreateChannelPrivateCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -205,7 +205,7 @@ func TestUpdateChannelPrivacy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateGroupChannel(t *testing.T) {
|
||||
func TestCreateGroupChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -233,7 +233,62 @@ func TestCreateGroupChannel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddUserToChannel(t *testing.T) {
|
||||
func TestCreateDirectChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
user1 := th.CreateUser()
|
||||
user2 := th.CreateUser()
|
||||
|
||||
if channel, err := th.App.CreateDirectChannel(user1.Id, user2.Id); err != nil {
|
||||
t.Fatal("Failed to create direct channel. Error: " + err.Message)
|
||||
} else {
|
||||
// there should be a ChannelMemberHistory record for both users
|
||||
histories := store.Must(th.App.Srv.Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, channel.Id)).([]*model.ChannelMemberHistoryResult)
|
||||
assert.Len(t, histories, 2)
|
||||
|
||||
historyId0 := histories[0].UserId
|
||||
historyId1 := histories[1].UserId
|
||||
switch historyId0 {
|
||||
case user1.Id:
|
||||
assert.Equal(t, user2.Id, historyId1)
|
||||
case user2.Id:
|
||||
assert.Equal(t, user1.Id, historyId1)
|
||||
default:
|
||||
t.Fatal("Unexpected user id " + historyId0 + " in ChannelMemberHistory table")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDirectChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
user1 := th.CreateUser()
|
||||
user2 := th.CreateUser()
|
||||
|
||||
// this function call implicitly creates a direct channel between the two users if one doesn't already exist
|
||||
if channel, err := th.App.GetDirectChannel(user1.Id, user2.Id); err != nil {
|
||||
t.Fatal("Failed to create direct channel. Error: " + err.Message)
|
||||
} else {
|
||||
// there should be a ChannelMemberHistory record for both users
|
||||
histories := store.Must(th.App.Srv.Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, channel.Id)).([]*model.ChannelMemberHistoryResult)
|
||||
assert.Len(t, histories, 2)
|
||||
|
||||
historyId0 := histories[0].UserId
|
||||
historyId1 := histories[1].UserId
|
||||
switch historyId0 {
|
||||
case user1.Id:
|
||||
assert.Equal(t, user2.Id, historyId1)
|
||||
case user2.Id:
|
||||
assert.Equal(t, user1.Id, historyId1)
|
||||
default:
|
||||
t.Fatal("Unexpected user id " + historyId0 + " in ChannelMemberHistory table")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddUserToChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -263,7 +318,7 @@ func TestAddUserToChannel(t *testing.T) {
|
||||
assert.Equal(t, groupUserIds, channelMemberHistoryUserIds)
|
||||
}
|
||||
|
||||
func TestRemoveUserFromChannel(t *testing.T) {
|
||||
func TestRemoveUserFromChannelUpdatesChannelMemberHistoryRecord(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
|
||||
@@ -74,9 +74,7 @@ func (a *App) setCollapsePreference(args *model.CommandArgs, isCollapse bool) *m
|
||||
|
||||
socketMessage := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCE_CHANGED, "", "", args.UserId, nil)
|
||||
socketMessage.Add("preference", pref.ToJson())
|
||||
a.Go(func() {
|
||||
a.Publish(socketMessage)
|
||||
})
|
||||
a.Publish(socketMessage)
|
||||
|
||||
var rmsg string
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
@@ -34,6 +35,7 @@ func (a *App) UpdateConfig(f func(*model.Config)) {
|
||||
updated := old.Clone()
|
||||
f(updated)
|
||||
a.config.Store(updated)
|
||||
|
||||
a.InvokeConfigListeners(old, updated)
|
||||
}
|
||||
|
||||
@@ -269,3 +271,16 @@ func (a *App) GetCookieDomain() string {
|
||||
func (a *App) GetSiteURL() string {
|
||||
return a.siteURL
|
||||
}
|
||||
|
||||
// ClientConfigWithNoAccounts gets the configuration in a format suitable for sending to the client.
|
||||
func (a *App) ClientConfigWithNoAccounts() map[string]string {
|
||||
respCfg := map[string]string{}
|
||||
for k, v := range a.ClientConfig() {
|
||||
respCfg[k] = v
|
||||
}
|
||||
|
||||
// NoAccounts is not actually part of the configuration, but is expected by the client.
|
||||
respCfg["NoAccounts"] = strconv.FormatBool(a.IsFirstUserAccount())
|
||||
|
||||
return respCfg
|
||||
}
|
||||
|
||||
@@ -63,3 +63,13 @@ func TestAsymmetricSigningKey(t *testing.T) {
|
||||
assert.NotNil(t, th.App.AsymmetricSigningKey())
|
||||
assert.NotEmpty(t, th.App.ClientConfig()["AsymmetricSigningPublicKey"])
|
||||
}
|
||||
|
||||
func TestClientConfigWithNoAccounts(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
config := th.App.ClientConfigWithNoAccounts()
|
||||
if _, ok := config["NoAccounts"]; !ok {
|
||||
t.Fatal("expected NoAccounts in returned config")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,11 +502,15 @@ func (a *App) trackConfig() {
|
||||
})
|
||||
|
||||
a.SendDiagnostic(TRACK_CONFIG_MESSAGE_EXPORT, map[string]interface{}{
|
||||
"enable_message_export": *cfg.MessageExportSettings.EnableExport,
|
||||
"export_format": *cfg.MessageExportSettings.ExportFormat,
|
||||
"daily_run_time": *cfg.MessageExportSettings.DailyRunTime,
|
||||
"default_export_from_timestamp": *cfg.MessageExportSettings.ExportFromTimestamp,
|
||||
"batch_size": *cfg.MessageExportSettings.BatchSize,
|
||||
"enable_message_export": *cfg.MessageExportSettings.EnableExport,
|
||||
"export_format": *cfg.MessageExportSettings.ExportFormat,
|
||||
"daily_run_time": *cfg.MessageExportSettings.DailyRunTime,
|
||||
"default_export_from_timestamp": *cfg.MessageExportSettings.ExportFromTimestamp,
|
||||
"batch_size": *cfg.MessageExportSettings.BatchSize,
|
||||
"global_relay_customer_type": *cfg.MessageExportSettings.GlobalRelaySettings.CustomerType,
|
||||
"is_default_global_relay_smtp_username": isDefault(*cfg.MessageExportSettings.GlobalRelaySettings.SmtpUsername, ""),
|
||||
"is_default_global_relay_smtp_password": isDefault(*cfg.MessageExportSettings.GlobalRelaySettings.SmtpPassword, ""),
|
||||
"is_default_global_relay_email_address": isDefault(*cfg.MessageExportSettings.GlobalRelaySettings.EmailAddress, ""),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,6 @@ func (a *App) CreateEmoji(sessionUserId string, emoji *model.Emoji, multiPartIma
|
||||
} else {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_EMOJI_ADDED, "", "", "", nil)
|
||||
message.Add("emoji", emoji.ToJson())
|
||||
|
||||
a.Publish(message)
|
||||
return result.Data.(*model.Emoji), nil
|
||||
}
|
||||
|
||||
14
app/login.go
14
app/login.go
@@ -9,8 +9,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/avct/uasurfer"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mssola/user_agent"
|
||||
)
|
||||
|
||||
func (a *App) AuthenticateUserForLogin(id, loginId, password, mfaToken, deviceId string, ldapOnly bool) (*model.User, *model.AppError) {
|
||||
@@ -71,19 +71,19 @@ func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User,
|
||||
session.SetExpireInDays(*a.Config().ServiceSettings.SessionLengthWebInDays)
|
||||
}
|
||||
|
||||
ua := user_agent.New(r.UserAgent())
|
||||
ua := uasurfer.Parse(r.UserAgent())
|
||||
|
||||
plat := ua.Platform()
|
||||
plat := ua.OS.Platform.String()
|
||||
if plat == "" {
|
||||
plat = "unknown"
|
||||
}
|
||||
|
||||
os := ua.OS()
|
||||
os := ua.OS.Name.String()
|
||||
if os == "" {
|
||||
os = "unknown"
|
||||
}
|
||||
|
||||
bname, bversion := ua.Browser()
|
||||
bname := ua.Browser.Name.String()
|
||||
if bname == "" {
|
||||
bname = "unknown"
|
||||
}
|
||||
@@ -92,9 +92,7 @@ func (a *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User,
|
||||
bname = "Desktop App"
|
||||
}
|
||||
|
||||
if bversion == "" {
|
||||
bversion = "0.0"
|
||||
}
|
||||
bversion := ua.Browser.Version
|
||||
|
||||
session.AddProp(model.SESSION_PROP_PLATFORM, plat)
|
||||
session.AddProp(model.SESSION_PROP_OS, os)
|
||||
|
||||
18
app/post.go
18
app/post.go
@@ -84,9 +84,7 @@ func (a *App) CreatePostAsUser(post *model.Post) (*model.Post, *model.AppError)
|
||||
if *a.Config().ServiceSettings.EnableChannelViewedMessages {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_CHANNEL_VIEWED, "", "", post.UserId, nil)
|
||||
message.Add("channel_id", post.ChannelId)
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,10 +312,7 @@ func (a *App) SendEphemeralPost(userId string, post *model.Post) *model.Post {
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_EPHEMERAL_MESSAGE, "", post.ChannelId, userId, nil)
|
||||
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
|
||||
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
|
||||
return post
|
||||
}
|
||||
@@ -417,10 +412,7 @@ func (a *App) PatchPost(postId string, patch *model.PostPatch) (*model.Post, *mo
|
||||
func (a *App) sendUpdatedPostEvent(post *model.Post) {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_EDITED, "", post.ChannelId, "", nil)
|
||||
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
|
||||
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
func (a *App) GetPostsPage(channelId string, page int, perPage int) (*model.PostList, *model.AppError) {
|
||||
@@ -560,10 +552,8 @@ func (a *App) DeletePost(postId string) (*model.Post, *model.AppError) {
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_POST_DELETED, "", post.ChannelId, "", nil)
|
||||
message.Add("post", a.PostWithProxyAddedToImageURLs(post).ToJson())
|
||||
a.Publish(message)
|
||||
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Go(func() {
|
||||
a.DeletePostFiles(post)
|
||||
})
|
||||
|
||||
@@ -55,9 +55,7 @@ func (a *App) UpdatePreferences(userId string, preferences model.Preferences) *m
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCES_CHANGED, "", "", userId, nil)
|
||||
message.Add("preferences", preferences.ToJson())
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -80,9 +78,7 @@ func (a *App) DeletePreferences(userId string, preferences model.Preferences) *m
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_PREFERENCES_DELETED, "", "", userId, nil)
|
||||
message.Add("preferences", preferences.ToJson())
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -84,28 +84,6 @@ func (cw *CorsWrapper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
const TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN = time.Second
|
||||
|
||||
type VaryBy struct {
|
||||
useIP bool
|
||||
useAuth bool
|
||||
}
|
||||
|
||||
func (m *VaryBy) Key(r *http.Request) string {
|
||||
key := ""
|
||||
|
||||
if m.useAuth {
|
||||
token, tokenLocation := ParseAuthTokenFromRequest(r)
|
||||
if tokenLocation != TokenLocationNotFound {
|
||||
key += token
|
||||
} else if m.useIP { // If we don't find an authentication token and IP based is enabled, fall back to IP
|
||||
key += utils.GetIpAddress(r)
|
||||
}
|
||||
} else if m.useIP { // Only if Auth based is not enabed do we use a plain IP based
|
||||
key = utils.GetIpAddress(r)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
func redirectHTTPToHTTPS(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Host == "" {
|
||||
http.Error(w, "Not Found", http.StatusNotFound)
|
||||
@@ -223,31 +201,6 @@ func (a *App) StartServer() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type tcpKeepAliveListener struct {
|
||||
*net.TCPListener
|
||||
}
|
||||
|
||||
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
|
||||
tc, err := ln.AcceptTCP()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tc.SetKeepAlive(true)
|
||||
tc.SetKeepAlivePeriod(3 * time.Minute)
|
||||
return tc, nil
|
||||
}
|
||||
|
||||
func (a *App) Listen(addr string) (net.Listener, error) {
|
||||
if addr == "" {
|
||||
addr = ":http"
|
||||
}
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tcpKeepAliveListener{ln.(*net.TCPListener)}, nil
|
||||
}
|
||||
|
||||
func (a *App) StopServer() {
|
||||
if a.Srv.Server != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), TIME_TO_WAIT_FOR_CONNECTIONS_TO_CLOSE_ON_SERVER_SHUTDOWN)
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestStartServerRateLimiterCriticalError(t *testing.T) {
|
||||
|
||||
// Attempt to use Rate Limiter with an invalid config
|
||||
a.UpdateConfig(func(cfg *model.Config) {
|
||||
*cfg.RateLimitSettings.Enable = true
|
||||
*cfg.RateLimitSettings.Enable = true
|
||||
*cfg.RateLimitSettings.MaxBurst = -100
|
||||
})
|
||||
|
||||
|
||||
@@ -138,6 +138,9 @@ func (a *App) ClearSessionCacheForUserSkipClusterSend(userId string) {
|
||||
session := ts.(*model.Session)
|
||||
if session.UserId == userId {
|
||||
a.sessionCache.Remove(key)
|
||||
if a.Metrics != nil {
|
||||
a.Metrics.IncrementMemCacheInvalidationCounterSession()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,13 +109,11 @@ func SlackParseUsers(data io.Reader) ([]SlackUser, error) {
|
||||
decoder := json.NewDecoder(data)
|
||||
|
||||
var users []SlackUser
|
||||
if err := decoder.Decode(&users); err != nil {
|
||||
// This actually returns errors that are ignored.
|
||||
// In this case it is erroring because of a null that Slack
|
||||
// introduced. So we just return the users here.
|
||||
return users, err
|
||||
}
|
||||
return users, nil
|
||||
err := decoder.Decode(&users)
|
||||
// This actually returns errors that are ignored.
|
||||
// In this case it is erroring because of a null that Slack
|
||||
// introduced. So we just return the users here.
|
||||
return users, err
|
||||
}
|
||||
|
||||
func SlackParsePosts(data io.Reader) ([]SlackPost, error) {
|
||||
|
||||
@@ -221,9 +221,7 @@ func (a *App) BroadcastStatus(status *model.Status) {
|
||||
event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
|
||||
event.Add("status", status.Status)
|
||||
event.Add("user_id", status.UserId)
|
||||
a.Go(func() {
|
||||
a.Publish(event)
|
||||
})
|
||||
a.Publish(event)
|
||||
}
|
||||
|
||||
func (a *App) SetStatusOffline(userId string, manual bool) {
|
||||
@@ -238,18 +236,7 @@ func (a *App) SetStatusOffline(userId string, manual bool) {
|
||||
|
||||
status = &model.Status{UserId: userId, Status: model.STATUS_OFFLINE, Manual: manual, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
|
||||
|
||||
a.AddStatusCache(status)
|
||||
|
||||
if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
|
||||
l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
|
||||
}
|
||||
|
||||
event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
|
||||
event.Add("status", model.STATUS_OFFLINE)
|
||||
event.Add("user_id", status.UserId)
|
||||
a.Go(func() {
|
||||
a.Publish(event)
|
||||
})
|
||||
a.SaveAndBroadcastStatus(status)
|
||||
}
|
||||
|
||||
func (a *App) SetStatusAwayIfNeeded(userId string, manual bool) {
|
||||
@@ -281,18 +268,7 @@ func (a *App) SetStatusAwayIfNeeded(userId string, manual bool) {
|
||||
status.Manual = manual
|
||||
status.ActiveChannel = ""
|
||||
|
||||
a.AddStatusCache(status)
|
||||
|
||||
if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
|
||||
l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
|
||||
}
|
||||
|
||||
event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
|
||||
event.Add("status", model.STATUS_AWAY)
|
||||
event.Add("user_id", status.UserId)
|
||||
a.Go(func() {
|
||||
a.Publish(event)
|
||||
})
|
||||
a.SaveAndBroadcastStatus(status)
|
||||
}
|
||||
|
||||
func (a *App) SetStatusDoNotDisturb(userId string) {
|
||||
@@ -309,18 +285,22 @@ func (a *App) SetStatusDoNotDisturb(userId string) {
|
||||
status.Status = model.STATUS_DND
|
||||
status.Manual = true
|
||||
|
||||
a.SaveAndBroadcastStatus(status)
|
||||
}
|
||||
|
||||
func (a *App) SaveAndBroadcastStatus(status *model.Status) *model.AppError {
|
||||
a.AddStatusCache(status)
|
||||
|
||||
if result := <-a.Srv.Store.Status().SaveOrUpdate(status); result.Err != nil {
|
||||
l4g.Error(utils.T("api.status.save_status.error"), userId, result.Err)
|
||||
l4g.Error(utils.T("api.status.save_status.error"), status.UserId, result.Err)
|
||||
}
|
||||
|
||||
event := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_STATUS_CHANGE, "", "", status.UserId, nil)
|
||||
event.Add("status", model.STATUS_DND)
|
||||
event.Add("status", status.Status)
|
||||
event.Add("user_id", status.UserId)
|
||||
a.Go(func() {
|
||||
a.Publish(event)
|
||||
})
|
||||
a.Publish(event)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetStatusFromCache(userId string) *model.Status {
|
||||
|
||||
40
app/status_test.go
Обычный файл
40
app/status_test.go
Обычный файл
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
func TestSaveStatus(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
user := th.BasicUser
|
||||
|
||||
for _, statusString := range []string{
|
||||
model.STATUS_ONLINE,
|
||||
model.STATUS_AWAY,
|
||||
model.STATUS_DND,
|
||||
model.STATUS_OFFLINE,
|
||||
} {
|
||||
t.Run(statusString, func(t *testing.T) {
|
||||
status := &model.Status{
|
||||
UserId: user.Id,
|
||||
Status: statusString,
|
||||
}
|
||||
|
||||
th.App.SaveAndBroadcastStatus(status)
|
||||
|
||||
after, err := th.App.GetStatus(user.Id)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get status after save: %v", err)
|
||||
} else if after.Status != statusString {
|
||||
t.Fatalf("failed to save status, got %v, expected %v", after.Status, statusString)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -139,9 +139,7 @@ func (a *App) sendTeamEvent(team *model.Team, event string) {
|
||||
|
||||
message := model.NewWebSocketEvent(event, "", "", "", nil)
|
||||
message.Add("team", sanitizedTeam.ToJson())
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
func (a *App) UpdateTeamMemberRoles(teamId string, userId string, newRoles string) (*model.TeamMember, *model.AppError) {
|
||||
@@ -182,10 +180,7 @@ func (a *App) UpdateTeamMemberRoles(teamId string, userId string, newRoles strin
|
||||
func (a *App) sendUpdatedMemberRoleEvent(userId string, member *model.TeamMember) {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_MEMBERROLE_UPDATED, "", "", userId, nil)
|
||||
message.Add("member", member.ToJson())
|
||||
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
func (a *App) AddUserToTeam(teamId string, userId string, userRequestorId string) (*model.Team, *model.AppError) {
|
||||
|
||||
31
app/user.go
31
app/user.go
@@ -34,7 +34,6 @@ const (
|
||||
TOKEN_TYPE_PASSWORD_RECOVERY = "password_recovery"
|
||||
TOKEN_TYPE_VERIFY_EMAIL = "verify_email"
|
||||
PASSWORD_RECOVER_EXPIRY_TIME = 1000 * 60 * 60 // 1 hour
|
||||
VERIFY_EMAIL_EXPIRY_TIME = 1000 * 60 * 60 // 1 hour
|
||||
IMAGE_PROFILE_PIXEL_DIMENSION = 128
|
||||
)
|
||||
|
||||
@@ -202,9 +201,7 @@ func (a *App) CreateUser(user *model.User) (*model.User, *model.AppError) {
|
||||
// This message goes to everyone, so the teamId, channelId and userId are irrelevant
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_NEW_USER, "", "", "", nil)
|
||||
message.Add("user_id", ruser.Id)
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
|
||||
return ruser, nil
|
||||
}
|
||||
@@ -508,6 +505,14 @@ func (a *App) GetUsersInChannel(channelId string, offset int, limit int) ([]*mod
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) GetUsersInChannelByStatus(channelId string, offset int, limit int) ([]*model.User, *model.AppError) {
|
||||
if result := <-a.Srv.Store.User().GetProfilesInChannelByStatus(channelId, offset, limit); result.Err != nil {
|
||||
return nil, result.Err
|
||||
} else {
|
||||
return result.Data.([]*model.User), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (a *App) GetUsersInChannelMap(channelId string, offset int, limit int, asAdmin bool) (map[string]*model.User, *model.AppError) {
|
||||
users, err := a.GetUsersInChannel(channelId, offset, limit)
|
||||
if err != nil {
|
||||
@@ -533,6 +538,15 @@ func (a *App) GetUsersInChannelPage(channelId string, page int, perPage int, asA
|
||||
return a.sanitizeProfiles(users, asAdmin), nil
|
||||
}
|
||||
|
||||
func (a *App) GetUsersInChannelPageByStatus(channelId string, page int, perPage int, asAdmin bool) ([]*model.User, *model.AppError) {
|
||||
users, err := a.GetUsersInChannelByStatus(channelId, page*perPage, perPage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return a.sanitizeProfiles(users, asAdmin), nil
|
||||
}
|
||||
|
||||
func (a *App) GetUsersNotInChannel(teamId string, channelId string, offset int, limit int) ([]*model.User, *model.AppError) {
|
||||
if result := <-a.Srv.Store.User().GetProfilesNotInChannel(teamId, channelId, offset, limit); result.Err != nil {
|
||||
return nil, result.Err
|
||||
@@ -832,7 +846,6 @@ func (a *App) SetProfileImageFromFile(userId string, file multipart.File) *model
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", nil)
|
||||
message.Add("user", user)
|
||||
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
@@ -901,10 +914,6 @@ func (a *App) UpdateActive(user *model.User, active bool) (*model.User, *model.A
|
||||
}
|
||||
}
|
||||
|
||||
if extra := <-a.Srv.Store.Channel().ExtraUpdateByUser(user.Id, model.GetMillis()); extra.Err != nil {
|
||||
return nil, extra.Err
|
||||
}
|
||||
|
||||
ruser := result.Data.([2]*model.User)[0]
|
||||
options := a.Config().GetSanitizeOptions()
|
||||
options["passwordupdate"] = false
|
||||
@@ -1002,9 +1011,7 @@ func (a *App) sendUpdatedUserEvent(user model.User, asAdmin bool) {
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_USER_UPDATED, "", "", "", nil)
|
||||
message.Add("user", user)
|
||||
a.Go(func() {
|
||||
a.Publish(message)
|
||||
})
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
func (a *App) UpdateUser(user *model.User, sendNotifications bool) (*model.User, *model.AppError) {
|
||||
|
||||
129
app/user_test.go
129
app/user_test.go
@@ -299,3 +299,132 @@ func createGitlabUser(t *testing.T, a *App, email string, username string) (*mod
|
||||
|
||||
return user, gitlabUserObj
|
||||
}
|
||||
|
||||
func TestGetUsersByStatus(t *testing.T) {
|
||||
th := Setup()
|
||||
defer th.TearDown()
|
||||
|
||||
team := th.CreateTeam()
|
||||
channel, err := th.App.CreateChannel(&model.Channel{
|
||||
DisplayName: "dn_" + model.NewId(),
|
||||
Name: "name_" + model.NewId(),
|
||||
Type: model.CHANNEL_OPEN,
|
||||
TeamId: team.Id,
|
||||
CreatorId: model.NewId(),
|
||||
}, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create channel: %v", err)
|
||||
}
|
||||
|
||||
createUserWithStatus := func(username string, status string) *model.User {
|
||||
id := model.NewId()
|
||||
|
||||
user, err := th.App.CreateUser(&model.User{
|
||||
Email: "success+" + id + "@simulator.amazonses.com",
|
||||
Username: "un_" + username + "_" + id,
|
||||
Nickname: "nn_" + id,
|
||||
Password: "Password1",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create user: %v", err)
|
||||
}
|
||||
|
||||
th.LinkUserToTeam(user, team)
|
||||
th.AddUserToChannel(user, channel)
|
||||
|
||||
th.App.SaveAndBroadcastStatus(&model.Status{
|
||||
UserId: user.Id,
|
||||
Status: status,
|
||||
Manual: true,
|
||||
})
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
// Creating these out of order in case that affects results
|
||||
awayUser1 := createUserWithStatus("away1", model.STATUS_AWAY)
|
||||
awayUser2 := createUserWithStatus("away2", model.STATUS_AWAY)
|
||||
dndUser1 := createUserWithStatus("dnd1", model.STATUS_DND)
|
||||
dndUser2 := createUserWithStatus("dnd2", model.STATUS_DND)
|
||||
offlineUser1 := createUserWithStatus("offline1", model.STATUS_OFFLINE)
|
||||
offlineUser2 := createUserWithStatus("offline2", model.STATUS_OFFLINE)
|
||||
onlineUser1 := createUserWithStatus("online1", model.STATUS_ONLINE)
|
||||
onlineUser2 := createUserWithStatus("online2", model.STATUS_ONLINE)
|
||||
|
||||
t.Run("sorting by status then alphabetical", func(t *testing.T) {
|
||||
usersByStatus, err := th.App.GetUsersInChannelPageByStatus(channel.Id, 0, 8, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedUsersByStatus := []*model.User{
|
||||
onlineUser1,
|
||||
onlineUser2,
|
||||
awayUser1,
|
||||
awayUser2,
|
||||
dndUser1,
|
||||
dndUser2,
|
||||
offlineUser1,
|
||||
offlineUser2,
|
||||
}
|
||||
|
||||
if len(usersByStatus) != len(expectedUsersByStatus) {
|
||||
t.Fatalf("received only %v users, expected %v", len(usersByStatus), len(expectedUsersByStatus))
|
||||
}
|
||||
|
||||
for i := range usersByStatus {
|
||||
if usersByStatus[i].Id != expectedUsersByStatus[i].Id {
|
||||
t.Fatalf("received user %v at index %v, expected %v", usersByStatus[i].Username, i, expectedUsersByStatus[i].Username)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("paging", func(t *testing.T) {
|
||||
usersByStatus, err := th.App.GetUsersInChannelPageByStatus(channel.Id, 0, 3, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(usersByStatus) != 3 {
|
||||
t.Fatal("received too many users")
|
||||
}
|
||||
|
||||
if usersByStatus[0].Id != onlineUser1.Id && usersByStatus[1].Id != onlineUser2.Id {
|
||||
t.Fatal("expected to receive online users first")
|
||||
}
|
||||
|
||||
if usersByStatus[2].Id != awayUser1.Id {
|
||||
t.Fatal("expected to receive away users second")
|
||||
}
|
||||
|
||||
usersByStatus, err = th.App.GetUsersInChannelPageByStatus(channel.Id, 1, 3, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if usersByStatus[0].Id != awayUser2.Id {
|
||||
t.Fatal("expected to receive away users second")
|
||||
}
|
||||
|
||||
if usersByStatus[1].Id != dndUser1.Id && usersByStatus[2].Id != dndUser2.Id {
|
||||
t.Fatal("expected to receive dnd users third")
|
||||
}
|
||||
|
||||
usersByStatus, err = th.App.GetUsersInChannelPageByStatus(channel.Id, 1, 4, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(usersByStatus) != 4 {
|
||||
t.Fatal("received too many users")
|
||||
}
|
||||
|
||||
if usersByStatus[0].Id != dndUser1.Id && usersByStatus[1].Id != dndUser2.Id {
|
||||
t.Fatal("expected to receive dnd users third")
|
||||
}
|
||||
|
||||
if usersByStatus[2].Id != offlineUser1.Id && usersByStatus[3].Id != offlineUser2.Id {
|
||||
t.Fatal("expected to receive offline users last")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -225,7 +225,7 @@ func SplitWebhookPost(post *model.Post) ([]*model.Post, *model.AppError) {
|
||||
|
||||
func (a *App) CreateWebhookPost(userId string, channel *model.Channel, text, overrideUsername, overrideIconUrl string, props model.StringInterface, postType string, postRootId string) (*model.Post, *model.AppError) {
|
||||
// parse links into Markdown format
|
||||
linkWithTextRegex := regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`)
|
||||
linkWithTextRegex := regexp.MustCompile(`<([^\n<\|>]+)\|([^\n>]+)>`)
|
||||
text = linkWithTextRegex.ReplaceAllString(text, "[${2}](${1})")
|
||||
|
||||
post := &model.Post{UserId: userId, ChannelId: channel.Id, Message: text, Type: postType, RootId: postRootId}
|
||||
|
||||
@@ -317,6 +317,64 @@ func TestCreateWebhookPost(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatal("should have failed - bad post type")
|
||||
}
|
||||
|
||||
expectedText := "`<>|<>|`"
|
||||
post, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", model.StringInterface{
|
||||
"attachments": []*model.SlackAttachment{
|
||||
{
|
||||
Text: "text",
|
||||
},
|
||||
},
|
||||
"webhook_display_name": hook.DisplayName,
|
||||
}, model.POST_SLACK_ATTACHMENT, "")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
assert.Equal(t, expectedText, post.Message)
|
||||
|
||||
expectedText = "< | \n|\n>"
|
||||
post, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", model.StringInterface{
|
||||
"attachments": []*model.SlackAttachment{
|
||||
{
|
||||
Text: "text",
|
||||
},
|
||||
},
|
||||
"webhook_display_name": hook.DisplayName,
|
||||
}, model.POST_SLACK_ATTACHMENT, "")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
assert.Equal(t, expectedText, post.Message)
|
||||
|
||||
expectedText = `commit bc95839e4a430ace453e8b209a3723c000c1729a
|
||||
Author: foo <foo@example.org>
|
||||
Date: Thu Mar 1 19:46:54 2018 +0300
|
||||
|
||||
commit message 2
|
||||
|
||||
test | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
commit 5df78b7139b543997838071cd912e375d8bd69b2
|
||||
Author: foo <foo@example.org>
|
||||
Date: Thu Mar 1 19:46:48 2018 +0300
|
||||
|
||||
commit message 1
|
||||
|
||||
test | 3 +++
|
||||
1 file changed, 3 insertions(+)`
|
||||
post, err = th.App.CreateWebhookPost(hook.UserId, th.BasicChannel, expectedText, "user", "http://iconurl", model.StringInterface{
|
||||
"attachments": []*model.SlackAttachment{
|
||||
{
|
||||
Text: "text",
|
||||
},
|
||||
},
|
||||
"webhook_display_name": hook.DisplayName,
|
||||
}, model.POST_SLACK_ATTACHMENT, "")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
assert.Equal(t, expectedText, post.Message)
|
||||
}
|
||||
|
||||
func TestSplitWebhookPost(t *testing.T) {
|
||||
|
||||
2
build/Jenkinsfile
поставляемый
2
build/Jenkinsfile
поставляемый
@@ -31,7 +31,7 @@ podTemplate(label: 'jenkins-slave',
|
||||
),
|
||||
containerTemplate(
|
||||
name: 'mattermost-inbucket',
|
||||
image: 'jhillyerd/inbucket:latest',
|
||||
image: 'jhillyerd/inbucket:release-1.2.0',
|
||||
resourceRequestCpu: '250m',
|
||||
resourceLimitCpu: '250m',
|
||||
resourceRequestMemory: '256Mi',
|
||||
|
||||
@@ -3,15 +3,27 @@ dist: | check-style test package
|
||||
|
||||
build-linux:
|
||||
@echo Build Linux amd64
|
||||
env GOOS=linux GOARCH=amd64 $(GO) install $(GOFLAGS) $(GO_LINKER_FLAGS) ./cmd/platform
|
||||
ifeq ($(BUILDER_GOOS_GOARCH),"linux_amd64")
|
||||
env GOOS=linux GOARCH=amd64 $(GO) build -i -o $(GOPATH)/bin/platform $(GOFLAGS) $(GO_LINKER_FLAGS) ./
|
||||
else
|
||||
env GOOS=linux GOARCH=amd64 $(GO) build -i -o $(GOPATH)/bin/linux_amd64/platform $(GOFLAGS) $(GO_LINKER_FLAGS) ./
|
||||
endif
|
||||
|
||||
build-osx:
|
||||
@echo Build OSX amd64
|
||||
env GOOS=darwin GOARCH=amd64 $(GO) install $(GOFLAGS) $(GO_LINKER_FLAGS) ./cmd/platform
|
||||
ifeq ($(BUILDER_GOOS_GOARCH),"darwin_amd64")
|
||||
env GOOS=darwin GOARCH=amd64 $(GO) build -i -o $(GOPATH)/bin/platform $(GOFLAGS) $(GO_LINKER_FLAGS) ./
|
||||
else
|
||||
env GOOS=darwin GOARCH=amd64 $(GO) build -i -o $(GOPATH)/bin/darwin_amd64/platform $(GOFLAGS) $(GO_LINKER_FLAGS) ./
|
||||
endif
|
||||
|
||||
build-windows:
|
||||
@echo Build Windows amd64
|
||||
env GOOS=windows GOARCH=amd64 $(GO) install $(GOFLAGS) $(GO_LINKER_FLAGS) ./cmd/platform
|
||||
ifeq ($(BUILDER_GOOS_GOARCH),"windows_amd64")
|
||||
env GOOS=windows GOARCH=amd64 $(GO) build -i -o $(GOPATH)/bin/platform.exe $(GOFLAGS) $(GO_LINKER_FLAGS) ./
|
||||
else
|
||||
env GOOS=windows GOARCH=amd64 $(GO) build -i -o $(GOPATH)/bin/windows_amd64/platform.exe $(GOFLAGS) $(GO_LINKER_FLAGS) ./
|
||||
endif
|
||||
|
||||
build: build-linux build-windows build-osx
|
||||
|
||||
|
||||
26
cmd/cmd.go
Обычный файл
26
cmd/cmd.go
Обычный файл
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type Command = cobra.Command
|
||||
|
||||
func Run(args []string) error {
|
||||
RootCmd.SetArgs(args)
|
||||
return RootCmd.Execute()
|
||||
}
|
||||
|
||||
var RootCmd = &cobra.Command{
|
||||
Use: "platform",
|
||||
Short: "Open source, self-hosted Slack-alternative",
|
||||
Long: `Mattermost offers workplace messaging across web, PC and phones with archiving, search and integration with your existing systems. Documentation available at https://docs.mattermost.com`,
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.PersistentFlags().StringP("config", "c", "config.json", "Configuration file to use.")
|
||||
RootCmd.PersistentFlags().Bool("disableconfigwatch", false, "When set config.json will not be loaded from disk when the file is changed.")
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"flag"
|
||||
@@ -30,7 +30,7 @@ func execArgs(t *testing.T, args []string) []string {
|
||||
return append(append(ret, "--", "--disableconfigwatch"), args...)
|
||||
}
|
||||
|
||||
func checkCommand(t *testing.T, args ...string) string {
|
||||
func CheckCommand(t *testing.T, args ...string) string {
|
||||
path, err := os.Executable()
|
||||
require.NoError(t, err)
|
||||
output, err := exec.Command(path, execArgs(t, args)...).CombinedOutput()
|
||||
@@ -38,16 +38,8 @@ func checkCommand(t *testing.T, args ...string) string {
|
||||
return strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(string(output)), "PASS"))
|
||||
}
|
||||
|
||||
func runCommand(t *testing.T, args ...string) error {
|
||||
func RunCommand(t *testing.T, args ...string) error {
|
||||
path, err := os.Executable()
|
||||
require.NoError(t, err)
|
||||
return exec.Command(path, execArgs(t, args)...).Run()
|
||||
}
|
||||
|
||||
func TestExecCommand(t *testing.T) {
|
||||
if filter := flag.Lookup("test.run").Value.String(); filter != "ExecCommand" {
|
||||
t.Skip("use -run ExecCommand to execute a command via the test executable")
|
||||
}
|
||||
rootCmd.SetArgs(flag.Args())
|
||||
require.NoError(t, rootCmd.Execute())
|
||||
}
|
||||
@@ -1,22 +1,24 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var channelCmd = &cobra.Command{
|
||||
var ChannelCmd = &cobra.Command{
|
||||
Use: "channel",
|
||||
Short: "Management of channels",
|
||||
}
|
||||
|
||||
var channelCreateCmd = &cobra.Command{
|
||||
var ChannelCreateCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create a channel",
|
||||
Long: `Create a channel.`,
|
||||
@@ -25,7 +27,7 @@ var channelCreateCmd = &cobra.Command{
|
||||
RunE: createChannelCmdF,
|
||||
}
|
||||
|
||||
var removeChannelUsersCmd = &cobra.Command{
|
||||
var RemoveChannelUsersCmd = &cobra.Command{
|
||||
Use: "remove [channel] [users]",
|
||||
Short: "Remove users from channel",
|
||||
Long: "Remove some users from channel",
|
||||
@@ -33,7 +35,7 @@ var removeChannelUsersCmd = &cobra.Command{
|
||||
RunE: removeChannelUsersCmdF,
|
||||
}
|
||||
|
||||
var addChannelUsersCmd = &cobra.Command{
|
||||
var AddChannelUsersCmd = &cobra.Command{
|
||||
Use: "add [channel] [users]",
|
||||
Short: "Add users to channel",
|
||||
Long: "Add some users to channel",
|
||||
@@ -41,7 +43,7 @@ var addChannelUsersCmd = &cobra.Command{
|
||||
RunE: addChannelUsersCmdF,
|
||||
}
|
||||
|
||||
var archiveChannelsCmd = &cobra.Command{
|
||||
var ArchiveChannelsCmd = &cobra.Command{
|
||||
Use: "archive [channels]",
|
||||
Short: "Archive channels",
|
||||
Long: `Archive some channels.
|
||||
@@ -51,7 +53,7 @@ Channels can be specified by [team]:[channel]. ie. myteam:mychannel or by channe
|
||||
RunE: archiveChannelsCmdF,
|
||||
}
|
||||
|
||||
var deleteChannelsCmd = &cobra.Command{
|
||||
var DeleteChannelsCmd = &cobra.Command{
|
||||
Use: "delete [channels]",
|
||||
Short: "Delete channels",
|
||||
Long: `Permanently delete some channels.
|
||||
@@ -61,7 +63,7 @@ Channels can be specified by [team]:[channel]. ie. myteam:mychannel or by channe
|
||||
RunE: deleteChannelsCmdF,
|
||||
}
|
||||
|
||||
var listChannelsCmd = &cobra.Command{
|
||||
var ListChannelsCmd = &cobra.Command{
|
||||
Use: "list [teams]",
|
||||
Short: "List all channels on specified teams.",
|
||||
Long: `List all channels on specified teams.
|
||||
@@ -70,7 +72,7 @@ Archived channels are appended with ' (archived)'.`,
|
||||
RunE: listChannelsCmdF,
|
||||
}
|
||||
|
||||
var moveChannelsCmd = &cobra.Command{
|
||||
var MoveChannelsCmd = &cobra.Command{
|
||||
Use: "move [team] [channels]",
|
||||
Short: "Moves channels to the specified team",
|
||||
Long: `Moves the provided channels to the specified team.
|
||||
@@ -80,7 +82,7 @@ Channels can be specified by [team]:[channel]. ie. myteam:mychannel or by channe
|
||||
RunE: moveChannelsCmdF,
|
||||
}
|
||||
|
||||
var restoreChannelsCmd = &cobra.Command{
|
||||
var RestoreChannelsCmd = &cobra.Command{
|
||||
Use: "restore [channels]",
|
||||
Short: "Restore some channels",
|
||||
Long: `Restore a previously deleted channel
|
||||
@@ -89,7 +91,7 @@ Channels can be specified by [team]:[channel]. ie. myteam:mychannel or by channe
|
||||
RunE: restoreChannelsCmdF,
|
||||
}
|
||||
|
||||
var modifyChannelCmd = &cobra.Command{
|
||||
var ModifyChannelCmd = &cobra.Command{
|
||||
Use: "modify [channel]",
|
||||
Short: "Modify a channel's public/private type",
|
||||
Long: `Change the public/private type of a channel.
|
||||
@@ -99,55 +101,57 @@ Channel can be specified by [team]:[channel]. ie. myteam:mychannel or by channel
|
||||
}
|
||||
|
||||
func init() {
|
||||
channelCreateCmd.Flags().String("name", "", "Channel Name")
|
||||
channelCreateCmd.Flags().String("display_name", "", "Channel Display Name")
|
||||
channelCreateCmd.Flags().String("team", "", "Team name or ID")
|
||||
channelCreateCmd.Flags().String("header", "", "Channel header")
|
||||
channelCreateCmd.Flags().String("purpose", "", "Channel purpose")
|
||||
channelCreateCmd.Flags().Bool("private", false, "Create a private channel.")
|
||||
ChannelCreateCmd.Flags().String("name", "", "Channel Name")
|
||||
ChannelCreateCmd.Flags().String("display_name", "", "Channel Display Name")
|
||||
ChannelCreateCmd.Flags().String("team", "", "Team name or ID")
|
||||
ChannelCreateCmd.Flags().String("header", "", "Channel header")
|
||||
ChannelCreateCmd.Flags().String("purpose", "", "Channel purpose")
|
||||
ChannelCreateCmd.Flags().Bool("private", false, "Create a private channel.")
|
||||
|
||||
moveChannelsCmd.Flags().String("username", "", "Required. Username who is moving the channel.")
|
||||
MoveChannelsCmd.Flags().String("username", "", "Required. Username who is moving the channel.")
|
||||
|
||||
deleteChannelsCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the channels.")
|
||||
DeleteChannelsCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the channels.")
|
||||
|
||||
modifyChannelCmd.Flags().Bool("private", false, "Convert the channel to a private channel")
|
||||
modifyChannelCmd.Flags().Bool("public", false, "Convert the channel to a public channel")
|
||||
modifyChannelCmd.Flags().String("username", "", "Required. Username who changes the channel privacy.")
|
||||
ModifyChannelCmd.Flags().Bool("private", false, "Convert the channel to a private channel")
|
||||
ModifyChannelCmd.Flags().Bool("public", false, "Convert the channel to a public channel")
|
||||
ModifyChannelCmd.Flags().String("username", "", "Required. Username who changes the channel privacy.")
|
||||
|
||||
channelCmd.AddCommand(
|
||||
channelCreateCmd,
|
||||
removeChannelUsersCmd,
|
||||
addChannelUsersCmd,
|
||||
archiveChannelsCmd,
|
||||
deleteChannelsCmd,
|
||||
listChannelsCmd,
|
||||
moveChannelsCmd,
|
||||
restoreChannelsCmd,
|
||||
modifyChannelCmd,
|
||||
ChannelCmd.AddCommand(
|
||||
ChannelCreateCmd,
|
||||
RemoveChannelUsersCmd,
|
||||
AddChannelUsersCmd,
|
||||
ArchiveChannelsCmd,
|
||||
DeleteChannelsCmd,
|
||||
ListChannelsCmd,
|
||||
MoveChannelsCmd,
|
||||
RestoreChannelsCmd,
|
||||
ModifyChannelCmd,
|
||||
)
|
||||
|
||||
cmd.RootCmd.AddCommand(ChannelCmd)
|
||||
}
|
||||
|
||||
func createChannelCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func createChannelCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name, errn := cmd.Flags().GetString("name")
|
||||
name, errn := command.Flags().GetString("name")
|
||||
if errn != nil || name == "" {
|
||||
return errors.New("Name is required")
|
||||
}
|
||||
displayname, errdn := cmd.Flags().GetString("display_name")
|
||||
displayname, errdn := command.Flags().GetString("display_name")
|
||||
if errdn != nil || displayname == "" {
|
||||
return errors.New("Display Name is required")
|
||||
}
|
||||
teamArg, errteam := cmd.Flags().GetString("team")
|
||||
teamArg, errteam := command.Flags().GetString("team")
|
||||
if errteam != nil || teamArg == "" {
|
||||
return errors.New("Team is required")
|
||||
}
|
||||
header, _ := cmd.Flags().GetString("header")
|
||||
purpose, _ := cmd.Flags().GetString("purpose")
|
||||
useprivate, _ := cmd.Flags().GetBool("private")
|
||||
header, _ := command.Flags().GetString("header")
|
||||
purpose, _ := command.Flags().GetString("purpose")
|
||||
useprivate, _ := command.Flags().GetBool("private")
|
||||
|
||||
channelType := model.CHANNEL_OPEN
|
||||
if useprivate {
|
||||
@@ -176,8 +180,8 @@ func createChannelCmdF(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeChannelUsersCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func removeChannelUsersCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -201,16 +205,16 @@ func removeChannelUsersCmdF(cmd *cobra.Command, args []string) error {
|
||||
|
||||
func removeUserFromChannel(a *app.App, channel *model.Channel, user *model.User, userArg string) {
|
||||
if user == nil {
|
||||
CommandPrintErrorln("Can't find user '" + userArg + "'")
|
||||
cmd.CommandPrintErrorln("Can't find user '" + userArg + "'")
|
||||
return
|
||||
}
|
||||
if err := a.RemoveUserFromChannel(user.Id, "", channel); err != nil {
|
||||
CommandPrintErrorln("Unable to remove '" + userArg + "' from " + channel.Name + ". Error: " + err.Error())
|
||||
cmd.CommandPrintErrorln("Unable to remove '" + userArg + "' from " + channel.Name + ". Error: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func addChannelUsersCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func addChannelUsersCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -234,16 +238,16 @@ func addChannelUsersCmdF(cmd *cobra.Command, args []string) error {
|
||||
|
||||
func addUserToChannel(a *app.App, channel *model.Channel, user *model.User, userArg string) {
|
||||
if user == nil {
|
||||
CommandPrintErrorln("Can't find user '" + userArg + "'")
|
||||
cmd.CommandPrintErrorln("Can't find user '" + userArg + "'")
|
||||
return
|
||||
}
|
||||
if _, err := a.AddUserToChannel(user, channel); err != nil {
|
||||
CommandPrintErrorln("Unable to add '" + userArg + "' from " + channel.Name + ". Error: " + err.Error())
|
||||
cmd.CommandPrintErrorln("Unable to add '" + userArg + "' from " + channel.Name + ". Error: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func archiveChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func archiveChannelsCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -255,19 +259,19 @@ func archiveChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
channels := getChannelsFromChannelArgs(a, args)
|
||||
for i, channel := range channels {
|
||||
if channel == nil {
|
||||
CommandPrintErrorln("Unable to find channel '" + args[i] + "'")
|
||||
cmd.CommandPrintErrorln("Unable to find channel '" + args[i] + "'")
|
||||
continue
|
||||
}
|
||||
if result := <-a.Srv.Store.Channel().Delete(channel.Id, model.GetMillis()); result.Err != nil {
|
||||
CommandPrintErrorln("Unable to archive channel '" + channel.Name + "' error: " + result.Err.Error())
|
||||
cmd.CommandPrintErrorln("Unable to archive channel '" + channel.Name + "' error: " + result.Err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func deleteChannelsCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -276,10 +280,10 @@ func deleteChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
return errors.New("Enter at least one channel to delete.")
|
||||
}
|
||||
|
||||
confirmFlag, _ := cmd.Flags().GetBool("confirm")
|
||||
confirmFlag, _ := command.Flags().GetBool("confirm")
|
||||
if !confirmFlag {
|
||||
var confirm string
|
||||
CommandPrettyPrintln("Are you sure you want to delete the channels specified? All data will be permanently deleted? (YES/NO): ")
|
||||
cmd.CommandPrettyPrintln("Are you sure you want to delete the channels specified? All data will be permanently deleted? (YES/NO): ")
|
||||
fmt.Scanln(&confirm)
|
||||
if confirm != "YES" {
|
||||
return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
|
||||
@@ -289,13 +293,13 @@ func deleteChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
channels := getChannelsFromChannelArgs(a, args)
|
||||
for i, channel := range channels {
|
||||
if channel == nil {
|
||||
CommandPrintErrorln("Unable to find channel '" + args[i] + "'")
|
||||
cmd.CommandPrintErrorln("Unable to find channel '" + args[i] + "'")
|
||||
continue
|
||||
}
|
||||
if err := deleteChannel(a, channel); err != nil {
|
||||
CommandPrintErrorln("Unable to delete channel '" + channel.Name + "' error: " + err.Error())
|
||||
cmd.CommandPrintErrorln("Unable to delete channel '" + channel.Name + "' error: " + err.Error())
|
||||
} else {
|
||||
CommandPrettyPrintln("Deleted channel '" + channel.Name + "'")
|
||||
cmd.CommandPrettyPrintln("Deleted channel '" + channel.Name + "'")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,8 +310,8 @@ func deleteChannel(a *app.App, channel *model.Channel) *model.AppError {
|
||||
return a.PermanentDeleteChannel(channel)
|
||||
}
|
||||
|
||||
func moveChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func moveChannelsCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -321,7 +325,7 @@ func moveChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
return errors.New("Unable to find destination team '" + args[0] + "'")
|
||||
}
|
||||
|
||||
username, erru := cmd.Flags().GetString("username")
|
||||
username, erru := command.Flags().GetString("username")
|
||||
if erru != nil || username == "" {
|
||||
return errors.New("Username is required")
|
||||
}
|
||||
@@ -330,14 +334,14 @@ func moveChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
channels := getChannelsFromChannelArgs(a, args[1:])
|
||||
for i, channel := range channels {
|
||||
if channel == nil {
|
||||
CommandPrintErrorln("Unable to find channel '" + args[i] + "'")
|
||||
cmd.CommandPrintErrorln("Unable to find channel '" + args[i] + "'")
|
||||
continue
|
||||
}
|
||||
originTeamID := channel.TeamId
|
||||
if err := moveChannel(a, team, channel, user); err != nil {
|
||||
CommandPrintErrorln("Unable to move channel '" + channel.Name + "' error: " + err.Error())
|
||||
cmd.CommandPrintErrorln("Unable to move channel '" + channel.Name + "' error: " + err.Error())
|
||||
} else {
|
||||
CommandPrettyPrintln("Moved channel '" + channel.Name + "' to " + team.Name + "(" + team.Id + ") from " + originTeamID + ".")
|
||||
cmd.CommandPrettyPrintln("Moved channel '" + channel.Name + "' to " + team.Name + "(" + team.Id + ") from " + originTeamID + ".")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,7 +362,7 @@ func moveChannel(a *app.App, team *model.Team, channel *model.Channel, user *mod
|
||||
if webhook.ChannelId == channel.Id {
|
||||
webhook.TeamId = team.Id
|
||||
if result := <-a.Srv.Store.Webhook().UpdateIncoming(webhook); result.Err != nil {
|
||||
CommandPrintErrorln("Failed to move incoming webhook '" + webhook.Id + "' to new team.")
|
||||
cmd.CommandPrintErrorln("Failed to move incoming webhook '" + webhook.Id + "' to new team.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -371,7 +375,7 @@ func moveChannel(a *app.App, team *model.Team, channel *model.Channel, user *mod
|
||||
if webhook.ChannelId == channel.Id {
|
||||
webhook.TeamId = team.Id
|
||||
if result := <-a.Srv.Store.Webhook().UpdateOutgoing(webhook); result.Err != nil {
|
||||
CommandPrintErrorln("Failed to move outgoing webhook '" + webhook.Id + "' to new team.")
|
||||
cmd.CommandPrintErrorln("Failed to move outgoing webhook '" + webhook.Id + "' to new team.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -380,8 +384,8 @@ func moveChannel(a *app.App, team *model.Team, channel *model.Channel, user *mod
|
||||
return nil
|
||||
}
|
||||
|
||||
func listChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func listChannelsCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -393,19 +397,19 @@ func listChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
teams := getTeamsFromTeamArgs(a, args)
|
||||
for i, team := range teams {
|
||||
if team == nil {
|
||||
CommandPrintErrorln("Unable to find team '" + args[i] + "'")
|
||||
cmd.CommandPrintErrorln("Unable to find team '" + args[i] + "'")
|
||||
continue
|
||||
}
|
||||
if result := <-a.Srv.Store.Channel().GetAll(team.Id); result.Err != nil {
|
||||
CommandPrintErrorln("Unable to list channels for '" + args[i] + "'")
|
||||
cmd.CommandPrintErrorln("Unable to list channels for '" + args[i] + "'")
|
||||
} else {
|
||||
channels := result.Data.([]*model.Channel)
|
||||
|
||||
for _, channel := range channels {
|
||||
if channel.DeleteAt > 0 {
|
||||
CommandPrettyPrintln(channel.Name + " (archived)")
|
||||
cmd.CommandPrettyPrintln(channel.Name + " (archived)")
|
||||
} else {
|
||||
CommandPrettyPrintln(channel.Name)
|
||||
cmd.CommandPrettyPrintln(channel.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -414,8 +418,8 @@ func listChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func restoreChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func restoreChannelsCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -427,19 +431,19 @@ func restoreChannelsCmdF(cmd *cobra.Command, args []string) error {
|
||||
channels := getChannelsFromChannelArgs(a, args)
|
||||
for i, channel := range channels {
|
||||
if channel == nil {
|
||||
CommandPrintErrorln("Unable to find channel '" + args[i] + "'")
|
||||
cmd.CommandPrintErrorln("Unable to find channel '" + args[i] + "'")
|
||||
continue
|
||||
}
|
||||
if result := <-a.Srv.Store.Channel().SetDeleteAt(channel.Id, 0, model.GetMillis()); result.Err != nil {
|
||||
CommandPrintErrorln("Unable to restore channel '" + args[i] + "'")
|
||||
cmd.CommandPrintErrorln("Unable to restore channel '" + args[i] + "'")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func modifyChannelCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func modifyChannelCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -448,13 +452,13 @@ func modifyChannelCmdF(cmd *cobra.Command, args []string) error {
|
||||
return errors.New("Enter at one channel to modify.")
|
||||
}
|
||||
|
||||
username, erru := cmd.Flags().GetString("username")
|
||||
username, erru := command.Flags().GetString("username")
|
||||
if erru != nil || username == "" {
|
||||
return errors.New("Username is required")
|
||||
}
|
||||
|
||||
public, _ := cmd.Flags().GetBool("public")
|
||||
private, _ := cmd.Flags().GetBool("private")
|
||||
public, _ := command.Flags().GetBool("public")
|
||||
private, _ := command.Flags().GetBool("private")
|
||||
|
||||
if public == private {
|
||||
return errors.New("You must specify only one of --public or --private")
|
||||
@@ -1,13 +1,14 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -18,13 +19,13 @@ func TestJoinChannel(t *testing.T) {
|
||||
|
||||
channel := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
|
||||
checkCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email)
|
||||
cmd.CheckCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email)
|
||||
|
||||
// Joining twice should succeed
|
||||
checkCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email)
|
||||
cmd.CheckCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email)
|
||||
|
||||
// should fail because channel does not exist
|
||||
require.Error(t, runCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name+"asdf", th.BasicUser2.Email))
|
||||
require.Error(t, cmd.RunCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name+"asdf", th.BasicUser2.Email))
|
||||
}
|
||||
|
||||
func TestRemoveChannel(t *testing.T) {
|
||||
@@ -33,15 +34,15 @@ func TestRemoveChannel(t *testing.T) {
|
||||
|
||||
channel := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
|
||||
checkCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email)
|
||||
cmd.CheckCommand(t, "channel", "add", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email)
|
||||
|
||||
// should fail because channel does not exist
|
||||
require.Error(t, runCommand(t, "channel", "remove", th.BasicTeam.Name+":doesnotexist", th.BasicUser2.Email))
|
||||
require.Error(t, cmd.RunCommand(t, "channel", "remove", th.BasicTeam.Name+":doesnotexist", th.BasicUser2.Email))
|
||||
|
||||
checkCommand(t, "channel", "remove", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email)
|
||||
cmd.CheckCommand(t, "channel", "remove", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email)
|
||||
|
||||
// Leaving twice should succeed
|
||||
checkCommand(t, "channel", "remove", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email)
|
||||
cmd.CheckCommand(t, "channel", "remove", th.BasicTeam.Name+":"+channel.Name, th.BasicUser2.Email)
|
||||
}
|
||||
|
||||
func TestMoveChannel(t *testing.T) {
|
||||
@@ -63,12 +64,12 @@ func TestMoveChannel(t *testing.T) {
|
||||
origin := team1.Name + ":" + channel.Name
|
||||
dest := team2.Name
|
||||
|
||||
checkCommand(t, "channel", "add", origin, adminEmail)
|
||||
cmd.CheckCommand(t, "channel", "add", origin, adminEmail)
|
||||
|
||||
// should fail with nill because errors are logged instead of returned when a channel does not exist
|
||||
require.Nil(t, runCommand(t, "channel", "move", dest, team1.Name+":doesnotexist", "--username", adminUsername))
|
||||
require.Nil(t, cmd.RunCommand(t, "channel", "move", dest, team1.Name+":doesnotexist", "--username", adminUsername))
|
||||
|
||||
checkCommand(t, "channel", "move", dest, origin, "--username", adminUsername)
|
||||
cmd.CheckCommand(t, "channel", "move", dest, origin, "--username", adminUsername)
|
||||
}
|
||||
|
||||
func TestListChannels(t *testing.T) {
|
||||
@@ -78,7 +79,7 @@ func TestListChannels(t *testing.T) {
|
||||
channel := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
th.BasicClient.Must(th.BasicClient.DeleteChannel(channel.Id))
|
||||
|
||||
output := checkCommand(t, "channel", "list", th.BasicTeam.Name)
|
||||
output := cmd.CheckCommand(t, "channel", "list", th.BasicTeam.Name)
|
||||
|
||||
if !strings.Contains(string(output), "town-square") {
|
||||
t.Fatal("should have channels")
|
||||
@@ -96,10 +97,10 @@ func TestRestoreChannel(t *testing.T) {
|
||||
channel := th.CreateChannel(th.BasicClient, th.BasicTeam)
|
||||
th.BasicClient.Must(th.BasicClient.DeleteChannel(channel.Id))
|
||||
|
||||
checkCommand(t, "channel", "restore", th.BasicTeam.Name+":"+channel.Name)
|
||||
cmd.CheckCommand(t, "channel", "restore", th.BasicTeam.Name+":"+channel.Name)
|
||||
|
||||
// restoring twice should succeed
|
||||
checkCommand(t, "channel", "restore", th.BasicTeam.Name+":"+channel.Name)
|
||||
cmd.CheckCommand(t, "channel", "restore", th.BasicTeam.Name+":"+channel.Name)
|
||||
}
|
||||
|
||||
func TestCreateChannel(t *testing.T) {
|
||||
@@ -109,8 +110,8 @@ func TestCreateChannel(t *testing.T) {
|
||||
id := model.NewId()
|
||||
name := "name" + id
|
||||
|
||||
checkCommand(t, "channel", "create", "--display_name", name, "--team", th.BasicTeam.Name, "--name", name)
|
||||
cmd.CheckCommand(t, "channel", "create", "--display_name", name, "--team", th.BasicTeam.Name, "--name", name)
|
||||
|
||||
name = name + "-private"
|
||||
checkCommand(t, "channel", "create", "--display_name", name, "--team", th.BasicTeam.Name, "--private", "--name", name)
|
||||
cmd.CheckCommand(t, "channel", "create", "--display_name", name, "--team", th.BasicTeam.Name, "--private", "--name", name)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,20 +1,22 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var commandCmd = &cobra.Command{
|
||||
var CommandCmd = &cobra.Command{
|
||||
Use: "command",
|
||||
Short: "Management of slash commands",
|
||||
}
|
||||
|
||||
var commandMoveCmd = &cobra.Command{
|
||||
var CommandMoveCmd = &cobra.Command{
|
||||
Use: "move",
|
||||
Short: "Move a slash command to a different team",
|
||||
Long: `Move a slash command to a different team. Commands can be specified by [team]:[command-trigger-word]. ie. myteam:trigger or by command ID.`,
|
||||
@@ -23,13 +25,14 @@ var commandMoveCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
commandCmd.AddCommand(
|
||||
commandMoveCmd,
|
||||
CommandCmd.AddCommand(
|
||||
CommandMoveCmd,
|
||||
)
|
||||
cmd.RootCmd.AddCommand(CommandCmd)
|
||||
}
|
||||
|
||||
func moveCommandCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func moveCommandCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -44,16 +47,16 @@ func moveCommandCmdF(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
commands := getCommandsFromCommandArgs(a, args[1:])
|
||||
CommandPrintErrorln(commands)
|
||||
cmd.CommandPrintErrorln(commands)
|
||||
for i, command := range commands {
|
||||
if command == nil {
|
||||
CommandPrintErrorln("Unable to find command '" + args[i+1] + "'")
|
||||
cmd.CommandPrintErrorln("Unable to find command '" + args[i+1] + "'")
|
||||
continue
|
||||
}
|
||||
if err := moveCommand(a, team, command); err != nil {
|
||||
CommandPrintErrorln("Unable to move command '" + command.Trigger + "' error: " + err.Error())
|
||||
cmd.CommandPrintErrorln("Unable to move command '" + command.Trigger + "' error: " + err.Error())
|
||||
} else {
|
||||
CommandPrettyPrintln("Moved command '" + command.Trigger + "'")
|
||||
cmd.CommandPrettyPrintln("Moved command '" + command.Trigger + "'")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,23 +1,25 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var configCmd = &cobra.Command{
|
||||
var ConfigCmd = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Configuration",
|
||||
}
|
||||
|
||||
var validateConfigCmd = &cobra.Command{
|
||||
var ValidateConfigCmd = &cobra.Command{
|
||||
Use: "validate",
|
||||
Short: "Validate config file",
|
||||
Long: "If the config file is valid, this command will output a success message and have a zero exit code. If it is invalid, this command will output an error and have a non-zero exit code.",
|
||||
@@ -25,15 +27,16 @@ var validateConfigCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
configCmd.AddCommand(
|
||||
validateConfigCmd,
|
||||
ConfigCmd.AddCommand(
|
||||
ValidateConfigCmd,
|
||||
)
|
||||
cmd.RootCmd.AddCommand(ConfigCmd)
|
||||
}
|
||||
|
||||
func configValidateCmdF(cmd *cobra.Command, args []string) error {
|
||||
func configValidateCmdF(command *cobra.Command, args []string) error {
|
||||
utils.TranslationsPreInit()
|
||||
model.AppErrorInit(utils.T)
|
||||
filePath, err := cmd.Flags().GetString("config")
|
||||
filePath, err := command.Flags().GetString("config")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -60,6 +63,6 @@ func configValidateCmdF(cmd *cobra.Command, args []string) error {
|
||||
return errors.New(utils.T(err.Id))
|
||||
}
|
||||
|
||||
CommandPrettyPrintln("The document is valid")
|
||||
cmd.CommandPrettyPrintln("The document is valid")
|
||||
return nil
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
|
||||
@@ -34,8 +35,8 @@ func TestConfigFlag(t *testing.T) {
|
||||
defer os.Chdir(prevDir)
|
||||
os.Chdir(dir)
|
||||
|
||||
require.Error(t, runCommand(t, "version"))
|
||||
checkCommand(t, "--config", "foo.json", "version")
|
||||
checkCommand(t, "--config", "./foo.json", "version")
|
||||
checkCommand(t, "--config", configPath, "version")
|
||||
require.Error(t, cmd.RunCommand(t, "version"))
|
||||
cmd.CheckCommand(t, "--config", "foo.json", "version")
|
||||
cmd.CheckCommand(t, "--config", "./foo.json", "version")
|
||||
cmd.CheckCommand(t, "--config", configPath, "version")
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -25,6 +26,6 @@ func TestConfigValidate(t *testing.T) {
|
||||
config.SetDefaults()
|
||||
require.NoError(t, ioutil.WriteFile(path, []byte(config.ToJson()), 0600))
|
||||
|
||||
assert.Error(t, runCommand(t, "--config", "foo.json", "config", "validate"))
|
||||
assert.NoError(t, runCommand(t, "--config", path, "config", "validate"))
|
||||
assert.Error(t, cmd.RunCommand(t, "--config", "foo.json", "config", "validate"))
|
||||
assert.NoError(t, cmd.RunCommand(t, "--config", path, "config", "validate"))
|
||||
}
|
||||
21
cmd/commands/exec_command_test.go
Обычный файл
21
cmd/commands/exec_command_test.go
Обычный файл
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
)
|
||||
|
||||
func TestExecCommand(t *testing.T) {
|
||||
if filter := flag.Lookup("test.run").Value.String(); filter != "ExecCommand" {
|
||||
t.Skip("use -run ExecCommand to execute a command via the test executable")
|
||||
}
|
||||
cmd.RootCmd.SetArgs(flag.Args())
|
||||
require.NoError(t, cmd.RootCmd.Execute())
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -8,15 +9,16 @@ import (
|
||||
|
||||
"fmt"
|
||||
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var importCmd = &cobra.Command{
|
||||
var ImportCmd = &cobra.Command{
|
||||
Use: "import",
|
||||
Short: "Import data.",
|
||||
}
|
||||
|
||||
var slackImportCmd = &cobra.Command{
|
||||
var SlackImportCmd = &cobra.Command{
|
||||
Use: "slack [team] [file]",
|
||||
Short: "Import a team from Slack.",
|
||||
Long: "Import a team from a Slack export zip file.",
|
||||
@@ -24,7 +26,7 @@ var slackImportCmd = &cobra.Command{
|
||||
RunE: slackImportCmdF,
|
||||
}
|
||||
|
||||
var bulkImportCmd = &cobra.Command{
|
||||
var BulkImportCmd = &cobra.Command{
|
||||
Use: "bulk [file]",
|
||||
Short: "Import bulk data.",
|
||||
Long: "Import data from a Mattermost Bulk Import File.",
|
||||
@@ -33,18 +35,19 @@ var bulkImportCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
bulkImportCmd.Flags().Bool("apply", false, "Save the import data to the database. Use with caution - this cannot be reverted.")
|
||||
bulkImportCmd.Flags().Bool("validate", false, "Validate the import data without making any changes to the system.")
|
||||
bulkImportCmd.Flags().Int("workers", 2, "How many workers to run whilst doing the import.")
|
||||
BulkImportCmd.Flags().Bool("apply", false, "Save the import data to the database. Use with caution - this cannot be reverted.")
|
||||
BulkImportCmd.Flags().Bool("validate", false, "Validate the import data without making any changes to the system.")
|
||||
BulkImportCmd.Flags().Int("workers", 2, "How many workers to run whilst doing the import.")
|
||||
|
||||
importCmd.AddCommand(
|
||||
bulkImportCmd,
|
||||
slackImportCmd,
|
||||
ImportCmd.AddCommand(
|
||||
BulkImportCmd,
|
||||
SlackImportCmd,
|
||||
)
|
||||
cmd.RootCmd.AddCommand(ImportCmd)
|
||||
}
|
||||
|
||||
func slackImportCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func slackImportCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -69,32 +72,32 @@ func slackImportCmdF(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
CommandPrettyPrintln("Running Slack Import. This may take a long time for large teams or teams with many messages.")
|
||||
cmd.CommandPrettyPrintln("Running Slack Import. This may take a long time for large teams or teams with many messages.")
|
||||
|
||||
a.SlackImport(fileReader, fileInfo.Size(), team.Id)
|
||||
|
||||
CommandPrettyPrintln("Finished Slack Import.")
|
||||
cmd.CommandPrettyPrintln("Finished Slack Import.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func bulkImportCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func bulkImportCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
apply, err := cmd.Flags().GetBool("apply")
|
||||
apply, err := command.Flags().GetBool("apply")
|
||||
if err != nil {
|
||||
return errors.New("Apply flag error")
|
||||
}
|
||||
|
||||
validate, err := cmd.Flags().GetBool("validate")
|
||||
validate, err := command.Flags().GetBool("validate")
|
||||
if err != nil {
|
||||
return errors.New("Validate flag error")
|
||||
}
|
||||
|
||||
workers, err := cmd.Flags().GetInt("workers")
|
||||
workers, err := command.Flags().GetInt("workers")
|
||||
if err != nil {
|
||||
return errors.New("Workers flag error")
|
||||
}
|
||||
@@ -110,28 +113,28 @@ func bulkImportCmdF(cmd *cobra.Command, args []string) error {
|
||||
defer fileReader.Close()
|
||||
|
||||
if apply && validate {
|
||||
CommandPrettyPrintln("Use only one of --apply or --validate.")
|
||||
cmd.CommandPrettyPrintln("Use only one of --apply or --validate.")
|
||||
return nil
|
||||
} else if apply && !validate {
|
||||
CommandPrettyPrintln("Running Bulk Import. This may take a long time.")
|
||||
cmd.CommandPrettyPrintln("Running Bulk Import. This may take a long time.")
|
||||
} else {
|
||||
CommandPrettyPrintln("Running Bulk Import Data Validation.")
|
||||
CommandPrettyPrintln("** This checks the validity of the entities in the data file, but does not persist any changes **")
|
||||
CommandPrettyPrintln("Use the --apply flag to perform the actual data import.")
|
||||
cmd.CommandPrettyPrintln("Running Bulk Import Data Validation.")
|
||||
cmd.CommandPrettyPrintln("** This checks the validity of the entities in the data file, but does not persist any changes **")
|
||||
cmd.CommandPrettyPrintln("Use the --apply flag to perform the actual data import.")
|
||||
}
|
||||
|
||||
CommandPrettyPrintln("")
|
||||
cmd.CommandPrettyPrintln("")
|
||||
|
||||
if err, lineNumber := a.BulkImport(fileReader, !apply, workers); err != nil {
|
||||
CommandPrettyPrintln(err.Error())
|
||||
cmd.CommandPrettyPrintln(err.Error())
|
||||
if lineNumber != 0 {
|
||||
CommandPrettyPrintln(fmt.Sprintf("Error occurred on data file line %v", lineNumber))
|
||||
cmd.CommandPrettyPrintln(fmt.Sprintf("Error occurred on data file line %v", lineNumber))
|
||||
}
|
||||
} else {
|
||||
if apply {
|
||||
CommandPrettyPrintln("Finished Bulk Import.")
|
||||
cmd.CommandPrettyPrintln("Finished Bulk Import.")
|
||||
} else {
|
||||
CommandPrettyPrintln("Validation complete. You can now perform the import by rerunning this command with the --apply flag.")
|
||||
cmd.CommandPrettyPrintln("Validation complete. You can now perform the import by rerunning this command with the --apply flag.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -8,27 +9,30 @@ import (
|
||||
"syscall"
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var jobserverCmd = &cobra.Command{
|
||||
var JobserverCmd = &cobra.Command{
|
||||
Use: "jobserver",
|
||||
Short: "Start the Mattermost job server",
|
||||
Run: jobserverCmdF,
|
||||
}
|
||||
|
||||
func init() {
|
||||
jobserverCmd.Flags().Bool("nojobs", false, "Do not run jobs on this jobserver.")
|
||||
jobserverCmd.Flags().Bool("noschedule", false, "Do not schedule jobs from this jobserver.")
|
||||
JobserverCmd.Flags().Bool("nojobs", false, "Do not run jobs on this jobserver.")
|
||||
JobserverCmd.Flags().Bool("noschedule", false, "Do not schedule jobs from this jobserver.")
|
||||
|
||||
cmd.RootCmd.AddCommand(JobserverCmd)
|
||||
}
|
||||
|
||||
func jobserverCmdF(cmd *cobra.Command, args []string) {
|
||||
func jobserverCmdF(command *cobra.Command, args []string) {
|
||||
// Options
|
||||
noJobs, _ := cmd.Flags().GetBool("nojobs")
|
||||
noSchedule, _ := cmd.Flags().GetBool("noschedule")
|
||||
noJobs, _ := command.Flags().GetBool("nojobs")
|
||||
noSchedule, _ := command.Flags().GetBool("noschedule")
|
||||
|
||||
// Initialize
|
||||
a, err := initDBCommandContext("config.json")
|
||||
a, err := cmd.InitDBCommandContext("config.json")
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var ldapCmd = &cobra.Command{
|
||||
var LdapCmd = &cobra.Command{
|
||||
Use: "ldap",
|
||||
Short: "LDAP related utilities",
|
||||
}
|
||||
|
||||
var ldapSyncCmd = &cobra.Command{
|
||||
var LdapSyncCmd = &cobra.Command{
|
||||
Use: "sync",
|
||||
Short: "Synchronize now",
|
||||
Long: "Synchronize all LDAP users now.",
|
||||
@@ -21,13 +23,14 @@ var ldapSyncCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
ldapCmd.AddCommand(
|
||||
ldapSyncCmd,
|
||||
LdapCmd.AddCommand(
|
||||
LdapSyncCmd,
|
||||
)
|
||||
cmd.RootCmd.AddCommand(LdapCmd)
|
||||
}
|
||||
|
||||
func ldapSyncCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func ldapSyncCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -35,9 +38,9 @@ func ldapSyncCmdF(cmd *cobra.Command, args []string) error {
|
||||
if ldapI := a.Ldap; ldapI != nil {
|
||||
job, err := ldapI.StartSynchronizeJob(true)
|
||||
if err != nil || job.Status == model.JOB_STATUS_ERROR || job.Status == model.JOB_STATUS_CANCELED {
|
||||
CommandPrintErrorln("ERROR: AD/LDAP Synchronization please check the server logs")
|
||||
cmd.CommandPrintErrorln("ERROR: AD/LDAP Synchronization please check the server logs")
|
||||
} else {
|
||||
CommandPrettyPrintln("SUCCESS: AD/LDAP Synchronization Complete")
|
||||
cmd.CommandPrettyPrintln("SUCCESS: AD/LDAP Synchronization Complete")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var licenseCmd = &cobra.Command{
|
||||
var LicenseCmd = &cobra.Command{
|
||||
Use: "license",
|
||||
Short: "Licensing commands",
|
||||
}
|
||||
|
||||
var uploadLicenseCmd = &cobra.Command{
|
||||
var UploadLicenseCmd = &cobra.Command{
|
||||
Use: "upload [license]",
|
||||
Short: "Upload a license.",
|
||||
Long: "Upload a license. Replaces current license.",
|
||||
@@ -23,11 +25,12 @@ var uploadLicenseCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
licenseCmd.AddCommand(uploadLicenseCmd)
|
||||
LicenseCmd.AddCommand(UploadLicenseCmd)
|
||||
cmd.RootCmd.AddCommand(LicenseCmd)
|
||||
}
|
||||
|
||||
func uploadLicenseCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func uploadLicenseCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -45,7 +48,7 @@ func uploadLicenseCmdF(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
CommandPrettyPrintln("Uploaded license file")
|
||||
cmd.CommandPrettyPrintln("Uploaded license file")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -10,11 +10,12 @@ import (
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var messageExportCmd = &cobra.Command{
|
||||
var MessageExportCmd = &cobra.Command{
|
||||
Use: "export",
|
||||
Short: "Export data from Mattermost",
|
||||
Long: "Export data from Mattermost in a format suitable for import into a third-party application",
|
||||
@@ -23,13 +24,14 @@ var messageExportCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
messageExportCmd.Flags().String("format", "actiance", "The format to export data in")
|
||||
messageExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
|
||||
messageExportCmd.Flags().Int("timeoutSeconds", -1, "The maximum number of seconds to wait for the job to complete before timing out.")
|
||||
MessageExportCmd.Flags().String("format", "actiance", "The format to export data in")
|
||||
MessageExportCmd.Flags().Int64("exportFrom", -1, "The timestamp of the earliest post to export, expressed in seconds since the unix epoch.")
|
||||
MessageExportCmd.Flags().Int("timeoutSeconds", -1, "The maximum number of seconds to wait for the job to complete before timing out.")
|
||||
cmd.RootCmd.AddCommand(MessageExportCmd)
|
||||
}
|
||||
|
||||
func messageExportCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func messageExportCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -39,20 +41,20 @@ func messageExportCmdF(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// for now, format is hard-coded to actiance. In time, we'll have to support other formats and inject them into job data
|
||||
if format, err := cmd.Flags().GetString("format"); err != nil {
|
||||
if format, err := command.Flags().GetString("format"); err != nil {
|
||||
return errors.New("format flag error")
|
||||
} else if format != "actiance" {
|
||||
return errors.New("unsupported export format")
|
||||
}
|
||||
|
||||
startTime, err := cmd.Flags().GetInt64("exportFrom")
|
||||
startTime, err := command.Flags().GetInt64("exportFrom")
|
||||
if err != nil {
|
||||
return errors.New("exportFrom flag error")
|
||||
} else if startTime < 0 {
|
||||
return errors.New("exportFrom must be a positive integer")
|
||||
}
|
||||
|
||||
timeoutSeconds, err := cmd.Flags().GetInt("timeoutSeconds")
|
||||
timeoutSeconds, err := command.Flags().GetInt("timeoutSeconds")
|
||||
if err != nil {
|
||||
return errors.New("timeoutSeconds error")
|
||||
} else if timeoutSeconds < 0 {
|
||||
@@ -69,9 +71,9 @@ func messageExportCmdF(cmd *cobra.Command, args []string) error {
|
||||
|
||||
job, err := messageExportI.StartSynchronizeJob(ctx, startTime)
|
||||
if err != nil || job.Status == model.JOB_STATUS_ERROR || job.Status == model.JOB_STATUS_CANCELED {
|
||||
CommandPrintErrorln("ERROR: Message export job failed. Please check the server logs")
|
||||
cmd.CommandPrintErrorln("ERROR: Message export job failed. Please check the server logs")
|
||||
} else {
|
||||
CommandPrettyPrintln("SUCCESS: Message export job complete")
|
||||
cmd.CommandPrettyPrintln("SUCCESS: Message export job complete")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
)
|
||||
@@ -24,7 +25,7 @@ func TestMessageExportNotEnabled(t *testing.T) {
|
||||
defer os.RemoveAll(filepath.Dir(configPath))
|
||||
|
||||
// should fail fast because the feature isn't enabled
|
||||
require.Error(t, runCommand(t, "--config", configPath, "export"))
|
||||
require.Error(t, cmd.RunCommand(t, "--config", configPath, "export"))
|
||||
}
|
||||
|
||||
func TestMessageExportInvalidFormat(t *testing.T) {
|
||||
@@ -32,7 +33,7 @@ func TestMessageExportInvalidFormat(t *testing.T) {
|
||||
defer os.RemoveAll(filepath.Dir(configPath))
|
||||
|
||||
// should fail fast because format isn't supported
|
||||
require.Error(t, runCommand(t, "--config", configPath, "--format", "not_actiance", "export"))
|
||||
require.Error(t, cmd.RunCommand(t, "--config", configPath, "--format", "not_actiance", "export"))
|
||||
}
|
||||
|
||||
func TestMessageExportNegativeExportFrom(t *testing.T) {
|
||||
@@ -40,7 +41,7 @@ func TestMessageExportNegativeExportFrom(t *testing.T) {
|
||||
defer os.RemoveAll(filepath.Dir(configPath))
|
||||
|
||||
// should fail fast because export from must be a valid timestamp
|
||||
require.Error(t, runCommand(t, "--config", configPath, "--format", "actiance", "--exportFrom", "-1", "export"))
|
||||
require.Error(t, cmd.RunCommand(t, "--config", configPath, "--format", "actiance", "--exportFrom", "-1", "export"))
|
||||
}
|
||||
|
||||
func TestMessageExportNegativeTimeoutSeconds(t *testing.T) {
|
||||
@@ -48,7 +49,7 @@ func TestMessageExportNegativeTimeoutSeconds(t *testing.T) {
|
||||
defer os.RemoveAll(filepath.Dir(configPath))
|
||||
|
||||
// should fail fast because timeout seconds must be a positive int
|
||||
require.Error(t, runCommand(t, "--config", configPath, "--format", "actiance", "--exportFrom", "0", "--timeoutSeconds", "-1", "export"))
|
||||
require.Error(t, cmd.RunCommand(t, "--config", configPath, "--format", "actiance", "--exportFrom", "0", "--timeoutSeconds", "-1", "export"))
|
||||
}
|
||||
|
||||
func writeTempConfig(t *testing.T, isMessageExportEnabled bool) string {
|
||||
53
cmd/commands/reset.go
Обычный файл
53
cmd/commands/reset.go
Обычный файл
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var ResetCmd = &cobra.Command{
|
||||
Use: "reset",
|
||||
Short: "Reset the database to initial state",
|
||||
Long: "Completely erases the database causing the loss of all data. This will reset Mattermost to its initial state.",
|
||||
RunE: resetCmdF,
|
||||
}
|
||||
|
||||
func init() {
|
||||
ResetCmd.Flags().Bool("confirm", false, "Confirm you really want to delete everything and a DB backup has been performed.")
|
||||
|
||||
cmd.RootCmd.AddCommand(ResetCmd)
|
||||
}
|
||||
|
||||
func resetCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
confirmFlag, _ := command.Flags().GetBool("confirm")
|
||||
if !confirmFlag {
|
||||
var confirm string
|
||||
cmd.CommandPrettyPrintln("Have you performed a database backup? (YES/NO): ")
|
||||
fmt.Scanln(&confirm)
|
||||
|
||||
if confirm != "YES" {
|
||||
return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
|
||||
}
|
||||
cmd.CommandPrettyPrintln("Are you sure you want to delete everything? All data will be permanently deleted? (YES/NO): ")
|
||||
fmt.Scanln(&confirm)
|
||||
if confirm != "YES" {
|
||||
return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
|
||||
}
|
||||
}
|
||||
|
||||
a.Srv.Store.DropAllTables()
|
||||
cmd.CommandPrettyPrintln("Database sucessfully reset")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rolesCmd = &cobra.Command{
|
||||
var RolesCmd = &cobra.Command{
|
||||
Use: "roles",
|
||||
Short: "Management of user roles",
|
||||
}
|
||||
|
||||
var makeSystemAdminCmd = &cobra.Command{
|
||||
var MakeSystemAdminCmd = &cobra.Command{
|
||||
Use: "system_admin [users]",
|
||||
Short: "Set a user as system admin",
|
||||
Long: "Make some users system admins",
|
||||
@@ -21,7 +23,7 @@ var makeSystemAdminCmd = &cobra.Command{
|
||||
RunE: makeSystemAdminCmdF,
|
||||
}
|
||||
|
||||
var makeMemberCmd = &cobra.Command{
|
||||
var MakeMemberCmd = &cobra.Command{
|
||||
Use: "member [users]",
|
||||
Short: "Remove system admin privileges",
|
||||
Long: "Remove system admin privileges from some users.",
|
||||
@@ -30,14 +32,15 @@ var makeMemberCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
rolesCmd.AddCommand(
|
||||
makeSystemAdminCmd,
|
||||
makeMemberCmd,
|
||||
RolesCmd.AddCommand(
|
||||
MakeSystemAdminCmd,
|
||||
MakeMemberCmd,
|
||||
)
|
||||
cmd.RootCmd.AddCommand(RolesCmd)
|
||||
}
|
||||
|
||||
func makeSystemAdminCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func makeSystemAdminCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -60,8 +63,8 @@ func makeSystemAdminCmdF(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeMemberCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func makeMemberCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
@@ -14,7 +15,7 @@ func TestAssignRole(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
checkCommand(t, "roles", "system_admin", th.BasicUser.Email)
|
||||
cmd.CheckCommand(t, "roles", "system_admin", th.BasicUser.Email)
|
||||
|
||||
if result := <-th.App.Srv.Store.User().GetByEmail(th.BasicUser.Email); result.Err != nil {
|
||||
t.Fatal()
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -16,15 +17,34 @@ import (
|
||||
|
||||
"github.com/icrowley/fake"
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var sampleDataCmd = &cobra.Command{
|
||||
var SampleDataCmd = &cobra.Command{
|
||||
Use: "sampledata",
|
||||
Short: "Generate sample data",
|
||||
RunE: sampleDataCmdF,
|
||||
}
|
||||
|
||||
func init() {
|
||||
SampleDataCmd.Flags().Int64P("seed", "s", 1, "Seed used for generating the random data (Different seeds generate different data).")
|
||||
SampleDataCmd.Flags().IntP("teams", "t", 2, "The number of sample teams.")
|
||||
SampleDataCmd.Flags().Int("channels-per-team", 10, "The number of sample channels per team.")
|
||||
SampleDataCmd.Flags().IntP("users", "u", 15, "The number of sample users.")
|
||||
SampleDataCmd.Flags().Int("team-memberships", 2, "The number of sample team memberships per user.")
|
||||
SampleDataCmd.Flags().Int("channel-memberships", 5, "The number of sample channel memberships per user in a team.")
|
||||
SampleDataCmd.Flags().Int("posts-per-channel", 100, "The number of sample post per channel.")
|
||||
SampleDataCmd.Flags().Int("direct-channels", 30, "The number of sample direct message channels.")
|
||||
SampleDataCmd.Flags().Int("posts-per-direct-channel", 15, "The number of sample posts per direct message channel.")
|
||||
SampleDataCmd.Flags().Int("group-channels", 15, "The number of sample group message channels.")
|
||||
SampleDataCmd.Flags().Int("posts-per-group-channel", 30, "The number of sample posts per group message channel.")
|
||||
SampleDataCmd.Flags().IntP("workers", "w", 2, "How many workers to run during the import.")
|
||||
SampleDataCmd.Flags().String("profile-images", "", "Optional. Path to folder with images to randomly pick as user profile image.")
|
||||
SampleDataCmd.Flags().StringP("bulk", "b", "", "Optional. Path to write a JSONL bulk file instead of loading into the database.")
|
||||
cmd.RootCmd.AddCommand(SampleDataCmd)
|
||||
}
|
||||
|
||||
func sliceIncludes(vs []string, t string) bool {
|
||||
for _, v := range vs {
|
||||
if v == t {
|
||||
@@ -109,81 +129,64 @@ func randomMessage(users []string) string {
|
||||
return message
|
||||
}
|
||||
|
||||
func init() {
|
||||
sampleDataCmd.Flags().Int64P("seed", "s", 1, "Seed used for generating the random data (Different seeds generate different data).")
|
||||
sampleDataCmd.Flags().IntP("teams", "t", 2, "The number of sample teams.")
|
||||
sampleDataCmd.Flags().Int("channels-per-team", 10, "The number of sample channels per team.")
|
||||
sampleDataCmd.Flags().IntP("users", "u", 15, "The number of sample users.")
|
||||
sampleDataCmd.Flags().Int("team-memberships", 2, "The number of sample team memberships per user.")
|
||||
sampleDataCmd.Flags().Int("channel-memberships", 5, "The number of sample channel memberships per user in a team.")
|
||||
sampleDataCmd.Flags().Int("posts-per-channel", 100, "The number of sample post per channel.")
|
||||
sampleDataCmd.Flags().Int("direct-channels", 30, "The number of sample direct message channels.")
|
||||
sampleDataCmd.Flags().Int("posts-per-direct-channel", 15, "The number of sample posts per direct message channel.")
|
||||
sampleDataCmd.Flags().Int("group-channels", 15, "The number of sample group message channels.")
|
||||
sampleDataCmd.Flags().Int("posts-per-group-channel", 30, "The number of sample posts per group message channel.")
|
||||
sampleDataCmd.Flags().IntP("workers", "w", 2, "How many workers to run during the import.")
|
||||
sampleDataCmd.Flags().String("profile-images", "", "Optional. Path to folder with images to randomly pick as user profile image.")
|
||||
sampleDataCmd.Flags().StringP("bulk", "b", "", "Optional. Path to write a JSONL bulk file instead of loading into the database.")
|
||||
}
|
||||
|
||||
func sampleDataCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func sampleDataCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
seed, err := cmd.Flags().GetInt64("seed")
|
||||
seed, err := command.Flags().GetInt64("seed")
|
||||
if err != nil {
|
||||
return errors.New("Invalid seed parameter")
|
||||
}
|
||||
bulk, err := cmd.Flags().GetString("bulk")
|
||||
bulk, err := command.Flags().GetString("bulk")
|
||||
if err != nil {
|
||||
return errors.New("Invalid bulk parameter")
|
||||
}
|
||||
teams, err := cmd.Flags().GetInt("teams")
|
||||
teams, err := command.Flags().GetInt("teams")
|
||||
if err != nil || teams < 0 {
|
||||
return errors.New("Invalid teams parameter")
|
||||
}
|
||||
channelsPerTeam, err := cmd.Flags().GetInt("channels-per-team")
|
||||
channelsPerTeam, err := command.Flags().GetInt("channels-per-team")
|
||||
if err != nil || channelsPerTeam < 0 {
|
||||
return errors.New("Invalid channels-per-team parameter")
|
||||
}
|
||||
users, err := cmd.Flags().GetInt("users")
|
||||
users, err := command.Flags().GetInt("users")
|
||||
if err != nil || users < 0 {
|
||||
return errors.New("Invalid users parameter")
|
||||
}
|
||||
teamMemberships, err := cmd.Flags().GetInt("team-memberships")
|
||||
teamMemberships, err := command.Flags().GetInt("team-memberships")
|
||||
if err != nil || teamMemberships < 0 {
|
||||
return errors.New("Invalid team-memberships parameter")
|
||||
}
|
||||
channelMemberships, err := cmd.Flags().GetInt("channel-memberships")
|
||||
channelMemberships, err := command.Flags().GetInt("channel-memberships")
|
||||
if err != nil || channelMemberships < 0 {
|
||||
return errors.New("Invalid channel-memberships parameter")
|
||||
}
|
||||
postsPerChannel, err := cmd.Flags().GetInt("posts-per-channel")
|
||||
postsPerChannel, err := command.Flags().GetInt("posts-per-channel")
|
||||
if err != nil || postsPerChannel < 0 {
|
||||
return errors.New("Invalid posts-per-channel parameter")
|
||||
}
|
||||
directChannels, err := cmd.Flags().GetInt("direct-channels")
|
||||
directChannels, err := command.Flags().GetInt("direct-channels")
|
||||
if err != nil || directChannels < 0 {
|
||||
return errors.New("Invalid direct-channels parameter")
|
||||
}
|
||||
postsPerDirectChannel, err := cmd.Flags().GetInt("posts-per-direct-channel")
|
||||
postsPerDirectChannel, err := command.Flags().GetInt("posts-per-direct-channel")
|
||||
if err != nil || postsPerDirectChannel < 0 {
|
||||
return errors.New("Invalid posts-per-direct-channel parameter")
|
||||
}
|
||||
groupChannels, err := cmd.Flags().GetInt("group-channels")
|
||||
groupChannels, err := command.Flags().GetInt("group-channels")
|
||||
if err != nil || groupChannels < 0 {
|
||||
return errors.New("Invalid group-channels parameter")
|
||||
}
|
||||
postsPerGroupChannel, err := cmd.Flags().GetInt("posts-per-group-channel")
|
||||
postsPerGroupChannel, err := command.Flags().GetInt("posts-per-group-channel")
|
||||
if err != nil || postsPerGroupChannel < 0 {
|
||||
return errors.New("Invalid posts-per-group-channel parameter")
|
||||
}
|
||||
workers, err := cmd.Flags().GetInt("workers")
|
||||
workers, err := command.Flags().GetInt("workers")
|
||||
if err != nil {
|
||||
return errors.New("Invalid workers parameter")
|
||||
}
|
||||
profileImagesPath, err := cmd.Flags().GetString("profile-images")
|
||||
profileImagesPath, err := command.Flags().GetString("profile-images")
|
||||
if err != nil {
|
||||
return errors.New("Invalid profile-images parameter")
|
||||
}
|
||||
@@ -312,7 +315,7 @@ func sampleDataCmdF(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
importErr, lineNumber := a.BulkImport(bulkFile, false, workers)
|
||||
if importErr != nil {
|
||||
return errors.New(fmt.Sprintf("%s: %s, %s (line: %d)", importErr.Where, importErr.Message, importErr.DetailedError, lineNumber))
|
||||
return fmt.Errorf("%s: %s, %s (line: %d)", importErr.Where, importErr.Message, importErr.DetailedError, lineNumber)
|
||||
}
|
||||
} else if bulk != "-" {
|
||||
err := bulkFile.Close()
|
||||
@@ -395,7 +398,7 @@ func createUser(idx int, teamMemberships int, channelMemberships int, teamsAndCh
|
||||
position := rand.Intn(len(possibleTeams))
|
||||
team := possibleTeams[position]
|
||||
possibleTeams = append(possibleTeams[:position], possibleTeams[position+1:]...)
|
||||
if teamChannels, err := teamsAndChannels[team]; err == true {
|
||||
if teamChannels, err := teamsAndChannels[team]; err {
|
||||
teams = append(teams, createTeamMembership(channelMemberships, teamChannels, &team))
|
||||
}
|
||||
}
|
||||
@@ -429,10 +432,7 @@ func createTeamMembership(numOfchannels int, teamChannels []string, teamName *st
|
||||
roles = "team_user team_admin"
|
||||
}
|
||||
channels := []app.UserChannelImportData{}
|
||||
teamChannelsCopy := []string{}
|
||||
for _, value := range teamChannels {
|
||||
teamChannelsCopy = append(teamChannelsCopy, value)
|
||||
}
|
||||
teamChannelsCopy := append([]string(nil), teamChannels...)
|
||||
for x := 0; x < numOfchannels; x++ {
|
||||
if len(teamChannelsCopy) == 0 {
|
||||
break
|
||||
@@ -1,12 +1,13 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -15,11 +16,11 @@ func TestSampledataBadParameters(t *testing.T) {
|
||||
defer th.TearDown()
|
||||
|
||||
// should fail because you need at least 1 worker
|
||||
require.Error(t, runCommand(t, "sampledata", "--workers", "0"))
|
||||
require.Error(t, cmd.RunCommand(t, "sampledata", "--workers", "0"))
|
||||
|
||||
// should fail because you have more team memberships than teams
|
||||
require.Error(t, runCommand(t, "sampledata", "--teams", "10", "--teams-memberships", "11"))
|
||||
require.Error(t, cmd.RunCommand(t, "sampledata", "--teams", "10", "--teams-memberships", "11"))
|
||||
|
||||
// should fail because you have more channel memberships than channels per team
|
||||
require.Error(t, runCommand(t, "sampledata", "--channels-per-team", "10", "--channel-memberships", "11"))
|
||||
require.Error(t, cmd.RunCommand(t, "sampledata", "--channels-per-team", "10", "--channel-memberships", "11"))
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"net"
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/api4"
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/manualtesting"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
@@ -31,17 +32,22 @@ var MaxNotificationsPerChannelDefault int64 = 1000000
|
||||
var serverCmd = &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "Run the Mattermost server",
|
||||
RunE: runServerCmd,
|
||||
RunE: serverCmdF,
|
||||
SilenceUsage: true,
|
||||
}
|
||||
|
||||
func runServerCmd(cmd *cobra.Command, args []string) error {
|
||||
config, err := cmd.Flags().GetString("config")
|
||||
func init() {
|
||||
cmd.RootCmd.AddCommand(serverCmd)
|
||||
cmd.RootCmd.RunE = serverCmdF
|
||||
}
|
||||
|
||||
func serverCmdF(command *cobra.Command, args []string) error {
|
||||
config, err := command.Flags().GetString("config")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
disableConfigWatch, _ := cmd.Flags().GetBool("disableconfigwatch")
|
||||
disableConfigWatch, _ := command.Flags().GetBool("disableconfigwatch")
|
||||
|
||||
interruptChan := make(chan os.Signal, 1)
|
||||
return runServer(config, disableConfigWatch, interruptChan)
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@@ -110,7 +110,7 @@ func TestRunServerSystemdNotification(t *testing.T) {
|
||||
panic(err)
|
||||
}
|
||||
data := buffer[0:count]
|
||||
ch<- string(data)
|
||||
ch <- string(data)
|
||||
}(socketReader)
|
||||
|
||||
// Start and stop the server
|
||||
@@ -1,22 +1,24 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var teamCmd = &cobra.Command{
|
||||
var TeamCmd = &cobra.Command{
|
||||
Use: "team",
|
||||
Short: "Management of teams",
|
||||
}
|
||||
|
||||
var teamCreateCmd = &cobra.Command{
|
||||
var TeamCreateCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create a team",
|
||||
Long: `Create a team.`,
|
||||
@@ -25,7 +27,7 @@ var teamCreateCmd = &cobra.Command{
|
||||
RunE: createTeamCmdF,
|
||||
}
|
||||
|
||||
var removeUsersCmd = &cobra.Command{
|
||||
var RemoveUsersCmd = &cobra.Command{
|
||||
Use: "remove [team] [users]",
|
||||
Short: "Remove users from team",
|
||||
Long: "Remove some users from team",
|
||||
@@ -33,7 +35,7 @@ var removeUsersCmd = &cobra.Command{
|
||||
RunE: removeUsersCmdF,
|
||||
}
|
||||
|
||||
var addUsersCmd = &cobra.Command{
|
||||
var AddUsersCmd = &cobra.Command{
|
||||
Use: "add [team] [users]",
|
||||
Short: "Add users to team",
|
||||
Long: "Add some users to team",
|
||||
@@ -41,7 +43,7 @@ var addUsersCmd = &cobra.Command{
|
||||
RunE: addUsersCmdF,
|
||||
}
|
||||
|
||||
var deleteTeamsCmd = &cobra.Command{
|
||||
var DeleteTeamsCmd = &cobra.Command{
|
||||
Use: "delete [teams]",
|
||||
Short: "Delete teams",
|
||||
Long: `Permanently delete some teams.
|
||||
@@ -51,37 +53,38 @@ Permanently deletes a team along with all related information including posts fr
|
||||
}
|
||||
|
||||
func init() {
|
||||
teamCreateCmd.Flags().String("name", "", "Team Name")
|
||||
teamCreateCmd.Flags().String("display_name", "", "Team Display Name")
|
||||
teamCreateCmd.Flags().Bool("private", false, "Create a private team.")
|
||||
teamCreateCmd.Flags().String("email", "", "Administrator Email (anyone with this email is automatically a team admin)")
|
||||
TeamCreateCmd.Flags().String("name", "", "Team Name")
|
||||
TeamCreateCmd.Flags().String("display_name", "", "Team Display Name")
|
||||
TeamCreateCmd.Flags().Bool("private", false, "Create a private team.")
|
||||
TeamCreateCmd.Flags().String("email", "", "Administrator Email (anyone with this email is automatically a team admin)")
|
||||
|
||||
deleteTeamsCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the team and a DB backup has been performed.")
|
||||
DeleteTeamsCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the team and a DB backup has been performed.")
|
||||
|
||||
teamCmd.AddCommand(
|
||||
teamCreateCmd,
|
||||
removeUsersCmd,
|
||||
addUsersCmd,
|
||||
deleteTeamsCmd,
|
||||
TeamCmd.AddCommand(
|
||||
TeamCreateCmd,
|
||||
RemoveUsersCmd,
|
||||
AddUsersCmd,
|
||||
DeleteTeamsCmd,
|
||||
)
|
||||
cmd.RootCmd.AddCommand(TeamCmd)
|
||||
}
|
||||
|
||||
func createTeamCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func createTeamCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name, errn := cmd.Flags().GetString("name")
|
||||
name, errn := command.Flags().GetString("name")
|
||||
if errn != nil || name == "" {
|
||||
return errors.New("Name is required")
|
||||
}
|
||||
displayname, errdn := cmd.Flags().GetString("display_name")
|
||||
displayname, errdn := command.Flags().GetString("display_name")
|
||||
if errdn != nil || displayname == "" {
|
||||
return errors.New("Display Name is required")
|
||||
}
|
||||
email, _ := cmd.Flags().GetString("email")
|
||||
useprivate, _ := cmd.Flags().GetBool("private")
|
||||
email, _ := command.Flags().GetString("email")
|
||||
useprivate, _ := command.Flags().GetBool("private")
|
||||
|
||||
teamType := model.TEAM_OPEN
|
||||
if useprivate {
|
||||
@@ -102,8 +105,8 @@ func createTeamCmdF(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeUsersCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func removeUsersCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -127,16 +130,16 @@ func removeUsersCmdF(cmd *cobra.Command, args []string) error {
|
||||
|
||||
func removeUserFromTeam(a *app.App, team *model.Team, user *model.User, userArg string) {
|
||||
if user == nil {
|
||||
CommandPrintErrorln("Can't find user '" + userArg + "'")
|
||||
cmd.CommandPrintErrorln("Can't find user '" + userArg + "'")
|
||||
return
|
||||
}
|
||||
if err := a.LeaveTeam(team, user, ""); err != nil {
|
||||
CommandPrintErrorln("Unable to remove '" + userArg + "' from " + team.Name + ". Error: " + err.Error())
|
||||
cmd.CommandPrintErrorln("Unable to remove '" + userArg + "' from " + team.Name + ". Error: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func addUsersCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func addUsersCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -160,16 +163,16 @@ func addUsersCmdF(cmd *cobra.Command, args []string) error {
|
||||
|
||||
func addUserToTeam(a *app.App, team *model.Team, user *model.User, userArg string) {
|
||||
if user == nil {
|
||||
CommandPrintErrorln("Can't find user '" + userArg + "'")
|
||||
cmd.CommandPrintErrorln("Can't find user '" + userArg + "'")
|
||||
return
|
||||
}
|
||||
if err := a.JoinUserToTeam(team, user, ""); err != nil {
|
||||
CommandPrintErrorln("Unable to add '" + userArg + "' to " + team.Name)
|
||||
cmd.CommandPrintErrorln("Unable to add '" + userArg + "' to " + team.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteTeamsCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func deleteTeamsCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -178,16 +181,16 @@ func deleteTeamsCmdF(cmd *cobra.Command, args []string) error {
|
||||
return errors.New("Not enough arguments.")
|
||||
}
|
||||
|
||||
confirmFlag, _ := cmd.Flags().GetBool("confirm")
|
||||
confirmFlag, _ := command.Flags().GetBool("confirm")
|
||||
if !confirmFlag {
|
||||
var confirm string
|
||||
CommandPrettyPrintln("Have you performed a database backup? (YES/NO): ")
|
||||
cmd.CommandPrettyPrintln("Have you performed a database backup? (YES/NO): ")
|
||||
fmt.Scanln(&confirm)
|
||||
|
||||
if confirm != "YES" {
|
||||
return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
|
||||
}
|
||||
CommandPrettyPrintln("Are you sure you want to delete the teams specified? All data will be permanently deleted? (YES/NO): ")
|
||||
cmd.CommandPrettyPrintln("Are you sure you want to delete the teams specified? All data will be permanently deleted? (YES/NO): ")
|
||||
fmt.Scanln(&confirm)
|
||||
if confirm != "YES" {
|
||||
return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
|
||||
@@ -197,13 +200,13 @@ func deleteTeamsCmdF(cmd *cobra.Command, args []string) error {
|
||||
teams := getTeamsFromTeamArgs(a, args)
|
||||
for i, team := range teams {
|
||||
if team == nil {
|
||||
CommandPrintErrorln("Unable to find team '" + args[i] + "'")
|
||||
cmd.CommandPrintErrorln("Unable to find team '" + args[i] + "'")
|
||||
continue
|
||||
}
|
||||
if err := deleteTeam(a, team); err != nil {
|
||||
CommandPrintErrorln("Unable to delete team '" + team.Name + "' error: " + err.Error())
|
||||
cmd.CommandPrintErrorln("Unable to delete team '" + team.Name + "' error: " + err.Error())
|
||||
} else {
|
||||
CommandPrettyPrintln("Deleted team '" + team.Name + "'")
|
||||
cmd.CommandPrettyPrintln("Deleted team '" + team.Name + "'")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
@@ -18,7 +19,7 @@ func TestCreateTeam(t *testing.T) {
|
||||
name := "name" + id
|
||||
displayName := "Name " + id
|
||||
|
||||
checkCommand(t, "team", "create", "--name", name, "--display_name", displayName)
|
||||
cmd.CheckCommand(t, "team", "create", "--name", name, "--display_name", displayName)
|
||||
|
||||
found := th.SystemAdminClient.Must(th.SystemAdminClient.FindTeamByName(name)).Data.(bool)
|
||||
|
||||
@@ -31,7 +32,7 @@ func TestJoinTeam(t *testing.T) {
|
||||
th := api.Setup().InitSystemAdmin().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
checkCommand(t, "team", "add", th.SystemAdminTeam.Name, th.BasicUser.Email)
|
||||
cmd.CheckCommand(t, "team", "add", th.SystemAdminTeam.Name, th.BasicUser.Email)
|
||||
|
||||
profiles := th.SystemAdminClient.Must(th.SystemAdminClient.GetProfilesInTeam(th.SystemAdminTeam.Id, 0, 1000, "")).Data.(map[string]*model.User)
|
||||
|
||||
@@ -53,7 +54,7 @@ func TestLeaveTeam(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
checkCommand(t, "team", "remove", th.BasicTeam.Name, th.BasicUser.Email)
|
||||
cmd.CheckCommand(t, "team", "remove", th.BasicTeam.Name, th.BasicUser.Email)
|
||||
|
||||
profiles := th.BasicClient.Must(th.BasicClient.GetProfilesInTeam(th.BasicTeam.Id, 0, 1000, "")).Data.(map[string]*model.User)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
@@ -14,39 +14,41 @@ import (
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/api4"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/utils"
|
||||
"github.com/mattermost/mattermost-server/wsapi"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var testCmd = &cobra.Command{
|
||||
var TestCmd = &cobra.Command{
|
||||
Use: "test",
|
||||
Short: "Testing Commands",
|
||||
Hidden: true,
|
||||
}
|
||||
|
||||
var runWebClientTestsCmd = &cobra.Command{
|
||||
var RunWebClientTestsCmd = &cobra.Command{
|
||||
Use: "web_client_tests",
|
||||
Short: "Run the web client tests",
|
||||
RunE: webClientTestsCmdF,
|
||||
}
|
||||
|
||||
var runServerForWebClientTestsCmd = &cobra.Command{
|
||||
var RunServerForWebClientTestsCmd = &cobra.Command{
|
||||
Use: "web_client_tests_server",
|
||||
Short: "Run the server configured for running the web client tests against it",
|
||||
RunE: serverForWebClientTestsCmdF,
|
||||
}
|
||||
|
||||
func init() {
|
||||
testCmd.AddCommand(
|
||||
runWebClientTestsCmd,
|
||||
runServerForWebClientTestsCmd,
|
||||
TestCmd.AddCommand(
|
||||
RunWebClientTestsCmd,
|
||||
RunServerForWebClientTestsCmd,
|
||||
)
|
||||
cmd.RootCmd.AddCommand(TestCmd)
|
||||
}
|
||||
|
||||
func webClientTestsCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func webClientTestsCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -67,8 +69,8 @@ func webClientTestsCmdF(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func serverForWebClientTestsCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func serverForWebClientTestsCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -101,17 +103,17 @@ func setupClientTests(cfg *model.Config) {
|
||||
cfg.ServiceSettings.EnableOutgoingWebhooks = false
|
||||
}
|
||||
|
||||
func executeTestCommand(cmd *exec.Cmd) {
|
||||
cmdOutPipe, err := cmd.StdoutPipe()
|
||||
func executeTestCommand(command *exec.Cmd) {
|
||||
cmdOutPipe, err := command.StdoutPipe()
|
||||
if err != nil {
|
||||
CommandPrintErrorln("Failed to run tests")
|
||||
cmd.CommandPrintErrorln("Failed to run tests")
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
|
||||
cmdErrOutPipe, err := cmd.StderrPipe()
|
||||
cmdErrOutPipe, err := command.StderrPipe()
|
||||
if err != nil {
|
||||
CommandPrintErrorln("Failed to run tests")
|
||||
cmd.CommandPrintErrorln("Failed to run tests")
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
@@ -130,8 +132,8 @@ func executeTestCommand(cmd *exec.Cmd) {
|
||||
}
|
||||
}()
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
CommandPrintErrorln("Client Tests failed")
|
||||
if err := command.Run(); err != nil {
|
||||
cmd.CommandPrintErrorln("Client Tests failed")
|
||||
os.Exit(1)
|
||||
return
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -10,16 +11,17 @@ import (
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var userCmd = &cobra.Command{
|
||||
var UserCmd = &cobra.Command{
|
||||
Use: "user",
|
||||
Short: "Management of users",
|
||||
}
|
||||
|
||||
var userActivateCmd = &cobra.Command{
|
||||
var UserActivateCmd = &cobra.Command{
|
||||
Use: "activate [emails, usernames, userIds]",
|
||||
Short: "Activate users",
|
||||
Long: "Activate users that have been deactivated.",
|
||||
@@ -28,7 +30,7 @@ var userActivateCmd = &cobra.Command{
|
||||
RunE: userActivateCmdF,
|
||||
}
|
||||
|
||||
var userDeactivateCmd = &cobra.Command{
|
||||
var UserDeactivateCmd = &cobra.Command{
|
||||
Use: "deactivate [emails, usernames, userIds]",
|
||||
Short: "Deactivate users",
|
||||
Long: "Deactivate users. Deactivated users are immediately logged out of all sessions and are unable to log back in.",
|
||||
@@ -37,7 +39,7 @@ var userDeactivateCmd = &cobra.Command{
|
||||
RunE: userDeactivateCmdF,
|
||||
}
|
||||
|
||||
var userCreateCmd = &cobra.Command{
|
||||
var UserCreateCmd = &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "Create a user",
|
||||
Long: "Create a user",
|
||||
@@ -45,7 +47,7 @@ var userCreateCmd = &cobra.Command{
|
||||
RunE: userCreateCmdF,
|
||||
}
|
||||
|
||||
var userInviteCmd = &cobra.Command{
|
||||
var UserInviteCmd = &cobra.Command{
|
||||
Use: "invite [email] [teams]",
|
||||
Short: "Send user an email invite to a team.",
|
||||
Long: `Send user an email invite to a team.
|
||||
@@ -56,7 +58,7 @@ You can specify teams by name or ID.`,
|
||||
RunE: userInviteCmdF,
|
||||
}
|
||||
|
||||
var resetUserPasswordCmd = &cobra.Command{
|
||||
var ResetUserPasswordCmd = &cobra.Command{
|
||||
Use: "password [user] [password]",
|
||||
Short: "Set a user's password",
|
||||
Long: "Set a user's password",
|
||||
@@ -64,7 +66,16 @@ var resetUserPasswordCmd = &cobra.Command{
|
||||
RunE: resetUserPasswordCmdF,
|
||||
}
|
||||
|
||||
var resetUserMfaCmd = &cobra.Command{
|
||||
var updateUserEmailCmd = &cobra.Command{
|
||||
Use: "email [user] [new email]",
|
||||
Short: "Change email of the user",
|
||||
Long: "Change email of the user.",
|
||||
Example: ` user email test user@example.com
|
||||
user activate username`,
|
||||
RunE: updateUserEmailCmdF,
|
||||
}
|
||||
|
||||
var ResetUserMfaCmd = &cobra.Command{
|
||||
Use: "resetmfa [users]",
|
||||
Short: "Turn off MFA",
|
||||
Long: `Turn off multi-factor authentication for a user.
|
||||
@@ -73,7 +84,7 @@ If MFA enforcement is enabled, the user will be forced to re-enable MFA as soon
|
||||
RunE: resetUserMfaCmdF,
|
||||
}
|
||||
|
||||
var deleteUserCmd = &cobra.Command{
|
||||
var DeleteUserCmd = &cobra.Command{
|
||||
Use: "delete [users]",
|
||||
Short: "Delete users and all posts",
|
||||
Long: "Permanently delete user and all related information including posts.",
|
||||
@@ -81,7 +92,7 @@ var deleteUserCmd = &cobra.Command{
|
||||
RunE: deleteUserCmdF,
|
||||
}
|
||||
|
||||
var deleteAllUsersCmd = &cobra.Command{
|
||||
var DeleteAllUsersCmd = &cobra.Command{
|
||||
Use: "deleteall",
|
||||
Short: "Delete all users and all posts",
|
||||
Long: "Permanently delete all users and all related information including posts.",
|
||||
@@ -89,7 +100,7 @@ var deleteAllUsersCmd = &cobra.Command{
|
||||
RunE: deleteAllUsersCommandF,
|
||||
}
|
||||
|
||||
var migrateAuthCmd = &cobra.Command{
|
||||
var MigrateAuthCmd = &cobra.Command{
|
||||
Use: "migrate_auth [from_auth] [to_auth] [migration-options]",
|
||||
Short: "Mass migrate user accounts authentication type",
|
||||
Long: `Migrates accounts from one authentication provider to another. For example, you can upgrade your authentication provider from email to ldap.`,
|
||||
@@ -127,7 +138,7 @@ var migrateAuthCmd = &cobra.Command{
|
||||
RunE: migrateAuthCmdF,
|
||||
}
|
||||
|
||||
var verifyUserCmd = &cobra.Command{
|
||||
var VerifyUserCmd = &cobra.Command{
|
||||
Use: "verify [users]",
|
||||
Short: "Verify email of users",
|
||||
Long: "Verify the emails of some users.",
|
||||
@@ -135,7 +146,7 @@ var verifyUserCmd = &cobra.Command{
|
||||
RunE: verifyUserCmdF,
|
||||
}
|
||||
|
||||
var searchUserCmd = &cobra.Command{
|
||||
var SearchUserCmd = &cobra.Command{
|
||||
Use: "search [users]",
|
||||
Short: "Search for users",
|
||||
Long: "Search for users based on username, email, or user ID.",
|
||||
@@ -144,23 +155,23 @@ var searchUserCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
userCreateCmd.Flags().String("username", "", "Required. Username for the new user account.")
|
||||
userCreateCmd.Flags().String("email", "", "Required. The email address for the new user account.")
|
||||
userCreateCmd.Flags().String("password", "", "Required. The password for the new user account.")
|
||||
userCreateCmd.Flags().String("nickname", "", "Optional. The nickname for the new user account.")
|
||||
userCreateCmd.Flags().String("firstname", "", "Optional. The first name for the new user account.")
|
||||
userCreateCmd.Flags().String("lastname", "", "Optional. The last name for the new user account.")
|
||||
userCreateCmd.Flags().String("locale", "", "Optional. The locale (ex: en, fr) for the new user account.")
|
||||
userCreateCmd.Flags().Bool("system_admin", false, "Optional. If supplied, the new user will be a system administrator. Defaults to false.")
|
||||
UserCreateCmd.Flags().String("username", "", "Required. Username for the new user account.")
|
||||
UserCreateCmd.Flags().String("email", "", "Required. The email address for the new user account.")
|
||||
UserCreateCmd.Flags().String("password", "", "Required. The password for the new user account.")
|
||||
UserCreateCmd.Flags().String("nickname", "", "Optional. The nickname for the new user account.")
|
||||
UserCreateCmd.Flags().String("firstname", "", "Optional. The first name for the new user account.")
|
||||
UserCreateCmd.Flags().String("lastname", "", "Optional. The last name for the new user account.")
|
||||
UserCreateCmd.Flags().String("locale", "", "Optional. The locale (ex: en, fr) for the new user account.")
|
||||
UserCreateCmd.Flags().Bool("system_admin", false, "Optional. If supplied, the new user will be a system administrator. Defaults to false.")
|
||||
|
||||
deleteUserCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the user and a DB backup has been performed.")
|
||||
DeleteUserCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the user and a DB backup has been performed.")
|
||||
|
||||
deleteAllUsersCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the user and a DB backup has been performed.")
|
||||
DeleteAllUsersCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the user and a DB backup has been performed.")
|
||||
|
||||
migrateAuthCmd.Flags().Bool("force", false, "Force the migration to occur even if there are duplicates on the LDAP server. Duplicates will not be migrated. (ldap only)")
|
||||
migrateAuthCmd.Flags().Bool("auto", false, "Automatically migrate all users. Assumes the usernames and emails are identical between Mattermost and SAML services. (saml only)")
|
||||
migrateAuthCmd.Flags().Bool("dryRun", false, "Run a simulation of the migration process without changing the database.")
|
||||
migrateAuthCmd.SetUsageTemplate(`Usage:
|
||||
MigrateAuthCmd.Flags().Bool("force", false, "Force the migration to occur even if there are duplicates on the LDAP server. Duplicates will not be migrated. (ldap only)")
|
||||
MigrateAuthCmd.Flags().Bool("auto", false, "Automatically migrate all users. Assumes the usernames and emails are identical between Mattermost and SAML services. (saml only)")
|
||||
MigrateAuthCmd.Flags().Bool("dryRun", false, "Run a simulation of the migration process without changing the database.")
|
||||
MigrateAuthCmd.SetUsageTemplate(`Usage:
|
||||
platform user migrate_auth [from_auth] [to_auth] [migration-options] [flags]
|
||||
|
||||
Examples:
|
||||
@@ -184,7 +195,7 @@ Flags:
|
||||
Global Flags:
|
||||
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}
|
||||
`)
|
||||
migrateAuthCmd.SetHelpTemplate(`Usage:
|
||||
MigrateAuthCmd.SetHelpTemplate(`Usage:
|
||||
platform user migrate_auth [from_auth] [to_auth] [migration-options] [flags]
|
||||
|
||||
Examples:
|
||||
@@ -221,23 +232,25 @@ Global Flags:
|
||||
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}
|
||||
`)
|
||||
|
||||
userCmd.AddCommand(
|
||||
userActivateCmd,
|
||||
userDeactivateCmd,
|
||||
userCreateCmd,
|
||||
userInviteCmd,
|
||||
resetUserPasswordCmd,
|
||||
resetUserMfaCmd,
|
||||
deleteUserCmd,
|
||||
deleteAllUsersCmd,
|
||||
migrateAuthCmd,
|
||||
verifyUserCmd,
|
||||
searchUserCmd,
|
||||
UserCmd.AddCommand(
|
||||
UserActivateCmd,
|
||||
UserDeactivateCmd,
|
||||
UserCreateCmd,
|
||||
UserInviteCmd,
|
||||
ResetUserPasswordCmd,
|
||||
updateUserEmailCmd,
|
||||
ResetUserMfaCmd,
|
||||
DeleteUserCmd,
|
||||
DeleteAllUsersCmd,
|
||||
MigrateAuthCmd,
|
||||
VerifyUserCmd,
|
||||
SearchUserCmd,
|
||||
)
|
||||
cmd.RootCmd.AddCommand(UserCmd)
|
||||
}
|
||||
|
||||
func userActivateCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func userActivateCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -256,7 +269,7 @@ func changeUsersActiveStatus(a *app.App, userArgs []string, active bool) {
|
||||
err := changeUserActiveStatus(a, user, userArgs[i], active)
|
||||
|
||||
if err != nil {
|
||||
CommandPrintErrorln(err.Error())
|
||||
cmd.CommandPrintErrorln(err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -275,8 +288,8 @@ func changeUserActiveStatus(a *app.App, user *model.User, userArg string, activa
|
||||
return nil
|
||||
}
|
||||
|
||||
func userDeactivateCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func userDeactivateCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -289,29 +302,29 @@ func userDeactivateCmdF(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func userCreateCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func userCreateCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
username, erru := cmd.Flags().GetString("username")
|
||||
username, erru := command.Flags().GetString("username")
|
||||
if erru != nil || username == "" {
|
||||
return errors.New("Username is required")
|
||||
}
|
||||
email, erre := cmd.Flags().GetString("email")
|
||||
email, erre := command.Flags().GetString("email")
|
||||
if erre != nil || email == "" {
|
||||
return errors.New("Email is required")
|
||||
}
|
||||
password, errp := cmd.Flags().GetString("password")
|
||||
password, errp := command.Flags().GetString("password")
|
||||
if errp != nil || password == "" {
|
||||
return errors.New("Password is required")
|
||||
}
|
||||
nickname, _ := cmd.Flags().GetString("nickname")
|
||||
firstname, _ := cmd.Flags().GetString("firstname")
|
||||
lastname, _ := cmd.Flags().GetString("lastname")
|
||||
locale, _ := cmd.Flags().GetString("locale")
|
||||
systemAdmin, _ := cmd.Flags().GetBool("system_admin")
|
||||
nickname, _ := command.Flags().GetString("nickname")
|
||||
firstname, _ := command.Flags().GetString("firstname")
|
||||
lastname, _ := command.Flags().GetString("lastname")
|
||||
locale, _ := command.Flags().GetString("locale")
|
||||
systemAdmin, _ := command.Flags().GetBool("system_admin")
|
||||
|
||||
user := &model.User{
|
||||
Username: username,
|
||||
@@ -329,13 +342,13 @@ func userCreateCmdF(cmd *cobra.Command, args []string) error {
|
||||
a.UpdateUserRoles(ruser.Id, "system_user system_admin", false)
|
||||
}
|
||||
|
||||
CommandPrettyPrintln("Created User")
|
||||
cmd.CommandPrettyPrintln("Created User")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func userInviteCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func userInviteCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -354,7 +367,7 @@ func userInviteCmdF(cmd *cobra.Command, args []string) error {
|
||||
err := inviteUser(a, email, team, args[i+1])
|
||||
|
||||
if err != nil {
|
||||
CommandPrintErrorln(err.Error())
|
||||
cmd.CommandPrintErrorln(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,13 +381,13 @@ func inviteUser(a *app.App, email string, team *model.Team, teamArg string) erro
|
||||
}
|
||||
|
||||
a.SendInviteEmails(team, "Administrator", invites, *a.Config().ServiceSettings.SiteURL)
|
||||
CommandPrettyPrintln("Invites may or may not have been sent.")
|
||||
cmd.CommandPrettyPrintln("Invites may or may not have been sent.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetUserPasswordCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func resetUserPasswordCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -396,8 +409,38 @@ func resetUserPasswordCmdF(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetUserMfaCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func updateUserEmailCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newEmail := args[1]
|
||||
|
||||
if !model.IsValidEmail(newEmail) {
|
||||
return errors.New("Invalid email: '" + newEmail + "'")
|
||||
}
|
||||
|
||||
if len(args) != 2 {
|
||||
return errors.New("Expected two arguments. See help text for details.")
|
||||
}
|
||||
|
||||
user := getUserFromUserArg(a, args[0])
|
||||
if user == nil {
|
||||
return errors.New("Unable to find user '" + args[0] + "'")
|
||||
}
|
||||
|
||||
user.Email = newEmail
|
||||
_, errUpdate := a.UpdateUser(user, true)
|
||||
if err != nil {
|
||||
return errUpdate
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetUserMfaCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -421,8 +464,8 @@ func resetUserMfaCmdF(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteUserCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func deleteUserCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -431,16 +474,16 @@ func deleteUserCmdF(cmd *cobra.Command, args []string) error {
|
||||
return errors.New("Expected at least one argument. See help text for details.")
|
||||
}
|
||||
|
||||
confirmFlag, _ := cmd.Flags().GetBool("confirm")
|
||||
confirmFlag, _ := command.Flags().GetBool("confirm")
|
||||
if !confirmFlag {
|
||||
var confirm string
|
||||
CommandPrettyPrintln("Have you performed a database backup? (YES/NO): ")
|
||||
cmd.CommandPrettyPrintln("Have you performed a database backup? (YES/NO): ")
|
||||
fmt.Scanln(&confirm)
|
||||
|
||||
if confirm != "YES" {
|
||||
return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
|
||||
}
|
||||
CommandPrettyPrintln("Are you sure you want to permanently delete the specified users? (YES/NO): ")
|
||||
cmd.CommandPrettyPrintln("Are you sure you want to permanently delete the specified users? (YES/NO): ")
|
||||
fmt.Scanln(&confirm)
|
||||
if confirm != "YES" {
|
||||
return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
|
||||
@@ -462,8 +505,8 @@ func deleteUserCmdF(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteAllUsersCommandF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func deleteAllUsersCommandF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -472,16 +515,16 @@ func deleteAllUsersCommandF(cmd *cobra.Command, args []string) error {
|
||||
return errors.New("Expected zero arguments.")
|
||||
}
|
||||
|
||||
confirmFlag, _ := cmd.Flags().GetBool("confirm")
|
||||
confirmFlag, _ := command.Flags().GetBool("confirm")
|
||||
if !confirmFlag {
|
||||
var confirm string
|
||||
CommandPrettyPrintln("Have you performed a database backup? (YES/NO): ")
|
||||
cmd.CommandPrettyPrintln("Have you performed a database backup? (YES/NO): ")
|
||||
fmt.Scanln(&confirm)
|
||||
|
||||
if confirm != "YES" {
|
||||
return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
|
||||
}
|
||||
CommandPrettyPrintln("Are you sure you want to permanently delete all user accounts? (YES/NO): ")
|
||||
cmd.CommandPrettyPrintln("Are you sure you want to permanently delete all user accounts? (YES/NO): ")
|
||||
fmt.Scanln(&confirm)
|
||||
if confirm != "YES" {
|
||||
return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
|
||||
@@ -492,19 +535,19 @@ func deleteAllUsersCommandF(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
CommandPrettyPrintln("All user accounts successfully deleted.")
|
||||
cmd.CommandPrettyPrintln("All user accounts successfully deleted.")
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateAuthCmdF(cmd *cobra.Command, args []string) error {
|
||||
func migrateAuthCmdF(command *cobra.Command, args []string) error {
|
||||
if args[1] == "saml" {
|
||||
return migrateAuthToSamlCmdF(cmd, args)
|
||||
return migrateAuthToSamlCmdF(command, args)
|
||||
}
|
||||
return migrateAuthToLdapCmdF(cmd, args)
|
||||
return migrateAuthToLdapCmdF(command, args)
|
||||
}
|
||||
|
||||
func migrateAuthToLdapCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func migrateAuthToLdapCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -525,28 +568,28 @@ func migrateAuthToLdapCmdF(cmd *cobra.Command, args []string) error {
|
||||
return errors.New("Invalid match_field argument")
|
||||
}
|
||||
|
||||
forceFlag, _ := cmd.Flags().GetBool("force")
|
||||
dryRunFlag, _ := cmd.Flags().GetBool("dryRun")
|
||||
forceFlag, _ := command.Flags().GetBool("force")
|
||||
dryRunFlag, _ := command.Flags().GetBool("dryRun")
|
||||
|
||||
if migrate := a.AccountMigration; migrate != nil {
|
||||
if err := migrate.MigrateToLdap(fromAuth, matchField, forceFlag, dryRunFlag); err != nil {
|
||||
return errors.New("Error while migrating users: " + err.Error())
|
||||
}
|
||||
|
||||
CommandPrettyPrintln("Sucessfully migrated accounts.")
|
||||
cmd.CommandPrettyPrintln("Sucessfully migrated accounts.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func migrateAuthToSamlCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func migrateAuthToSamlCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dryRunFlag, _ := cmd.Flags().GetBool("dryRun")
|
||||
autoFlag, _ := cmd.Flags().GetBool("auto")
|
||||
dryRunFlag, _ := command.Flags().GetBool("dryRun")
|
||||
autoFlag, _ := command.Flags().GetBool("auto")
|
||||
|
||||
matchesFile := ""
|
||||
matches := map[string]string{}
|
||||
@@ -570,7 +613,7 @@ func migrateAuthToSamlCmdF(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if autoFlag && !dryRunFlag {
|
||||
var confirm string
|
||||
CommandPrettyPrintln("You are about to perform an automatic \"" + fromAuth + " to saml\" migration. This must only be done if your current Mattermost users with " + fromAuth + " auth have the same username and email in your SAML service. Otherwise, provide the usernames and emails from your SAML Service using the \"users file\" without the \"--auto\" option.\n\nDo you want to proceed with automatic migration anyway? (YES/NO):")
|
||||
cmd.CommandPrettyPrintln("You are about to perform an automatic \"" + fromAuth + " to saml\" migration. This must only be done if your current Mattermost users with " + fromAuth + " auth have the same username and email in your SAML service. Otherwise, provide the usernames and emails from your SAML Service using the \"users file\" without the \"--auto\" option.\n\nDo you want to proceed with automatic migration anyway? (YES/NO):")
|
||||
fmt.Scanln(&confirm)
|
||||
|
||||
if confirm != "YES" {
|
||||
@@ -588,14 +631,14 @@ func migrateAuthToSamlCmdF(cmd *cobra.Command, args []string) error {
|
||||
return errors.New("Error while migrating users: " + err.Error())
|
||||
}
|
||||
l4g.Close()
|
||||
CommandPrettyPrintln("Sucessfully migrated accounts.")
|
||||
cmd.CommandPrettyPrintln("Sucessfully migrated accounts.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyUserCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func verifyUserCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -608,19 +651,19 @@ func verifyUserCmdF(cmd *cobra.Command, args []string) error {
|
||||
|
||||
for i, user := range users {
|
||||
if user == nil {
|
||||
CommandPrintErrorln("Unable to find user '" + args[i] + "'")
|
||||
cmd.CommandPrintErrorln("Unable to find user '" + args[i] + "'")
|
||||
continue
|
||||
}
|
||||
if cresult := <-a.Srv.Store.User().VerifyEmail(user.Id); cresult.Err != nil {
|
||||
CommandPrintErrorln("Unable to verify '" + args[i] + "' email. Error: " + cresult.Err.Error())
|
||||
cmd.CommandPrintErrorln("Unable to verify '" + args[i] + "' email. Error: " + cresult.Err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func searchUserCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func searchUserCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -633,21 +676,21 @@ func searchUserCmdF(cmd *cobra.Command, args []string) error {
|
||||
|
||||
for i, user := range users {
|
||||
if i > 0 {
|
||||
CommandPrettyPrintln("------------------------------")
|
||||
cmd.CommandPrettyPrintln("------------------------------")
|
||||
}
|
||||
if user == nil {
|
||||
CommandPrintErrorln("Unable to find user '" + args[i] + "'")
|
||||
cmd.CommandPrintErrorln("Unable to find user '" + args[i] + "'")
|
||||
continue
|
||||
}
|
||||
|
||||
CommandPrettyPrintln("id: " + user.Id)
|
||||
CommandPrettyPrintln("username: " + user.Username)
|
||||
CommandPrettyPrintln("nickname: " + user.Nickname)
|
||||
CommandPrettyPrintln("position: " + user.Position)
|
||||
CommandPrettyPrintln("first_name: " + user.FirstName)
|
||||
CommandPrettyPrintln("last_name: " + user.LastName)
|
||||
CommandPrettyPrintln("email: " + user.Email)
|
||||
CommandPrettyPrintln("auth_service: " + user.AuthService)
|
||||
cmd.CommandPrettyPrintln("id: " + user.Id)
|
||||
cmd.CommandPrettyPrintln("username: " + user.Username)
|
||||
cmd.CommandPrettyPrintln("nickname: " + user.Nickname)
|
||||
cmd.CommandPrettyPrintln("position: " + user.Position)
|
||||
cmd.CommandPrettyPrintln("first_name: " + user.FirstName)
|
||||
cmd.CommandPrettyPrintln("last_name: " + user.LastName)
|
||||
cmd.CommandPrettyPrintln("email: " + user.Email)
|
||||
cmd.CommandPrettyPrintln("auth_service: " + user.AuthService)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -1,13 +1,15 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/api"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCreateUserWithTeam(t *testing.T) {
|
||||
@@ -18,9 +20,9 @@ func TestCreateUserWithTeam(t *testing.T) {
|
||||
email := "success+" + id + "@simulator.amazonses.com"
|
||||
username := "name" + id
|
||||
|
||||
checkCommand(t, "user", "create", "--email", email, "--password", "mypassword1", "--username", username)
|
||||
cmd.CheckCommand(t, "user", "create", "--email", email, "--password", "mypassword1", "--username", username)
|
||||
|
||||
checkCommand(t, "team", "add", th.SystemAdminTeam.Id, email)
|
||||
cmd.CheckCommand(t, "team", "add", th.SystemAdminTeam.Id, email)
|
||||
|
||||
profiles := th.SystemAdminClient.Must(th.SystemAdminClient.GetProfilesInTeam(th.SystemAdminTeam.Id, 0, 1000, "")).Data.(map[string]*model.User)
|
||||
|
||||
@@ -46,7 +48,7 @@ func TestCreateUserWithoutTeam(t *testing.T) {
|
||||
email := "success+" + id + "@simulator.amazonses.com"
|
||||
username := "name" + id
|
||||
|
||||
checkCommand(t, "user", "create", "--email", email, "--password", "mypassword1", "--username", username)
|
||||
cmd.CheckCommand(t, "user", "create", "--email", email, "--password", "mypassword1", "--username", username)
|
||||
|
||||
if result := <-th.App.Srv.Store.User().GetByEmail(email); result.Err != nil {
|
||||
t.Fatal()
|
||||
@@ -62,7 +64,7 @@ func TestResetPassword(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
checkCommand(t, "user", "password", th.BasicUser.Email, "password2")
|
||||
cmd.CheckCommand(t, "user", "password", th.BasicUser.Email, "password2")
|
||||
|
||||
th.BasicClient.Logout()
|
||||
th.BasicUser.Password = "password2"
|
||||
@@ -74,8 +76,35 @@ func TestMakeUserActiveAndInactive(t *testing.T) {
|
||||
defer th.TearDown()
|
||||
|
||||
// first inactivate the user
|
||||
checkCommand(t, "user", "deactivate", th.BasicUser.Email)
|
||||
cmd.CheckCommand(t, "user", "deactivate", th.BasicUser.Email)
|
||||
|
||||
// activate the inactive user
|
||||
checkCommand(t, "user", "activate", th.BasicUser.Email)
|
||||
cmd.CheckCommand(t, "user", "activate", th.BasicUser.Email)
|
||||
}
|
||||
|
||||
func TestChangeUserEmail(t *testing.T) {
|
||||
th := api.Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
newEmail := model.NewId() + "@mattermost-test.com"
|
||||
|
||||
cmd.CheckCommand(t, "user", "email", th.BasicUser.Username, newEmail)
|
||||
if result := <-th.App.Srv.Store.User().GetByEmail(th.BasicUser.Email); result.Err == nil {
|
||||
t.Fatal("should've updated to the new email")
|
||||
}
|
||||
if result := <-th.App.Srv.Store.User().GetByEmail(newEmail); result.Err != nil {
|
||||
t.Fatal()
|
||||
} else {
|
||||
user := result.Data.(*model.User)
|
||||
if user.Email != newEmail {
|
||||
t.Fatal("should've updated to the new email")
|
||||
}
|
||||
}
|
||||
|
||||
// should fail because using an invalid email
|
||||
require.Error(t, cmd.RunCommand(t, "user", "email", th.BasicUser.Username, "wrong$email.com"))
|
||||
|
||||
// should fail because user not found
|
||||
require.Error(t, cmd.RunCommand(t, "user", "email", "invalidUser", newEmail))
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
@@ -1,23 +1,29 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
"github.com/mattermost/mattermost-server/store"
|
||||
"github.com/mattermost/mattermost-server/store/sqlstore"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
var VersionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Display version information",
|
||||
RunE: versionCmdF,
|
||||
}
|
||||
|
||||
func versionCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
func init() {
|
||||
cmd.RootCmd.AddCommand(VersionCmd)
|
||||
}
|
||||
|
||||
func versionCmdF(command *cobra.Command, args []string) error {
|
||||
a, err := cmd.InitDBCommandContextCobra(command)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -28,12 +34,12 @@ func versionCmdF(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
func printVersion(a *app.App) {
|
||||
CommandPrintln("Version: " + model.CurrentVersion)
|
||||
CommandPrintln("Build Number: " + model.BuildNumber)
|
||||
CommandPrintln("Build Date: " + model.BuildDate)
|
||||
CommandPrintln("Build Hash: " + model.BuildHash)
|
||||
CommandPrintln("Build Enterprise Ready: " + model.BuildEnterpriseReady)
|
||||
cmd.CommandPrintln("Version: " + model.CurrentVersion)
|
||||
cmd.CommandPrintln("Build Number: " + model.BuildNumber)
|
||||
cmd.CommandPrintln("Build Date: " + model.BuildDate)
|
||||
cmd.CommandPrintln("Build Hash: " + model.BuildHash)
|
||||
cmd.CommandPrintln("Build Enterprise Ready: " + model.BuildEnterpriseReady)
|
||||
if supplier, ok := a.Srv.Store.(*store.LayeredStore).DatabaseLayer.(*sqlstore.SqlSupplier); ok {
|
||||
CommandPrintln("DB Version: " + supplier.GetCurrentSchemaVersion())
|
||||
cmd.CommandPrintln("DB Version: " + supplier.GetCurrentSchemaVersion())
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
)
|
||||
|
||||
func TestVersion(t *testing.T) {
|
||||
checkCommand(t, "version")
|
||||
cmd.CheckCommand(t, "version")
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/app"
|
||||
@@ -10,13 +10,13 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func initDBCommandContextCobra(cmd *cobra.Command) (*app.App, error) {
|
||||
func InitDBCommandContextCobra(cmd *cobra.Command) (*app.App, error) {
|
||||
config, err := cmd.Flags().GetString("config")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
a, err := initDBCommandContext(config)
|
||||
a, err := InitDBCommandContext(config)
|
||||
if err != nil {
|
||||
// Returning an error just prints the usage message, so actually panic
|
||||
panic(err)
|
||||
@@ -25,7 +25,7 @@ func initDBCommandContextCobra(cmd *cobra.Command) (*app.App, error) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func initDBCommandContext(configFileLocation string) (*app.App, error) {
|
||||
func InitDBCommandContext(configFileLocation string) (*app.App, error) {
|
||||
if err := utils.TranslationsPreInit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
package main
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,88 +0,0 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
// Plugins
|
||||
_ "github.com/mattermost/mattermost-server/model/gitlab"
|
||||
|
||||
// Enterprise Imports
|
||||
_ "github.com/mattermost/mattermost-server/imports"
|
||||
|
||||
// Enterprise Deps
|
||||
_ "github.com/dgryski/dgoogauth"
|
||||
_ "github.com/go-ldap/ldap"
|
||||
_ "github.com/hashicorp/memberlist"
|
||||
_ "github.com/mattermost/rsc/qr"
|
||||
_ "github.com/prometheus/client_golang/prometheus"
|
||||
_ "github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
_ "github.com/tylerb/graceful"
|
||||
_ "gopkg.in/olivere/elastic.v5"
|
||||
|
||||
// Temp imports for new dependencies
|
||||
_ "github.com/gorilla/schema"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringP("config", "c", "config.json", "Configuration file to use.")
|
||||
rootCmd.PersistentFlags().Bool("disableconfigwatch", false, "When set config.json will not be loaded from disk when the file is changed.")
|
||||
|
||||
resetCmd.Flags().Bool("confirm", false, "Confirm you really want to delete everything and a DB backup has been performed.")
|
||||
|
||||
rootCmd.AddCommand(serverCmd, versionCmd, userCmd, teamCmd, licenseCmd, importCmd, resetCmd, channelCmd, rolesCmd, testCmd, ldapCmd, configCmd, jobserverCmd, commandCmd, messageExportCmd, sampleDataCmd)
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "platform",
|
||||
Short: "Open source, self-hosted Slack-alternative",
|
||||
Long: `Mattermost offers workplace messaging across web, PC and phones with archiving, search and integration with your existing systems. Documentation available at https://docs.mattermost.com`,
|
||||
RunE: runServerCmd,
|
||||
}
|
||||
|
||||
var resetCmd = &cobra.Command{
|
||||
Use: "reset",
|
||||
Short: "Reset the database to initial state",
|
||||
Long: "Completely erases the database causing the loss of all data. This will reset Mattermost to its initial state.",
|
||||
RunE: resetCmdF,
|
||||
}
|
||||
|
||||
func resetCmdF(cmd *cobra.Command, args []string) error {
|
||||
a, err := initDBCommandContextCobra(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
confirmFlag, _ := cmd.Flags().GetBool("confirm")
|
||||
if !confirmFlag {
|
||||
var confirm string
|
||||
CommandPrettyPrintln("Have you performed a database backup? (YES/NO): ")
|
||||
fmt.Scanln(&confirm)
|
||||
|
||||
if confirm != "YES" {
|
||||
return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
|
||||
}
|
||||
CommandPrettyPrintln("Are you sure you want to delete everything? All data will be permanently deleted? (YES/NO): ")
|
||||
fmt.Scanln(&confirm)
|
||||
if confirm != "YES" {
|
||||
return errors.New("ABORTED: You did not answer YES exactly, in all capitals.")
|
||||
}
|
||||
}
|
||||
|
||||
a.Srv.Store.DropAllTables()
|
||||
CommandPrettyPrintln("Database sucessfully reset")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -358,7 +358,13 @@
|
||||
"DailyRunTime": "01:00",
|
||||
"ExportFromTimestamp": 0,
|
||||
"FileLocation": "export",
|
||||
"BatchSize": 10000
|
||||
"BatchSize": 10000,
|
||||
"GlobalRelaySettings": {
|
||||
"CustomerType": "A9",
|
||||
"SmtpUsername": "",
|
||||
"SmtpPassword": "",
|
||||
"EmailAddress": ""
|
||||
}
|
||||
},
|
||||
"JobSettings": {
|
||||
"RunJobs": true,
|
||||
|
||||
@@ -29,8 +29,10 @@ type MetricsInterface interface {
|
||||
|
||||
IncrementMemCacheHitCounter(cacheName string)
|
||||
IncrementMemCacheMissCounter(cacheName string)
|
||||
IncrementMemCacheInvalidationCounter(cacheName string)
|
||||
IncrementMemCacheMissCounterSession()
|
||||
IncrementMemCacheHitCounterSession()
|
||||
IncrementMemCacheInvalidationCounterSession()
|
||||
|
||||
IncrementWebsocketEvent(eventType string)
|
||||
IncrementWebSocketBroadcast(eventType string)
|
||||
|
||||
8
glide.lock
сгенерированный
8
glide.lock
сгенерированный
@@ -1,11 +1,13 @@
|
||||
hash: 6779beaa11fdb9c520471fb87c0a1a6ecc34a4c82610d942c44fba2f27a29936
|
||||
updated: 2018-02-15T18:28:32.209282461-08:00
|
||||
hash: 822849f55f8ab4b5c7545597b209edb6114bcf1009a552a9ee2503ff8d3fda09
|
||||
updated: 2018-03-07T13:01:49.575101746+01:00
|
||||
imports:
|
||||
- name: github.com/alecthomas/log4go
|
||||
version: 3fbce08846379ec7f4f6bc7fce6dd01ce28fae4c
|
||||
repo: https://github.com/mattermost/log4go.git
|
||||
- name: github.com/armon/go-metrics
|
||||
version: 7aa49fde808223f8dadfdbfd3a20ff6c19e5f9ec
|
||||
- name: github.com/avct/uasurfer
|
||||
version: c4be5581ec9617d04f5c5e02b893903ead0b1eed
|
||||
- name: github.com/beorn7/perks
|
||||
version: 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
|
||||
subpackages:
|
||||
@@ -138,8 +140,6 @@ imports:
|
||||
version: b8bc1bf767474819792c23f32d8286a45736f1c6
|
||||
- name: github.com/mitchellh/mapstructure
|
||||
version: a4e142e9c047c904fa2f1e144d9a84e6133024bc
|
||||
- name: github.com/mssola/user_agent
|
||||
version: 5243daae23628aeae9b6268541406bd5e95d5964
|
||||
- name: github.com/nicksnyder/go-i18n
|
||||
version: 0dc1626d56435e9d605a29875701721c54bc9bbd
|
||||
subpackages:
|
||||
|
||||
@@ -77,3 +77,4 @@ import:
|
||||
subpackages:
|
||||
- store/memstore
|
||||
- package: gopkg.in/yaml.v2
|
||||
- package: github.com/avct/uasurfer
|
||||
|
||||
26
i18n/de.json
26
i18n/de.json
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "Dateiupload nicht möglich. Header können nicht geparst werden."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "Unable to upload files. Incorrect number of files specified."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "Datei über den maximalen Dimensionen konnte nicht hochgeladen werden: {{.Filename}}"
|
||||
@@ -1814,6 +1818,10 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " Eine oder mehrere Dateien in einer Direktnachricht hochgeladen"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " Eine oder mehrere Dateien hochgeladen in "
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " in "
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "Plugins wurden deaktiviert."
|
||||
"translation": "Plugins have been disabled. Please check your logs for details."
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "Kann Benutzer nicht auf AD/LDAP-Server finden: "
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "Email already used by another SAML user."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "User not found in the users file."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "Username already used by another Mattermost user."
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "SAML Login war nicht erfolgreich da Verschlüsselung nicht aktiviert ist. Bitte kontaktieren Sie Ihren Systemadministrator."
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "Ungültiger Wert für Webserver-Verbindungssicherheit."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "Die WebRTC-Gateway-Websocket-URL muss gesetzt und eine gültige URL sein sowie mit ws:// oder wss:// beginnen."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "Ungültiger Wert für write timeout."
|
||||
|
||||
48
i18n/en.json
48
i18n/en.json
@@ -1830,14 +1830,14 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only",
|
||||
"translation": " uploaded one or more files in "
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " uploaded one or more files"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " uploaded one or more files in a direct message"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " uploaded one or more files"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " in "
|
||||
@@ -4258,6 +4258,10 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "Unable to find user on AD/LDAP server: "
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "Email already used by another SAML user."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "User not found in the users file."
|
||||
@@ -4266,10 +4270,6 @@
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "Username already used by another Mattermost user."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "Email already used by another SAML user."
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "SAML login was unsuccessful because one of the attributes is incorrect. Please contact your System Administrator."
|
||||
@@ -4958,6 +4958,30 @@
|
||||
"id": "model.config.is_valid.message_export.batch_size.app_error",
|
||||
"translation": "Message export job BatchSize must be a positive integer"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.message_export.export_type.app_error",
|
||||
"translation": "Message export job ExportFormat must be one of either 'actiance' or 'globalrelay'"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.message_export.global_relay.config_missing.app_error",
|
||||
"translation": "Message export job ExportFormat is set to 'globalrelay', but GlobalRelaySettings are missing"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.message_export.global_relay.customer_type.app_error",
|
||||
"translation": "Message export GlobalRelaySettings.CustomerType must be set to one of either 'A9' or 'A10'"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.message_export.global_relay.email_address.app_error",
|
||||
"translation": "Message export job GlobalRelaySettings.EmailAddress must be set to a valid email address"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.message_export.global_relay.smtp_username.app_error",
|
||||
"translation": "Message export job GlobalRelaySettings.SmtpUsername must be set"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.message_export.global_relay.smtp_password.app_error",
|
||||
"translation": "Message export job GlobalRelaySettings.SmtpPassword must be set"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.message_export.daily_runtime.app_error",
|
||||
"translation": "Message export job DailyRuntime must be a 24-hour time stamp in the form HH:MM."
|
||||
@@ -5046,10 +5070,6 @@
|
||||
"id": "model.config.is_valid.site_url.app_error",
|
||||
"translation": "Site URL must be a valid URL and start with http:// or https://"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "Websocket URL must be a valid URL and start with ws:// or wss://"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.site_url_email_batching.app_error",
|
||||
"translation": "Unable to enable email batching when SiteURL isn't set."
|
||||
@@ -5118,6 +5138,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "Invalid value for webserver connection security."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "Websocket URL must be a valid URL and start with ws:// or wss://"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "Invalid value for write timeout."
|
||||
|
||||
26
i18n/es.json
26
i18n/es.json
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "No se puedo cargar el archivo. El encabezado no puede ser analizado."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "No se pudo subir los archivos. El número de archivos especificado es incorrecto."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "No se pudo cargar el archivo que supera las dimensiones máximas: {{.Filename}}"
|
||||
@@ -1814,6 +1818,10 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " subió uno o más archivos en un mensaje directo"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " subió uno o más archivos"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " en "
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "Los Plugins se han deshabilitado."
|
||||
"translation": "Los Plugins han sido inhabilitados. Por favor revisa los logs para más detalles."
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "No se puede encontrar el usuario en el servidor AD/LDAP: "
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "Dirección de correo electrónico ya se encuentra en uso por otro usuario SAML."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "Usuario no encontrado en el archivo de usuarios."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "Nombre de usuario ya se encuentra en uso por otro usuario de Mattermost."
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "El inicio de sesión con SAML no tuvo éxito porque uno de sus atributos es incorrecto. Por favor, póngase en contacto con su Administrador del Sistema."
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "Valor no válido para la seguridad de conexión del servidor."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "La dirección URL del Websocket debe ser una dirección válida y comenzar con ws:// o wss://."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "Valor no válido para el tiempo de espera de escritura."
|
||||
|
||||
26
i18n/fr.json
26
i18n/fr.json
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "Impossible d'envoyer le fichier. L'entête ne peut être analysé."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "Unable to upload files. Incorrect number of files specified."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "Le fichier est au-dessus des limites de dimensions, il n'a pas pu être envoyé : {{.Filename}}"
|
||||
@@ -1814,6 +1818,10 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " a envoyé un ou plusieurs fichiers dans un message personnel"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " a envoyé un ou plusieurs fichiers dans "
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " dans "
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "Les plugins ont été désactivés."
|
||||
"translation": "Plugins have been disabled. Please check your logs for details."
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "Impossible de trouver l'utilisateur sur le serveur AD/LDAP : "
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "Email already used by another SAML user."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "User not found in the users file."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "Username already used by another Mattermost user."
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "La connexion via SAML a échoué car un des attributs est incorrect. Veuillez contacter votre administrateur système."
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "Valeur invalide pour la sécurité de la connexion au serveur web."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "URL de site invalide. Il doit s'agir d'une URL valide et commencer par http:// ou https://."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "Valeur invalide pour le délai d'attente d'écriture."
|
||||
|
||||
26
i18n/it.json
26
i18n/it.json
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "Impossibile caricare il file. Lettura dell'intestazione fallita."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "Impossibile caricare i file. Specifica numero di file non valido."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "Non è stato possibile caricare il file che supera le dimensioni massime: {{.Filename}}"
|
||||
@@ -1814,6 +1818,10 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " ha caricato uno o più file in un messaggio diretto"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " ha caricato uno o più file"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " in "
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "Il plugin è stato disattivato."
|
||||
"translation": "I plugin sono disattivati. Controllare i log per ulteriori informazioni."
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "Impossible trovare l'utente sul server AD/LDAP: "
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "Email in uso da un altro utente SAML."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "Utente non trovato nel file degli utenti."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "Nome utente in uso da un altro utente Mattermost."
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "Il login SAML non ha avuto successo a causa di un attributo non corretto. Contattare l'amministratore di sistema."
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "Valore non valido per la sicurezza connessione webserver."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "L'URL del websocket deve essere un URL valido ed iniziare con ws:// o wss://"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "Valore non valido per il timeout scrittura."
|
||||
|
||||
28
i18n/ja.json
28
i18n/ja.json
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "ファイルをアップロードできません。ヘッダーを解析できません。"
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "ファイルをアップロードできませんでした。指定されたファイル数が正しくありません。"
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "最大サイズ以上のファイルはアップロードできません: {{.Filename}}"
|
||||
@@ -1814,6 +1818,10 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " ファイルをダイレクトメッセージにアップロードしました"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " ファイルをアップロードしました"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " in "
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "プラグインは無効化されています。"
|
||||
"translation": "プラグインは無効化されています。詳しくはログを確認してください。"
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -3700,7 +3708,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.team.join_user_to_team.max_accounts.app_error",
|
||||
"translation": "このチームは登録ユーザー数の上限に達しました。システム管理者に上限値の設定を変更するように依頼してください。"
|
||||
"translation": "このチームは登録ユーザー数の上限に達しました。システム管理者に上限値を上げるよう依頼してください。"
|
||||
},
|
||||
{
|
||||
"id": "app.user_access_token.disabled",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "AD/LDAPサーバー上でユーザを見つけることができませんでした: "
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "電子メールアドレスは既に別のSAMLユーザーによって使用されています。"
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "ユーザーはユーザーファイル内で見つかりませんでした。"
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "ユーザー名は既に別のMattermostユーザーによって使用されています。"
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "SAMLログインは、属性の一つが不正のため、失敗しました。システム管理者に連絡してください。"
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "ウェブサーバーの接続のセキュリティーが不正な値です。"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "ウェブソケットURLは、ws://またはwss://で始まる有効なURLにしてください。"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "書き込みタイムアウトが不正な値です。"
|
||||
|
||||
26
i18n/ko.json
26
i18n/ko.json
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "파일을 업로드 할 수 없습니다. 머릿말 파싱에 실패하였습니다."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "Unable to upload files. Incorrect number of files specified."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "File above maximum dimensions could not be uploaded: {{.Filename}}"
|
||||
@@ -1814,6 +1818,10 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " uploaded one or more files in a direct message"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " uploaded one or more files"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " in "
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "Plugins have been disabled."
|
||||
"translation": "Plugins have been disabled. Please check your logs for details."
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "AD/LDAP 서버에서 사용자를 찾을 수 없음: "
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "Email already used by another SAML user."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "User not found in the users file."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "Username already used by another Mattermost user."
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "속성 중 하나가 올바르지 않아 SAML 로그인에 실패했습니다. 시스템 관리자에게 문의하십시오."
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "webserver connection security에 대해 허용되지 않은 값입니다."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "WebRTC Gateway Websocket Url은 URL형식이여야 하며 ws:// 또는 wss://로 시작해야합니다."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "write timeout에 대해 잘못된 값입니다."
|
||||
|
||||
26
i18n/nl.json
26
i18n/nl.json
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "Unable to upload file. Header cannot be parsed."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "Unable to upload files. Incorrect number of files specified."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "File above maximum dimensions could not be uploaded: {{.Filename}}"
|
||||
@@ -1814,6 +1818,10 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " uploaded one or more files in a direct message"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " uploaded one or more files"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " in "
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "Plugins have been disabled."
|
||||
"translation": "Plugins have been disabled. Please check your logs for details."
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "Kon gebruiker niet vinden op de AD/LDAP server: "
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "Email already used by another SAML user."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "User not found in the users file."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "Username already used by another Mattermost user."
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "SAML aanmelding is mislukt vanwege verkeerde attributen. Neem contact op met de beheerder."
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "Invalid value for webserver connection security."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "WebRTC Gateway Websocket Url moet een geldige URL zijn en starten met ws:// of wss://."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "Invalid value for write timeout."
|
||||
|
||||
26
i18n/pl.json
26
i18n/pl.json
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "Nie można pobrać pliku. Problem z parsowaniem nagłówka."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "Unable to upload files. Incorrect number of files specified."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "Plik większy od maksymalnego rozmiaru nie został załadowany: {{.Filename}}"
|
||||
@@ -1814,6 +1818,10 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " Wysłał jeden lub więcej plików w bezpośredniej wiadomości"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " Wyślij jeden albo więcej plików w "
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": "w"
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "Plugins have been disabled."
|
||||
"translation": "Plugins have been disabled. Please check your logs for details."
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "Nie można znaleźć użytkownika na serwerze AD/LDAP:"
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "Email already used by another SAML user."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "User not found in the users file."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "Username already used by another Mattermost user."
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "Logowanie przez SAML nie powiodło się, ponieważ jeden z atrybutów jest niepoprawny. Skontaktuj się z administratorem systemu."
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "Nieprawidłowa wartość zabezpieczenia połeczenia dla serwera web."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "Adres URL strony musi być prawidłowy i zaczynać się od http:// lub https://"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "Nieprawidłowa wartość upłynięcia limitu czasu."
|
||||
|
||||
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "Não foi possível carregar o arquivo. O cabeçalho não pode ser analisado."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "Unable to upload files. Incorrect number of files specified."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "Arquivo acima das dimensões máximas não pode ser enviado: {{.Filename}}"
|
||||
@@ -1814,6 +1818,10 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " enviado um ou mais arquivos em uma mensagem direta"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " enviado um ou mais arquivos em "
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " em "
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "Os plugins foram desabilitados."
|
||||
"translation": "Plugins have been disabled. Please check your logs for details."
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "Não foi possível localizar usuário no servidor AD/LDAP: "
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "Email already used by another SAML user."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "User not found in the users file."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "Username already used by another Mattermost user."
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "SAML login não foi bem sucedido porque um dos atributos está incorreto. Entre em contato com o Administrador do Sistema."
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "Valor inválido para segurança de conexão do servidor web."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "A URL Websocket do WebRTC Gateway deve ser uma URL válida e começar com ws:// ou wss://."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "Valor inválido para limite de escrita."
|
||||
|
||||
26
i18n/ru.json
26
i18n/ru.json
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "Невозможно загрузить файл. Заголовок не может быть распознан."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "Unable to upload files. Incorrect number of files specified."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "Размер файла превышает максимальный размер и не может быть загружен: {{.Filename}}"
|
||||
@@ -1814,6 +1818,10 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " Загружены один или несколько файлов для сообщения"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " Загружено один или несколько файлов в "
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " в "
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "Plugins have been disabled."
|
||||
"translation": "Plugins have been disabled. Please check your logs for details."
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "Не удалось найти пользователя на сервере AD/LDAP: "
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "Email already used by another SAML user."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "User not found in the users file."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "Username already used by another Mattermost user."
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "Попытка входа с использованием SAML не удалась из-за некорректного атрибута. Пожалуйста, свяжитесь с системным администратором."
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "Недопустимое значение настроек безопасности соединения веб-сервера."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "Ссылка на шлюз WebRTC должна быть действующей и начинаться с ws:// или wss://."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "Недопустимое значение для времени ожидания записи."
|
||||
|
||||
44
i18n/tr.json
44
i18n/tr.json
@@ -1036,7 +1036,7 @@
|
||||
},
|
||||
{
|
||||
"id": "api.context.mfa_required.app_error",
|
||||
"translation": "Bu sunucuda çok aşamalı kimlik doğrulaması kullanılıyor."
|
||||
"translation": "Bu sunucuda çok aşamalı kimlik doğrulaması zorunludur."
|
||||
},
|
||||
{
|
||||
"id": "api.context.missing_teamid.app_error",
|
||||
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "Dosya yüklenemedi. Üst bilgi işlenemedi."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "Belirtilen dosya sayısı hatalı olduğundan dosyalar yüklenemedi."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "Dosya en büyük boyutları aştığından yüklenemedi: {{.Filename}}"
|
||||
@@ -1808,12 +1812,16 @@
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only",
|
||||
"translation": " şunun içine bir ya da bir kaç dosya yükledi "
|
||||
"translation": " şuraya bir ya da bir kaç dosya yükledi "
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " bir doğrudan ileti içine bir ya da bir kaç dosya yükledi"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " bir ya da bir kaç dosya yükledi "
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " içinde "
|
||||
@@ -2184,7 +2192,7 @@
|
||||
},
|
||||
{
|
||||
"id": "api.team.add_user_to_team.missing_parameter.app_error",
|
||||
"translation": "Takıma kullanıcı eklemek için parametre gerekli."
|
||||
"translation": "Takıma kullanıcı eklemek için parametre zorunludur."
|
||||
},
|
||||
{
|
||||
"id": "api.team.create_team.email_disabled.app_error",
|
||||
@@ -2300,11 +2308,11 @@
|
||||
},
|
||||
{
|
||||
"id": "api.team.move_channel.post.error",
|
||||
"translation": "Kanal amacı iletisi gönderilemedi"
|
||||
"translation": "Kanal taşındı iletisi gönderilemedi."
|
||||
},
|
||||
{
|
||||
"id": "api.team.move_channel.success",
|
||||
"translation": "This channel has been moved to this team from %v."
|
||||
"translation": "Bu kanal %v üzerinden bu takıma taşındı."
|
||||
},
|
||||
{
|
||||
"id": "api.team.permanent_delete_team.attempting.warn",
|
||||
@@ -3072,7 +3080,7 @@
|
||||
},
|
||||
{
|
||||
"id": "api.webhook.incoming.error",
|
||||
"translation": "Could not decode the multipart payload of incoming webhook."
|
||||
"translation": "Gelen web bağlantsının birden çok parçalı yükünün kodu çözülemedi."
|
||||
},
|
||||
{
|
||||
"id": "api.webhook.init.debug",
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "Uygulama ekleri devre dışı bırakılmış."
|
||||
"translation": "Uygulama ekleri devre dışı bırakıldı. Ayrıntılar için günlük kayıtlarına bakın."
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "Kullanıcı AD/LDAP sunucusu üzerinde bulunamadı:"
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "E-posta başka bir SAML kullanıcısı tarafından kullanılıyor."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "Kullanıcı, kullanıcılar dosyasında bulunamadı."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "Bu kullanıcı adı başka bir Mattermost kullanıcısı tarafından kullanılıyor."
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "Özniteliklerden biri hatalı olduğundan SAML oturumu açılamadı. Lütfen sistem yöneticiniz ile görüşün."
|
||||
@@ -4884,7 +4904,7 @@
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.message_export.export_type.app_error",
|
||||
"translation": "Message export job ExportFormat must be one of either 'actiance' or 'globalrelay'"
|
||||
"translation": "İleti dışa aktarma görevinin Dışa Aktarma Biçimi 'actiance' ya da 'genelaktarım' olmalıdır"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.message_export.file_location.app_error",
|
||||
@@ -4896,7 +4916,7 @@
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.message_export.global_relay_email_address.app_error",
|
||||
"translation": "Message export job GlobalRelayEmailAddress must be set to a valid email address"
|
||||
"translation": "İleti dışa aktarma görevinin GenelAktarıcıE-postaAdresi geçerli bir e-posta adresi olmalıdır"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.password_length.app_error",
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "Web sunucusu bağlantı güvenliği değeri geçersiz."
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "Web soketi adresi ws:// ya da wss:// ile başlayan geçerli bir adres olmalıdır"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "Yazma zaman aşımı değeri geçersiz."
|
||||
@@ -7112,7 +7136,7 @@
|
||||
},
|
||||
{
|
||||
"id": "utils.mail.sendMail.attachments.write_error",
|
||||
"translation": "Failed to write attachment to email"
|
||||
"translation": "Ek dosya e-postaya eklenemedi"
|
||||
},
|
||||
{
|
||||
"id": "utils.mail.send_mail.close.app_error",
|
||||
|
||||
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "无法上传文件。标题无法被解析。"
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "无法上传文件。指定的文件数不匹配。"
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "无法上传超过最大尺寸的文件:{{.Filename}}"
|
||||
@@ -1814,6 +1818,10 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": " 在私信里上传一个或更多个文件"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": " 已上传一个或更多个文件"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " 在 "
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "插件已禁用。"
|
||||
"translation": "日志已停用。请检查您的日志了解详情。"
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -3700,7 +3708,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.team.join_user_to_team.max_accounts.app_error",
|
||||
"translation": "这个团队已经达到允许的最大用户数量。请与系统管理员联系以设置更高的限制。"
|
||||
"translation": "这个团队已经达到允许的最大帐号数量。请与系统管理员联系以设置更高的限制。"
|
||||
},
|
||||
{
|
||||
"id": "app.user_access_token.disabled",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "未在 AD/LDAP 服务器上找到用户:"
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "电子邮箱地址已被其他 SAML 用户使用。"
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "没有在用户文件里找到用户。"
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "用户名已被其他 Mattermost 用户使用。"
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "SAML登入因不正确属性而失败。请联系您的系统管理员。"
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "错误的网页服务器连接安全值。"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "Websocket 网址必须时有效的网址并且以 ws:// 或 wss:// 开头。"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "错误的写入超时值。"
|
||||
@@ -7112,7 +7136,7 @@
|
||||
},
|
||||
{
|
||||
"id": "utils.mail.sendMail.attachments.write_error",
|
||||
"translation": "Failed to write attachment to email"
|
||||
"translation": "给邮件添加附件失败"
|
||||
},
|
||||
{
|
||||
"id": "utils.mail.send_mail.close.app_error",
|
||||
|
||||
@@ -1368,6 +1368,10 @@
|
||||
"id": "api.file.upload_file.bad_parse.app_error",
|
||||
"translation": "無法上傳檔案。無法解析標頭。"
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.incorrect_number_of_files.app_error",
|
||||
"translation": "Unable to upload files. Incorrect number of files specified."
|
||||
},
|
||||
{
|
||||
"id": "api.file.upload_file.large_image.app_error",
|
||||
"translation": "無法上傳超過最大尺寸的檔案:{{.Filename}}"
|
||||
@@ -1814,6 +1818,10 @@
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_dm",
|
||||
"translation": "在直接傳訊中已上傳一個或更多檔案"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_image_only_no_channel",
|
||||
"translation": "已上傳一個或更多檔案"
|
||||
},
|
||||
{
|
||||
"id": "api.post.send_notifications_and_forget.push_in",
|
||||
"translation": " 於 "
|
||||
@@ -3648,7 +3656,7 @@
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.disabled.app_error",
|
||||
"translation": "模組已被停用。"
|
||||
"translation": "Plugins have been disabled. Please check your logs for details."
|
||||
},
|
||||
{
|
||||
"id": "app.plugin.extract.app_error",
|
||||
@@ -4182,6 +4190,18 @@
|
||||
"id": "ent.migration.migratetoldap.user_not_found",
|
||||
"translation": "找不到使用者,AD/LDAP 伺服器:"
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.email_already_used_by_other_user",
|
||||
"translation": "Email already used by another SAML user."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.user_not_found_in_users_mapping_file",
|
||||
"translation": "User not found in the users file."
|
||||
},
|
||||
{
|
||||
"id": "ent.migration.migratetosaml.username_already_used_by_other_user",
|
||||
"translation": "Username already used by another Mattermost user."
|
||||
},
|
||||
{
|
||||
"id": "ent.saml.attribute.app_error",
|
||||
"translation": "由於不正確的屬性,SAML 登入失敗。請聯繫系統管理員。"
|
||||
@@ -5026,6 +5046,10 @@
|
||||
"id": "model.config.is_valid.webserver_security.app_error",
|
||||
"translation": "網頁伺服器連線安全的值不正確。"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.websocket_url.app_error",
|
||||
"translation": "WebRTC 閘道 Websocket 網址必須是以 ws:// 或 wss:// 起始的有效網址。"
|
||||
},
|
||||
{
|
||||
"id": "model.config.is_valid.write_timeout.app_error",
|
||||
"translation": "寫入逾時的值不正確。"
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package jobs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
l4g "github.com/alecthomas/log4go"
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
|
||||
type TestWorker struct {
|
||||
srv *JobServer
|
||||
name string
|
||||
stop chan bool
|
||||
stopped chan bool
|
||||
jobs chan model.Job
|
||||
}
|
||||
|
||||
func (srv *JobServer) MakeTestWorker(name string) *TestWorker {
|
||||
return &TestWorker{
|
||||
srv: srv,
|
||||
name: name,
|
||||
stop: make(chan bool, 1),
|
||||
stopped: make(chan bool, 1),
|
||||
jobs: make(chan model.Job),
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *TestWorker) Run() {
|
||||
l4g.Debug("Worker %v: Started", worker.name)
|
||||
|
||||
defer func() {
|
||||
l4g.Debug("Worker %v: Finished", worker.name)
|
||||
worker.stopped <- true
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-worker.stop:
|
||||
l4g.Debug("Worker %v: Received stop signal", worker.name)
|
||||
return
|
||||
case job := <-worker.jobs:
|
||||
l4g.Debug("Worker %v: Received a new candidate job.", worker.name)
|
||||
worker.DoJob(&job)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *TestWorker) DoJob(job *model.Job) {
|
||||
if claimed, err := worker.srv.ClaimJob(job); err != nil {
|
||||
l4g.Error("Job: %v: Error occurred while trying to claim job: %v", job.Id, err.Error())
|
||||
return
|
||||
} else if !claimed {
|
||||
return
|
||||
}
|
||||
|
||||
cancelCtx, cancelCancelWatcher := context.WithCancel(context.Background())
|
||||
cancelWatcherChan := make(chan interface{}, 1)
|
||||
go worker.srv.CancellationWatcher(cancelCtx, job.Id, cancelWatcherChan)
|
||||
|
||||
defer cancelCancelWatcher()
|
||||
|
||||
counter := 0
|
||||
for {
|
||||
select {
|
||||
case <-cancelWatcherChan:
|
||||
l4g.Debug("Job %v: Job has been canceled via CancellationWatcher.", job.Id)
|
||||
if err := worker.srv.SetJobCanceled(job); err != nil {
|
||||
l4g.Error("Failed to mark job: %v as canceled. Error: %v", job.Id, err.Error())
|
||||
}
|
||||
return
|
||||
case <-worker.stop:
|
||||
l4g.Debug("Job %v: Job has been canceled via Worker Stop.", job.Id)
|
||||
if err := worker.srv.SetJobCanceled(job); err != nil {
|
||||
l4g.Error("Failed to mark job: %v as canceled. Error: %v", job.Id, err.Error())
|
||||
}
|
||||
return
|
||||
case <-time.After(5 * time.Second):
|
||||
counter++
|
||||
if counter > 10 {
|
||||
l4g.Debug("Job %v: Job completed.", job.Id)
|
||||
if err := worker.srv.SetJobSuccess(job); err != nil {
|
||||
l4g.Error("Failed to mark job: %v as succeeded. Error: %v", job.Id, err.Error())
|
||||
}
|
||||
return
|
||||
} else {
|
||||
if err := worker.srv.SetJobProgress(job, int64(counter*10)); err != nil {
|
||||
l4g.Error("Job: %v: an error occured while trying to set job progress: %v", job.Id, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *TestWorker) Stop() {
|
||||
l4g.Debug("Worker %v: Stopping", worker.name)
|
||||
worker.stop <- true
|
||||
<-worker.stopped
|
||||
}
|
||||
|
||||
func (worker *TestWorker) JobChannel() chan<- model.Job {
|
||||
return worker.jobs
|
||||
}
|
||||
36
main.go
Обычный файл
36
main.go
Обычный файл
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/mattermost/mattermost-server/cmd"
|
||||
_ "github.com/mattermost/mattermost-server/cmd/commands"
|
||||
|
||||
// Plugins
|
||||
_ "github.com/mattermost/mattermost-server/model/gitlab"
|
||||
|
||||
// Enterprise Imports
|
||||
_ "github.com/mattermost/mattermost-server/imports"
|
||||
|
||||
// Enterprise Deps
|
||||
_ "github.com/dgryski/dgoogauth"
|
||||
_ "github.com/go-ldap/ldap"
|
||||
_ "github.com/hashicorp/memberlist"
|
||||
_ "github.com/mattermost/rsc/qr"
|
||||
_ "github.com/prometheus/client_golang/prometheus"
|
||||
_ "github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
_ "github.com/tylerb/graceful"
|
||||
_ "gopkg.in/olivere/elastic.v5"
|
||||
|
||||
// Temp imports for new dependencies
|
||||
_ "github.com/gorilla/schema"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := cmd.Run(os.Args[1:]); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -86,12 +86,7 @@ func (o *Channel) Etag() string {
|
||||
return Etag(o.Id, o.UpdateAt)
|
||||
}
|
||||
|
||||
func (o *Channel) StatsEtag() string {
|
||||
return Etag(o.Id, o.ExtraUpdateAt)
|
||||
}
|
||||
|
||||
func (o *Channel) IsValid() *AppError {
|
||||
|
||||
if len(o.Id) != 26 {
|
||||
return NewAppError("Channel.IsValid", "model.channel.is_valid.id.app_error", nil, "", http.StatusBadRequest)
|
||||
}
|
||||
|
||||
@@ -699,7 +699,7 @@ func (c *Client4) GetUsersNotInTeam(teamId string, page int, perPage int, etag s
|
||||
}
|
||||
}
|
||||
|
||||
// GetUsersInChannel returns a page of users on a team. Page counting starts at 0.
|
||||
// GetUsersInChannel returns a page of users in a channel. Page counting starts at 0.
|
||||
func (c *Client4) GetUsersInChannel(channelId string, page int, perPage int, etag string) ([]*User, *Response) {
|
||||
query := fmt.Sprintf("?in_channel=%v&page=%v&per_page=%v", channelId, page, perPage)
|
||||
if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
|
||||
@@ -710,7 +710,18 @@ func (c *Client4) GetUsersInChannel(channelId string, page int, perPage int, eta
|
||||
}
|
||||
}
|
||||
|
||||
// GetUsersNotInChannel returns a page of users on a team. Page counting starts at 0.
|
||||
// GetUsersInChannelStatus returns a page of users in a channel. Page counting starts at 0. Sorted by Status
|
||||
func (c *Client4) GetUsersInChannelByStatus(channelId string, page int, perPage int, etag string) ([]*User, *Response) {
|
||||
query := fmt.Sprintf("?in_channel=%v&page=%v&per_page=%v&sort=status", channelId, page, perPage)
|
||||
if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
} else {
|
||||
defer closeBody(r)
|
||||
return UserListFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
}
|
||||
|
||||
// GetUsersNotInChannel returns a page of users not in a channel. Page counting starts at 0.
|
||||
func (c *Client4) GetUsersNotInChannel(teamId, channelId string, page int, perPage int, etag string) ([]*User, *Response) {
|
||||
query := fmt.Sprintf("?in_team=%v¬_in_channel=%v&page=%v&per_page=%v", teamId, channelId, page, perPage)
|
||||
if r, err := c.DoApiGet(c.GetUsersRoute()+query, etag); err != nil {
|
||||
|
||||
@@ -6,7 +6,6 @@ package model
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ClusterInfo struct {
|
||||
@@ -22,11 +21,6 @@ func (me *ClusterInfo) ToJson() string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (me *ClusterInfo) Copy() *ClusterInfo {
|
||||
json := me.ToJson()
|
||||
return ClusterInfoFromJson(strings.NewReader(json))
|
||||
}
|
||||
|
||||
func ClusterInfoFromJson(data io.Reader) *ClusterInfo {
|
||||
var me *ClusterInfo
|
||||
json.NewDecoder(data).Decode(&me)
|
||||
|
||||
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Ссылка в новой задаче
Block a user