Этот коммит содержится в:
Christopher Speller
2016-07-15 11:20:39 -04:00
коммит произвёл Harrison Healey
родитель b339b5c982
Коммит 942ae4c527
15 изменённых файлов: 1 добавлений и 610 удалений

Просмотреть файл

@@ -1,292 +0,0 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package api
import (
"archive/zip"
"encoding/json"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"io"
"os"
)
const (
EXPORT_PATH = "export/"
EXPORT_FILENAME = "MattermostExport.zip"
EXPORT_OPTIONS_FILE = "options.json"
EXPORT_TEAMS_FOLDER = "teams"
EXPORT_CHANNELS_FOLDER = "channels"
EXPORT_CHANNEL_MEMBERS_FOLDER = "members"
EXPORT_POSTS_FOLDER = "posts"
EXPORT_USERS_FOLDER = "users"
EXPORT_LOCAL_STORAGE_FOLDER = "files"
)
type ExportWriter interface {
Create(name string) (io.Writer, error)
}
type ExportOptions struct {
TeamsToExport []string `json:"teams"`
ChannelsToExport []string `json:"channels"`
UsersToExport []string `json:"users"`
ExportLocalStorage bool `json:"export_local_storage"`
}
func (options *ExportOptions) ToJson() string {
b, err := json.Marshal(options)
if err != nil {
return ""
} else {
return string(b)
}
}
func ExportOptionsFromJson(data io.Reader) *ExportOptions {
decoder := json.NewDecoder(data)
var o ExportOptions
decoder.Decode(&o)
return &o
}
func ExportToFile(options *ExportOptions) (link string, err *model.AppError) {
// Open file for export
if file, err := openFileWriteStream(EXPORT_PATH + EXPORT_FILENAME); err != nil {
return "", err
} else {
defer closeFileWriteStream(file)
ExportToWriter(file, options)
}
return model.API_URL_SUFFIX + "/files/get_export", nil
}
func ExportToWriter(w io.Writer, options *ExportOptions) *model.AppError {
// Open a writer to write to zip file
zipWriter := zip.NewWriter(w)
defer zipWriter.Close()
// Write our options to file
if optionsFile, err := zipWriter.Create(EXPORT_OPTIONS_FILE); err != nil {
return model.NewLocAppError("ExportToWriter", "api.export.options.create.app_error", nil, err.Error())
} else {
if _, err := optionsFile.Write([]byte(options.ToJson())); err != nil {
return model.NewLocAppError("ExportToWriter", "api.export.options.write.app_error", nil, err.Error())
}
}
// Export Teams
ExportTeams(zipWriter, options)
return nil
}
func ExportTeams(writer ExportWriter, options *ExportOptions) *model.AppError {
// Get the teams
var teams []*model.Team
if len(options.TeamsToExport) == 0 {
if result := <-Srv.Store.Team().GetAll(); result.Err != nil {
return result.Err
} else {
teams = result.Data.([]*model.Team)
}
} else {
for _, teamId := range options.TeamsToExport {
if result := <-Srv.Store.Team().Get(teamId); result.Err != nil {
return result.Err
} else {
team := result.Data.(*model.Team)
teams = append(teams, team)
}
}
}
// Export the teams
for i := range teams {
// Sanitize
teams[i].PreExport()
if teamFile, err := writer.Create(EXPORT_TEAMS_FOLDER + "/" + teams[i].Name + ".json"); err != nil {
return model.NewLocAppError("ExportTeams", "api.export.open_file.app_error", nil, err.Error())
} else {
if _, err := teamFile.Write([]byte(teams[i].ToJson())); err != nil {
return model.NewLocAppError("ExportTeams", "api.export.write_file.app_error", nil, err.Error())
}
}
}
// Export the channels, local storage and users
for _, team := range teams {
if err := ExportChannels(writer, options, team.Id); err != nil {
return err
}
if err := ExportUsers(writer, options, team.Id); err != nil {
return err
}
if err := ExportLocalStorage(writer, options, team.Id); err != nil {
return err
}
}
return nil
}
func ExportChannels(writer ExportWriter, options *ExportOptions, teamId string) *model.AppError {
// Get the channels
var channels []*model.Channel
if len(options.ChannelsToExport) == 0 {
if result := <-Srv.Store.Channel().GetForExport(teamId); result.Err != nil {
return result.Err
} else {
channels = result.Data.([]*model.Channel)
}
} else {
for _, channelId := range options.ChannelsToExport {
if result := <-Srv.Store.Channel().Get(channelId); result.Err != nil {
return result.Err
} else {
channel := result.Data.(*model.Channel)
channels = append(channels, channel)
}
}
}
for i := range channels {
// Get members
mchan := Srv.Store.Channel().GetMembers(channels[i].Id)
// Sanitize
channels[i].PreExport()
if channelFile, err := writer.Create(EXPORT_CHANNELS_FOLDER + "/" + channels[i].Id + ".json"); err != nil {
return model.NewLocAppError("ExportChannels", "api.export.open_file.app_error", nil, err.Error())
} else {
if _, err := channelFile.Write([]byte(channels[i].ToJson())); err != nil {
return model.NewLocAppError("ExportChannels", "api.export.write_file.app_error", nil, err.Error())
}
}
var members []model.ChannelMember
if result := <-mchan; result.Err != nil {
return result.Err
} else {
members = result.Data.([]model.ChannelMember)
}
if membersFile, err := writer.Create(EXPORT_CHANNELS_FOLDER + "/" + channels[i].Id + "_members.json"); err != nil {
return model.NewLocAppError("ExportChannels", "api.export.open_file.app_error", nil, err.Error())
} else {
result, err2 := json.Marshal(members)
if err2 != nil {
return model.NewLocAppError("ExportChannels", "api.export.json.app_error", nil, err.Error())
}
if _, err3 := membersFile.Write([]byte(result)); err3 != nil {
return model.NewLocAppError("ExportChannels", "api.export.write_file.app_error", nil, err.Error())
}
}
}
for _, channel := range channels {
if err := ExportPosts(writer, options, channel.Id); err != nil {
return err
}
}
return nil
}
func ExportPosts(writer ExportWriter, options *ExportOptions, channelId string) *model.AppError {
// Get the posts
var posts []*model.Post
if result := <-Srv.Store.Post().GetForExport(channelId); result.Err != nil {
return result.Err
} else {
posts = result.Data.([]*model.Post)
}
// Export the posts
if postsFile, err := writer.Create(EXPORT_POSTS_FOLDER + "/" + channelId + "_posts.json"); err != nil {
return model.NewLocAppError("ExportPosts", "api.export.open_file.app_error", nil, err.Error())
} else {
result, err2 := json.Marshal(posts)
if err2 != nil {
return model.NewLocAppError("ExportPosts", "api.export.json.app_error", nil, err.Error())
}
if _, err3 := postsFile.Write([]byte(result)); err3 != nil {
return model.NewLocAppError("ExportPosts", "api.export.write_file.app_error", nil, err.Error())
}
}
return nil
}
func ExportUsers(writer ExportWriter, options *ExportOptions, teamId string) *model.AppError {
// Get the users
var users []*model.User
if result := <-Srv.Store.User().GetForExport(teamId); result.Err != nil {
return result.Err
} else {
users = result.Data.([]*model.User)
}
// Write the users
if usersFile, err := writer.Create(EXPORT_USERS_FOLDER + "/" + teamId + "_users.json"); err != nil {
return model.NewLocAppError("ExportUsers", "api.export.open_file.app_error", nil, err.Error())
} else {
result, err2 := json.Marshal(users)
if err2 != nil {
return model.NewLocAppError("ExportUsers", "api.export.json.app_error", nil, err.Error())
}
if _, err3 := usersFile.Write([]byte(result)); err3 != nil {
return model.NewLocAppError("ExportUsers", "api.export.write_file.app_error", nil, err.Error())
}
}
return nil
}
func copyDirToExportWriter(writer ExportWriter, inPath string, outPath string) *model.AppError {
dir, err := os.Open(inPath)
if err != nil {
return model.NewLocAppError("copyDirToExportWriter", "api.export.open_dir.app_error", nil, err.Error())
}
fileInfoList, err := dir.Readdir(0)
if err != nil {
return model.NewLocAppError("copyDirToExportWriter", "api.export.read_dir.app_error", nil, err.Error())
}
for _, fileInfo := range fileInfoList {
if fileInfo.IsDir() {
copyDirToExportWriter(writer, inPath+"/"+fileInfo.Name(), outPath+"/"+fileInfo.Name())
} else {
if toFile, err := writer.Create(outPath + "/" + fileInfo.Name()); err != nil {
return model.NewLocAppError("copyDirToExportWriter", "api.export.open_file.app_error", nil, err.Error())
} else {
fromFile, err := os.Open(inPath + "/" + fileInfo.Name())
if err != nil {
return model.NewLocAppError("copyDirToExportWriter", "api.export.open.app_error", nil, err.Error())
}
io.Copy(toFile, fromFile)
}
}
}
return nil
}
func ExportLocalStorage(writer ExportWriter, options *ExportOptions, teamId string) *model.AppError {
teamDir := utils.Cfg.FileSettings.Directory + "teams/" + teamId
if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 {
return model.NewLocAppError("ExportLocalStorage", "api.export.s3.app_error", nil, "")
} else if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_LOCAL {
if err := copyDirToExportWriter(writer, teamDir, EXPORT_LOCAL_STORAGE_FOLDER); err != nil {
return err
}
}
return nil
}

Просмотреть файл

@@ -65,7 +65,6 @@ func InitFile() {
BaseRoutes.Files.Handle("/get/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiUserRequiredTrustRequester(getFile)).Methods("GET") BaseRoutes.Files.Handle("/get/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiUserRequiredTrustRequester(getFile)).Methods("GET")
BaseRoutes.Files.Handle("/get_info/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiUserRequired(getFileInfo)).Methods("GET") BaseRoutes.Files.Handle("/get_info/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiUserRequired(getFileInfo)).Methods("GET")
BaseRoutes.Files.Handle("/get_public_link", ApiUserRequired(getPublicLink)).Methods("POST") BaseRoutes.Files.Handle("/get_public_link", ApiUserRequired(getPublicLink)).Methods("POST")
BaseRoutes.Files.Handle("/get_export", ApiUserRequired(getExport)).Methods("GET")
BaseRoutes.Public.Handle("/files/get/{team_id:[A-Za-z0-9]+}/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiAppHandlerTrustRequesterIndependent(getPublicFile)).Methods("GET") BaseRoutes.Public.Handle("/files/get/{team_id:[A-Za-z0-9]+}/{channel_id:[A-Za-z0-9]+}/{user_id:[A-Za-z0-9]+}/{filename:([A-Za-z0-9]+/)?.+(\\.[A-Za-z0-9]{3,})?}", ApiAppHandlerTrustRequesterIndependent(getPublicFile)).Methods("GET")
} }
@@ -528,23 +527,6 @@ func getPublicLink(c *Context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte(model.StringToJson(url))) w.Write([]byte(model.StringToJson(url)))
} }
func getExport(c *Context, w http.ResponseWriter, r *http.Request) {
if !c.HasPermissionsToTeam(c.TeamId, "export") || !c.IsTeamAdmin() {
c.Err = model.NewLocAppError("getExport", "api.file.get_export.team_admin.app_error", nil, "userId="+c.Session.UserId)
c.Err.StatusCode = http.StatusForbidden
return
}
data, err := ReadFile(EXPORT_PATH + EXPORT_FILENAME)
if err != nil {
c.Err = model.NewLocAppError("getExport", "api.file.get_export.retrieve.app_error", nil, err.Error())
return
}
w.Header().Set("Content-Disposition", "attachment; filename="+EXPORT_FILENAME)
w.Header().Set("Content-Type", "application/octet-stream")
w.Write(data)
}
func WriteFile(f []byte, path string) *model.AppError { func WriteFile(f []byte, path string) *model.AppError {
if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 { if utils.Cfg.FileSettings.DriverName == model.IMAGE_DRIVER_S3 {

Просмотреть файл

@@ -195,4 +195,4 @@
"LocaleAttribute": "", "LocaleAttribute": "",
"LoginButtonText": "With SAML" "LoginButtonText": "With SAML"
} }
} }

Просмотреть файл

@@ -683,38 +683,6 @@
"id": "api.emoji.upload.large_image.app_error", "id": "api.emoji.upload.large_image.app_error",
"translation": "Unable to create emoji. Image must be at most 128 by 128 pixels." "translation": "Unable to create emoji. Image must be at most 128 by 128 pixels."
}, },
{
"id": "api.export.json.app_error",
"translation": "Unable to convert to json"
},
{
"id": "api.export.open.app_error",
"translation": "Unable to open file"
},
{
"id": "api.export.open_dir.app_error",
"translation": "Unable to open directory"
},
{
"id": "api.export.open_file.app_error",
"translation": "Unable to open file for export"
},
{
"id": "api.export.options.create.app_error",
"translation": "Unable to create options file"
},
{
"id": "api.export.options.write.app_error",
"translation": "Unable to write to options file"
},
{
"id": "api.export.read_dir.app_error",
"translation": "Unable to read directory"
},
{
"id": "api.export.s3.app_error",
"translation": "S3 is not supported for local storage export."
},
{ {
"id": "api.file.file_upload.exceeds", "id": "api.file.file_upload.exceeds",
"translation": "File exceeds max image size." "translation": "File exceeds max image size."
@@ -723,14 +691,6 @@
"id": "api.file.file_upload.exceeds", "id": "api.file.file_upload.exceeds",
"translation": "File exceeds max image size." "translation": "File exceeds max image size."
}, },
{
"id": "api.file.get_export.retrieve.app_error",
"translation": "Unable to retrieve exported file. Please re-export"
},
{
"id": "api.file.get_export.team_admin.app_error",
"translation": "Only a team admin can retrieve exported data."
},
{ {
"id": "api.file.get_file.not_found.app_error", "id": "api.file.get_file.not_found.app_error",
"translation": "Could not find file." "translation": "Could not find file."
@@ -1315,10 +1275,6 @@
"id": "api.team.email_teams.sending.error", "id": "api.team.email_teams.sending.error",
"translation": "An error occurred while sending an email in emailTeams err=%v" "translation": "An error occurred while sending an email in emailTeams err=%v"
}, },
{
"id": "api.team.export_team.admin.app_error",
"translation": "Only a team admin can export data."
},
{ {
"id": "api.team.get_invite_info.not_open_team", "id": "api.team.get_invite_info.not_open_team",
"translation": "Invite is invalid because this is not an open team." "translation": "Invite is invalid because this is not an open team."
@@ -3455,10 +3411,6 @@
"id": "store.sql_channel.get_extra_members.app_error", "id": "store.sql_channel.get_extra_members.app_error",
"translation": "We couldn't get the extra info for channel members" "translation": "We couldn't get the extra info for channel members"
}, },
{
"id": "store.sql_channel.get_for_export.app_error",
"translation": "We couldn't get all the channels"
},
{ {
"id": "store.sql_channel.get_member.app_error", "id": "store.sql_channel.get_member.app_error",
"translation": "We couldn't get the channel member" "translation": "We couldn't get the channel member"
@@ -3751,10 +3703,6 @@
"id": "store.sql_post.analytics_user_counts_posts_by_day.app_error", "id": "store.sql_post.analytics_user_counts_posts_by_day.app_error",
"translation": "We couldn't get user counts with posts" "translation": "We couldn't get user counts with posts"
}, },
{
"id": "store.sql_post.compliance_export.app_error",
"translation": "We couldn't get posts for compliance export"
},
{ {
"id": "store.sql_post.delete.app_error", "id": "store.sql_post.delete.app_error",
"translation": "We couldn't delete the post" "translation": "We couldn't delete the post"
@@ -3763,10 +3711,6 @@
"id": "store.sql_post.get.app_error", "id": "store.sql_post.get.app_error",
"translation": "We couldn't get the post" "translation": "We couldn't get the post"
}, },
{
"id": "store.sql_post.get_for_export.app_error",
"translation": "We couldn't get the posts for the channel"
},
{ {
"id": "store.sql_post.get_parents_posts.app_error", "id": "store.sql_post.get_parents_posts.app_error",
"translation": "We couldn't get the parent post for the channel" "translation": "We couldn't get the parent post for the channel"
@@ -4075,10 +4019,6 @@
"id": "store.sql_user.get_by_username.app_error", "id": "store.sql_user.get_by_username.app_error",
"translation": "We couldn't find an existing account matching your username for this team. This team may require an invite from the team owner to join." "translation": "We couldn't find an existing account matching your username for this team. This team may require an invite from the team owner to join."
}, },
{
"id": "store.sql_user.get_for_export.app_error",
"translation": "We encountered an error while finding user profiles"
},
{ {
"id": "store.sql_user.get_for_login.app_error", "id": "store.sql_user.get_for_login.app_error",
"translation": "We couldn't find an existing account matching your credentials. This team may require an invite from the team owner to join." "translation": "We couldn't find an existing account matching your credentials. This team may require an invite from the team owner to join."

Просмотреть файл

@@ -124,9 +124,6 @@ func (o *Channel) ExtraUpdated() {
o.ExtraUpdateAt = GetMillis() o.ExtraUpdateAt = GetMillis()
} }
func (o *Channel) PreExport() {
}
func GetDMNameFromIds(userId1, userId2 string) string { func GetDMNameFromIds(userId1, userId2 string) string {
if userId1 > userId2 { if userId1 > userId2 {
return userId2 + "__" + userId1 return userId2 + "__" + userId1

Просмотреть файл

@@ -162,9 +162,6 @@ func (o *Post) AddProp(key string, value interface{}) {
o.Props[key] = value o.Props[key] = value
} }
func (o *Post) PreExport() {
}
func (o *Post) IsSystemMessage() bool { func (o *Post) IsSystemMessage() bool {
return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX return len(o.Type) >= len(POST_SYSTEM_MESSAGE_PREFIX) && o.Type[:len(POST_SYSTEM_MESSAGE_PREFIX)] == POST_SYSTEM_MESSAGE_PREFIX
} }

Просмотреть файл

@@ -224,9 +224,6 @@ func CleanTeamName(s string) string {
return s return s
} }
func (o *Team) PreExport() {
}
func (o *Team) Sanitize() { func (o *Team) Sanitize() {
o.Email = "" o.Email = ""
o.AllowedDomains = "" o.AllowedDomains = ""

Просмотреть файл

@@ -371,17 +371,6 @@ func (u *User) IsLDAPUser() bool {
return false return false
} }
func (u *User) PreExport() {
u.Password = ""
u.AuthData = new(string)
*u.AuthData = ""
u.LastActivityAt = 0
u.LastPingAt = 0
u.LastPasswordUpdate = 0
u.LastPictureUpdate = 0
u.FailedAttempts = 0
}
// UserFromJson will decode the input and return a User // UserFromJson will decode the input and return a User
func UserFromJson(data io.Reader) *User { func UserFromJson(data io.Reader) *User {
decoder := json.NewDecoder(data) decoder := json.NewDecoder(data)

Просмотреть файл

@@ -982,28 +982,6 @@ func (s SqlChannelStore) IncrementMentionCount(channelId string, userId string)
return storeChannel return storeChannel
} }
func (s SqlChannelStore) GetForExport(teamId string) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
result := StoreResult{}
var data []*model.Channel
_, err := s.GetReplica().Select(&data, "SELECT * FROM Channels WHERE TeamId = :TeamId AND DeleteAt = 0 AND Type = 'O'", map[string]interface{}{"TeamId": teamId})
if err != nil {
result.Err = model.NewLocAppError("SqlChannelStore.GetAllChannels", "store.sql_channel.get_for_export.app_error", nil, "teamId="+teamId+", err="+err.Error())
} else {
result.Data = data
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (s SqlChannelStore) GetAll(teamId string) StoreChannel { func (s SqlChannelStore) GetAll(teamId string) StoreChannel {
storeChannel := make(StoreChannel) storeChannel := make(StoreChannel)

Просмотреть файл

@@ -784,30 +784,6 @@ func (s SqlPostStore) Search(teamId string, userId string, params *model.SearchP
return storeChannel return storeChannel
} }
func (s SqlPostStore) GetForExport(channelId string) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
result := StoreResult{}
var posts []*model.Post
_, err := s.GetReplica().Select(
&posts,
"SELECT * FROM Posts WHERE ChannelId = :ChannelId AND DeleteAt = 0",
map[string]interface{}{"ChannelId": channelId})
if err != nil {
result.Err = model.NewLocAppError("SqlPostStore.GetForExport", "store.sql_post.get_for_export.app_error", nil, "channelId="+channelId+err.Error())
} else {
result.Data = posts
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (s SqlPostStore) AnalyticsUserCountsWithPostsByDay(teamId string) StoreChannel { func (s SqlPostStore) AnalyticsUserCountsWithPostsByDay(teamId string) StoreChannel {
storeChannel := make(StoreChannel) storeChannel := make(StoreChannel)

Просмотреть файл

@@ -969,34 +969,6 @@ func (us SqlUserStore) VerifyEmail(userId string) StoreChannel {
return storeChannel return storeChannel
} }
func (us SqlUserStore) GetForExport(teamId string) StoreChannel {
storeChannel := make(StoreChannel)
go func() {
result := StoreResult{}
var users []*model.User
if _, err := us.GetReplica().Select(&users, "SELECT Users.* FROM Users, TeamMembers WHERE TeamMembers.TeamId = :TeamId AND Users.Id = TeamMembers.UserId", map[string]interface{}{"TeamId": teamId}); err != nil {
result.Err = model.NewLocAppError("SqlUserStore.GetForExport", "store.sql_user.get_for_export.app_error", nil, err.Error())
} else {
for _, u := range users {
u.Password = ""
u.AuthData = new(string)
*u.AuthData = ""
}
result.Data = users
}
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (us SqlUserStore) GetTotalUsersCount() StoreChannel { func (us SqlUserStore) GetTotalUsersCount() StoreChannel {
storeChannel := make(StoreChannel) storeChannel := make(StoreChannel)

Просмотреть файл

@@ -83,7 +83,6 @@ type ChannelStore interface {
GetChannels(teamId string, userId string) StoreChannel GetChannels(teamId string, userId string) StoreChannel
GetMoreChannels(teamId string, userId string) StoreChannel GetMoreChannels(teamId string, userId string) StoreChannel
GetChannelCounts(teamId string, userId string) StoreChannel GetChannelCounts(teamId string, userId string) StoreChannel
GetForExport(teamId string) StoreChannel
GetAll(teamId string) StoreChannel GetAll(teamId string) StoreChannel
SaveMember(member *model.ChannelMember) StoreChannel SaveMember(member *model.ChannelMember) StoreChannel
@@ -117,7 +116,6 @@ type PostStore interface {
GetPostsSince(channelId string, time int64) StoreChannel GetPostsSince(channelId string, time int64) StoreChannel
GetEtag(channelId string) StoreChannel GetEtag(channelId string) StoreChannel
Search(teamId string, userId string, params *model.SearchParams) StoreChannel Search(teamId string, userId string, params *model.SearchParams) StoreChannel
GetForExport(channelId string) StoreChannel
AnalyticsUserCountsWithPostsByDay(teamId string) StoreChannel AnalyticsUserCountsWithPostsByDay(teamId string) StoreChannel
AnalyticsPostCountsByDay(teamId string) StoreChannel AnalyticsPostCountsByDay(teamId string) StoreChannel
AnalyticsPostCount(teamId string, mustHaveFile bool, mustHaveHashtag bool) StoreChannel AnalyticsPostCount(teamId string, mustHaveFile bool, mustHaveHashtag bool) StoreChannel
@@ -151,7 +149,6 @@ type UserStore interface {
GetEtagForProfiles(teamId string) StoreChannel GetEtagForProfiles(teamId string) StoreChannel
GetEtagForDirectProfiles(userId string) StoreChannel GetEtagForDirectProfiles(userId string) StoreChannel
UpdateFailedPasswordAttempts(userId string, attempts int) StoreChannel UpdateFailedPasswordAttempts(userId string, attempts int) StoreChannel
GetForExport(teamId string) StoreChannel
GetTotalUsersCount() StoreChannel GetTotalUsersCount() StoreChannel
GetTotalActiveUsersCount() StoreChannel GetTotalActiveUsersCount() StoreChannel
GetSystemAdminProfiles() StoreChannel GetSystemAdminProfiles() StoreChannel

Просмотреть файл

@@ -1,127 +0,0 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import Client from 'utils/web_client.jsx';
import {FormattedMessage} from 'react-intl';
import React from 'react';
import {Link} from 'react-router/es6';
export default class TeamExportTab extends React.Component {
constructor(props) {
super(props);
this.state = {status: 'request', link: '', err: ''};
this.onExportSuccess = this.onExportSuccess.bind(this);
this.onExportFailure = this.onExportFailure.bind(this);
this.doExport = this.doExport.bind(this);
}
onExportSuccess(data) {
this.setState({status: 'ready', link: data.link, err: ''});
}
onExportFailure(e) {
this.setState({status: 'failure', link: '', err: e.message});
}
doExport() {
if (this.state.status === 'in-progress') {
return;
}
this.setState({status: 'in-progress'});
Client.exportTeam(this.onExportSuccess, this.onExportFailure);
}
render() {
var messageSection = '';
switch (this.state.status) {
case 'request':
messageSection = '';
break;
case 'in-progress':
messageSection = (
<p className='confirm-import alert alert-warning'>
<i className='fa fa-spinner fa-pulse'/>
<FormattedMessage
id='team_export_tab.exporting'
defaultMessage=' Exporting...'
/>
</p>
);
break;
case 'ready':
messageSection = (
<p className='confirm-import alert alert-success'>
<i className='fa fa-check'/>
<FormattedMessage
id='team_export_tab.ready'
defaultMessage=' Ready for '
/>
<Link
to={this.state.link}
download={true}
>
<FormattedMessage
id='team_export_tab.download'
defaultMessage='download'
/>
</Link>
</p>
);
break;
case 'failure':
messageSection = (
<p className='confirm-import alert alert-warning'>
<i className='fa fa-warning'/>
<FormattedMessage
id='team_export_tab.unable'
defaultMessage=' Unable to export: {error}'
values={{
error: this.state.err
}}
/>
</p>
);
break;
}
return (
<div
ref='wrapper'
className='user-settings'
>
<h3 className='tab-header'>
<FormattedMessage
id='team_export_tab.export'
defaultMessage='Export'
/>
</h3>
<div className='divider-dark first'/>
<ul className='section-max'>
<li className='col-xs-12 section-title'>
<FormattedMessage
id='team_export_tab.exportTeam'
defaultMessage='Export your team'
/>
</li>
<li className='col-xs-offset-3 col-xs-8'>
<ul className='setting-list'>
<li className='setting-list-item'>
<a
className='btn btn-sm btn-primary btn-file sel-btn'
href='#'
onClick={this.doExport}
>
<FormattedMessage
id='team_export_tab.export'
defaultMessage='Export'
/>
</a>
</li>
</ul>
</li>
</ul>
<div className='divider-dark'/>
{messageSection}
</div>
);
}
}

Просмотреть файл

@@ -3,7 +3,6 @@
import TeamStore from 'stores/team_store.jsx'; import TeamStore from 'stores/team_store.jsx';
import ImportTab from './team_import_tab.jsx'; import ImportTab from './team_import_tab.jsx';
import ExportTab from './team_export_tab.jsx';
import GeneralTab from './team_general_tab.jsx'; import GeneralTab from './team_general_tab.jsx';
import * as Utils from 'utils/utils.jsx'; import * as Utils from 'utils/utils.jsx';
@@ -58,13 +57,6 @@ export default class TeamSettings extends React.Component {
</div> </div>
); );
break; break;
case 'export':
result = (
<div>
<ExportTab/>
</div>
);
break;
default: default:
result = ( result = (
<div/> <div/>

Просмотреть файл

@@ -17,10 +17,6 @@ const holders = defineMessages({
importTab: { importTab: {
id: 'team_settings_modal.importTab', id: 'team_settings_modal.importTab',
defaultMessage: 'Import' defaultMessage: 'Import'
},
exportTab: {
id: 'team_settings_modal.exportTab',
defaultMessage: 'Export'
} }
}); });
@@ -71,9 +67,6 @@ class TeamSettingsModal extends React.Component {
tabs.push({name: 'general', uiName: formatMessage(holders.generalTab), icon: 'icon fa fa-cog'}); tabs.push({name: 'general', uiName: formatMessage(holders.generalTab), icon: 'icon fa fa-cog'});
tabs.push({name: 'import', uiName: formatMessage(holders.importTab), icon: 'icon fa fa-upload'}); tabs.push({name: 'import', uiName: formatMessage(holders.importTab), icon: 'icon fa fa-upload'});
// To enable export uncomment this line
//tabs.push({name: 'export', uiName: formatMessage(holders.exportTab), icon: 'fa fa-download'});
return ( return (
<div <div
className='modal fade' className='modal fade'