Cleaning up old export code (#3601)
Этот коммит содержится в:
коммит произвёл
Harrison Healey
родитель
b339b5c982
Коммит
942ae4c527
292
api/export.go
292
api/export.go
@@ -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
|
|
||||||
}
|
|
||||||
18
api/file.go
18
api/file.go
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
60
i18n/en.json
60
i18n/en.json
@@ -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'
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user