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