github.com/gigforks/mattermost-server@v4.9.1-0.20180619094218-800d97fa55d0+incompatible/app/import.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package app
     5  
     6  import (
     7  	"bufio"
     8  	"bytes"
     9  	"encoding/json"
    10  	"fmt"
    11  	"io"
    12  	"net/http"
    13  	"os"
    14  	"regexp"
    15  	"strings"
    16  	"sync"
    17  	"time"
    18  	"unicode/utf8"
    19  
    20  	"github.com/mattermost/mattermost-server/mlog"
    21  	"github.com/mattermost/mattermost-server/model"
    22  	"github.com/mattermost/mattermost-server/store"
    23  )
    24  
    25  // Import Data Models
    26  
    27  type LineImportData struct {
    28  	Type          string                   `json:"type"`
    29  	Team          *TeamImportData          `json:"team"`
    30  	Channel       *ChannelImportData       `json:"channel"`
    31  	User          *UserImportData          `json:"user"`
    32  	Post          *PostImportData          `json:"post"`
    33  	DirectChannel *DirectChannelImportData `json:"direct_channel"`
    34  	DirectPost    *DirectPostImportData    `json:"direct_post"`
    35  	Version       *int                     `json:"version"`
    36  }
    37  
    38  type TeamImportData struct {
    39  	Name            *string `json:"name"`
    40  	DisplayName     *string `json:"display_name"`
    41  	Type            *string `json:"type"`
    42  	Description     *string `json:"description"`
    43  	AllowOpenInvite *bool   `json:"allow_open_invite"`
    44  }
    45  
    46  type ChannelImportData struct {
    47  	Team        *string `json:"team"`
    48  	Name        *string `json:"name"`
    49  	DisplayName *string `json:"display_name"`
    50  	Type        *string `json:"type"`
    51  	Header      *string `json:"header"`
    52  	Purpose     *string `json:"purpose"`
    53  }
    54  
    55  type UserImportData struct {
    56  	ProfileImage *string `json:"profile_image"`
    57  	Username     *string `json:"username"`
    58  	Email        *string `json:"email"`
    59  	AuthService  *string `json:"auth_service"`
    60  	AuthData     *string `json:"auth_data"`
    61  	Password     *string `json:"password"`
    62  	Nickname     *string `json:"nickname"`
    63  	FirstName    *string `json:"first_name"`
    64  	LastName     *string `json:"last_name"`
    65  	Position     *string `json:"position"`
    66  	Roles        *string `json:"roles"`
    67  	Locale       *string `json:"locale"`
    68  
    69  	Teams *[]UserTeamImportData `json:"teams"`
    70  
    71  	Theme              *string `json:"theme"`
    72  	UseMilitaryTime    *string `json:"military_time"`
    73  	CollapsePreviews   *string `json:"link_previews"`
    74  	MessageDisplay     *string `json:"message_display"`
    75  	ChannelDisplayMode *string `json:"channel_display_mode"`
    76  	TutorialStep       *string `json:"tutorial_step"`
    77  
    78  	NotifyProps *UserNotifyPropsImportData `json:"notify_props"`
    79  }
    80  
    81  type UserNotifyPropsImportData struct {
    82  	Desktop         *string `json:"desktop"`
    83  	DesktopDuration *string `json:"desktop_duration"`
    84  	DesktopSound    *string `json:"desktop_sound"`
    85  
    86  	Email *string `json:"email"`
    87  
    88  	Mobile           *string `json:"mobile"`
    89  	MobilePushStatus *string `json:"mobile_push_status"`
    90  
    91  	ChannelTrigger  *string `json:"channel"`
    92  	CommentsTrigger *string `json:"comments"`
    93  	MentionKeys     *string `json:"mention_keys"`
    94  }
    95  
    96  type UserTeamImportData struct {
    97  	Name     *string                  `json:"name"`
    98  	Roles    *string                  `json:"roles"`
    99  	Channels *[]UserChannelImportData `json:"channels"`
   100  }
   101  
   102  type UserChannelImportData struct {
   103  	Name        *string                           `json:"name"`
   104  	Roles       *string                           `json:"roles"`
   105  	NotifyProps *UserChannelNotifyPropsImportData `json:"notify_props"`
   106  	Favorite    *bool                             `json:"favorite"`
   107  }
   108  
   109  type UserChannelNotifyPropsImportData struct {
   110  	Desktop    *string `json:"desktop"`
   111  	Mobile     *string `json:"mobile"`
   112  	MarkUnread *string `json:"mark_unread"`
   113  }
   114  
   115  type ReactionImportData struct {
   116  	User      *string `json:"user"`
   117  	CreateAt  *int64  `json:"create_at"`
   118  	EmojiName *string `json:"emoji_name"`
   119  }
   120  
   121  type ReplyImportData struct {
   122  	User *string `json:"user"`
   123  
   124  	Message  *string `json:"message"`
   125  	CreateAt *int64  `json:"create_at"`
   126  
   127  	FlaggedBy *[]string             `json:"flagged_by"`
   128  	Reactions *[]ReactionImportData `json:"reactions"`
   129  }
   130  
   131  type PostImportData struct {
   132  	Team    *string `json:"team"`
   133  	Channel *string `json:"channel"`
   134  	User    *string `json:"user"`
   135  
   136  	Message  *string `json:"message"`
   137  	CreateAt *int64  `json:"create_at"`
   138  
   139  	FlaggedBy *[]string             `json:"flagged_by"`
   140  	Reactions *[]ReactionImportData `json:"reactions"`
   141  	Replies   *[]ReplyImportData    `json:"replies"`
   142  }
   143  
   144  type DirectChannelImportData struct {
   145  	Members     *[]string `json:"members"`
   146  	FavoritedBy *[]string `json:"favorited_by"`
   147  
   148  	Header *string `json:"header"`
   149  }
   150  
   151  type DirectPostImportData struct {
   152  	ChannelMembers *[]string `json:"channel_members"`
   153  	User           *string   `json:"user"`
   154  
   155  	Message  *string `json:"message"`
   156  	CreateAt *int64  `json:"create_at"`
   157  
   158  	FlaggedBy *[]string             `json:"flagged_by"`
   159  	Reactions *[]ReactionImportData `json:"reactions"`
   160  	Replies   *[]ReplyImportData    `json:"replies"`
   161  }
   162  
   163  type LineImportWorkerData struct {
   164  	LineImportData
   165  	LineNumber int
   166  }
   167  
   168  type LineImportWorkerError struct {
   169  	Error      *model.AppError
   170  	LineNumber int
   171  }
   172  
   173  //
   174  // -- Bulk Import Functions --
   175  // These functions import data directly into the database. Security and permission checks are bypassed but validity is
   176  // still enforced.
   177  //
   178  
   179  func (a *App) bulkImportWorker(dryRun bool, wg *sync.WaitGroup, lines <-chan LineImportWorkerData, errors chan<- LineImportWorkerError) {
   180  	for line := range lines {
   181  		if err := a.ImportLine(line.LineImportData, dryRun); err != nil {
   182  			errors <- LineImportWorkerError{err, line.LineNumber}
   183  		}
   184  	}
   185  	wg.Done()
   186  }
   187  
   188  func (a *App) BulkImport(fileReader io.Reader, dryRun bool, workers int) (*model.AppError, int) {
   189  	scanner := bufio.NewScanner(fileReader)
   190  	lineNumber := 0
   191  
   192  	errorsChan := make(chan LineImportWorkerError, (2*workers)+1) // size chosen to ensure it never gets filled up completely.
   193  	var wg sync.WaitGroup
   194  	var linesChan chan LineImportWorkerData
   195  	lastLineType := ""
   196  
   197  	for scanner.Scan() {
   198  		decoder := json.NewDecoder(strings.NewReader(scanner.Text()))
   199  		lineNumber++
   200  
   201  		var line LineImportData
   202  		if err := decoder.Decode(&line); err != nil {
   203  			return model.NewAppError("BulkImport", "app.import.bulk_import.json_decode.error", nil, err.Error(), http.StatusBadRequest), lineNumber
   204  		} else {
   205  			if lineNumber == 1 {
   206  				importDataFileVersion, apperr := processImportDataFileVersionLine(line)
   207  				if apperr != nil {
   208  					return apperr, lineNumber
   209  				}
   210  
   211  				if importDataFileVersion != 1 {
   212  					return model.NewAppError("BulkImport", "app.import.bulk_import.unsupported_version.error", nil, "", http.StatusBadRequest), lineNumber
   213  				}
   214  			} else {
   215  				if line.Type != lastLineType {
   216  					if lastLineType != "" {
   217  						// Changing type. Clear out the worker queue before continuing.
   218  						close(linesChan)
   219  						wg.Wait()
   220  
   221  						// Check no errors occurred while waiting for the queue to empty.
   222  						if len(errorsChan) != 0 {
   223  							err := <-errorsChan
   224  							return err.Error, err.LineNumber
   225  						}
   226  					}
   227  
   228  					// Set up the workers and channel for this type.
   229  					lastLineType = line.Type
   230  					linesChan = make(chan LineImportWorkerData, workers)
   231  					for i := 0; i < workers; i++ {
   232  						wg.Add(1)
   233  						go a.bulkImportWorker(dryRun, &wg, linesChan, errorsChan)
   234  					}
   235  				}
   236  
   237  				select {
   238  				case linesChan <- LineImportWorkerData{line, lineNumber}:
   239  				case err := <-errorsChan:
   240  					close(linesChan)
   241  					wg.Wait()
   242  					return err.Error, err.LineNumber
   243  				}
   244  			}
   245  		}
   246  	}
   247  
   248  	// No more lines. Clear out the worker queue before continuing.
   249  	close(linesChan)
   250  	wg.Wait()
   251  
   252  	// Check no errors occurred while waiting for the queue to empty.
   253  	if len(errorsChan) != 0 {
   254  		err := <-errorsChan
   255  		return err.Error, err.LineNumber
   256  	}
   257  
   258  	if err := scanner.Err(); err != nil {
   259  		return model.NewAppError("BulkImport", "app.import.bulk_import.file_scan.error", nil, err.Error(), http.StatusInternalServerError), 0
   260  	}
   261  
   262  	return nil, 0
   263  }
   264  
   265  func processImportDataFileVersionLine(line LineImportData) (int, *model.AppError) {
   266  	if line.Type != "version" || line.Version == nil {
   267  		return -1, model.NewAppError("BulkImport", "app.import.process_import_data_file_version_line.invalid_version.error", nil, "", http.StatusBadRequest)
   268  	}
   269  
   270  	return *line.Version, nil
   271  }
   272  
   273  func (a *App) ImportLine(line LineImportData, dryRun bool) *model.AppError {
   274  	switch {
   275  	case line.Type == "team":
   276  		if line.Team == nil {
   277  			return model.NewAppError("BulkImport", "app.import.import_line.null_team.error", nil, "", http.StatusBadRequest)
   278  		} else {
   279  			return a.ImportTeam(line.Team, dryRun)
   280  		}
   281  	case line.Type == "channel":
   282  		if line.Channel == nil {
   283  			return model.NewAppError("BulkImport", "app.import.import_line.null_channel.error", nil, "", http.StatusBadRequest)
   284  		} else {
   285  			return a.ImportChannel(line.Channel, dryRun)
   286  		}
   287  	case line.Type == "user":
   288  		if line.User == nil {
   289  			return model.NewAppError("BulkImport", "app.import.import_line.null_user.error", nil, "", http.StatusBadRequest)
   290  		} else {
   291  			return a.ImportUser(line.User, dryRun)
   292  		}
   293  	case line.Type == "post":
   294  		if line.Post == nil {
   295  			return model.NewAppError("BulkImport", "app.import.import_line.null_post.error", nil, "", http.StatusBadRequest)
   296  		} else {
   297  			return a.ImportPost(line.Post, dryRun)
   298  		}
   299  	case line.Type == "direct_channel":
   300  		if line.DirectChannel == nil {
   301  			return model.NewAppError("BulkImport", "app.import.import_line.null_direct_channel.error", nil, "", http.StatusBadRequest)
   302  		} else {
   303  			return a.ImportDirectChannel(line.DirectChannel, dryRun)
   304  		}
   305  	case line.Type == "direct_post":
   306  		if line.DirectPost == nil {
   307  			return model.NewAppError("BulkImport", "app.import.import_line.null_direct_post.error", nil, "", http.StatusBadRequest)
   308  		} else {
   309  			return a.ImportDirectPost(line.DirectPost, dryRun)
   310  		}
   311  	default:
   312  		return model.NewAppError("BulkImport", "app.import.import_line.unknown_line_type.error", map[string]interface{}{"Type": line.Type}, "", http.StatusBadRequest)
   313  	}
   314  }
   315  
   316  func (a *App) ImportTeam(data *TeamImportData, dryRun bool) *model.AppError {
   317  	if err := validateTeamImportData(data); err != nil {
   318  		return err
   319  	}
   320  
   321  	// If this is a Dry Run, do not continue any further.
   322  	if dryRun {
   323  		return nil
   324  	}
   325  
   326  	var team *model.Team
   327  	if result := <-a.Srv.Store.Team().GetByName(*data.Name); result.Err == nil {
   328  		team = result.Data.(*model.Team)
   329  	} else {
   330  		team = &model.Team{}
   331  	}
   332  
   333  	team.Name = *data.Name
   334  	team.DisplayName = *data.DisplayName
   335  	team.Type = *data.Type
   336  
   337  	if data.Description != nil {
   338  		team.Description = *data.Description
   339  	}
   340  
   341  	if data.AllowOpenInvite != nil {
   342  		team.AllowOpenInvite = *data.AllowOpenInvite
   343  	}
   344  
   345  	if team.Id == "" {
   346  		if _, err := a.CreateTeam(team); err != nil {
   347  			return err
   348  		}
   349  	} else {
   350  		if _, err := a.UpdateTeam(team); err != nil {
   351  			return err
   352  		}
   353  	}
   354  
   355  	return nil
   356  }
   357  
   358  func validateTeamImportData(data *TeamImportData) *model.AppError {
   359  
   360  	if data.Name == nil {
   361  		return model.NewAppError("BulkImport", "app.import.validate_team_import_data.name_missing.error", nil, "", http.StatusBadRequest)
   362  	} else if len(*data.Name) > model.TEAM_NAME_MAX_LENGTH {
   363  		return model.NewAppError("BulkImport", "app.import.validate_team_import_data.name_length.error", nil, "", http.StatusBadRequest)
   364  	} else if model.IsReservedTeamName(*data.Name) {
   365  		return model.NewAppError("BulkImport", "app.import.validate_team_import_data.name_reserved.error", nil, "", http.StatusBadRequest)
   366  	} else if !model.IsValidTeamName(*data.Name) {
   367  		return model.NewAppError("BulkImport", "app.import.validate_team_import_data.name_characters.error", nil, "", http.StatusBadRequest)
   368  	}
   369  
   370  	if data.DisplayName == nil {
   371  		return model.NewAppError("BulkImport", "app.import.validate_team_import_data.display_name_missing.error", nil, "", http.StatusBadRequest)
   372  	} else if utf8.RuneCountInString(*data.DisplayName) == 0 || utf8.RuneCountInString(*data.DisplayName) > model.TEAM_DISPLAY_NAME_MAX_RUNES {
   373  		return model.NewAppError("BulkImport", "app.import.validate_team_import_data.display_name_length.error", nil, "", http.StatusBadRequest)
   374  	}
   375  
   376  	if data.Type == nil {
   377  		return model.NewAppError("BulkImport", "app.import.validate_team_import_data.type_missing.error", nil, "", http.StatusBadRequest)
   378  	} else if *data.Type != model.TEAM_OPEN && *data.Type != model.TEAM_INVITE {
   379  		return model.NewAppError("BulkImport", "app.import.validate_team_import_data.type_invalid.error", nil, "", http.StatusBadRequest)
   380  	}
   381  
   382  	if data.Description != nil && len(*data.Description) > model.TEAM_DESCRIPTION_MAX_LENGTH {
   383  		return model.NewAppError("BulkImport", "app.import.validate_team_import_data.description_length.error", nil, "", http.StatusBadRequest)
   384  	}
   385  
   386  	return nil
   387  }
   388  
   389  func (a *App) ImportChannel(data *ChannelImportData, dryRun bool) *model.AppError {
   390  	if err := validateChannelImportData(data); err != nil {
   391  		return err
   392  	}
   393  
   394  	// If this is a Dry Run, do not continue any further.
   395  	if dryRun {
   396  		return nil
   397  	}
   398  
   399  	var team *model.Team
   400  	if result := <-a.Srv.Store.Team().GetByName(*data.Team); result.Err != nil {
   401  		return model.NewAppError("BulkImport", "app.import.import_channel.team_not_found.error", map[string]interface{}{"TeamName": *data.Team}, result.Err.Error(), http.StatusBadRequest)
   402  	} else {
   403  		team = result.Data.(*model.Team)
   404  	}
   405  
   406  	var channel *model.Channel
   407  	if result := <-a.Srv.Store.Channel().GetByNameIncludeDeleted(team.Id, *data.Name, true); result.Err == nil {
   408  		channel = result.Data.(*model.Channel)
   409  	} else {
   410  		channel = &model.Channel{}
   411  	}
   412  
   413  	channel.TeamId = team.Id
   414  	channel.Name = *data.Name
   415  	channel.DisplayName = *data.DisplayName
   416  	channel.Type = *data.Type
   417  
   418  	if data.Header != nil {
   419  		channel.Header = *data.Header
   420  	}
   421  
   422  	if data.Purpose != nil {
   423  		channel.Purpose = *data.Purpose
   424  	}
   425  
   426  	if channel.Id == "" {
   427  		if _, err := a.CreateChannel(channel, false); err != nil {
   428  			return err
   429  		}
   430  	} else {
   431  		if _, err := a.UpdateChannel(channel); err != nil {
   432  			return err
   433  		}
   434  	}
   435  
   436  	return nil
   437  }
   438  
   439  func validateChannelImportData(data *ChannelImportData) *model.AppError {
   440  
   441  	if data.Team == nil {
   442  		return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.team_missing.error", nil, "", http.StatusBadRequest)
   443  	}
   444  
   445  	if data.Name == nil {
   446  		return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.name_missing.error", nil, "", http.StatusBadRequest)
   447  	} else if len(*data.Name) > model.CHANNEL_NAME_MAX_LENGTH {
   448  		return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.name_length.error", nil, "", http.StatusBadRequest)
   449  	} else if !model.IsValidChannelIdentifier(*data.Name) {
   450  		return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.name_characters.error", nil, "", http.StatusBadRequest)
   451  	}
   452  
   453  	if data.DisplayName == nil {
   454  		return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.display_name_missing.error", nil, "", http.StatusBadRequest)
   455  	} else if utf8.RuneCountInString(*data.DisplayName) == 0 || utf8.RuneCountInString(*data.DisplayName) > model.CHANNEL_DISPLAY_NAME_MAX_RUNES {
   456  		return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.display_name_length.error", nil, "", http.StatusBadRequest)
   457  	}
   458  
   459  	if data.Type == nil {
   460  		return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.type_missing.error", nil, "", http.StatusBadRequest)
   461  	} else if *data.Type != model.CHANNEL_OPEN && *data.Type != model.CHANNEL_PRIVATE {
   462  		return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.type_invalid.error", nil, "", http.StatusBadRequest)
   463  	}
   464  
   465  	if data.Header != nil && utf8.RuneCountInString(*data.Header) > model.CHANNEL_HEADER_MAX_RUNES {
   466  		return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.header_length.error", nil, "", http.StatusBadRequest)
   467  	}
   468  
   469  	if data.Purpose != nil && utf8.RuneCountInString(*data.Purpose) > model.CHANNEL_PURPOSE_MAX_RUNES {
   470  		return model.NewAppError("BulkImport", "app.import.validate_channel_import_data.purpose_length.error", nil, "", http.StatusBadRequest)
   471  	}
   472  
   473  	return nil
   474  }
   475  
   476  func (a *App) ImportUser(data *UserImportData, dryRun bool) *model.AppError {
   477  	if err := validateUserImportData(data); err != nil {
   478  		return err
   479  	}
   480  
   481  	// If this is a Dry Run, do not continue any further.
   482  	if dryRun {
   483  		return nil
   484  	}
   485  
   486  	// We want to avoid database writes if nothing has changed.
   487  	hasUserChanged := false
   488  	hasNotifyPropsChanged := false
   489  	hasUserRolesChanged := false
   490  	hasUserAuthDataChanged := false
   491  	hasUserEmailVerifiedChanged := false
   492  
   493  	var user *model.User
   494  	if result := <-a.Srv.Store.User().GetByUsername(*data.Username); result.Err == nil {
   495  		user = result.Data.(*model.User)
   496  	} else {
   497  		user = &model.User{}
   498  		user.MakeNonNil()
   499  		hasUserChanged = true
   500  	}
   501  
   502  	user.Username = *data.Username
   503  
   504  	if user.Email != *data.Email {
   505  		hasUserChanged = true
   506  		hasUserEmailVerifiedChanged = true // Changing the email resets email verified to false by default.
   507  		user.Email = *data.Email
   508  	}
   509  
   510  	var password string
   511  	var authService string
   512  	var authData *string
   513  
   514  	if data.AuthService != nil {
   515  		if user.AuthService != *data.AuthService {
   516  			hasUserAuthDataChanged = true
   517  		}
   518  		authService = *data.AuthService
   519  	}
   520  
   521  	// AuthData and Password are mutually exclusive.
   522  	if data.AuthData != nil {
   523  		if user.AuthData == nil || *user.AuthData != *data.AuthData {
   524  			hasUserAuthDataChanged = true
   525  		}
   526  		authData = data.AuthData
   527  		password = ""
   528  	} else if data.Password != nil {
   529  		password = *data.Password
   530  		authData = nil
   531  	} else {
   532  		// If no AuthData or Password is specified, we must generate a password.
   533  		password = model.NewId()
   534  		authData = nil
   535  	}
   536  
   537  	user.Password = password
   538  	user.AuthService = authService
   539  	user.AuthData = authData
   540  
   541  	// Automatically assume all emails are verified.
   542  	emailVerified := true
   543  	if user.EmailVerified != emailVerified {
   544  		user.EmailVerified = emailVerified
   545  		hasUserEmailVerifiedChanged = true
   546  	}
   547  
   548  	if data.Nickname != nil {
   549  		if user.Nickname != *data.Nickname {
   550  			user.Nickname = *data.Nickname
   551  			hasUserChanged = true
   552  		}
   553  	}
   554  
   555  	if data.FirstName != nil {
   556  		if user.FirstName != *data.FirstName {
   557  			user.FirstName = *data.FirstName
   558  			hasUserChanged = true
   559  		}
   560  	}
   561  
   562  	if data.LastName != nil {
   563  		if user.LastName != *data.LastName {
   564  			user.LastName = *data.LastName
   565  			hasUserChanged = true
   566  		}
   567  	}
   568  
   569  	if data.Position != nil {
   570  		if user.Position != *data.Position {
   571  			user.Position = *data.Position
   572  			hasUserChanged = true
   573  		}
   574  	}
   575  
   576  	if data.Locale != nil {
   577  		if user.Locale != *data.Locale {
   578  			user.Locale = *data.Locale
   579  			hasUserChanged = true
   580  		}
   581  	} else {
   582  		if user.Locale != *a.Config().LocalizationSettings.DefaultClientLocale {
   583  			user.Locale = *a.Config().LocalizationSettings.DefaultClientLocale
   584  			hasUserChanged = true
   585  		}
   586  	}
   587  
   588  	var roles string
   589  	if data.Roles != nil {
   590  		if user.Roles != *data.Roles {
   591  			roles = *data.Roles
   592  			hasUserRolesChanged = true
   593  		}
   594  	} else if len(user.Roles) == 0 {
   595  		// Set SYSTEM_USER roles on newly created users by default.
   596  		if user.Roles != model.SYSTEM_USER_ROLE_ID {
   597  			roles = model.SYSTEM_USER_ROLE_ID
   598  			hasUserRolesChanged = true
   599  		}
   600  	}
   601  	user.Roles = roles
   602  
   603  	if data.NotifyProps != nil {
   604  		if data.NotifyProps.Desktop != nil {
   605  			if value, ok := user.NotifyProps[model.DESKTOP_NOTIFY_PROP]; !ok || value != *data.NotifyProps.Desktop {
   606  				user.AddNotifyProp(model.DESKTOP_NOTIFY_PROP, *data.NotifyProps.Desktop)
   607  				hasNotifyPropsChanged = true
   608  			}
   609  		}
   610  
   611  		if data.NotifyProps.DesktopDuration != nil {
   612  			if value, ok := user.NotifyProps[model.DESKTOP_DURATION_NOTIFY_PROP]; !ok || value != *data.NotifyProps.DesktopDuration {
   613  				user.AddNotifyProp(model.DESKTOP_DURATION_NOTIFY_PROP, *data.NotifyProps.DesktopDuration)
   614  				hasNotifyPropsChanged = true
   615  			}
   616  		}
   617  
   618  		if data.NotifyProps.DesktopSound != nil {
   619  			if value, ok := user.NotifyProps[model.DESKTOP_SOUND_NOTIFY_PROP]; !ok || value != *data.NotifyProps.DesktopSound {
   620  				user.AddNotifyProp(model.DESKTOP_SOUND_NOTIFY_PROP, *data.NotifyProps.DesktopSound)
   621  				hasNotifyPropsChanged = true
   622  			}
   623  		}
   624  
   625  		if data.NotifyProps.Email != nil {
   626  			if value, ok := user.NotifyProps[model.EMAIL_NOTIFY_PROP]; !ok || value != *data.NotifyProps.Email {
   627  				user.AddNotifyProp(model.EMAIL_NOTIFY_PROP, *data.NotifyProps.Email)
   628  				hasNotifyPropsChanged = true
   629  			}
   630  		}
   631  
   632  		if data.NotifyProps.Mobile != nil {
   633  			if value, ok := user.NotifyProps[model.PUSH_NOTIFY_PROP]; !ok || value != *data.NotifyProps.Mobile {
   634  				user.AddNotifyProp(model.PUSH_NOTIFY_PROP, *data.NotifyProps.Mobile)
   635  				hasNotifyPropsChanged = true
   636  			}
   637  		}
   638  
   639  		if data.NotifyProps.MobilePushStatus != nil {
   640  			if value, ok := user.NotifyProps[model.PUSH_STATUS_NOTIFY_PROP]; !ok || value != *data.NotifyProps.MobilePushStatus {
   641  				user.AddNotifyProp(model.PUSH_STATUS_NOTIFY_PROP, *data.NotifyProps.MobilePushStatus)
   642  				hasNotifyPropsChanged = true
   643  			}
   644  		}
   645  
   646  		if data.NotifyProps.ChannelTrigger != nil {
   647  			if value, ok := user.NotifyProps[model.CHANNEL_MENTIONS_NOTIFY_PROP]; !ok || value != *data.NotifyProps.ChannelTrigger {
   648  				user.AddNotifyProp(model.CHANNEL_MENTIONS_NOTIFY_PROP, *data.NotifyProps.ChannelTrigger)
   649  				hasNotifyPropsChanged = true
   650  			}
   651  		}
   652  
   653  		if data.NotifyProps.CommentsTrigger != nil {
   654  			if value, ok := user.NotifyProps[model.COMMENTS_NOTIFY_PROP]; !ok || value != *data.NotifyProps.CommentsTrigger {
   655  				user.AddNotifyProp(model.COMMENTS_NOTIFY_PROP, *data.NotifyProps.CommentsTrigger)
   656  				hasNotifyPropsChanged = true
   657  			}
   658  		}
   659  
   660  		if data.NotifyProps.MentionKeys != nil {
   661  			if value, ok := user.NotifyProps[model.MENTION_KEYS_NOTIFY_PROP]; !ok || value != *data.NotifyProps.MentionKeys {
   662  				user.AddNotifyProp(model.MENTION_KEYS_NOTIFY_PROP, *data.NotifyProps.MentionKeys)
   663  				hasNotifyPropsChanged = true
   664  			}
   665  		}
   666  	}
   667  
   668  	var err *model.AppError
   669  	var savedUser *model.User
   670  	if user.Id == "" {
   671  		if savedUser, err = a.createUser(user); err != nil {
   672  			return err
   673  		}
   674  	} else {
   675  		if hasUserChanged {
   676  			if savedUser, err = a.UpdateUser(user, false); err != nil {
   677  				return err
   678  			}
   679  		}
   680  		if hasUserRolesChanged {
   681  			if savedUser, err = a.UpdateUserRoles(user.Id, roles, false); err != nil {
   682  				return err
   683  			}
   684  		}
   685  		if hasNotifyPropsChanged {
   686  			if savedUser, err = a.UpdateUserNotifyProps(user.Id, user.NotifyProps); err != nil {
   687  				return err
   688  			}
   689  		}
   690  		if len(password) > 0 {
   691  			if err = a.UpdatePassword(user, password); err != nil {
   692  				return err
   693  			}
   694  		} else {
   695  			if hasUserAuthDataChanged {
   696  				if res := <-a.Srv.Store.User().UpdateAuthData(user.Id, authService, authData, user.Email, false); res.Err != nil {
   697  					return res.Err
   698  				}
   699  			}
   700  		}
   701  		if emailVerified {
   702  			if hasUserEmailVerifiedChanged {
   703  				if err := a.VerifyUserEmail(user.Id); err != nil {
   704  					return err
   705  				}
   706  			}
   707  		}
   708  	}
   709  
   710  	if savedUser == nil {
   711  		savedUser = user
   712  	}
   713  
   714  	if data.ProfileImage != nil {
   715  		file, err := os.Open(*data.ProfileImage)
   716  		if err != nil {
   717  			mlog.Error(fmt.Sprint("api.import.import_user.profile_image.error FIXME: NOT FOUND IN TRANSLATIONS FILE", err))
   718  		}
   719  		if err := a.SetProfileImageFromFile(savedUser.Id, file); err != nil {
   720  			mlog.Error(fmt.Sprint("api.import.import_user.profile_image.error FIXME: NOT FOUND IN TRANSLATIONS FILE", err))
   721  		}
   722  	}
   723  
   724  	// Preferences.
   725  	var preferences model.Preferences
   726  
   727  	if data.Theme != nil {
   728  		preferences = append(preferences, model.Preference{
   729  			UserId:   savedUser.Id,
   730  			Category: model.PREFERENCE_CATEGORY_THEME,
   731  			Name:     "",
   732  			Value:    *data.Theme,
   733  		})
   734  	}
   735  
   736  	if data.UseMilitaryTime != nil {
   737  		preferences = append(preferences, model.Preference{
   738  			UserId:   savedUser.Id,
   739  			Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS,
   740  			Name:     "use_military_time",
   741  			Value:    *data.UseMilitaryTime,
   742  		})
   743  	}
   744  
   745  	if data.CollapsePreviews != nil {
   746  		preferences = append(preferences, model.Preference{
   747  			UserId:   savedUser.Id,
   748  			Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS,
   749  			Name:     "collapse_previews",
   750  			Value:    *data.CollapsePreviews,
   751  		})
   752  	}
   753  
   754  	if data.MessageDisplay != nil {
   755  		preferences = append(preferences, model.Preference{
   756  			UserId:   savedUser.Id,
   757  			Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS,
   758  			Name:     "message_display",
   759  			Value:    *data.MessageDisplay,
   760  		})
   761  	}
   762  
   763  	if data.ChannelDisplayMode != nil {
   764  		preferences = append(preferences, model.Preference{
   765  			UserId:   savedUser.Id,
   766  			Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS,
   767  			Name:     "channel_display_mode",
   768  			Value:    *data.ChannelDisplayMode,
   769  		})
   770  	}
   771  
   772  	if data.TutorialStep != nil {
   773  		preferences = append(preferences, model.Preference{
   774  			UserId:   savedUser.Id,
   775  			Category: model.PREFERENCE_CATEGORY_TUTORIAL_STEPS,
   776  			Name:     savedUser.Id,
   777  			Value:    *data.TutorialStep,
   778  		})
   779  	}
   780  
   781  	if len(preferences) > 0 {
   782  		if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil {
   783  			return model.NewAppError("BulkImport", "app.import.import_user.save_preferences.error", nil, result.Err.Error(), http.StatusInternalServerError)
   784  		}
   785  	}
   786  
   787  	return a.ImportUserTeams(savedUser, data.Teams)
   788  }
   789  
   790  func (a *App) ImportUserTeams(user *model.User, data *[]UserTeamImportData) *model.AppError {
   791  	if data == nil {
   792  		return nil
   793  	}
   794  
   795  	for _, tdata := range *data {
   796  		team, err := a.GetTeamByName(*tdata.Name)
   797  		if err != nil {
   798  			return err
   799  		}
   800  
   801  		var roles string
   802  		if tdata.Roles == nil {
   803  			roles = model.TEAM_USER_ROLE_ID
   804  		} else {
   805  			roles = *tdata.Roles
   806  		}
   807  
   808  		var member *model.TeamMember
   809  		if member, _, err = a.joinUserToTeam(team, user); err != nil {
   810  			return err
   811  		}
   812  
   813  		if member.Roles != roles {
   814  			if _, err := a.UpdateTeamMemberRoles(team.Id, user.Id, roles); err != nil {
   815  				return err
   816  			}
   817  		}
   818  
   819  		if defaultChannel, err := a.GetChannelByName(model.DEFAULT_CHANNEL, team.Id); err != nil {
   820  			return err
   821  		} else if _, err = a.addUserToChannel(user, defaultChannel, member); err != nil {
   822  			return err
   823  		}
   824  
   825  		if err := a.ImportUserChannels(user, team, member, tdata.Channels); err != nil {
   826  			return err
   827  		}
   828  	}
   829  
   830  	return nil
   831  }
   832  
   833  func (a *App) ImportUserChannels(user *model.User, team *model.Team, teamMember *model.TeamMember, data *[]UserChannelImportData) *model.AppError {
   834  	if data == nil {
   835  		return nil
   836  	}
   837  
   838  	var preferences model.Preferences
   839  
   840  	// Loop through all channels.
   841  	for _, cdata := range *data {
   842  		channel, err := a.GetChannelByName(*cdata.Name, team.Id)
   843  		if err != nil {
   844  			return err
   845  		}
   846  
   847  		var roles string
   848  		if cdata.Roles == nil {
   849  			roles = model.CHANNEL_USER_ROLE_ID
   850  		} else {
   851  			roles = *cdata.Roles
   852  		}
   853  
   854  		var member *model.ChannelMember
   855  		member, err = a.GetChannelMember(channel.Id, user.Id)
   856  		if err != nil {
   857  			member, err = a.addUserToChannel(user, channel, teamMember)
   858  			if err != nil {
   859  				return err
   860  			}
   861  		}
   862  
   863  		if member.Roles != roles {
   864  			if _, err := a.UpdateChannelMemberRoles(channel.Id, user.Id, roles); err != nil {
   865  				return err
   866  			}
   867  		}
   868  
   869  		if cdata.NotifyProps != nil {
   870  			notifyProps := member.NotifyProps
   871  
   872  			if cdata.NotifyProps.Desktop != nil {
   873  				notifyProps[model.DESKTOP_NOTIFY_PROP] = *cdata.NotifyProps.Desktop
   874  			}
   875  
   876  			if cdata.NotifyProps.Mobile != nil {
   877  				notifyProps[model.PUSH_NOTIFY_PROP] = *cdata.NotifyProps.Mobile
   878  			}
   879  
   880  			if cdata.NotifyProps.MarkUnread != nil {
   881  				notifyProps[model.MARK_UNREAD_NOTIFY_PROP] = *cdata.NotifyProps.MarkUnread
   882  			}
   883  
   884  			if _, err := a.UpdateChannelMemberNotifyProps(notifyProps, channel.Id, user.Id); err != nil {
   885  				return err
   886  			}
   887  		}
   888  
   889  		if cdata.Favorite != nil && *cdata.Favorite {
   890  			preferences = append(preferences, model.Preference{
   891  				UserId:   user.Id,
   892  				Category: model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL,
   893  				Name:     channel.Id,
   894  				Value:    "true",
   895  			})
   896  		}
   897  	}
   898  
   899  	if len(preferences) > 0 {
   900  		if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil {
   901  			return model.NewAppError("BulkImport", "app.import.import_user_channels.save_preferences.error", nil, result.Err.Error(), http.StatusInternalServerError)
   902  		}
   903  	}
   904  
   905  	return nil
   906  }
   907  
   908  func validateUserImportData(data *UserImportData) *model.AppError {
   909  	if data.ProfileImage != nil {
   910  		if _, err := os.Stat(*data.ProfileImage); os.IsNotExist(err) {
   911  			return model.NewAppError("BulkImport", "app.import.validate_user_import_data.profile_image.error", nil, "", http.StatusBadRequest)
   912  		}
   913  	}
   914  
   915  	if data.Username == nil {
   916  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.username_missing.error", nil, "", http.StatusBadRequest)
   917  	} else if !model.IsValidUsername(*data.Username) {
   918  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.username_invalid.error", nil, "", http.StatusBadRequest)
   919  	}
   920  
   921  	if data.Email == nil {
   922  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.email_missing.error", nil, "", http.StatusBadRequest)
   923  	} else if len(*data.Email) == 0 || len(*data.Email) > model.USER_EMAIL_MAX_LENGTH {
   924  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.email_length.error", nil, "", http.StatusBadRequest)
   925  	}
   926  
   927  	if data.AuthService != nil && len(*data.AuthService) == 0 {
   928  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.auth_service_length.error", nil, "", http.StatusBadRequest)
   929  	}
   930  
   931  	if data.AuthData != nil && data.Password != nil {
   932  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.auth_data_and_password.error", nil, "", http.StatusBadRequest)
   933  	}
   934  
   935  	if data.AuthData != nil && len(*data.AuthData) > model.USER_AUTH_DATA_MAX_LENGTH {
   936  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.auth_data_length.error", nil, "", http.StatusBadRequest)
   937  	}
   938  
   939  	if data.Password != nil && len(*data.Password) == 0 {
   940  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.pasword_length.error", nil, "", http.StatusBadRequest)
   941  	}
   942  
   943  	if data.Password != nil && len(*data.Password) > model.USER_PASSWORD_MAX_LENGTH {
   944  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.password_length.error", nil, "", http.StatusBadRequest)
   945  	}
   946  
   947  	if data.Nickname != nil && utf8.RuneCountInString(*data.Nickname) > model.USER_NICKNAME_MAX_RUNES {
   948  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.nickname_length.error", nil, "", http.StatusBadRequest)
   949  	}
   950  
   951  	if data.FirstName != nil && utf8.RuneCountInString(*data.FirstName) > model.USER_FIRST_NAME_MAX_RUNES {
   952  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.first_name_length.error", nil, "", http.StatusBadRequest)
   953  	}
   954  
   955  	if data.LastName != nil && utf8.RuneCountInString(*data.LastName) > model.USER_LAST_NAME_MAX_RUNES {
   956  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.last_name_length.error", nil, "", http.StatusBadRequest)
   957  	}
   958  
   959  	if data.Position != nil && utf8.RuneCountInString(*data.Position) > model.USER_POSITION_MAX_RUNES {
   960  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.position_length.error", nil, "", http.StatusBadRequest)
   961  	}
   962  
   963  	if data.Roles != nil && !model.IsValidUserRoles(*data.Roles) {
   964  		return model.NewAppError("BulkImport", "app.import.validate_user_import_data.roles_invalid.error", nil, "", http.StatusBadRequest)
   965  	}
   966  
   967  	if data.NotifyProps != nil {
   968  		if data.NotifyProps.Desktop != nil && !model.IsValidUserNotifyLevel(*data.NotifyProps.Desktop) {
   969  			return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_desktop_invalid.error", nil, "", http.StatusBadRequest)
   970  		}
   971  
   972  		if data.NotifyProps.DesktopDuration != nil && !model.IsValidNumberString(*data.NotifyProps.DesktopDuration) {
   973  			return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_desktop_duration_invalid.error", nil, "", http.StatusBadRequest)
   974  		}
   975  
   976  		if data.NotifyProps.DesktopSound != nil && !model.IsValidTrueOrFalseString(*data.NotifyProps.DesktopSound) {
   977  			return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_desktop_sound_invalid.error", nil, "", http.StatusBadRequest)
   978  		}
   979  
   980  		if data.NotifyProps.Email != nil && !model.IsValidTrueOrFalseString(*data.NotifyProps.Email) {
   981  			return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_email_invalid.error", nil, "", http.StatusBadRequest)
   982  		}
   983  
   984  		if data.NotifyProps.Mobile != nil && !model.IsValidUserNotifyLevel(*data.NotifyProps.Mobile) {
   985  			return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_mobile_invalid.error", nil, "", http.StatusBadRequest)
   986  		}
   987  
   988  		if data.NotifyProps.MobilePushStatus != nil && !model.IsValidPushStatusNotifyLevel(*data.NotifyProps.MobilePushStatus) {
   989  			return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_mobile_push_status_invalid.error", nil, "", http.StatusBadRequest)
   990  		}
   991  
   992  		if data.NotifyProps.ChannelTrigger != nil && !model.IsValidTrueOrFalseString(*data.NotifyProps.ChannelTrigger) {
   993  			return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_channel_trigger_invalid.error", nil, "", http.StatusBadRequest)
   994  		}
   995  
   996  		if data.NotifyProps.CommentsTrigger != nil && !model.IsValidCommentsNotifyLevel(*data.NotifyProps.CommentsTrigger) {
   997  			return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_comments_trigger_invalid.error", nil, "", http.StatusBadRequest)
   998  		}
   999  	}
  1000  
  1001  	if data.Teams != nil {
  1002  		return validateUserTeamsImportData(data.Teams)
  1003  	} else {
  1004  		return nil
  1005  	}
  1006  }
  1007  
  1008  func validateUserTeamsImportData(data *[]UserTeamImportData) *model.AppError {
  1009  	if data == nil {
  1010  		return nil
  1011  	}
  1012  
  1013  	for _, tdata := range *data {
  1014  		if tdata.Name == nil {
  1015  			return model.NewAppError("BulkImport", "app.import.validate_user_teams_import_data.team_name_missing.error", nil, "", http.StatusBadRequest)
  1016  		}
  1017  
  1018  		if tdata.Roles != nil && !model.IsValidUserRoles(*tdata.Roles) {
  1019  			return model.NewAppError("BulkImport", "app.import.validate_user_teams_import_data.invalid_roles.error", nil, "", http.StatusBadRequest)
  1020  		}
  1021  
  1022  		if tdata.Channels != nil {
  1023  			if err := validateUserChannelsImportData(tdata.Channels); err != nil {
  1024  				return err
  1025  			}
  1026  		}
  1027  	}
  1028  
  1029  	return nil
  1030  }
  1031  
  1032  func validateUserChannelsImportData(data *[]UserChannelImportData) *model.AppError {
  1033  	if data == nil {
  1034  		return nil
  1035  	}
  1036  
  1037  	for _, cdata := range *data {
  1038  		if cdata.Name == nil {
  1039  			return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.channel_name_missing.error", nil, "", http.StatusBadRequest)
  1040  		}
  1041  
  1042  		if cdata.Roles != nil && !model.IsValidUserRoles(*cdata.Roles) {
  1043  			return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.invalid_roles.error", nil, "", http.StatusBadRequest)
  1044  		}
  1045  
  1046  		if cdata.NotifyProps != nil {
  1047  			if cdata.NotifyProps.Desktop != nil && !model.IsChannelNotifyLevelValid(*cdata.NotifyProps.Desktop) {
  1048  				return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.invalid_notify_props_desktop.error", nil, "", http.StatusBadRequest)
  1049  			}
  1050  
  1051  			if cdata.NotifyProps.Mobile != nil && !model.IsChannelNotifyLevelValid(*cdata.NotifyProps.Mobile) {
  1052  				return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.invalid_notify_props_mobile.error", nil, "", http.StatusBadRequest)
  1053  			}
  1054  
  1055  			if cdata.NotifyProps.MarkUnread != nil && !model.IsChannelMarkUnreadLevelValid(*cdata.NotifyProps.MarkUnread) {
  1056  				return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.invalid_notify_props_mark_unread.error", nil, "", http.StatusBadRequest)
  1057  			}
  1058  		}
  1059  	}
  1060  
  1061  	return nil
  1062  }
  1063  
  1064  func (a *App) ImportReaction(data *ReactionImportData, post *model.Post, dryRun bool) *model.AppError {
  1065  	if err := validateReactionImportData(data, post.CreateAt); err != nil {
  1066  		return err
  1067  	}
  1068  
  1069  	var user *model.User
  1070  	if result := <-a.Srv.Store.User().GetByUsername(*data.User); result.Err != nil {
  1071  		return model.NewAppError("BulkImport", "app.import.import_post.user_not_found.error", map[string]interface{}{"Username": data.User}, result.Err.Error(), http.StatusBadRequest)
  1072  	} else {
  1073  		user = result.Data.(*model.User)
  1074  	}
  1075  	reaction := &model.Reaction{
  1076  		UserId:    user.Id,
  1077  		PostId:    post.Id,
  1078  		EmojiName: *data.EmojiName,
  1079  		CreateAt:  *data.CreateAt,
  1080  	}
  1081  	if result := <-a.Srv.Store.Reaction().Save(reaction); result.Err != nil {
  1082  		return result.Err
  1083  	}
  1084  	return nil
  1085  }
  1086  
  1087  func (a *App) ImportReply(data *ReplyImportData, post *model.Post, dryRun bool) *model.AppError {
  1088  	if err := validateReplyImportData(data, post.CreateAt, a.MaxPostSize()); err != nil {
  1089  		return err
  1090  	}
  1091  
  1092  	var user *model.User
  1093  	if result := <-a.Srv.Store.User().GetByUsername(*data.User); result.Err != nil {
  1094  		return model.NewAppError("BulkImport", "app.import.import_post.user_not_found.error", map[string]interface{}{"Username": data.User}, result.Err.Error(), http.StatusBadRequest)
  1095  	} else {
  1096  		user = result.Data.(*model.User)
  1097  	}
  1098  
  1099  	// Check if this post already exists.
  1100  	var replies []*model.Post
  1101  	if result := <-a.Srv.Store.Post().GetPostsCreatedAt(post.ChannelId, *data.CreateAt); result.Err != nil {
  1102  		return result.Err
  1103  	} else {
  1104  		replies = result.Data.([]*model.Post)
  1105  	}
  1106  
  1107  	var reply *model.Post
  1108  	for _, r := range replies {
  1109  		if r.Message == *data.Message {
  1110  			reply = r
  1111  			break
  1112  		}
  1113  	}
  1114  
  1115  	if reply == nil {
  1116  		reply = &model.Post{}
  1117  	}
  1118  	reply.UserId = user.Id
  1119  	reply.ChannelId = post.ChannelId
  1120  	reply.ParentId = post.Id
  1121  	reply.RootId = post.Id
  1122  	reply.Message = *data.Message
  1123  	reply.CreateAt = *data.CreateAt
  1124  
  1125  	if reply.Id == "" {
  1126  		if result := <-a.Srv.Store.Post().Save(reply); result.Err != nil {
  1127  			return result.Err
  1128  		}
  1129  	} else {
  1130  		if result := <-a.Srv.Store.Post().Overwrite(reply); result.Err != nil {
  1131  			return result.Err
  1132  		}
  1133  	}
  1134  	return nil
  1135  }
  1136  
  1137  func (a *App) ImportPost(data *PostImportData, dryRun bool) *model.AppError {
  1138  	if err := validatePostImportData(data, a.MaxPostSize()); err != nil {
  1139  		return err
  1140  	}
  1141  
  1142  	// If this is a Dry Run, do not continue any further.
  1143  	if dryRun {
  1144  		return nil
  1145  	}
  1146  
  1147  	var team *model.Team
  1148  	if result := <-a.Srv.Store.Team().GetByName(*data.Team); result.Err != nil {
  1149  		return model.NewAppError("BulkImport", "app.import.import_post.team_not_found.error", map[string]interface{}{"TeamName": *data.Team}, result.Err.Error(), http.StatusBadRequest)
  1150  	} else {
  1151  		team = result.Data.(*model.Team)
  1152  	}
  1153  
  1154  	var channel *model.Channel
  1155  	if result := <-a.Srv.Store.Channel().GetByName(team.Id, *data.Channel, false); result.Err != nil {
  1156  		return model.NewAppError("BulkImport", "app.import.import_post.channel_not_found.error", map[string]interface{}{"ChannelName": *data.Channel}, result.Err.Error(), http.StatusBadRequest)
  1157  	} else {
  1158  		channel = result.Data.(*model.Channel)
  1159  	}
  1160  
  1161  	var user *model.User
  1162  	if result := <-a.Srv.Store.User().GetByUsername(*data.User); result.Err != nil {
  1163  		return model.NewAppError("BulkImport", "app.import.import_post.user_not_found.error", map[string]interface{}{"Username": *data.User}, result.Err.Error(), http.StatusBadRequest)
  1164  	} else {
  1165  		user = result.Data.(*model.User)
  1166  	}
  1167  
  1168  	// Check if this post already exists.
  1169  	var posts []*model.Post
  1170  	if result := <-a.Srv.Store.Post().GetPostsCreatedAt(channel.Id, *data.CreateAt); result.Err != nil {
  1171  		return result.Err
  1172  	} else {
  1173  		posts = result.Data.([]*model.Post)
  1174  	}
  1175  
  1176  	var post *model.Post
  1177  	for _, p := range posts {
  1178  		if p.Message == *data.Message {
  1179  			post = p
  1180  			break
  1181  		}
  1182  	}
  1183  
  1184  	if post == nil {
  1185  		post = &model.Post{}
  1186  	}
  1187  
  1188  	post.ChannelId = channel.Id
  1189  	post.Message = *data.Message
  1190  	post.UserId = user.Id
  1191  	post.CreateAt = *data.CreateAt
  1192  
  1193  	post.Hashtags, _ = model.ParseHashtags(post.Message)
  1194  
  1195  	if post.Id == "" {
  1196  		if result := <-a.Srv.Store.Post().Save(post); result.Err != nil {
  1197  			return result.Err
  1198  		}
  1199  	} else {
  1200  		if result := <-a.Srv.Store.Post().Overwrite(post); result.Err != nil {
  1201  			return result.Err
  1202  		}
  1203  	}
  1204  
  1205  	if data.FlaggedBy != nil {
  1206  		var preferences model.Preferences
  1207  
  1208  		for _, username := range *data.FlaggedBy {
  1209  			var user *model.User
  1210  
  1211  			if result := <-a.Srv.Store.User().GetByUsername(username); result.Err != nil {
  1212  				return model.NewAppError("BulkImport", "app.import.import_post.user_not_found.error", map[string]interface{}{"Username": username}, result.Err.Error(), http.StatusBadRequest)
  1213  			} else {
  1214  				user = result.Data.(*model.User)
  1215  			}
  1216  
  1217  			preferences = append(preferences, model.Preference{
  1218  				UserId:   user.Id,
  1219  				Category: model.PREFERENCE_CATEGORY_FLAGGED_POST,
  1220  				Name:     post.Id,
  1221  				Value:    "true",
  1222  			})
  1223  		}
  1224  
  1225  		if len(preferences) > 0 {
  1226  			if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil {
  1227  				return model.NewAppError("BulkImport", "app.import.import_post.save_preferences.error", nil, result.Err.Error(), http.StatusInternalServerError)
  1228  			}
  1229  		}
  1230  	}
  1231  
  1232  	if data.Reactions != nil {
  1233  		for _, reaction := range *data.Reactions {
  1234  			if err := a.ImportReaction(&reaction, post, dryRun); err != nil {
  1235  				return err
  1236  			}
  1237  		}
  1238  	}
  1239  
  1240  	if data.Replies != nil {
  1241  		for _, reply := range *data.Replies {
  1242  			if err := a.ImportReply(&reply, post, dryRun); err != nil {
  1243  				return err
  1244  			}
  1245  		}
  1246  	}
  1247  
  1248  	return nil
  1249  }
  1250  
  1251  func validateReactionImportData(data *ReactionImportData, parentCreateAt int64) *model.AppError {
  1252  	if data.User == nil {
  1253  		return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.user_missing.error", nil, "", http.StatusBadRequest)
  1254  	}
  1255  
  1256  	if data.EmojiName == nil {
  1257  		return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.emoji_name_missing.error", nil, "", http.StatusBadRequest)
  1258  	} else if utf8.RuneCountInString(*data.EmojiName) > model.EMOJI_NAME_MAX_LENGTH {
  1259  		return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.emoji_name_length.error", nil, "", http.StatusBadRequest)
  1260  	}
  1261  
  1262  	if data.CreateAt == nil {
  1263  		return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.create_at_missing.error", nil, "", http.StatusBadRequest)
  1264  	} else if *data.CreateAt == 0 {
  1265  		return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.create_at_zero.error", nil, "", http.StatusBadRequest)
  1266  	} else if *data.CreateAt < parentCreateAt {
  1267  		return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.create_at_before_parent.error", nil, "", http.StatusBadRequest)
  1268  	}
  1269  
  1270  	return nil
  1271  }
  1272  
  1273  func validateReplyImportData(data *ReplyImportData, parentCreateAt int64, maxPostSize int) *model.AppError {
  1274  	if data.User == nil {
  1275  		return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.user_missing.error", nil, "", http.StatusBadRequest)
  1276  	}
  1277  
  1278  	if data.Message == nil {
  1279  		return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.message_missing.error", nil, "", http.StatusBadRequest)
  1280  	} else if utf8.RuneCountInString(*data.Message) > maxPostSize {
  1281  		return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.message_length.error", nil, "", http.StatusBadRequest)
  1282  	}
  1283  
  1284  	if data.CreateAt == nil {
  1285  		return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.create_at_missing.error", nil, "", http.StatusBadRequest)
  1286  	} else if *data.CreateAt == 0 {
  1287  		return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.create_at_zero.error", nil, "", http.StatusBadRequest)
  1288  	} else if *data.CreateAt < parentCreateAt {
  1289  		return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.create_at_before_parent.error", nil, "", http.StatusBadRequest)
  1290  	}
  1291  
  1292  	return nil
  1293  }
  1294  
  1295  func validatePostImportData(data *PostImportData, maxPostSize int) *model.AppError {
  1296  	if data.Team == nil {
  1297  		return model.NewAppError("BulkImport", "app.import.validate_post_import_data.team_missing.error", nil, "", http.StatusBadRequest)
  1298  	}
  1299  
  1300  	if data.Channel == nil {
  1301  		return model.NewAppError("BulkImport", "app.import.validate_post_import_data.channel_missing.error", nil, "", http.StatusBadRequest)
  1302  	}
  1303  
  1304  	if data.User == nil {
  1305  		return model.NewAppError("BulkImport", "app.import.validate_post_import_data.user_missing.error", nil, "", http.StatusBadRequest)
  1306  	}
  1307  
  1308  	if data.Message == nil {
  1309  		return model.NewAppError("BulkImport", "app.import.validate_post_import_data.message_missing.error", nil, "", http.StatusBadRequest)
  1310  	} else if utf8.RuneCountInString(*data.Message) > maxPostSize {
  1311  		return model.NewAppError("BulkImport", "app.import.validate_post_import_data.message_length.error", nil, "", http.StatusBadRequest)
  1312  	}
  1313  
  1314  	if data.CreateAt == nil {
  1315  		return model.NewAppError("BulkImport", "app.import.validate_post_import_data.create_at_missing.error", nil, "", http.StatusBadRequest)
  1316  	} else if *data.CreateAt == 0 {
  1317  		return model.NewAppError("BulkImport", "app.import.validate_post_import_data.create_at_zero.error", nil, "", http.StatusBadRequest)
  1318  	}
  1319  
  1320  	if data.Reactions != nil {
  1321  		for _, reaction := range *data.Reactions {
  1322  			validateReactionImportData(&reaction, *data.CreateAt)
  1323  		}
  1324  	}
  1325  
  1326  	if data.Replies != nil {
  1327  		for _, reply := range *data.Replies {
  1328  			validateReplyImportData(&reply, *data.CreateAt, maxPostSize)
  1329  		}
  1330  	}
  1331  
  1332  	return nil
  1333  }
  1334  
  1335  func (a *App) ImportDirectChannel(data *DirectChannelImportData, dryRun bool) *model.AppError {
  1336  	if err := validateDirectChannelImportData(data); err != nil {
  1337  		return err
  1338  	}
  1339  
  1340  	// If this is a Dry Run, do not continue any further.
  1341  	if dryRun {
  1342  		return nil
  1343  	}
  1344  
  1345  	var userIds []string
  1346  	userMap := make(map[string]string)
  1347  	for _, username := range *data.Members {
  1348  		if result := <-a.Srv.Store.User().GetByUsername(username); result.Err == nil {
  1349  			user := result.Data.(*model.User)
  1350  			userIds = append(userIds, user.Id)
  1351  			userMap[username] = user.Id
  1352  		} else {
  1353  			return model.NewAppError("BulkImport", "app.import.import_direct_channel.member_not_found.error", nil, result.Err.Error(), http.StatusBadRequest)
  1354  		}
  1355  	}
  1356  
  1357  	var channel *model.Channel
  1358  
  1359  	if len(userIds) == 2 {
  1360  		ch, err := a.createDirectChannel(userIds[0], userIds[1])
  1361  		if err != nil && err.Id != store.CHANNEL_EXISTS_ERROR {
  1362  			return model.NewAppError("BulkImport", "app.import.import_direct_channel.create_direct_channel.error", nil, err.Error(), http.StatusBadRequest)
  1363  		} else {
  1364  			channel = ch
  1365  		}
  1366  	} else {
  1367  		ch, err := a.createGroupChannel(userIds, userIds[0])
  1368  		if err != nil && err.Id != store.CHANNEL_EXISTS_ERROR {
  1369  			return model.NewAppError("BulkImport", "app.import.import_direct_channel.create_group_channel.error", nil, err.Error(), http.StatusBadRequest)
  1370  		} else {
  1371  			channel = ch
  1372  		}
  1373  	}
  1374  
  1375  	var preferences model.Preferences
  1376  
  1377  	for _, userId := range userIds {
  1378  		preferences = append(preferences, model.Preference{
  1379  			UserId:   userId,
  1380  			Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW,
  1381  			Name:     channel.Id,
  1382  			Value:    "true",
  1383  		})
  1384  	}
  1385  
  1386  	if data.FavoritedBy != nil {
  1387  		for _, favoriter := range *data.FavoritedBy {
  1388  			preferences = append(preferences, model.Preference{
  1389  				UserId:   userMap[favoriter],
  1390  				Category: model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL,
  1391  				Name:     channel.Id,
  1392  				Value:    "true",
  1393  			})
  1394  		}
  1395  	}
  1396  
  1397  	if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil {
  1398  		result.Err.StatusCode = http.StatusBadRequest
  1399  		return result.Err
  1400  	}
  1401  
  1402  	if data.Header != nil {
  1403  		channel.Header = *data.Header
  1404  		if result := <-a.Srv.Store.Channel().Update(channel); result.Err != nil {
  1405  			return model.NewAppError("BulkImport", "app.import.import_direct_channel.update_header_failed.error", nil, result.Err.Error(), http.StatusBadRequest)
  1406  		}
  1407  	}
  1408  
  1409  	return nil
  1410  }
  1411  
  1412  func validateDirectChannelImportData(data *DirectChannelImportData) *model.AppError {
  1413  	if data.Members == nil {
  1414  		return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_required.error", nil, "", http.StatusBadRequest)
  1415  	}
  1416  
  1417  	if len(*data.Members) != 2 {
  1418  		if len(*data.Members) < model.CHANNEL_GROUP_MIN_USERS {
  1419  			return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_too_few.error", nil, "", http.StatusBadRequest)
  1420  		} else if len(*data.Members) > model.CHANNEL_GROUP_MAX_USERS {
  1421  			return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_too_many.error", nil, "", http.StatusBadRequest)
  1422  		}
  1423  	}
  1424  
  1425  	if data.Header != nil && utf8.RuneCountInString(*data.Header) > model.CHANNEL_HEADER_MAX_RUNES {
  1426  		return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.header_length.error", nil, "", http.StatusBadRequest)
  1427  	}
  1428  
  1429  	if data.FavoritedBy != nil {
  1430  		for _, favoriter := range *data.FavoritedBy {
  1431  			found := false
  1432  			for _, member := range *data.Members {
  1433  				if favoriter == member {
  1434  					found = true
  1435  					break
  1436  				}
  1437  			}
  1438  			if !found {
  1439  				return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.unknown_favoriter.error", map[string]interface{}{"Username": favoriter}, "", http.StatusBadRequest)
  1440  			}
  1441  		}
  1442  	}
  1443  
  1444  	return nil
  1445  }
  1446  
  1447  func (a *App) ImportDirectPost(data *DirectPostImportData, dryRun bool) *model.AppError {
  1448  	if err := validateDirectPostImportData(data, a.MaxPostSize()); err != nil {
  1449  		return err
  1450  	}
  1451  
  1452  	// If this is a Dry Run, do not continue any further.
  1453  	if dryRun {
  1454  		return nil
  1455  	}
  1456  
  1457  	var userIds []string
  1458  	for _, username := range *data.ChannelMembers {
  1459  		if result := <-a.Srv.Store.User().GetByUsername(username); result.Err == nil {
  1460  			user := result.Data.(*model.User)
  1461  			userIds = append(userIds, user.Id)
  1462  		} else {
  1463  			return model.NewAppError("BulkImport", "app.import.import_direct_post.channel_member_not_found.error", nil, result.Err.Error(), http.StatusBadRequest)
  1464  		}
  1465  	}
  1466  
  1467  	var channel *model.Channel
  1468  	if len(userIds) == 2 {
  1469  		ch, err := a.createDirectChannel(userIds[0], userIds[1])
  1470  		if err != nil && err.Id != store.CHANNEL_EXISTS_ERROR {
  1471  			return model.NewAppError("BulkImport", "app.import.import_direct_post.create_direct_channel.error", nil, err.Error(), http.StatusBadRequest)
  1472  		} else {
  1473  			channel = ch
  1474  		}
  1475  	} else {
  1476  		ch, err := a.createGroupChannel(userIds, userIds[0])
  1477  		if err != nil && err.Id != store.CHANNEL_EXISTS_ERROR {
  1478  			return model.NewAppError("BulkImport", "app.import.import_direct_post.create_group_channel.error", nil, err.Error(), http.StatusBadRequest)
  1479  		} else {
  1480  			channel = ch
  1481  		}
  1482  	}
  1483  
  1484  	var user *model.User
  1485  	if result := <-a.Srv.Store.User().GetByUsername(*data.User); result.Err != nil {
  1486  		return model.NewAppError("BulkImport", "app.import.import_direct_post.user_not_found.error", map[string]interface{}{"Username": *data.User}, "", http.StatusBadRequest)
  1487  	} else {
  1488  		user = result.Data.(*model.User)
  1489  	}
  1490  
  1491  	// Check if this post already exists.
  1492  	var posts []*model.Post
  1493  	if result := <-a.Srv.Store.Post().GetPostsCreatedAt(channel.Id, *data.CreateAt); result.Err != nil {
  1494  		return result.Err
  1495  	} else {
  1496  		posts = result.Data.([]*model.Post)
  1497  	}
  1498  
  1499  	var post *model.Post
  1500  	for _, p := range posts {
  1501  		if p.Message == *data.Message {
  1502  			post = p
  1503  			break
  1504  		}
  1505  	}
  1506  
  1507  	if post == nil {
  1508  		post = &model.Post{}
  1509  	}
  1510  
  1511  	post.ChannelId = channel.Id
  1512  	post.Message = *data.Message
  1513  	post.UserId = user.Id
  1514  	post.CreateAt = *data.CreateAt
  1515  
  1516  	post.Hashtags, _ = model.ParseHashtags(post.Message)
  1517  
  1518  	if post.Id == "" {
  1519  		if result := <-a.Srv.Store.Post().Save(post); result.Err != nil {
  1520  			return result.Err
  1521  		}
  1522  	} else {
  1523  		if result := <-a.Srv.Store.Post().Overwrite(post); result.Err != nil {
  1524  			return result.Err
  1525  		}
  1526  	}
  1527  
  1528  	if data.FlaggedBy != nil {
  1529  		var preferences model.Preferences
  1530  
  1531  		for _, username := range *data.FlaggedBy {
  1532  			var user *model.User
  1533  
  1534  			if result := <-a.Srv.Store.User().GetByUsername(username); result.Err != nil {
  1535  				return model.NewAppError("BulkImport", "app.import.import_direct_post.user_not_found.error", map[string]interface{}{"Username": username}, "", http.StatusBadRequest)
  1536  			} else {
  1537  				user = result.Data.(*model.User)
  1538  			}
  1539  
  1540  			preferences = append(preferences, model.Preference{
  1541  				UserId:   user.Id,
  1542  				Category: model.PREFERENCE_CATEGORY_FLAGGED_POST,
  1543  				Name:     post.Id,
  1544  				Value:    "true",
  1545  			})
  1546  		}
  1547  
  1548  		if len(preferences) > 0 {
  1549  			if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil {
  1550  				return model.NewAppError("BulkImport", "app.import.import_direct_post.save_preferences.error", nil, result.Err.Error(), http.StatusInternalServerError)
  1551  			}
  1552  		}
  1553  	}
  1554  
  1555  	if data.Reactions != nil {
  1556  		for _, reaction := range *data.Reactions {
  1557  			if err := a.ImportReaction(&reaction, post, dryRun); err != nil {
  1558  				return err
  1559  			}
  1560  		}
  1561  	}
  1562  
  1563  	if data.Replies != nil {
  1564  		for _, reply := range *data.Replies {
  1565  			if err := a.ImportReply(&reply, post, dryRun); err != nil {
  1566  				return err
  1567  			}
  1568  		}
  1569  	}
  1570  
  1571  	return nil
  1572  }
  1573  
  1574  func validateDirectPostImportData(data *DirectPostImportData, maxPostSize int) *model.AppError {
  1575  	if data.ChannelMembers == nil {
  1576  		return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.channel_members_required.error", nil, "", http.StatusBadRequest)
  1577  	}
  1578  
  1579  	if len(*data.ChannelMembers) != 2 {
  1580  		if len(*data.ChannelMembers) < model.CHANNEL_GROUP_MIN_USERS {
  1581  			return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.channel_members_too_few.error", nil, "", http.StatusBadRequest)
  1582  		} else if len(*data.ChannelMembers) > model.CHANNEL_GROUP_MAX_USERS {
  1583  			return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.channel_members_too_many.error", nil, "", http.StatusBadRequest)
  1584  		}
  1585  	}
  1586  
  1587  	if data.User == nil {
  1588  		return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.user_missing.error", nil, "", http.StatusBadRequest)
  1589  	}
  1590  
  1591  	if data.Message == nil {
  1592  		return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.message_missing.error", nil, "", http.StatusBadRequest)
  1593  	} else if utf8.RuneCountInString(*data.Message) > maxPostSize {
  1594  		return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.message_length.error", nil, "", http.StatusBadRequest)
  1595  	}
  1596  
  1597  	if data.CreateAt == nil {
  1598  		return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.create_at_missing.error", nil, "", http.StatusBadRequest)
  1599  	} else if *data.CreateAt == 0 {
  1600  		return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.create_at_zero.error", nil, "", http.StatusBadRequest)
  1601  	}
  1602  
  1603  	if data.FlaggedBy != nil {
  1604  		for _, flagger := range *data.FlaggedBy {
  1605  			found := false
  1606  			for _, member := range *data.ChannelMembers {
  1607  				if flagger == member {
  1608  					found = true
  1609  					break
  1610  				}
  1611  			}
  1612  			if !found {
  1613  				return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.unknown_flagger.error", map[string]interface{}{"Username": flagger}, "", http.StatusBadRequest)
  1614  			}
  1615  		}
  1616  	}
  1617  
  1618  	if data.Reactions != nil {
  1619  		for _, reaction := range *data.Reactions {
  1620  			validateReactionImportData(&reaction, *data.CreateAt)
  1621  		}
  1622  	}
  1623  
  1624  	if data.Replies != nil {
  1625  		for _, reply := range *data.Replies {
  1626  			validateReplyImportData(&reply, *data.CreateAt, maxPostSize)
  1627  		}
  1628  	}
  1629  
  1630  	return nil
  1631  }
  1632  
  1633  //
  1634  // -- Old SlackImport Functions --
  1635  // Import functions are sutible for entering posts and users into the database without
  1636  // some of the usual checks. (IsValid is still run)
  1637  //
  1638  
  1639  func (a *App) OldImportPost(post *model.Post) {
  1640  	// Workaround for empty messages, which may be the case if they are webhook posts.
  1641  	firstIteration := true
  1642  	maxPostSize := a.MaxPostSize()
  1643  	for messageRuneCount := utf8.RuneCountInString(post.Message); messageRuneCount > 0 || firstIteration; messageRuneCount = utf8.RuneCountInString(post.Message) {
  1644  		firstIteration = false
  1645  		var remainder string
  1646  		if messageRuneCount > maxPostSize {
  1647  			remainder = string(([]rune(post.Message))[maxPostSize:])
  1648  			post.Message = truncateRunes(post.Message, maxPostSize)
  1649  		} else {
  1650  			remainder = ""
  1651  		}
  1652  
  1653  		post.Hashtags, _ = model.ParseHashtags(post.Message)
  1654  
  1655  		if result := <-a.Srv.Store.Post().Save(post); result.Err != nil {
  1656  			mlog.Debug(fmt.Sprintf("Error saving post. user=%v, message=%v", post.UserId, post.Message))
  1657  		}
  1658  
  1659  		for _, fileId := range post.FileIds {
  1660  			if result := <-a.Srv.Store.FileInfo().AttachToPost(fileId, post.Id); result.Err != nil {
  1661  				mlog.Error(fmt.Sprintf("Error attaching files to post. postId=%v, fileIds=%v, message=%v", post.Id, post.FileIds, result.Err), mlog.String("post_id", post.Id))
  1662  			}
  1663  		}
  1664  
  1665  		post.Id = ""
  1666  		post.CreateAt++
  1667  		post.Message = remainder
  1668  	}
  1669  }
  1670  
  1671  func (a *App) OldImportUser(team *model.Team, user *model.User) *model.User {
  1672  	user.MakeNonNil()
  1673  
  1674  	user.Roles = model.SYSTEM_USER_ROLE_ID
  1675  
  1676  	if result := <-a.Srv.Store.User().Save(user); result.Err != nil {
  1677  		mlog.Error(fmt.Sprintf("Error saving user. err=%v", result.Err))
  1678  		return nil
  1679  	} else {
  1680  		ruser := result.Data.(*model.User)
  1681  
  1682  		if cresult := <-a.Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil {
  1683  			mlog.Error(fmt.Sprintf("Failed to set email verified err=%v", cresult.Err))
  1684  		}
  1685  
  1686  		if err := a.JoinUserToTeam(team, user, ""); err != nil {
  1687  			mlog.Error(fmt.Sprintf("Failed to join team when importing err=%v", err))
  1688  		}
  1689  
  1690  		return ruser
  1691  	}
  1692  }
  1693  
  1694  func (a *App) OldImportChannel(channel *model.Channel) *model.Channel {
  1695  	if result := <-a.Srv.Store.Channel().Save(channel, *a.Config().TeamSettings.MaxChannelsPerTeam); result.Err != nil {
  1696  		return nil
  1697  	} else {
  1698  		sc := result.Data.(*model.Channel)
  1699  
  1700  		return sc
  1701  	}
  1702  }
  1703  
  1704  func (a *App) OldImportFile(timestamp time.Time, file io.Reader, teamId string, channelId string, userId string, fileName string) (*model.FileInfo, error) {
  1705  	buf := bytes.NewBuffer(nil)
  1706  	io.Copy(buf, file)
  1707  	data := buf.Bytes()
  1708  
  1709  	fileInfo, err := a.DoUploadFile(timestamp, teamId, channelId, userId, fileName, data)
  1710  	if err != nil {
  1711  		return nil, err
  1712  	}
  1713  
  1714  	img, width, height := prepareImage(data)
  1715  	if img != nil {
  1716  		a.generateThumbnailImage(*img, fileInfo.ThumbnailPath, width, height)
  1717  		a.generatePreviewImage(*img, fileInfo.PreviewPath, width)
  1718  	}
  1719  
  1720  	return fileInfo, nil
  1721  }
  1722  
  1723  func (a *App) OldImportIncomingWebhookPost(post *model.Post, props model.StringInterface) {
  1724  	linkWithTextRegex := regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`)
  1725  	post.Message = linkWithTextRegex.ReplaceAllString(post.Message, "[${2}](${1})")
  1726  
  1727  	post.AddProp("from_webhook", "true")
  1728  
  1729  	if _, ok := props["override_username"]; !ok {
  1730  		post.AddProp("override_username", model.DEFAULT_WEBHOOK_USERNAME)
  1731  	}
  1732  
  1733  	if len(props) > 0 {
  1734  		for key, val := range props {
  1735  			if key == "attachments" {
  1736  				if attachments, success := val.([]*model.SlackAttachment); success {
  1737  					parseSlackAttachment(post, attachments)
  1738  				}
  1739  			} else if key != "from_webhook" {
  1740  				post.AddProp(key, val)
  1741  			}
  1742  		}
  1743  	}
  1744  
  1745  	a.OldImportPost(post)
  1746  }