summaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-12-06 10:49:34 -0500
committerGitHub <noreply@github.com>2016-12-06 10:49:34 -0500
commit026553e4f87bfc647a5c03129752e30fc523fa07 (patch)
treed5403c760151c0fa26fc6d020f7f4326ea9d6f8a /cmd
parentdcf11a14d8363c79ab62aefca46834d6daa615ab (diff)
downloadchat-026553e4f87bfc647a5c03129752e30fc523fa07.tar.gz
chat-026553e4f87bfc647a5c03129752e30fc523fa07.tar.bz2
chat-026553e4f87bfc647a5c03129752e30fc523fa07.zip
Improving command line interface (#4689)
Diffstat (limited to 'cmd')
-rw-r--r--cmd/platform/channel.go284
-rw-r--r--cmd/platform/channelargs.go59
-rw-r--r--cmd/platform/import.go62
-rw-r--r--cmd/platform/init.go41
-rw-r--r--cmd/platform/ldap.go39
-rw-r--r--cmd/platform/license.go50
-rw-r--r--cmd/platform/mattermost.go84
-rw-r--r--cmd/platform/oldcommands.go1102
-rw-r--r--cmd/platform/output.go32
-rw-r--r--cmd/platform/roles.go78
-rw-r--r--cmd/platform/server.go277
-rw-r--r--cmd/platform/team.go205
-rw-r--r--cmd/platform/teamargs.go32
-rw-r--r--cmd/platform/test.go82
-rw-r--r--cmd/platform/user.go432
-rw-r--r--cmd/platform/userargs.go38
-rw-r--r--cmd/platform/version.go30
17 files changed, 2927 insertions, 0 deletions
diff --git a/cmd/platform/channel.go b/cmd/platform/channel.go
new file mode 100644
index 000000000..cf5ef61bc
--- /dev/null
+++ b/cmd/platform/channel.go
@@ -0,0 +1,284 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+package main
+
+import (
+ "errors"
+
+ "github.com/mattermost/platform/api"
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/utils"
+ "github.com/spf13/cobra"
+)
+
+var channelCmd = &cobra.Command{
+ Use: "channel",
+ Short: "Management of channels",
+}
+
+var channelCreateCmd = &cobra.Command{
+ Use: "create",
+ Short: "Create a channel",
+ Long: `Create a channel.`,
+ Example: ` channel create --team myteam --name mynewchannel --display_name "My New Channel"
+ channel create --team myteam --name mynewprivatechannel --display_name "My New Private Channel" --private`,
+ RunE: createChannelCmdF,
+}
+
+var removeChannelUsersCmd = &cobra.Command{
+ Use: "remove [channel] [users]",
+ Short: "Remove users from channel",
+ Long: "Remove some users from channel",
+ Example: " channel remove mychannel user@example.com username",
+ RunE: removeChannelUsersCmdF,
+}
+
+var addChannelUsersCmd = &cobra.Command{
+ Use: "add [channel] [users]",
+ Short: "Add users to channel",
+ Long: "Add some users to channel",
+ Example: " channel add mychannel user@example.com username",
+ RunE: addChannelUsersCmdF,
+}
+
+var deleteChannelsCmd = &cobra.Command{
+ Use: "delete [channels]",
+ Short: "Delete channels",
+ Long: `Permanently delete some channels.
+Permanently deletes a channel along with all related information including posts from the database.
+Channels can be specified by [team]:[channel]. ie. myteam:mychannel or by channel ID.`,
+ Example: " channel delete myteam:mychannel",
+ RunE: deleteChannelsCmdF,
+}
+
+var listChannelsCmd = &cobra.Command{
+ Use: "list [teams]",
+ Short: "List all channels on specified teams.",
+ Long: `List all channels on specified teams.
+Archived channels are appended with ' (archived)'.`,
+ Example: " channel list myteam",
+ RunE: listChannelsCmdF,
+}
+
+var restoreChannelsCmd = &cobra.Command{
+ Use: "restore [channels]",
+ Short: "Restore some channels",
+ Long: `Restore a previously deleted channel
+Channels can be specified by [team]:[channel]. ie. myteam:mychannel or by channel ID.`,
+ Example: " channel restore myteam:mychannel",
+ RunE: restoreChannelsCmdF,
+}
+
+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.")
+
+ channelCmd.AddCommand(
+ channelCreateCmd,
+ removeChannelUsersCmd,
+ addChannelUsersCmd,
+ deleteChannelsCmd,
+ listChannelsCmd,
+ restoreChannelsCmd,
+ )
+}
+
+func createChannelCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if !utils.IsLicensed {
+ return errors.New(utils.T("cli.license.critical"))
+ }
+
+ name, errn := cmd.Flags().GetString("name")
+ if errn != nil || name == "" {
+ return errors.New("Name is required")
+ }
+ displayname, errdn := cmd.Flags().GetString("display_name")
+ if errdn != nil || displayname == "" {
+ return errors.New("Display Name is required")
+ }
+ teamArg, errteam := cmd.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")
+
+ channelType := model.CHANNEL_OPEN
+ if useprivate {
+ channelType = model.CHANNEL_PRIVATE
+ }
+
+ team := getTeamFromTeamArg(teamArg)
+
+ channel := &model.Channel{
+ TeamId: team.Id,
+ Name: name,
+ DisplayName: displayname,
+ Header: header,
+ Purpose: purpose,
+ Type: channelType,
+ }
+
+ c := getMockContext()
+ if _, err := api.CreateChannel(c, channel, false); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func removeChannelUsersCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if !utils.IsLicensed {
+ return errors.New(utils.T("cli.license.critical"))
+ }
+
+ if len(args) < 2 {
+ return errors.New("Not enough arguments.")
+ }
+
+ channel := getChannelFromChannelArg(args[0])
+ if channel == nil {
+ return errors.New("Unable to find channel '" + args[0] + "'")
+ }
+
+ users := getUsersFromUserArgs(args[1:])
+ for i, user := range users {
+ removeUserFromChannel(channel, user, args[i+1])
+ }
+
+ return nil
+}
+
+func removeUserFromChannel(channel *model.Channel, user *model.User, userArg string) {
+ if user == nil {
+ CommandPrintErrorln("Can't find user '" + userArg + "'")
+ return
+ }
+ if err := api.RemoveUserFromChannel(user.Id, "", channel); err != nil {
+ CommandPrintErrorln("Unable to remove '" + userArg + "' from " + channel.Name + ". Error: " + err.Error())
+ }
+}
+
+func addChannelUsersCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if !utils.IsLicensed {
+ return errors.New(utils.T("cli.license.critical"))
+ }
+
+ if len(args) < 2 {
+ return errors.New("Not enough arguments.")
+ }
+
+ channel := getChannelFromChannelArg(args[0])
+ if channel == nil {
+ return errors.New("Unable to find channel '" + args[0] + "'")
+ }
+
+ users := getUsersFromUserArgs(args[1:])
+ for i, user := range users {
+ addUserToChannel(channel, user, args[i+1])
+ }
+
+ return nil
+}
+
+func addUserToChannel(channel *model.Channel, user *model.User, userArg string) {
+ if user == nil {
+ CommandPrintErrorln("Can't find user '" + userArg + "'")
+ return
+ }
+ if _, err := api.AddUserToChannel(user, channel); err != nil {
+ CommandPrintErrorln("Unable to add '" + userArg + "' from " + channel.Name + ". Error: " + err.Error())
+ }
+}
+
+func deleteChannelsCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if len(args) < 1 {
+ return errors.New("Enter at least one channel to delete.")
+ }
+
+ channels := getChannelsFromChannelArgs(args)
+ for i, channel := range channels {
+ if channel == nil {
+ CommandPrintErrorln("Unable to find channel '" + args[i] + "'")
+ continue
+ }
+ if result := <-api.Srv.Store.Channel().Delete(channel.Id, model.GetMillis()); result.Err != nil {
+ CommandPrintErrorln("Unable to delete channel '" + channel.Name + "' error: " + result.Err.Error())
+ }
+ }
+
+ return nil
+}
+
+func listChannelsCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if !utils.IsLicensed {
+ return errors.New(utils.T("cli.license.critical"))
+ }
+
+ if len(args) < 1 {
+ return errors.New("Enter at least one team.")
+ }
+
+ teams := getTeamsFromTeamArgs(args)
+ for i, team := range teams {
+ if team == nil {
+ CommandPrintErrorln("Unable to find team '" + args[i] + "'")
+ continue
+ }
+ if result := <-api.Srv.Store.Channel().GetAll(team.Id); result.Err != nil {
+ 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)")
+ } else {
+ CommandPrettyPrintln(channel.Name)
+ }
+ }
+ }
+ }
+
+ return nil
+}
+
+func restoreChannelsCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if !utils.IsLicensed {
+ return errors.New(utils.T("cli.license.critical"))
+ }
+
+ if len(args) < 1 {
+ return errors.New("Enter at least one channel.")
+ }
+
+ channels := getChannelsFromChannelArgs(args)
+ for i, channel := range channels {
+ if channel == nil {
+ CommandPrintErrorln("Unable to find channel '" + args[i] + "'")
+ continue
+ }
+ if result := <-api.Srv.Store.Channel().SetDeleteAt(channel.Id, 0, model.GetMillis()); result.Err != nil {
+ CommandPrintErrorln("Unable to restore channel '" + args[i] + "'")
+ }
+ }
+
+ return nil
+}
diff --git a/cmd/platform/channelargs.go b/cmd/platform/channelargs.go
new file mode 100644
index 000000000..136d73fd2
--- /dev/null
+++ b/cmd/platform/channelargs.go
@@ -0,0 +1,59 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+package main
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/mattermost/platform/api"
+ "github.com/mattermost/platform/model"
+)
+
+const CHANNEL_ARG_SEPARATOR = ":"
+
+func getChannelsFromChannelArgs(channelArgs []string) []*model.Channel {
+ channels := make([]*model.Channel, 0, len(channelArgs))
+ for _, channelArg := range channelArgs {
+ channel := getChannelFromChannelArg(channelArg)
+ channels = append(channels, channel)
+ }
+ return channels
+}
+
+func parseChannelArg(channelArg string) (string, string) {
+ result := strings.SplitN(channelArg, CHANNEL_ARG_SEPARATOR, 2)
+ if len(result) == 1 {
+ return "", channelArg
+ }
+ return result[0], result[1]
+}
+
+func getChannelFromChannelArg(channelArg string) *model.Channel {
+ teamArg, channelPart := parseChannelArg(channelArg)
+ if teamArg == "" && channelPart == "" {
+ return nil
+ }
+
+ var channel *model.Channel
+ if teamArg != "" {
+ team := getTeamFromTeamArg(teamArg)
+ if team == nil {
+ return nil
+ }
+
+ if result := <-api.Srv.Store.Channel().GetByNameIncludeDeleted(team.Id, channelPart); result.Err == nil {
+ channel = result.Data.(*model.Channel)
+ } else {
+ fmt.Println(result.Err.Error())
+ }
+ }
+
+ if channel == nil {
+ if result := <-api.Srv.Store.Channel().Get(channelPart); result.Err == nil {
+ channel = result.Data.(*model.Channel)
+ }
+ }
+
+ return channel
+}
diff --git a/cmd/platform/import.go b/cmd/platform/import.go
new file mode 100644
index 000000000..b482cda7e
--- /dev/null
+++ b/cmd/platform/import.go
@@ -0,0 +1,62 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+package main
+
+import (
+ "errors"
+ "os"
+
+ "github.com/mattermost/platform/api"
+ "github.com/spf13/cobra"
+)
+
+var importCmd = &cobra.Command{
+ Use: "import",
+ Short: "Import data.",
+}
+
+var slackImportCmd = &cobra.Command{
+ Use: "slack [team] [file]",
+ Short: "Import a team from Slack.",
+ Long: "Import a team from a Slack export zip file.",
+ Example: " import slack myteam slack_export.zip",
+ RunE: slackImportCmdF,
+}
+
+func init() {
+ importCmd.AddCommand(
+ slackImportCmd,
+ )
+}
+
+func slackImportCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if len(args) != 2 {
+ return errors.New("Incorrect number of arguments.")
+ }
+
+ team := getTeamFromTeamArg(args[0])
+ if team == nil {
+ return errors.New("Unable to find team '" + args[0] + "'")
+ }
+
+ fileReader, err := os.Open(args[1])
+ if err != nil {
+ return err
+ }
+ defer fileReader.Close()
+
+ fileInfo, err := fileReader.Stat()
+ if err != nil {
+ return err
+ }
+
+ CommandPrettyPrintln("Running Slack Import. This may take a long time for large teams or teams with many messages.")
+
+ api.SlackImport(fileReader, fileInfo.Size(), team.Id)
+
+ CommandPrettyPrintln("Finished Slack Import.")
+
+ return nil
+}
diff --git a/cmd/platform/init.go b/cmd/platform/init.go
new file mode 100644
index 000000000..7e07cce8a
--- /dev/null
+++ b/cmd/platform/init.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/mattermost/platform/api"
+ "github.com/mattermost/platform/utils"
+ "github.com/spf13/cobra"
+)
+
+func doLoadConfig(filename string) (err string) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = fmt.Sprintf("%v", r)
+ }
+ }()
+ utils.TranslationsPreInit()
+ utils.LoadConfig(filename)
+ return ""
+}
+
+func initDBCommandContextCobra(cmd *cobra.Command) error {
+ config, err := cmd.Flags().GetString("config")
+ if err != nil {
+ return err
+ }
+ initDBCommandContext(config)
+
+ return nil
+}
+
+func initDBCommandContext(configFileLocation string) {
+ if errstr := doLoadConfig(configFileLocation); errstr != "" {
+ return
+ }
+
+ utils.ConfigureCmdLineLog()
+
+ api.NewServer()
+ api.InitStores()
+}
diff --git a/cmd/platform/ldap.go b/cmd/platform/ldap.go
new file mode 100644
index 000000000..8f3aa0c81
--- /dev/null
+++ b/cmd/platform/ldap.go
@@ -0,0 +1,39 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+package main
+
+import (
+ "github.com/mattermost/platform/einterfaces"
+ "github.com/spf13/cobra"
+)
+
+var ldapCmd = &cobra.Command{
+ Use: "ldap",
+ Short: "LDAP related utilities",
+}
+
+var ldapSyncCmd = &cobra.Command{
+ Use: "sync",
+ Short: "Synchronize now",
+ Long: "Synchronize all LDAP users now.",
+ Example: " ldap sync",
+ RunE: ldapSyncCmdF,
+}
+
+func init() {
+ ldapCmd.AddCommand(
+ ldapSyncCmd,
+ )
+}
+
+func ldapSyncCmdF(cmd *cobra.Command, args []string) error {
+ if ldapI := einterfaces.GetLdapInterface(); ldapI != nil {
+ if err := ldapI.Syncronize(); err != nil {
+ CommandPrintErrorln("ERROR: AD/LDAP Synchronization Failed")
+ } else {
+ CommandPrettyPrintln("SUCCESS: AD/LDAP Synchronization Complete")
+ }
+ }
+
+ return nil
+}
diff --git a/cmd/platform/license.go b/cmd/platform/license.go
new file mode 100644
index 000000000..776f62856
--- /dev/null
+++ b/cmd/platform/license.go
@@ -0,0 +1,50 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+package main
+
+import (
+ "errors"
+ "io/ioutil"
+
+ "github.com/mattermost/platform/api"
+ "github.com/spf13/cobra"
+)
+
+var licenseCmd = &cobra.Command{
+ Use: "license",
+ Short: "Licensing commands",
+}
+
+var uploadLicenseCmd = &cobra.Command{
+ Use: "upload [license]",
+ Short: "Upload a license.",
+ Long: "Upload a license. Replaces current license.",
+ Example: " license upload /path/to/license/mylicensefile.mattermost-license",
+ RunE: uploadLicenseCmdF,
+}
+
+func init() {
+ licenseCmd.AddCommand(uploadLicenseCmd)
+}
+
+func uploadLicenseCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if len(args) != 1 {
+ return errors.New("Enter one license file to upload")
+ }
+
+ var fileBytes []byte
+ var err error
+ if fileBytes, err = ioutil.ReadFile(args[0]); err != nil {
+ return err
+ }
+
+ if _, err := api.SaveLicense(fileBytes); err != nil {
+ return err
+ }
+
+ CommandPrettyPrintln("Uploaded license file")
+
+ return nil
+}
diff --git a/cmd/platform/mattermost.go b/cmd/platform/mattermost.go
new file mode 100644
index 000000000..d8210b012
--- /dev/null
+++ b/cmd/platform/mattermost.go
@@ -0,0 +1,84 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "os"
+
+ "github.com/mattermost/platform/api"
+ "github.com/spf13/cobra"
+
+ // Plugins
+ _ "github.com/mattermost/platform/model/gitlab"
+
+ // Enterprise Deps
+ _ "github.com/dgryski/dgoogauth"
+ _ "github.com/go-ldap/ldap"
+ _ "github.com/mattermost/rsc/qr"
+)
+
+//ENTERPRISE_IMPORTS
+
+func main() {
+ 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,
+ }
+ rootCmd.PersistentFlags().StringP("config", "c", "config.json", "Configuration file to use.")
+
+ 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)
+
+ flag.Usage = func() {
+ rootCmd.Usage()
+ }
+ parseCmds()
+
+ if flagRunCmds {
+ CommandPrintErrorln("---------------------------------------------------------------------------------------------")
+ CommandPrintErrorln("DEPRECATED! All previous commands are now deprecated. Run: platform help to see the new ones.")
+ CommandPrintErrorln("---------------------------------------------------------------------------------------------")
+ doLegacyCommands()
+ } else {
+ if err := rootCmd.Execute(); err != nil {
+ os.Exit(1)
+ }
+ }
+}
+
+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 {
+ 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.")
+ }
+ }
+
+ api.Srv.Store.DropAllTables()
+ CommandPrettyPrintln("Database sucessfully reset")
+
+ return nil
+}
diff --git a/cmd/platform/oldcommands.go b/cmd/platform/oldcommands.go
new file mode 100644
index 000000000..1cf898882
--- /dev/null
+++ b/cmd/platform/oldcommands.go
@@ -0,0 +1,1102 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+ "time"
+
+ l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/platform/api"
+ "github.com/mattermost/platform/einterfaces"
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/store"
+ "github.com/mattermost/platform/utils"
+ "github.com/mattermost/platform/web"
+)
+
+var flagCmdUpdateDb30 bool
+var flagCmdCreateTeam bool
+var flagCmdCreateUser bool
+var flagCmdInviteUser bool
+var flagCmdAssignRole bool
+var flagCmdCreateChannel bool
+var flagCmdJoinChannel bool
+var flagCmdLeaveChannel bool
+var flagCmdListChannels bool
+var flagCmdRestoreChannel bool
+var flagCmdJoinTeam bool
+var flagCmdLeaveTeam bool
+var flagCmdVersion bool
+var flagCmdRunWebClientTests bool
+var flagCmdRunJavascriptClientTests bool
+var flagCmdResetPassword bool
+var flagCmdResetMfa bool
+var flagCmdPermanentDeleteUser bool
+var flagCmdPermanentDeleteTeam bool
+var flagCmdPermanentDeleteAllUsers bool
+var flagCmdResetDatabase bool
+var flagCmdRunLdapSync bool
+var flagCmdMigrateAccounts bool
+var flagCmdActivateUser bool
+var flagCmdSlackImport bool
+var flagUsername string
+var flagCmdUploadLicense bool
+var flagConfigFile string
+var flagLicenseFile string
+var flagEmail string
+var flagPassword string
+var flagTeamName string
+var flagChannelName string
+var flagConfirmBackup string
+var flagRole string
+var flagRunCmds bool
+var flagFromAuth string
+var flagToAuth string
+var flagMatchField string
+var flagChannelType string
+var flagChannelHeader string
+var flagChannelPurpose string
+var flagUserSetInactive bool
+var flagImportArchive string
+
+func doLegacyCommands() {
+ doLoadConfig(flagConfigFile)
+ utils.InitTranslations(utils.Cfg.LocalizationSettings)
+ utils.ConfigureCmdLineLog()
+ api.NewServer()
+ api.InitStores()
+ api.InitRouter()
+ api.InitApi()
+ web.InitWeb()
+
+ if model.BuildEnterpriseReady == "true" {
+ api.LoadLicense()
+ }
+
+ runCmds()
+}
+
+func parseCmds() {
+ flag.StringVar(&flagConfigFile, "config", "config.json", "")
+ flag.StringVar(&flagUsername, "username", "", "")
+ flag.StringVar(&flagLicenseFile, "license", "", "")
+ flag.StringVar(&flagEmail, "email", "", "")
+ flag.StringVar(&flagPassword, "password", "", "")
+ flag.StringVar(&flagTeamName, "team_name", "", "")
+ flag.StringVar(&flagChannelName, "channel_name", "", "")
+ flag.StringVar(&flagConfirmBackup, "confirm_backup", "", "")
+ flag.StringVar(&flagFromAuth, "from_auth", "", "")
+ flag.StringVar(&flagToAuth, "to_auth", "", "")
+ flag.StringVar(&flagMatchField, "match_field", "email", "")
+ flag.StringVar(&flagRole, "role", "", "")
+ flag.StringVar(&flagChannelType, "channel_type", "O", "")
+ flag.StringVar(&flagChannelHeader, "channel_header", "", "")
+ flag.StringVar(&flagChannelPurpose, "channel_purpose", "", "")
+ flag.StringVar(&flagImportArchive, "import_archive", "", "")
+
+ flag.BoolVar(&flagCmdUpdateDb30, "upgrade_db_30", false, "")
+ flag.BoolVar(&flagCmdCreateTeam, "create_team", false, "")
+ flag.BoolVar(&flagCmdCreateUser, "create_user", false, "")
+ flag.BoolVar(&flagCmdInviteUser, "invite_user", false, "")
+ flag.BoolVar(&flagCmdAssignRole, "assign_role", false, "")
+ flag.BoolVar(&flagCmdCreateChannel, "create_channel", false, "")
+ flag.BoolVar(&flagCmdJoinChannel, "join_channel", false, "")
+ flag.BoolVar(&flagCmdLeaveChannel, "leave_channel", false, "")
+ flag.BoolVar(&flagCmdListChannels, "list_channels", false, "")
+ flag.BoolVar(&flagCmdRestoreChannel, "restore_channel", false, "")
+ flag.BoolVar(&flagCmdJoinTeam, "join_team", false, "")
+ flag.BoolVar(&flagCmdLeaveTeam, "leave_team", false, "")
+ flag.BoolVar(&flagCmdVersion, "version", false, "")
+ flag.BoolVar(&flagCmdRunWebClientTests, "run_web_client_tests", false, "")
+ flag.BoolVar(&flagCmdRunJavascriptClientTests, "run_javascript_client_tests", false, "")
+ flag.BoolVar(&flagCmdResetPassword, "reset_password", false, "")
+ flag.BoolVar(&flagCmdResetMfa, "reset_mfa", false, "")
+ flag.BoolVar(&flagCmdPermanentDeleteUser, "permanent_delete_user", false, "")
+ flag.BoolVar(&flagCmdPermanentDeleteTeam, "permanent_delete_team", false, "")
+ flag.BoolVar(&flagCmdPermanentDeleteAllUsers, "permanent_delete_all_users", false, "")
+ flag.BoolVar(&flagCmdResetDatabase, "reset_database", false, "")
+ flag.BoolVar(&flagCmdRunLdapSync, "ldap_sync", false, "")
+ flag.BoolVar(&flagCmdMigrateAccounts, "migrate_accounts", false, "")
+ flag.BoolVar(&flagCmdUploadLicense, "upload_license", false, "")
+ flag.BoolVar(&flagCmdActivateUser, "activate_user", false, "")
+ flag.BoolVar(&flagCmdSlackImport, "slack_import", false, "")
+ flag.BoolVar(&flagUserSetInactive, "inactive", false, "")
+
+ flag.Parse()
+
+ flagRunCmds = (flagCmdCreateTeam ||
+ flagCmdCreateUser ||
+ flagCmdInviteUser ||
+ flagCmdLeaveTeam ||
+ flagCmdAssignRole ||
+ flagCmdCreateChannel ||
+ flagCmdJoinChannel ||
+ flagCmdLeaveChannel ||
+ flagCmdListChannels ||
+ flagCmdRestoreChannel ||
+ flagCmdJoinTeam ||
+ flagCmdResetPassword ||
+ flagCmdResetMfa ||
+ flagCmdVersion ||
+ flagCmdRunWebClientTests ||
+ flagCmdRunJavascriptClientTests ||
+ flagCmdPermanentDeleteUser ||
+ flagCmdPermanentDeleteTeam ||
+ flagCmdPermanentDeleteAllUsers ||
+ flagCmdResetDatabase ||
+ flagCmdRunLdapSync ||
+ flagCmdMigrateAccounts ||
+ flagCmdUploadLicense ||
+ flagCmdActivateUser ||
+ flagCmdSlackImport)
+}
+
+func runCmds() {
+ cmdVersion()
+ cmdRunClientTests()
+ cmdCreateTeam()
+ cmdCreateUser()
+ cmdInviteUser()
+ cmdLeaveTeam()
+ cmdAssignRole()
+ cmdCreateChannel()
+ cmdJoinChannel()
+ cmdLeaveChannel()
+ cmdListChannels()
+ cmdRestoreChannel()
+ cmdJoinTeam()
+ cmdResetPassword()
+ cmdResetMfa()
+ cmdPermDeleteUser()
+ cmdPermDeleteTeam()
+ cmdPermDeleteAllUsers()
+ cmdResetDatabase()
+ cmdUploadLicense()
+ cmdRunLdapSync()
+ cmdRunMigrateAccounts()
+ cmdActivateUser()
+ cmdSlackImport()
+}
+
+func cmdRunClientTests() {
+ if flagCmdRunWebClientTests {
+ setupClientTests()
+ api.StartServer()
+ runWebClientTests()
+ api.StopServer()
+ }
+}
+
+func cmdUpdateDb30() {
+ if flagCmdUpdateDb30 {
+ // This command is a no-op for backwards compatibility
+ flushLogAndExit(0)
+ }
+}
+
+func cmdCreateTeam() {
+ if flagCmdCreateTeam {
+ if len(flagTeamName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
+ os.Exit(1)
+ }
+
+ if len(flagEmail) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
+ os.Exit(1)
+ }
+
+ c := getMockContext()
+
+ team := &model.Team{}
+ team.DisplayName = flagTeamName
+ team.Name = flagTeamName
+ team.Email = flagEmail
+ team.Type = model.TEAM_OPEN
+
+ api.CreateTeam(c, team)
+ if c.Err != nil {
+ if c.Err.Id != "store.sql_team.save.domain_exists.app_error" {
+ l4g.Error("%v", c.Err)
+ flushLogAndExit(1)
+ }
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdCreateUser() {
+ if flagCmdCreateUser {
+ if len(flagEmail) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
+ os.Exit(1)
+ }
+
+ if len(flagPassword) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -password")
+ os.Exit(1)
+ }
+
+ var team *model.Team
+ user := &model.User{}
+ user.Email = flagEmail
+ user.Password = flagPassword
+
+ if len(flagUsername) == 0 {
+ splits := strings.Split(strings.Replace(flagEmail, "@", " ", -1), " ")
+ user.Username = splits[0]
+ } else {
+ user.Username = flagUsername
+ }
+
+ if len(flagTeamName) > 0 {
+ if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ team = result.Data.(*model.Team)
+ }
+ }
+
+ ruser, err := api.CreateUser(user)
+ if err != nil {
+ if err.Id != "store.sql_user.save.email_exists.app_error" {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ }
+ }
+
+ if team != nil {
+ err = api.JoinUserToTeam(team, ruser)
+ if err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ }
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdInviteUser() {
+ if flagCmdInviteUser {
+ if len(flagTeamName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
+ os.Exit(1)
+ }
+
+ if len(flagEmail) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
+ os.Exit(1)
+ }
+
+ if len(*utils.Cfg.ServiceSettings.SiteURL) == 0 {
+ fmt.Fprintln(os.Stderr, "SiteURL must be specified in config.json")
+ os.Exit(1)
+ }
+
+ var team *model.Team
+ if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ team = result.Data.(*model.Team)
+ }
+
+ var user *model.User
+ if result := <-api.Srv.Store.User().GetByEmail(team.Email); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ invites := []string{flagEmail}
+ api.InviteMembers(team, user.GetDisplayName(), invites)
+
+ os.Exit(0)
+ }
+}
+
+func cmdVersion() {
+ if flagCmdVersion {
+ fmt.Fprintln(os.Stderr, "Version: "+model.CurrentVersion)
+ fmt.Fprintln(os.Stderr, "Build Number: "+model.BuildNumber)
+ fmt.Fprintln(os.Stderr, "Build Date: "+model.BuildDate)
+ fmt.Fprintln(os.Stderr, "Build Hash: "+model.BuildHash)
+ fmt.Fprintln(os.Stderr, "Build Enterprise Ready: "+model.BuildEnterpriseReady)
+ fmt.Fprintln(os.Stderr, "DB Version: "+api.Srv.Store.(*store.SqlStore).SchemaVersion)
+
+ os.Exit(0)
+ }
+}
+
+func cmdAssignRole() {
+ if flagCmdAssignRole {
+ if len(flagEmail) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
+ os.Exit(1)
+ }
+
+ // Do some conversions
+ if flagRole == "system_admin" {
+ flagRole = "system_user system_admin"
+ }
+
+ if flagRole == "" {
+ flagRole = "system_user"
+ }
+
+ if !model.IsValidUserRoles(flagRole) {
+ fmt.Fprintln(os.Stderr, "flag invalid argument: -role")
+ os.Exit(1)
+ }
+
+ var user *model.User
+ if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ if !user.IsInRole(flagRole) {
+ api.UpdateUserRoles(user, flagRole)
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdCreateChannel() {
+ if flagCmdCreateChannel {
+ if len(flagTeamName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
+ os.Exit(1)
+ }
+
+ if len(flagChannelName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -channel_name")
+ os.Exit(1)
+ }
+
+ if len(flagEmail) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
+ os.Exit(1)
+ }
+
+ if flagChannelType != "O" && flagChannelType != "P" {
+ fmt.Fprintln(os.Stderr, "flag channel_type must have on of the following values: O or P")
+ os.Exit(1)
+ }
+
+ if !utils.IsLicensed {
+ fmt.Fprintln(os.Stderr, utils.T("cli.license.critical"))
+ os.Exit(1)
+ }
+
+ var team *model.Team
+ if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
+ l4g.Error("%v %v", utils.T(result.Err.Message), result.Err.DetailedError)
+ flushLogAndExit(1)
+ } else {
+ team = result.Data.(*model.Team)
+ }
+
+ var user *model.User
+ if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil {
+ l4g.Error("%v %v", utils.T(result.Err.Message), result.Err.DetailedError)
+ flushLogAndExit(1)
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ c := getMockContext()
+ c.Session.UserId = user.Id
+
+ channel := &model.Channel{}
+ channel.DisplayName = flagChannelName
+ channel.CreatorId = user.Id
+ channel.Name = flagChannelName
+ channel.TeamId = team.Id
+ channel.Type = flagChannelType
+ channel.Header = flagChannelHeader
+ channel.Purpose = flagChannelPurpose
+
+ if _, err := api.CreateChannel(c, channel, true); err != nil {
+ l4g.Error("%v %v", utils.T(err.Message), err.DetailedError)
+ flushLogAndExit(1)
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdJoinChannel() {
+ if flagCmdJoinChannel {
+ if len(flagTeamName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
+ os.Exit(1)
+ }
+
+ if len(flagEmail) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
+ os.Exit(1)
+ }
+
+ if len(flagChannelName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -channel_name")
+ os.Exit(1)
+ }
+
+ if !utils.IsLicensed {
+ fmt.Fprintln(os.Stderr, utils.T("cli.license.critical"))
+ os.Exit(1)
+ }
+
+ var team *model.Team
+ if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ team = result.Data.(*model.Team)
+ }
+
+ var user *model.User
+ if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ var channel *model.Channel
+ if result := <-api.Srv.Store.Channel().GetByName(team.Id, flagChannelName); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ channel = result.Data.(*model.Channel)
+ }
+
+ _, err := api.AddUserToChannel(user, channel)
+ if err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdLeaveChannel() {
+ if flagCmdLeaveChannel {
+ if len(flagTeamName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
+ os.Exit(1)
+ }
+
+ if len(flagEmail) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
+ os.Exit(1)
+ }
+
+ if len(flagChannelName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -channel_name")
+ os.Exit(1)
+ }
+
+ if flagChannelName == model.DEFAULT_CHANNEL {
+ fmt.Fprintln(os.Stderr, "flag has invalid argument: -channel_name (cannot leave town-square)")
+ os.Exit(1)
+ }
+
+ if !utils.IsLicensed {
+ fmt.Fprintln(os.Stderr, utils.T("cli.license.critical"))
+ os.Exit(1)
+ }
+
+ var team *model.Team
+ if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ team = result.Data.(*model.Team)
+ }
+
+ var user *model.User
+ if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ var channel *model.Channel
+ if result := <-api.Srv.Store.Channel().GetByName(team.Id, flagChannelName); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ channel = result.Data.(*model.Channel)
+ }
+
+ err := api.RemoveUserFromChannel(user.Id, user.Id, channel)
+ if err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdListChannels() {
+ if flagCmdListChannels {
+ if len(flagTeamName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
+ os.Exit(1)
+ }
+
+ if !utils.IsLicensed {
+ fmt.Fprintln(os.Stderr, utils.T("cli.license.critical"))
+ os.Exit(1)
+ }
+
+ var team *model.Team
+ if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ team = result.Data.(*model.Team)
+ }
+
+ if result := <-api.Srv.Store.Channel().GetAll(team.Id); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ channels := result.Data.([]*model.Channel)
+
+ for _, channel := range channels {
+
+ if channel.DeleteAt > 0 {
+ fmt.Fprintln(os.Stdout, channel.Name+" (archived)")
+ } else {
+ fmt.Fprintln(os.Stdout, channel.Name)
+ }
+ }
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdRestoreChannel() {
+ if flagCmdRestoreChannel {
+ if len(flagTeamName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
+ os.Exit(1)
+ }
+
+ if len(flagChannelName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -channel_name")
+ os.Exit(1)
+ }
+
+ if !utils.IsLicensed {
+ fmt.Fprintln(os.Stderr, utils.T("cli.license.critical"))
+ os.Exit(1)
+ }
+
+ var team *model.Team
+ if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ team = result.Data.(*model.Team)
+ }
+
+ var channel *model.Channel
+ if result := <-api.Srv.Store.Channel().GetAll(team.Id); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ channels := result.Data.([]*model.Channel)
+
+ for _, ctemp := range channels {
+ if ctemp.Name == flagChannelName {
+ channel = ctemp
+ break
+ }
+ }
+ }
+
+ if result := <-api.Srv.Store.Channel().SetDeleteAt(channel.Id, 0, model.GetMillis()); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdJoinTeam() {
+ if flagCmdJoinTeam {
+ if len(flagTeamName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
+ os.Exit(1)
+ }
+
+ if len(flagEmail) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
+ os.Exit(1)
+ }
+
+ var team *model.Team
+ if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ team = result.Data.(*model.Team)
+ }
+
+ var user *model.User
+ if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ err := api.JoinUserToTeam(team, user)
+ if err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdLeaveTeam() {
+ if flagCmdLeaveTeam {
+ if len(flagTeamName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
+ os.Exit(1)
+ }
+
+ if len(flagEmail) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
+ os.Exit(1)
+ }
+
+ var team *model.Team
+ if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ team = result.Data.(*model.Team)
+ }
+
+ var user *model.User
+ if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ err := api.LeaveTeam(team, user)
+
+ if err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdResetPassword() {
+ if flagCmdResetPassword {
+ if len(flagEmail) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
+ os.Exit(1)
+ }
+
+ if len(flagPassword) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -password")
+ os.Exit(1)
+ }
+
+ if len(flagPassword) < 5 {
+ fmt.Fprintln(os.Stderr, "flag invalid argument needs to be more than 4 characters: -password")
+ os.Exit(1)
+ }
+
+ var user *model.User
+ if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ if result := <-api.Srv.Store.User().UpdatePassword(user.Id, model.HashPassword(flagPassword)); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdResetMfa() {
+ if flagCmdResetMfa {
+ if len(flagEmail) == 0 && len(flagUsername) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email OR -username")
+ os.Exit(1)
+ }
+
+ var user *model.User
+ if len(flagEmail) > 0 {
+ if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ user = result.Data.(*model.User)
+ }
+ } else {
+ if result := <-api.Srv.Store.User().GetByUsername(flagUsername); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ user = result.Data.(*model.User)
+ }
+ }
+
+ if err := api.DeactivateMfa(user.Id); err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdPermDeleteUser() {
+ if flagCmdPermanentDeleteUser {
+ if len(flagEmail) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
+ os.Exit(1)
+ }
+
+ var user *model.User
+ if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ if len(flagConfirmBackup) == 0 {
+ fmt.Print("Have you performed a database backup? (YES/NO): ")
+ fmt.Scanln(&flagConfirmBackup)
+ }
+
+ if flagConfirmBackup != "YES" {
+ fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.")
+ flushLogAndExit(1)
+ }
+
+ var confirm string
+ fmt.Printf("Are you sure you want to delete the user %v? All data will be permanently deleted? (YES/NO): ", user.Email)
+ fmt.Scanln(&confirm)
+ if confirm != "YES" {
+ fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.")
+ flushLogAndExit(1)
+ }
+
+ if err := api.PermanentDeleteUser(user); err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ } else {
+ fmt.Print("SUCCESS: User deleted.")
+ flushLogAndExit(0)
+ }
+ }
+}
+
+func cmdPermDeleteTeam() {
+ if flagCmdPermanentDeleteTeam {
+ if len(flagTeamName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
+ os.Exit(1)
+ }
+
+ var team *model.Team
+ if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ team = result.Data.(*model.Team)
+ }
+
+ if len(flagConfirmBackup) == 0 {
+ fmt.Print("Have you performed a database backup? (YES/NO): ")
+ fmt.Scanln(&flagConfirmBackup)
+ }
+
+ if flagConfirmBackup != "YES" {
+ fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.")
+ flushLogAndExit(1)
+ }
+
+ var confirm string
+ fmt.Printf("Are you sure you want to delete the team %v? All data will be permanently deleted? (YES/NO): ", team.Name)
+ fmt.Scanln(&confirm)
+ if confirm != "YES" {
+ fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.")
+ flushLogAndExit(1)
+ }
+
+ if err := api.PermanentDeleteTeam(team); err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ } else {
+ fmt.Print("SUCCESS: Team deleted.")
+ flushLogAndExit(0)
+ }
+ }
+}
+
+func cmdPermDeleteAllUsers() {
+ if flagCmdPermanentDeleteAllUsers {
+ if len(flagConfirmBackup) == 0 {
+ fmt.Print("Have you performed a database backup? (YES/NO): ")
+ fmt.Scanln(&flagConfirmBackup)
+ }
+
+ if flagConfirmBackup != "YES" {
+ fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.")
+ flushLogAndExit(1)
+ }
+
+ var confirm string
+ fmt.Printf("Are you sure you want to delete all the users? All data will be permanently deleted? (YES/NO): ")
+ fmt.Scanln(&confirm)
+ if confirm != "YES" {
+ fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.")
+ flushLogAndExit(1)
+ }
+
+ if err := api.PermanentDeleteAllUsers(); err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ } else {
+ fmt.Print("SUCCESS: All users deleted.")
+ flushLogAndExit(0)
+ }
+ }
+}
+
+func cmdResetDatabase() {
+ if flagCmdResetDatabase {
+
+ if len(flagConfirmBackup) == 0 {
+ fmt.Print("Have you performed a database backup? (YES/NO): ")
+ fmt.Scanln(&flagConfirmBackup)
+ }
+
+ if flagConfirmBackup != "YES" {
+ fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.")
+ flushLogAndExit(1)
+ }
+
+ var confirm string
+ fmt.Printf("Are you sure you want to delete everything? ALL data will be permanently deleted? (YES/NO): ")
+ fmt.Scanln(&confirm)
+ if confirm != "YES" {
+ fmt.Print("ABORTED: You did not answer YES exactly, in all capitals.")
+ flushLogAndExit(1)
+ }
+
+ api.Srv.Store.DropAllTables()
+ fmt.Print("SUCCESS: Database reset.")
+ flushLogAndExit(0)
+ }
+
+}
+
+func cmdRunLdapSync() {
+ if flagCmdRunLdapSync {
+ if ldapI := einterfaces.GetLdapInterface(); ldapI != nil {
+ if err := ldapI.Syncronize(); err != nil {
+ fmt.Println("ERROR: AD/LDAP Syncronization Failed")
+ l4g.Error("%v", err.Error())
+ flushLogAndExit(1)
+ } else {
+ fmt.Println("SUCCESS: AD/LDAP Syncronization Complete")
+ flushLogAndExit(0)
+ }
+ }
+ }
+}
+
+func cmdRunMigrateAccounts() {
+ if flagCmdMigrateAccounts {
+ if len(flagFromAuth) == 0 || (flagFromAuth != "email" && flagFromAuth != "gitlab" && flagFromAuth != "saml") {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -from_auth")
+ os.Exit(1)
+ }
+
+ if len(flagToAuth) == 0 || flagToAuth != "ldap" {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -from_auth")
+ os.Exit(1)
+ }
+
+ // Email auth in Mattermost system is represented by ""
+ if flagFromAuth == "email" {
+ flagFromAuth = ""
+ }
+
+ if len(flagMatchField) == 0 || (flagMatchField != "email" && flagMatchField != "username") {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -match_field")
+ os.Exit(1)
+ }
+
+ if migrate := einterfaces.GetAccountMigrationInterface(); migrate != nil {
+ if err := migrate.MigrateToLdap(flagFromAuth, flagMatchField); err != nil {
+ fmt.Println("ERROR: Account migration failed.")
+ l4g.Error("%v", err.Error())
+ flushLogAndExit(1)
+ } else {
+ fmt.Println("SUCCESS: Account migration complete.")
+ flushLogAndExit(0)
+ }
+ }
+ }
+}
+
+func cmdUploadLicense() {
+ if flagCmdUploadLicense {
+ if model.BuildEnterpriseReady != "true" {
+ fmt.Fprintln(os.Stderr, "build must be enterprise ready")
+ os.Exit(1)
+ }
+
+ if len(flagLicenseFile) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
+ os.Exit(1)
+ }
+
+ var fileBytes []byte
+ var err error
+ if fileBytes, err = ioutil.ReadFile(flagLicenseFile); err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ }
+
+ if _, err := api.SaveLicense(fileBytes); err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ } else {
+ flushLogAndExit(0)
+ }
+
+ flushLogAndExit(0)
+ }
+}
+
+func cmdActivateUser() {
+ if flagCmdActivateUser {
+ if len(flagEmail) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -email")
+ os.Exit(1)
+ }
+
+ var user *model.User
+ if result := <-api.Srv.Store.User().GetByEmail(flagEmail); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ user = result.Data.(*model.User)
+ }
+
+ if user.IsLDAPUser() {
+ l4g.Error("%v", utils.T("api.user.update_active.no_deactivate_ldap.app_error"))
+ }
+
+ if _, err := api.UpdateActive(user, !flagUserSetInactive); err != nil {
+ l4g.Error("%v", err)
+ }
+
+ os.Exit(0)
+ }
+}
+
+func cmdSlackImport() {
+ if flagCmdSlackImport {
+ if len(flagTeamName) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -team_name")
+ os.Exit(1)
+ }
+
+ if len(flagImportArchive) == 0 {
+ fmt.Fprintln(os.Stderr, "flag needs an argument: -import_archive")
+ os.Exit(1)
+ }
+
+ var team *model.Team
+ if result := <-api.Srv.Store.Team().GetByName(flagTeamName); result.Err != nil {
+ l4g.Error("%v", result.Err)
+ flushLogAndExit(1)
+ } else {
+ team = result.Data.(*model.Team)
+ }
+
+ fileReader, err := os.Open(flagImportArchive)
+ if err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ }
+ defer fileReader.Close()
+
+ fileInfo, err := fileReader.Stat()
+ if err != nil {
+ l4g.Error("%v", err)
+ flushLogAndExit(1)
+ }
+
+ fmt.Fprintln(os.Stdout, "Running Slack Import. This may take a long time for large teams or teams with many messages.")
+
+ api.SlackImport(fileReader, fileInfo.Size(), team.Id)
+
+ flushLogAndExit(0)
+ }
+}
+
+func flushLogAndExit(code int) {
+ l4g.Close()
+ time.Sleep(time.Second)
+ os.Exit(code)
+}
+
+func getMockContext() *api.Context {
+ c := &api.Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ c.T = utils.TfuncWithFallback(model.DEFAULT_LOCALE)
+ c.Locale = model.DEFAULT_LOCALE
+
+ if *utils.Cfg.ServiceSettings.SiteURL != "" {
+ c.SetSiteURL(*utils.Cfg.ServiceSettings.SiteURL)
+ }
+
+ return c
+}
diff --git a/cmd/platform/output.go b/cmd/platform/output.go
new file mode 100644
index 000000000..5b7f91385
--- /dev/null
+++ b/cmd/platform/output.go
@@ -0,0 +1,32 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+package main
+
+import (
+ "fmt"
+ "os"
+)
+
+func CommandPrintln(a ...interface{}) (int, error) {
+ return fmt.Println(a...)
+}
+
+func CommandPrint(a ...interface{}) (int, error) {
+ return fmt.Print(a...)
+}
+
+func CommandPrintErrorln(a ...interface{}) (int, error) {
+ return fmt.Fprintln(os.Stderr, a...)
+}
+
+func CommandPrintError(a ...interface{}) (int, error) {
+ return fmt.Fprint(os.Stderr, a...)
+}
+
+func CommandPrettyPrintln(a ...interface{}) (int, error) {
+ return fmt.Fprintln(os.Stderr, a...)
+}
+
+func CommandPrettyPrint(a ...interface{}) (int, error) {
+ return fmt.Fprint(os.Stderr, a...)
+}
diff --git a/cmd/platform/roles.go b/cmd/platform/roles.go
new file mode 100644
index 000000000..7b635c5a3
--- /dev/null
+++ b/cmd/platform/roles.go
@@ -0,0 +1,78 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+package main
+
+import (
+ "errors"
+
+ "github.com/mattermost/platform/api"
+ "github.com/spf13/cobra"
+)
+
+var rolesCmd = &cobra.Command{
+ Use: "roles",
+ Short: "Management of user roles",
+}
+
+var makeSystemAdminCmd = &cobra.Command{
+ Use: "system_admin [users]",
+ Short: "Set a user as system admin",
+ Long: "Make some users system admins",
+ Example: " roles system_admin user1",
+ RunE: makeSystemAdminCmdF,
+}
+
+var makeMemberCmd = &cobra.Command{
+ Use: "member [users]",
+ Short: "Remove system admin privileges",
+ Long: "Remove system admin privileges from some users.",
+ Example: " roles member user1",
+ RunE: makeMemberCmdF,
+}
+
+func init() {
+ rolesCmd.AddCommand(
+ makeSystemAdminCmd,
+ makeMemberCmd,
+ )
+}
+
+func makeSystemAdminCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+ if len(args) < 1 {
+ return errors.New("Enter at least one user.")
+ }
+
+ users := getUsersFromUserArgs(args)
+ for i, user := range users {
+ if user == nil {
+ return errors.New("Unable to find user '" + args[i] + "'")
+ }
+
+ if _, err := api.UpdateUserRoles(user, "system_admin system_user"); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func makeMemberCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+ if len(args) < 1 {
+ return errors.New("Enter at least one user.")
+ }
+
+ users := getUsersFromUserArgs(args)
+ for i, user := range users {
+ if user == nil {
+ return errors.New("Unable to find user '" + args[i] + "'")
+ }
+
+ if _, err := api.UpdateUserRoles(user, "system_user"); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/cmd/platform/server.go b/cmd/platform/server.go
new file mode 100644
index 000000000..c5aae960a
--- /dev/null
+++ b/cmd/platform/server.go
@@ -0,0 +1,277 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package main
+
+import (
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "os"
+ "os/signal"
+ "runtime"
+ "strconv"
+ "syscall"
+ "time"
+
+ l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/platform/api"
+ "github.com/mattermost/platform/einterfaces"
+ "github.com/mattermost/platform/manualtesting"
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/utils"
+ "github.com/mattermost/platform/web"
+ "github.com/spf13/cobra"
+)
+
+var serverCmd = &cobra.Command{
+ Use: "server",
+ Short: "Run the Mattermost server",
+ RunE: runServerCmd,
+}
+
+func runServerCmd(cmd *cobra.Command, args []string) error {
+ config, err := cmd.Flags().GetString("config")
+ if err != nil {
+ return err
+ }
+ runServer(config)
+ return nil
+}
+
+func runServer(configFileLocation string) {
+ if errstr := doLoadConfig(configFileLocation); errstr != "" {
+ l4g.Exit("Unable to load mattermost configuration file: ", errstr)
+ return
+ }
+
+ utils.InitTranslations(utils.Cfg.LocalizationSettings)
+ utils.TestConnection(utils.Cfg)
+
+ pwd, _ := os.Getwd()
+ l4g.Info(utils.T("mattermost.current_version"), model.CurrentVersion, model.BuildNumber, model.BuildDate, model.BuildHash, model.BuildHashEnterprise)
+ l4g.Info(utils.T("mattermost.entreprise_enabled"), model.BuildEnterpriseReady)
+ l4g.Info(utils.T("mattermost.working_dir"), pwd)
+ l4g.Info(utils.T("mattermost.config_file"), utils.FindConfigFile(configFileLocation))
+
+ // Enable developer settings if this is a "dev" build
+ if model.BuildNumber == "dev" {
+ *utils.Cfg.ServiceSettings.EnableDeveloper = true
+ }
+
+ cmdUpdateDb30()
+
+ api.NewServer()
+ api.InitStores()
+ api.InitRouter()
+ api.InitApi()
+ web.InitWeb()
+
+ if model.BuildEnterpriseReady == "true" {
+ api.LoadLicense()
+ }
+
+ if !utils.IsLicensed && len(utils.Cfg.SqlSettings.DataSourceReplicas) > 1 {
+ l4g.Critical(utils.T("store.sql.read_replicas_not_licensed.critical"))
+ return
+ }
+
+ resetStatuses()
+
+ api.StartServer()
+
+ // If we allow testing then listen for manual testing URL hits
+ if utils.Cfg.ServiceSettings.EnableTesting {
+ manualtesting.InitManualTesting()
+ }
+
+ setDiagnosticId()
+ go runSecurityAndDiagnosticsJob()
+
+ if complianceI := einterfaces.GetComplianceInterface(); complianceI != nil {
+ complianceI.StartComplianceDailyJob()
+ }
+
+ if einterfaces.GetClusterInterface() != nil {
+ einterfaces.GetClusterInterface().StartInterNodeCommunication()
+ }
+
+ if einterfaces.GetMetricsInterface() != nil {
+ einterfaces.GetMetricsInterface().StartServer()
+ }
+
+ // wait for kill signal before attempting to gracefully shutdown
+ // the running service
+ c := make(chan os.Signal)
+ signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+ <-c
+
+ if einterfaces.GetClusterInterface() != nil {
+ einterfaces.GetClusterInterface().StopInterNodeCommunication()
+ }
+
+ if einterfaces.GetMetricsInterface() != nil {
+ einterfaces.GetMetricsInterface().StopServer()
+ }
+
+ api.StopServer()
+}
+
+func runSecurityAndDiagnosticsJob() {
+ doSecurityAndDiagnostics()
+ model.CreateRecurringTask("Security and Diagnostics", doSecurityAndDiagnostics, time.Hour*4)
+}
+
+func resetStatuses() {
+ if result := <-api.Srv.Store.Status().ResetAll(); result.Err != nil {
+ l4g.Error(utils.T("mattermost.reset_status.error"), result.Err.Error())
+ }
+}
+
+func setDiagnosticId() {
+ if result := <-api.Srv.Store.System().Get(); result.Err == nil {
+ props := result.Data.(model.StringMap)
+
+ id := props[model.SYSTEM_DIAGNOSTIC_ID]
+ if len(id) == 0 {
+ id = model.NewId()
+ systemId := &model.System{Name: model.SYSTEM_DIAGNOSTIC_ID, Value: id}
+ <-api.Srv.Store.System().Save(systemId)
+ }
+
+ utils.CfgDiagnosticId = id
+ }
+}
+
+func doSecurityAndDiagnostics() {
+ if *utils.Cfg.ServiceSettings.EnableSecurityFixAlert {
+ if result := <-api.Srv.Store.System().Get(); result.Err == nil {
+ props := result.Data.(model.StringMap)
+ lastSecurityTime, _ := strconv.ParseInt(props[model.SYSTEM_LAST_SECURITY_TIME], 10, 0)
+ currentTime := model.GetMillis()
+
+ if (currentTime - lastSecurityTime) > 1000*60*60*24*1 {
+ l4g.Debug(utils.T("mattermost.security_checks.debug"))
+
+ v := url.Values{}
+
+ v.Set(utils.PROP_DIAGNOSTIC_ID, utils.CfgDiagnosticId)
+ v.Set(utils.PROP_DIAGNOSTIC_BUILD, model.CurrentVersion+"."+model.BuildNumber)
+ v.Set(utils.PROP_DIAGNOSTIC_ENTERPRISE_READY, model.BuildEnterpriseReady)
+ v.Set(utils.PROP_DIAGNOSTIC_DATABASE, utils.Cfg.SqlSettings.DriverName)
+ v.Set(utils.PROP_DIAGNOSTIC_OS, runtime.GOOS)
+ v.Set(utils.PROP_DIAGNOSTIC_CATEGORY, utils.VAL_DIAGNOSTIC_CATEGORY_DEFAULT)
+
+ if len(props[model.SYSTEM_RAN_UNIT_TESTS]) > 0 {
+ v.Set(utils.PROP_DIAGNOSTIC_UNIT_TESTS, "1")
+ } else {
+ v.Set(utils.PROP_DIAGNOSTIC_UNIT_TESTS, "0")
+ }
+
+ systemSecurityLastTime := &model.System{Name: model.SYSTEM_LAST_SECURITY_TIME, Value: strconv.FormatInt(currentTime, 10)}
+ if lastSecurityTime == 0 {
+ <-api.Srv.Store.System().Save(systemSecurityLastTime)
+ } else {
+ <-api.Srv.Store.System().Update(systemSecurityLastTime)
+ }
+
+ if ucr := <-api.Srv.Store.User().GetTotalUsersCount(); ucr.Err == nil {
+ v.Set(utils.PROP_DIAGNOSTIC_USER_COUNT, strconv.FormatInt(ucr.Data.(int64), 10))
+ }
+
+ if ucr := <-api.Srv.Store.Status().GetTotalActiveUsersCount(); ucr.Err == nil {
+ v.Set(utils.PROP_DIAGNOSTIC_ACTIVE_USER_COUNT, strconv.FormatInt(ucr.Data.(int64), 10))
+ }
+
+ if tcr := <-api.Srv.Store.Team().AnalyticsTeamCount(); tcr.Err == nil {
+ v.Set(utils.PROP_DIAGNOSTIC_TEAM_COUNT, strconv.FormatInt(tcr.Data.(int64), 10))
+ }
+
+ res, err := http.Get(utils.DIAGNOSTIC_URL + "/security?" + v.Encode())
+ if err != nil {
+ l4g.Error(utils.T("mattermost.security_info.error"))
+ return
+ }
+
+ bulletins := model.SecurityBulletinsFromJson(res.Body)
+ ioutil.ReadAll(res.Body)
+ res.Body.Close()
+
+ for _, bulletin := range bulletins {
+ if bulletin.AppliesToVersion == model.CurrentVersion {
+ if props["SecurityBulletin_"+bulletin.Id] == "" {
+ if results := <-api.Srv.Store.User().GetSystemAdminProfiles(); results.Err != nil {
+ l4g.Error(utils.T("mattermost.system_admins.error"))
+ return
+ } else {
+ users := results.Data.(map[string]*model.User)
+
+ resBody, err := http.Get(utils.DIAGNOSTIC_URL + "/bulletins/" + bulletin.Id)
+ if err != nil {
+ l4g.Error(utils.T("mattermost.security_bulletin.error"))
+ return
+ }
+
+ body, err := ioutil.ReadAll(resBody.Body)
+ res.Body.Close()
+ if err != nil || resBody.StatusCode != 200 {
+ l4g.Error(utils.T("mattermost.security_bulletin_read.error"))
+ return
+ }
+
+ for _, user := range users {
+ l4g.Info(utils.T("mattermost.send_bulletin.info"), bulletin.Id, user.Email)
+ utils.SendMail(user.Email, utils.T("mattermost.bulletin.subject"), string(body))
+ }
+ }
+
+ bulletinSeen := &model.System{Name: "SecurityBulletin_" + bulletin.Id, Value: bulletin.Id}
+ <-api.Srv.Store.System().Save(bulletinSeen)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if *utils.Cfg.LogSettings.EnableDiagnostics {
+ utils.SendGeneralDiagnostics()
+ sendServerDiagnostics()
+ }
+}
+
+func sendServerDiagnostics() {
+ var userCount int64
+ var activeUserCount int64
+ var teamCount int64
+
+ if ucr := <-api.Srv.Store.User().GetTotalUsersCount(); ucr.Err == nil {
+ userCount = ucr.Data.(int64)
+ }
+
+ if ucr := <-api.Srv.Store.Status().GetTotalActiveUsersCount(); ucr.Err == nil {
+ activeUserCount = ucr.Data.(int64)
+ }
+
+ if tcr := <-api.Srv.Store.Team().AnalyticsTeamCount(); tcr.Err == nil {
+ teamCount = tcr.Data.(int64)
+ }
+
+ utils.SendDiagnostic(utils.TRACK_ACTIVITY, map[string]interface{}{
+ "registered_users": userCount,
+ "active_users": activeUserCount,
+ "teams": teamCount,
+ })
+
+ edition := model.BuildEnterpriseReady
+ version := model.CurrentVersion
+ database := utils.Cfg.SqlSettings.DriverName
+ operatingSystem := runtime.GOOS
+
+ utils.SendDiagnostic(utils.TRACK_VERSION, map[string]interface{}{
+ "edition": edition,
+ "version": version,
+ "database": database,
+ "operating_system": operatingSystem,
+ })
+}
diff --git a/cmd/platform/team.go b/cmd/platform/team.go
new file mode 100644
index 000000000..8fecda6e1
--- /dev/null
+++ b/cmd/platform/team.go
@@ -0,0 +1,205 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+package main
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/mattermost/platform/api"
+ "github.com/mattermost/platform/model"
+ "github.com/spf13/cobra"
+)
+
+var teamCmd = &cobra.Command{
+ Use: "team",
+ Short: "Management of teams",
+}
+
+var teamCreateCmd = &cobra.Command{
+ Use: "create",
+ Short: "Create a team",
+ Long: `Create a team.`,
+ Example: ` team create --name mynewteam --display_name "My New Team"
+ teams create --name private --display_name "My New Private Team" --private`,
+ RunE: createTeamCmdF,
+}
+
+var removeUsersCmd = &cobra.Command{
+ Use: "remove [team] [users]",
+ Short: "Remove users from team",
+ Long: "Remove some users from team",
+ Example: " team remove myteam user@example.com username",
+ RunE: removeUsersCmdF,
+}
+
+var addUsersCmd = &cobra.Command{
+ Use: "add [team] [users]",
+ Short: "Add users to team",
+ Long: "Add some users to team",
+ Example: " team add myteam user@example.com username",
+ RunE: addUsersCmdF,
+}
+
+var deleteTeamsCmd = &cobra.Command{
+ Use: "delete [teams]",
+ Short: "Delete teams",
+ Long: `Permanently delete some teams.
+Permanently deletes a team along with all related information including posts from the database.`,
+ Example: " team delete myteam",
+ RunE: deleteTeamsCmdF,
+}
+
+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)")
+
+ 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,
+ )
+}
+
+func createTeamCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ name, errn := cmd.Flags().GetString("name")
+ if errn != nil || name == "" {
+ return errors.New("Name is required")
+ }
+ displayname, errdn := cmd.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")
+
+ teamType := model.TEAM_OPEN
+ if useprivate {
+ teamType = model.TEAM_INVITE
+ }
+
+ team := &model.Team{
+ Name: name,
+ DisplayName: displayname,
+ Email: email,
+ Type: teamType,
+ }
+
+ c := getMockContext()
+ api.CreateTeam(c, team)
+ if c.Err != nil {
+ return errors.New("Team creation failed: " + c.Err.Error())
+ }
+
+ return nil
+}
+
+func removeUsersCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if len(args) < 2 {
+ return errors.New("Not enough arguments.")
+ }
+
+ team := getTeamFromTeamArg(args[0])
+ if team == nil {
+ return errors.New("Unable to find team '" + args[0] + "'")
+ }
+
+ users := getUsersFromUserArgs(args[1:])
+ for i, user := range users {
+ removeUserFromTeam(team, user, args[i+1])
+ }
+
+ return nil
+}
+
+func removeUserFromTeam(team *model.Team, user *model.User, userArg string) {
+ if user == nil {
+ CommandPrintErrorln("Can't find user '" + userArg + "'")
+ return
+ }
+ if err := api.LeaveTeam(team, user); err != nil {
+ CommandPrintErrorln("Unable to remove '" + userArg + "' from " + team.Name + ". Error: " + err.Error())
+ }
+}
+
+func addUsersCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if len(args) < 2 {
+ return errors.New("Not enough arguments.")
+ }
+
+ team := getTeamFromTeamArg(args[0])
+ if team == nil {
+ return errors.New("Unable to find team '" + args[0] + "'")
+ }
+
+ users := getUsersFromUserArgs(args[1:])
+ for i, user := range users {
+ addUserToTeam(team, user, args[i+1])
+ }
+
+ return nil
+}
+
+func addUserToTeam(team *model.Team, user *model.User, userArg string) {
+ if user == nil {
+ CommandPrintErrorln("Can't find user '" + userArg + "'")
+ return
+ }
+ if err := api.JoinUserToTeam(team, user); err != nil {
+ CommandPrintErrorln("Unable to add '" + userArg + "' to " + team.Name)
+ }
+}
+
+func deleteTeamsCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if len(args) < 1 {
+ return errors.New("Not enough arguments.")
+ }
+
+ 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 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.")
+ }
+ }
+
+ teams := getTeamsFromTeamArgs(args)
+ for i, team := range teams {
+ if team == nil {
+ CommandPrintErrorln("Unable to find team '" + args[i] + "'")
+ continue
+ }
+ if err := deleteTeam(team); err != nil {
+ CommandPrintErrorln("Unable to delete team '" + team.Name + "' error: " + err.Error())
+ } else {
+ CommandPrettyPrintln("Deleted team '" + team.Name + "'")
+ }
+ }
+
+ return nil
+}
+
+func deleteTeam(team *model.Team) *model.AppError {
+ return api.PermanentDeleteTeam(team)
+}
diff --git a/cmd/platform/teamargs.go b/cmd/platform/teamargs.go
new file mode 100644
index 000000000..5ad56f5d9
--- /dev/null
+++ b/cmd/platform/teamargs.go
@@ -0,0 +1,32 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+package main
+
+import (
+ "github.com/mattermost/platform/api"
+ "github.com/mattermost/platform/model"
+)
+
+func getTeamsFromTeamArgs(teamArgs []string) []*model.Team {
+ teams := make([]*model.Team, 0, len(teamArgs))
+ for _, teamArg := range teamArgs {
+ team := getTeamFromTeamArg(teamArg)
+ teams = append(teams, team)
+ }
+ return teams
+}
+
+func getTeamFromTeamArg(teamArg string) *model.Team {
+ var team *model.Team
+ if result := <-api.Srv.Store.Team().GetByName(teamArg); result.Err == nil {
+ team = result.Data.(*model.Team)
+ }
+
+ if team == nil {
+ if result := <-api.Srv.Store.Team().Get(teamArg); result.Err == nil {
+ team = result.Data.(*model.Team)
+ }
+ }
+
+ return team
+}
diff --git a/cmd/platform/test.go b/cmd/platform/test.go
new file mode 100644
index 000000000..d82734c75
--- /dev/null
+++ b/cmd/platform/test.go
@@ -0,0 +1,82 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "os/exec"
+
+ "github.com/mattermost/platform/api"
+ "github.com/mattermost/platform/utils"
+ "github.com/spf13/cobra"
+)
+
+var testCmd = &cobra.Command{
+ Use: "test",
+ Short: "Testing Commands",
+ Hidden: true,
+}
+
+var runWebClientTestsCmd = &cobra.Command{
+ Use: "web_client_tests",
+ Short: "Run the web client tests",
+ RunE: webClientTestsCmdF,
+}
+
+func init() {
+ testCmd.AddCommand(
+ runWebClientTestsCmd,
+ )
+}
+
+func webClientTestsCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+ utils.InitTranslations(utils.Cfg.LocalizationSettings)
+ api.InitRouter()
+ api.InitApi()
+ setupClientTests()
+ api.StartServer()
+ runWebClientTests()
+ api.StopServer()
+
+ return nil
+}
+
+func setupClientTests() {
+ *utils.Cfg.TeamSettings.EnableOpenServer = true
+ *utils.Cfg.ServiceSettings.EnableCommands = false
+ *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations = false
+ *utils.Cfg.ServiceSettings.EnableCustomEmoji = true
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = false
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = false
+ utils.SetDefaultRolesBasedOnConfig()
+}
+
+func executeTestCommand(cmd *exec.Cmd) {
+ cmdOutPipe, err := cmd.StdoutPipe()
+ if err != nil {
+ CommandPrintErrorln("Failed to run tests")
+ return
+ }
+
+ cmdOutReader := bufio.NewScanner(cmdOutPipe)
+ go func() {
+ for cmdOutReader.Scan() {
+ fmt.Println(cmdOutReader.Text())
+ }
+ }()
+
+ if err := cmd.Run(); err != nil {
+ CommandPrintErrorln("Client Tests failed")
+ return
+ }
+}
+
+func runWebClientTests() {
+ os.Chdir("webapp")
+ cmd := exec.Command("npm", "test")
+ executeTestCommand(cmd)
+}
diff --git a/cmd/platform/user.go b/cmd/platform/user.go
new file mode 100644
index 000000000..43a00e68d
--- /dev/null
+++ b/cmd/platform/user.go
@@ -0,0 +1,432 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+package main
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/mattermost/platform/api"
+ "github.com/mattermost/platform/einterfaces"
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/utils"
+ "github.com/spf13/cobra"
+)
+
+var userCmd = &cobra.Command{
+ Use: "user",
+ Short: "Management of users",
+}
+
+var userActivateCmd = &cobra.Command{
+ Use: "activate [emails, usernames, userIds]",
+ Short: "Activate users",
+ Long: "Activate users that have been deactivated.",
+ Example: ` user activate user@example.com
+ user activate username`,
+ RunE: userActivateCmdF,
+}
+
+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.",
+ Example: ` user deactivate user@example.com
+ user deactivate username`,
+ RunE: userDeactivateCmdF,
+}
+
+var userCreateCmd = &cobra.Command{
+ Use: "create",
+ Short: "Create a user",
+ Long: "Create a user",
+ Example: ` user create --email user@example.com --username userexample --password Password1
+ user create --firstname Joe --system_admin --email joe@example.com --username joe --password Password1`,
+ RunE: userCreateCmdF,
+}
+
+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.
+You can invite a user to multiple teams by listing them.
+You can specify teams by name or ID.`,
+ Example: ` user invite user@example.com myteam
+ user invite user@example.com myteam1 myteam2`,
+ RunE: userInviteCmdF,
+}
+
+var resetUserPasswordCmd = &cobra.Command{
+ Use: "password [user] [password]",
+ Short: "Set a user's password",
+ Long: "Set a user's password",
+ Example: " user password user@example.com Password1",
+ RunE: resetUserPasswordCmdF,
+}
+
+var resetUserMfaCmd = &cobra.Command{
+ Use: "resetmfa [users]",
+ Short: "Turn off MFA",
+ Long: `Turn off multi-factor authentication for a user.
+If MFA enforcement is enabled, the user will be forced to re-enable MFA as soon as they login.`,
+ Example: " user resetmfa user@example.com",
+ RunE: resetUserMfaCmdF,
+}
+
+var deleteUserCmd = &cobra.Command{
+ Use: "delete [users]",
+ Short: "Delete users and all posts",
+ Long: "Permanently delete user and all related information including posts.",
+ Example: " user delete user@example.com",
+ RunE: deleteUserCmdF,
+}
+
+var deleteAllUsersCmd = &cobra.Command{
+ Use: "deleteall",
+ Short: "Delete all users and all posts",
+ Long: "Permanently delete all users and all related information including posts.",
+ Example: " user deleteall",
+ RunE: deleteUserCmdF,
+}
+
+var migrateAuthCmd = &cobra.Command{
+ Use: "migrate_auth [from_auth] [to_auth] [match_field]",
+ 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.
+
+from_auth:
+ The authentication service to migrate users accounts from.
+ Supported options: email, gitlab, saml.
+
+to_auth:
+ The authentication service to migrate users to.
+ Supported options: ldap.
+
+match_field:
+ The field that is guaranteed to be the same in both authentication services. For example, if the users emails are consistent set to email.
+ Supported options: email, username.
+
+Will display any accounts that are not migrated successfully.`,
+ Example: " user migrate_auth email ladp email",
+ RunE: migrateAuthCmdF,
+}
+
+var verifyUserCmd = &cobra.Command{
+ Use: "verify [users]",
+ Short: "Verify email of users",
+ Long: "Verify the emails of some users.",
+ Example: " user verify user1",
+ RunE: verifyUserCmdF,
+}
+
+func init() {
+ userCreateCmd.Flags().String("username", "", "Username")
+ userCreateCmd.Flags().String("email", "", "Email")
+ userCreateCmd.Flags().String("password", "", "Password")
+ userCreateCmd.Flags().String("nickname", "", "Nickname")
+ userCreateCmd.Flags().String("firstname", "", "First Name")
+ userCreateCmd.Flags().String("lastname", "", "Last Name")
+ userCreateCmd.Flags().String("locale", "", "Locale (ex: en, fr)")
+ userCreateCmd.Flags().Bool("system_admin", false, "Make the user a system administrator")
+
+ 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.")
+
+ userCmd.AddCommand(
+ userActivateCmd,
+ userDeactivateCmd,
+ userCreateCmd,
+ userInviteCmd,
+ resetUserPasswordCmd,
+ resetUserMfaCmd,
+ deleteUserCmd,
+ deleteAllUsersCmd,
+ migrateAuthCmd,
+ verifyUserCmd,
+ )
+}
+
+func userActivateCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if len(args) < 1 {
+ return errors.New("Enter user(s) to activate.")
+ }
+
+ changeUsersActiveStatus(args, true)
+ return nil
+}
+
+func changeUsersActiveStatus(userArgs []string, active bool) {
+ users := getUsersFromUserArgs(userArgs)
+ for i, user := range users {
+ changeUserActiveStatus(user, userArgs[i], active)
+ }
+}
+
+func changeUserActiveStatus(user *model.User, userArg string, activate bool) {
+ if user == nil {
+ CommandPrintErrorln("Can't find user '" + userArg + "'")
+ return
+ }
+ if user.IsLDAPUser() {
+ CommandPrintErrorln(utils.T("api.user.update_active.no_deactivate_ldap.app_error"))
+ return
+ }
+ if _, err := api.UpdateActive(user, activate); err != nil {
+ CommandPrintErrorln("Unable to change activation status of user: " + userArg)
+ }
+}
+
+func userDeactivateCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+
+ if len(args) < 1 {
+ return errors.New("Enter user(s) to deactivate.")
+ }
+
+ changeUsersActiveStatus(args, false)
+ return nil
+}
+
+func userCreateCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+ username, erru := cmd.Flags().GetString("username")
+ if erru != nil || username == "" {
+ return errors.New("Username is required")
+ }
+ email, erre := cmd.Flags().GetString("email")
+ if erre != nil || email == "" {
+ return errors.New("Email is required")
+ }
+ password, errp := cmd.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")
+ system_admin, _ := cmd.Flags().GetBool("system_admin")
+
+ user := &model.User{
+ Username: username,
+ Email: email,
+ Password: password,
+ Nickname: nickname,
+ FirstName: firstname,
+ LastName: lastname,
+ Locale: locale,
+ }
+
+ ruser, err := api.CreateUser(user)
+ if err != nil {
+ return errors.New("Unable to create user. Error: " + err.Error())
+ }
+
+ if system_admin {
+ api.UpdateUserRoles(ruser, "system_user system_admin")
+ }
+
+ CommandPrettyPrintln("Created User")
+
+ return nil
+}
+
+func userInviteCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+ utils.InitHTML()
+
+ if len(args) < 2 {
+ return errors.New("Not enough arguments.")
+ }
+
+ email := args[0]
+ if !model.IsValidEmail(email) {
+ return errors.New("Invalid email")
+ }
+
+ teams := getTeamsFromTeamArgs(args[1:])
+ for i, team := range teams {
+ inviteUser(email, team, args[i+1])
+ }
+
+ return nil
+}
+
+func inviteUser(email string, team *model.Team, teamArg string) {
+ invites := []string{email}
+ if team == nil {
+ CommandPrintErrorln("Can't find team '" + teamArg + "'")
+ return
+ }
+ api.InviteMembers(team, "Administrator", invites)
+ CommandPrettyPrintln("Invites may or may not have been sent.")
+}
+
+func resetUserPasswordCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+ if len(args) != 2 {
+ return errors.New("Incorect number of arguments.")
+ }
+
+ user := getUserFromUserArg(args[0])
+ if user == nil {
+ return errors.New("Unable to find user '" + args[0] + "'")
+ }
+ password := args[1]
+
+ if result := <-api.Srv.Store.User().UpdatePassword(user.Id, model.HashPassword(password)); result.Err != nil {
+ return result.Err
+ }
+
+ return nil
+}
+
+func resetUserMfaCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+ if len(args) < 1 {
+ return errors.New("Enter at least one user.")
+ }
+
+ users := getUsersFromUserArgs(args)
+
+ for i, user := range users {
+ if user == nil {
+ return errors.New("Unable to find user '" + args[i] + "'")
+ }
+
+ if err := api.DeactivateMfa(user.Id); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func deleteUserCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+ if len(args) < 1 {
+ return errors.New("Enter at least one user.")
+ }
+
+ 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 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.")
+ }
+ }
+
+ users := getUsersFromUserArgs(args)
+
+ for i, user := range users {
+ if user == nil {
+ return errors.New("Unable to find user '" + args[i] + "'")
+ }
+
+ if err := api.PermanentDeleteUser(user); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func deleteAllUsersCommandF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+ if len(args) > 0 {
+ return errors.New("Don't enter any agruments.")
+ }
+
+ 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 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.")
+ }
+ }
+
+ if err := api.PermanentDeleteAllUsers(); err != nil {
+ return err
+ } else {
+ CommandPrettyPrintln("Sucsessfull. All users deleted.")
+ }
+
+ return nil
+}
+
+func migrateAuthCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+ if len(args) != 3 {
+ return errors.New("Enter the correct number of arguments.")
+ }
+
+ fromAuth := args[0]
+ toAuth := args[1]
+ matchField := args[2]
+
+ if len(fromAuth) == 0 || (fromAuth != "email" && fromAuth != "gitlab" && fromAuth != "saml") {
+ return errors.New("Invalid from_auth argument")
+ }
+
+ if len(toAuth) == 0 || toAuth != "ldap" {
+ return errors.New("Invalid to_auth argument")
+ }
+
+ // Email auth in Mattermost system is represented by ""
+ if fromAuth == "email" {
+ fromAuth = ""
+ }
+
+ if len(matchField) == 0 || (matchField != "email" && matchField != "username") {
+ return errors.New("Invalid match_field argument")
+ }
+
+ if migrate := einterfaces.GetAccountMigrationInterface(); migrate != nil {
+ if err := migrate.MigrateToLdap(fromAuth, matchField); err != nil {
+ return errors.New("Error while migrating users: " + err.Error())
+ } else {
+ CommandPrettyPrintln("Sucessfully migrated accounts.")
+ }
+ }
+
+ return nil
+}
+
+func verifyUserCmdF(cmd *cobra.Command, args []string) error {
+ initDBCommandContextCobra(cmd)
+ if len(args) < 1 {
+ return errors.New("Enter at least one user.")
+ }
+
+ users := getUsersFromUserArgs(args)
+
+ for i, user := range users {
+ if user == nil {
+ CommandPrintErrorln("Unable to find user '" + args[i] + "'")
+ }
+ if cresult := <-api.Srv.Store.User().VerifyEmail(user.Id); cresult.Err != nil {
+ CommandPrintErrorln("Unable to verify '" + args[i] + "' email. Error: " + cresult.Err.Error())
+ }
+ }
+
+ return nil
+}
diff --git a/cmd/platform/userargs.go b/cmd/platform/userargs.go
new file mode 100644
index 000000000..9ac00ae70
--- /dev/null
+++ b/cmd/platform/userargs.go
@@ -0,0 +1,38 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+package main
+
+import (
+ "github.com/mattermost/platform/api"
+ "github.com/mattermost/platform/model"
+)
+
+func getUsersFromUserArgs(userArgs []string) []*model.User {
+ users := make([]*model.User, 0, len(userArgs))
+ for _, userArg := range userArgs {
+ user := getUserFromUserArg(userArg)
+ users = append(users, user)
+ }
+ return users
+}
+
+func getUserFromUserArg(userArg string) *model.User {
+ var user *model.User
+ if result := <-api.Srv.Store.User().GetByEmail(userArg); result.Err == nil {
+ user = result.Data.(*model.User)
+ }
+
+ if user == nil {
+ if result := <-api.Srv.Store.User().GetByUsername(userArg); result.Err == nil {
+ user = result.Data.(*model.User)
+ }
+ }
+
+ if user == nil {
+ if result := <-api.Srv.Store.User().Get(userArg); result.Err == nil {
+ user = result.Data.(*model.User)
+ }
+ }
+
+ return user
+}
diff --git a/cmd/platform/version.go b/cmd/platform/version.go
new file mode 100644
index 000000000..8da34e3a9
--- /dev/null
+++ b/cmd/platform/version.go
@@ -0,0 +1,30 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+package main
+
+import (
+ "github.com/mattermost/platform/api"
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/store"
+ "github.com/spf13/cobra"
+)
+
+var versionCmd = &cobra.Command{
+ Use: "version",
+ Short: "Display version information",
+ Run: versionCmdF,
+}
+
+func versionCmdF(cmd *cobra.Command, args []string) {
+ initDBCommandContextCobra(cmd)
+ printVersion()
+}
+
+func printVersion() {
+ 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)
+ CommandPrintln("DB Version: " + api.Srv.Store.(*store.SqlStore).SchemaVersion)
+}