diff --git a/app/export.go b/app/export.go index 5ae02720b7c..1401f391c72 100644 --- a/app/export.go +++ b/app/export.go @@ -5,14 +5,17 @@ package app import ( "encoding/json" + "errors" "io" "net/http" + "os" + "path/filepath" "strings" "github.com/mattermost/mattermost-server/model" ) -func (a *App) BulkExport(writer io.Writer) *model.AppError { +func (a *App) BulkExport(writer io.Writer, file string, pathToEmojiDir string, dirNameToExportEmoji string) *model.AppError { if err := a.ExportVersion(writer); err != nil { return err } @@ -32,6 +35,9 @@ func (a *App) BulkExport(writer io.Writer) *model.AppError { if err := a.ExportAllPosts(writer); err != nil { return err } + if err := a.ExportCustomEmoji(writer, file, pathToEmojiDir, dirNameToExportEmoji); err != nil { + return err + } return nil } @@ -338,3 +344,88 @@ func (a *App) BuildPostReactions(postId string) (*[]ReactionImportData, *model.A return &reactionsOfPost, nil } + +func (a *App) ExportCustomEmoji(writer io.Writer, file string, pathToEmojiDir string, dirNameToExportEmoji string) *model.AppError { + pageNumber := 0 + for { + customEmojiList, err := a.GetEmojiList(pageNumber, 100, model.EMOJI_SORT_BY_NAME) + + if err != nil { + return err + } + + if len(customEmojiList) == 0 { + break + } + + pageNumber++ + + pathToDir := a.createDirForEmoji(file, dirNameToExportEmoji) + + for _, emoji := range customEmojiList { + emojiImagePath := pathToEmojiDir + emoji.Id + "/image" + err := a.copyEmojiImages(emoji.Id, emojiImagePath, pathToDir) + if err != nil { + return model.NewAppError("BulkExport", "app.export.export_custom_emoji.copy_emoji_images.error", nil, "err="+err.Error(), http.StatusBadRequest) + } + + filePath := dirNameToExportEmoji + "/" + emoji.Id + "/image" + + emojiImportObject := ImportLineFromEmoji(emoji, filePath) + + if err := a.ExportWriteLine(writer, emojiImportObject); err != nil { + return err + } + } + } + + return nil +} + +// Creates directory named 'exported_emoji' to copy the emoji files +// Directory and the file specified by admin share the same path +func (a *App) createDirForEmoji(file string, dirName string) string { + pathToFile, _ := filepath.Abs(file) + pathSlice := strings.Split(pathToFile, "/") + if len(pathSlice) > 0 { + pathSlice = pathSlice[:len(pathSlice)-1] + } + pathToDir := strings.Join(pathSlice, "/") + "/" + dirName + + if _, err := os.Stat(pathToDir); os.IsNotExist(err) { + os.Mkdir(pathToDir, os.ModePerm) + } + return pathToDir +} + +// Copies emoji files from 'data/emoji' dir to 'exported_emoji' dir +func (a *App) copyEmojiImages(emojiId string, emojiImagePath string, pathToDir string) error { + var err error + + fromPath, err := os.Open(emojiImagePath) + if fromPath == nil || err != nil { + return errors.New("Error reading " + emojiImagePath + "file") + } + defer fromPath.Close() + + emojiDir := pathToDir + "/" + emojiId + + if _, err := os.Stat(emojiDir); os.IsNotExist(err) { + os.Mkdir(emojiDir, os.ModePerm) + } + if err != nil { + return errors.New("Error creating directory for the emoji " + err.Error()) + } + toPath, err := os.OpenFile(emojiDir+"/image", os.O_RDWR|os.O_CREATE, 0666) + if err != nil { + return errors.New("Error creating the image file " + err.Error()) + } + defer toPath.Close() + + _, err = io.Copy(toPath, fromPath) + if err != nil { + return errors.New("Error copying emojis " + err.Error()) + } + + return nil +} diff --git a/app/export_converters.go b/app/export_converters.go index efc3a7266a5..931e841365c 100644 --- a/app/export_converters.go +++ b/app/export_converters.go @@ -139,3 +139,13 @@ func ImportReactionFromPost(reaction *model.Reaction) *ReactionImportData { CreateAt: &reaction.CreateAt, } } + +func ImportLineFromEmoji(emoji *model.Emoji, filePath string) *LineImportData { + return &LineImportData{ + Type: "emoji", + Emoji: &EmojiImportData{ + Name: &emoji.Name, + Image: &filePath, + }, + } +} diff --git a/app/export_test.go b/app/export_test.go index 015fde3d073..1be0d798367 100644 --- a/app/export_test.go +++ b/app/export_test.go @@ -1,6 +1,7 @@ package app import ( + "os" "testing" "github.com/stretchr/testify/assert" @@ -103,3 +104,69 @@ func TestExportUserChannels(t *testing.T) { } } } + +func TestDirCreationForEmoji(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + pathToDir := th.App.createDirForEmoji("test.json", "exported_emoji_test") + defer os.Remove(pathToDir) + if _, err := os.Stat(pathToDir); os.IsNotExist(err) { + t.Fatal("Directory exported_emoji_test should exist") + } +} + +func TestCopyEmojiImages(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + emoji := &model.Emoji{ + Id: th.BasicUser.Id, + } + + // Creating a dir named `exported_emoji_test` in the root of the repo + pathToDir := "../exported_emoji_test" + + os.Mkdir(pathToDir, 0777) + defer os.RemoveAll(pathToDir) + + filePath := "../data/emoji/" + emoji.Id + emojiImagePath := filePath + "/image" + + var _, err = os.Stat(filePath) + if os.IsNotExist(err) { + os.MkdirAll(filePath, 0777) + } + + // Creating a file with the name `image` to copy it to `exported_emoji_test` + os.OpenFile(filePath+"/image", os.O_RDONLY|os.O_CREATE, 0777) + defer os.RemoveAll(filePath) + + copyError := th.App.copyEmojiImages(emoji.Id, emojiImagePath, pathToDir) + if copyError != nil { + t.Fatal(copyError) + } + + if _, err := os.Stat(pathToDir + "/" + emoji.Id + "/image"); os.IsNotExist(err) { + t.Fatal("File should exist ", err) + } +} + +func TestExportCustomEmoji(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + filePath := "../demo.json" + + fileWriter, _ := os.Create(filePath) + defer os.Remove(filePath) + + pathToEmojiDir := "../data/emoji/" + dirNameToExportEmoji := "exported_emoji_test" + + err := th.App.ExportCustomEmoji(fileWriter, filePath, pathToEmojiDir, dirNameToExportEmoji) + defer os.RemoveAll("../" + dirNameToExportEmoji) + if err != nil { + t.Fatal(err) + } +} diff --git a/cmd/mattermost/commands/export.go b/cmd/mattermost/commands/export.go index 1d8eaeb2964..0515731f15f 100644 --- a/cmd/mattermost/commands/export.go +++ b/cmd/mattermost/commands/export.go @@ -180,7 +180,14 @@ func bulkExportCmdF(command *cobra.Command, args []string) error { } defer fileWriter.Close() - if err := a.BulkExport(fileWriter); err != nil { + // Path to directory of custom emoji + pathToEmojiDir := "data/emoji/" + + // Name of the directory to export custom emoji + dirNameToExportEmoji := "exported_emoji" + + // args[0] points to the filename/filepath passed with export bulk command + if err := a.BulkExport(fileWriter, args[0], pathToEmojiDir, dirNameToExportEmoji); err != nil { CommandPrettyPrintln(err.Error()) return err }