github.com/psyb0t/mattermost-server@v4.6.1-0.20180125161845-5503a1351abf+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 err := a.ImportUserChannels(user, team, member, tdata.Channels); err != nil { 821 return err 822 } 823 } 824 825 return nil 826 } 827 828 func (a *App) ImportUserChannels(user *model.User, team *model.Team, teamMember *model.TeamMember, data *[]UserChannelImportData) *model.AppError { 829 if data == nil { 830 return nil 831 } 832 833 var preferences model.Preferences 834 835 // Loop through all channels. 836 for _, cdata := range *data { 837 channel, err := a.GetChannelByName(*cdata.Name, team.Id) 838 if err != nil { 839 return err 840 } 841 842 var roles string 843 if cdata.Roles == nil { 844 roles = model.CHANNEL_USER_ROLE_ID 845 } else { 846 roles = *cdata.Roles 847 } 848 849 var member *model.ChannelMember 850 member, err = a.GetChannelMember(channel.Id, user.Id) 851 if err != nil { 852 member, err = a.addUserToChannel(user, channel, teamMember) 853 if err != nil { 854 return err 855 } 856 } 857 858 if member.Roles != roles { 859 if _, err := a.UpdateChannelMemberRoles(channel.Id, user.Id, roles); err != nil { 860 return err 861 } 862 } 863 864 if cdata.NotifyProps != nil { 865 notifyProps := member.NotifyProps 866 867 if cdata.NotifyProps.Desktop != nil { 868 notifyProps[model.DESKTOP_NOTIFY_PROP] = *cdata.NotifyProps.Desktop 869 } 870 871 if cdata.NotifyProps.Mobile != nil { 872 notifyProps[model.PUSH_NOTIFY_PROP] = *cdata.NotifyProps.Mobile 873 } 874 875 if cdata.NotifyProps.MarkUnread != nil { 876 notifyProps[model.MARK_UNREAD_NOTIFY_PROP] = *cdata.NotifyProps.MarkUnread 877 } 878 879 if _, err := a.UpdateChannelMemberNotifyProps(notifyProps, channel.Id, user.Id); err != nil { 880 return err 881 } 882 } 883 884 if cdata.Favorite != nil && *cdata.Favorite { 885 preferences = append(preferences, model.Preference{ 886 UserId: user.Id, 887 Category: model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL, 888 Name: channel.Id, 889 Value: "true", 890 }) 891 } 892 } 893 894 if len(preferences) > 0 { 895 if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil { 896 return model.NewAppError("BulkImport", "app.import.import_user_channels.save_preferences.error", nil, "", http.StatusInternalServerError) 897 } 898 } 899 900 return nil 901 } 902 903 func validateUserImportData(data *UserImportData) *model.AppError { 904 if data.ProfileImage != nil { 905 if _, err := os.Stat(*data.ProfileImage); os.IsNotExist(err) { 906 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.profile_image.error", nil, "", http.StatusBadRequest) 907 } 908 } 909 910 if data.Username == nil { 911 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.username_missing.error", nil, "", http.StatusBadRequest) 912 } else if !model.IsValidUsername(*data.Username) { 913 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.username_invalid.error", nil, "", http.StatusBadRequest) 914 } 915 916 if data.Email == nil { 917 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.email_missing.error", nil, "", http.StatusBadRequest) 918 } else if len(*data.Email) == 0 || len(*data.Email) > model.USER_EMAIL_MAX_LENGTH { 919 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.email_length.error", nil, "", http.StatusBadRequest) 920 } 921 922 if data.AuthService != nil && len(*data.AuthService) == 0 { 923 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.auth_service_length.error", nil, "", http.StatusBadRequest) 924 } 925 926 if data.AuthData != nil && data.Password != nil { 927 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.auth_data_and_password.error", nil, "", http.StatusBadRequest) 928 } 929 930 if data.AuthData != nil && len(*data.AuthData) > model.USER_AUTH_DATA_MAX_LENGTH { 931 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.auth_data_length.error", nil, "", http.StatusBadRequest) 932 } 933 934 if data.Password != nil && len(*data.Password) == 0 { 935 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.pasword_length.error", nil, "", http.StatusBadRequest) 936 } 937 938 if data.Password != nil && len(*data.Password) > model.USER_PASSWORD_MAX_LENGTH { 939 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.password_length.error", nil, "", http.StatusBadRequest) 940 } 941 942 if data.Nickname != nil && utf8.RuneCountInString(*data.Nickname) > model.USER_NICKNAME_MAX_RUNES { 943 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.nickname_length.error", nil, "", http.StatusBadRequest) 944 } 945 946 if data.FirstName != nil && utf8.RuneCountInString(*data.FirstName) > model.USER_FIRST_NAME_MAX_RUNES { 947 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.first_name_length.error", nil, "", http.StatusBadRequest) 948 } 949 950 if data.LastName != nil && utf8.RuneCountInString(*data.LastName) > model.USER_LAST_NAME_MAX_RUNES { 951 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.last_name_length.error", nil, "", http.StatusBadRequest) 952 } 953 954 if data.Position != nil && utf8.RuneCountInString(*data.Position) > model.USER_POSITION_MAX_RUNES { 955 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.position_length.error", nil, "", http.StatusBadRequest) 956 } 957 958 if data.Roles != nil && !model.IsValidUserRoles(*data.Roles) { 959 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.roles_invalid.error", nil, "", http.StatusBadRequest) 960 } 961 962 if data.NotifyProps != nil { 963 if data.NotifyProps.Desktop != nil && !model.IsValidUserNotifyLevel(*data.NotifyProps.Desktop) { 964 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_desktop_invalid.error", nil, "", http.StatusBadRequest) 965 } 966 967 if data.NotifyProps.DesktopDuration != nil && !model.IsValidNumberString(*data.NotifyProps.DesktopDuration) { 968 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_desktop_duration_invalid.error", nil, "", http.StatusBadRequest) 969 } 970 971 if data.NotifyProps.DesktopSound != nil && !model.IsValidTrueOrFalseString(*data.NotifyProps.DesktopSound) { 972 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_desktop_sound_invalid.error", nil, "", http.StatusBadRequest) 973 } 974 975 if data.NotifyProps.Email != nil && !model.IsValidTrueOrFalseString(*data.NotifyProps.Email) { 976 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_email_invalid.error", nil, "", http.StatusBadRequest) 977 } 978 979 if data.NotifyProps.Mobile != nil && !model.IsValidUserNotifyLevel(*data.NotifyProps.Mobile) { 980 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_mobile_invalid.error", nil, "", http.StatusBadRequest) 981 } 982 983 if data.NotifyProps.MobilePushStatus != nil && !model.IsValidPushStatusNotifyLevel(*data.NotifyProps.MobilePushStatus) { 984 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_mobile_push_status_invalid.error", nil, "", http.StatusBadRequest) 985 } 986 987 if data.NotifyProps.ChannelTrigger != nil && !model.IsValidTrueOrFalseString(*data.NotifyProps.ChannelTrigger) { 988 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_channel_trigger_invalid.error", nil, "", http.StatusBadRequest) 989 } 990 991 if data.NotifyProps.CommentsTrigger != nil && !model.IsValidCommentsNotifyLevel(*data.NotifyProps.CommentsTrigger) { 992 return model.NewAppError("BulkImport", "app.import.validate_user_import_data.notify_props_comments_trigger_invalid.error", nil, "", http.StatusBadRequest) 993 } 994 } 995 996 if data.Teams != nil { 997 return validateUserTeamsImportData(data.Teams) 998 } else { 999 return nil 1000 } 1001 } 1002 1003 func validateUserTeamsImportData(data *[]UserTeamImportData) *model.AppError { 1004 if data == nil { 1005 return nil 1006 } 1007 1008 for _, tdata := range *data { 1009 if tdata.Name == nil { 1010 return model.NewAppError("BulkImport", "app.import.validate_user_teams_import_data.team_name_missing.error", nil, "", http.StatusBadRequest) 1011 } 1012 1013 if tdata.Roles != nil && !model.IsValidUserRoles(*tdata.Roles) { 1014 return model.NewAppError("BulkImport", "app.import.validate_user_teams_import_data.invalid_roles.error", nil, "", http.StatusBadRequest) 1015 } 1016 1017 if tdata.Channels != nil { 1018 if err := validateUserChannelsImportData(tdata.Channels); err != nil { 1019 return err 1020 } 1021 } 1022 } 1023 1024 return nil 1025 } 1026 1027 func validateUserChannelsImportData(data *[]UserChannelImportData) *model.AppError { 1028 if data == nil { 1029 return nil 1030 } 1031 1032 for _, cdata := range *data { 1033 if cdata.Name == nil { 1034 return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.channel_name_missing.error", nil, "", http.StatusBadRequest) 1035 } 1036 1037 if cdata.Roles != nil && !model.IsValidUserRoles(*cdata.Roles) { 1038 return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.invalid_roles.error", nil, "", http.StatusBadRequest) 1039 } 1040 1041 if cdata.NotifyProps != nil { 1042 if cdata.NotifyProps.Desktop != nil && !model.IsChannelNotifyLevelValid(*cdata.NotifyProps.Desktop) { 1043 return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.invalid_notify_props_desktop.error", nil, "", http.StatusBadRequest) 1044 } 1045 1046 if cdata.NotifyProps.Mobile != nil && !model.IsChannelNotifyLevelValid(*cdata.NotifyProps.Mobile) { 1047 return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.invalid_notify_props_mobile.error", nil, "", http.StatusBadRequest) 1048 } 1049 1050 if cdata.NotifyProps.MarkUnread != nil && !model.IsChannelMarkUnreadLevelValid(*cdata.NotifyProps.MarkUnread) { 1051 return model.NewAppError("BulkImport", "app.import.validate_user_channels_import_data.invalid_notify_props_mark_unread.error", nil, "", http.StatusBadRequest) 1052 } 1053 } 1054 } 1055 1056 return nil 1057 } 1058 1059 func (a *App) ImportReaction(data *ReactionImportData, post *model.Post, dryRun bool) *model.AppError { 1060 if err := validateReactionImportData(data, post.CreateAt); err != nil { 1061 return err 1062 } 1063 1064 var user *model.User 1065 if result := <-a.Srv.Store.User().GetByUsername(*data.User); result.Err != nil { 1066 return model.NewAppError("BulkImport", "app.import.import_post.user_not_found.error", map[string]interface{}{"Username": data.User}, "", http.StatusBadRequest) 1067 } else { 1068 user = result.Data.(*model.User) 1069 } 1070 reaction := &model.Reaction{ 1071 UserId: user.Id, 1072 PostId: post.Id, 1073 EmojiName: *data.EmojiName, 1074 CreateAt: *data.CreateAt, 1075 } 1076 if result := <-a.Srv.Store.Reaction().Save(reaction); result.Err != nil { 1077 return result.Err 1078 } 1079 return nil 1080 } 1081 1082 func (a *App) ImportReply(data *ReplyImportData, post *model.Post, dryRun bool) *model.AppError { 1083 if err := validateReplyImportData(data, post.CreateAt); err != nil { 1084 return err 1085 } 1086 1087 var user *model.User 1088 if result := <-a.Srv.Store.User().GetByUsername(*data.User); result.Err != nil { 1089 return model.NewAppError("BulkImport", "app.import.import_post.user_not_found.error", map[string]interface{}{"Username": data.User}, "", http.StatusBadRequest) 1090 } else { 1091 user = result.Data.(*model.User) 1092 } 1093 1094 // Check if this post already exists. 1095 var replies []*model.Post 1096 if result := <-a.Srv.Store.Post().GetPostsCreatedAt(post.ChannelId, *data.CreateAt); result.Err != nil { 1097 return result.Err 1098 } else { 1099 replies = result.Data.([]*model.Post) 1100 } 1101 1102 var reply *model.Post 1103 for _, r := range replies { 1104 if r.Message == *data.Message { 1105 reply = r 1106 break 1107 } 1108 } 1109 1110 if reply == nil { 1111 reply = &model.Post{} 1112 } 1113 reply.UserId = user.Id 1114 reply.ChannelId = post.ChannelId 1115 reply.ParentId = post.Id 1116 reply.RootId = post.Id 1117 reply.Message = *data.Message 1118 reply.CreateAt = *data.CreateAt 1119 1120 if reply.Id == "" { 1121 if result := <-a.Srv.Store.Post().Save(reply); result.Err != nil { 1122 return result.Err 1123 } 1124 } else { 1125 if result := <-a.Srv.Store.Post().Overwrite(reply); result.Err != nil { 1126 return result.Err 1127 } 1128 } 1129 return nil 1130 } 1131 1132 func (a *App) ImportPost(data *PostImportData, dryRun bool) *model.AppError { 1133 if err := validatePostImportData(data); err != nil { 1134 return err 1135 } 1136 1137 // If this is a Dry Run, do not continue any further. 1138 if dryRun { 1139 return nil 1140 } 1141 1142 var team *model.Team 1143 if result := <-a.Srv.Store.Team().GetByName(*data.Team); result.Err != nil { 1144 return model.NewAppError("BulkImport", "app.import.import_post.team_not_found.error", map[string]interface{}{"TeamName": *data.Team}, "", http.StatusBadRequest) 1145 } else { 1146 team = result.Data.(*model.Team) 1147 } 1148 1149 var channel *model.Channel 1150 if result := <-a.Srv.Store.Channel().GetByName(team.Id, *data.Channel, false); result.Err != nil { 1151 return model.NewAppError("BulkImport", "app.import.import_post.channel_not_found.error", map[string]interface{}{"ChannelName": *data.Channel}, "", http.StatusBadRequest) 1152 } else { 1153 channel = result.Data.(*model.Channel) 1154 } 1155 1156 var user *model.User 1157 if result := <-a.Srv.Store.User().GetByUsername(*data.User); result.Err != nil { 1158 return model.NewAppError("BulkImport", "app.import.import_post.user_not_found.error", map[string]interface{}{"Username": *data.User}, "", http.StatusBadRequest) 1159 } else { 1160 user = result.Data.(*model.User) 1161 } 1162 1163 // Check if this post already exists. 1164 var posts []*model.Post 1165 if result := <-a.Srv.Store.Post().GetPostsCreatedAt(channel.Id, *data.CreateAt); result.Err != nil { 1166 return result.Err 1167 } else { 1168 posts = result.Data.([]*model.Post) 1169 } 1170 1171 var post *model.Post 1172 for _, p := range posts { 1173 if p.Message == *data.Message { 1174 post = p 1175 break 1176 } 1177 } 1178 1179 if post == nil { 1180 post = &model.Post{} 1181 } 1182 1183 post.ChannelId = channel.Id 1184 post.Message = *data.Message 1185 post.UserId = user.Id 1186 post.CreateAt = *data.CreateAt 1187 1188 post.Hashtags, _ = model.ParseHashtags(post.Message) 1189 1190 if post.Id == "" { 1191 if result := <-a.Srv.Store.Post().Save(post); result.Err != nil { 1192 return result.Err 1193 } 1194 } else { 1195 if result := <-a.Srv.Store.Post().Overwrite(post); result.Err != nil { 1196 return result.Err 1197 } 1198 } 1199 1200 if data.FlaggedBy != nil { 1201 var preferences model.Preferences 1202 1203 for _, username := range *data.FlaggedBy { 1204 var user *model.User 1205 1206 if result := <-a.Srv.Store.User().GetByUsername(username); result.Err != nil { 1207 return model.NewAppError("BulkImport", "app.import.import_post.user_not_found.error", map[string]interface{}{"Username": username}, "", http.StatusBadRequest) 1208 } else { 1209 user = result.Data.(*model.User) 1210 } 1211 1212 preferences = append(preferences, model.Preference{ 1213 UserId: user.Id, 1214 Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, 1215 Name: post.Id, 1216 Value: "true", 1217 }) 1218 } 1219 1220 if len(preferences) > 0 { 1221 if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil { 1222 return model.NewAppError("BulkImport", "app.import.import_post.save_preferences.error", nil, "", http.StatusInternalServerError) 1223 } 1224 } 1225 } 1226 1227 if data.Reactions != nil { 1228 for _, reaction := range *data.Reactions { 1229 if err := a.ImportReaction(&reaction, post, dryRun); err != nil { 1230 return err 1231 } 1232 } 1233 } 1234 1235 if data.Replies != nil { 1236 for _, reply := range *data.Replies { 1237 if err := a.ImportReply(&reply, post, dryRun); err != nil { 1238 return err 1239 } 1240 } 1241 } 1242 1243 return nil 1244 } 1245 1246 func validateReactionImportData(data *ReactionImportData, parentCreateAt int64) *model.AppError { 1247 if data.User == nil { 1248 return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.user_missing.error", nil, "", http.StatusBadRequest) 1249 } 1250 1251 if data.EmojiName == nil { 1252 return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.emoji_name_missing.error", nil, "", http.StatusBadRequest) 1253 } else if utf8.RuneCountInString(*data.EmojiName) > model.EMOJI_NAME_MAX_LENGTH { 1254 return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.emoji_name_length.error", nil, "", http.StatusBadRequest) 1255 } 1256 1257 if data.CreateAt == nil { 1258 return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.create_at_missing.error", nil, "", http.StatusBadRequest) 1259 } else if *data.CreateAt == 0 { 1260 return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.create_at_zero.error", nil, "", http.StatusBadRequest) 1261 } else if *data.CreateAt < parentCreateAt { 1262 return model.NewAppError("BulkImport", "app.import.validate_reaction_import_data.create_at_before_parent.error", nil, "", http.StatusBadRequest) 1263 } 1264 1265 return nil 1266 } 1267 1268 func validateReplyImportData(data *ReplyImportData, parentCreateAt int64) *model.AppError { 1269 if data.User == nil { 1270 return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.user_missing.error", nil, "", http.StatusBadRequest) 1271 } 1272 1273 if data.Message == nil { 1274 return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.message_missing.error", nil, "", http.StatusBadRequest) 1275 } else if utf8.RuneCountInString(*data.Message) > model.POST_MESSAGE_MAX_RUNES { 1276 return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.message_length.error", nil, "", http.StatusBadRequest) 1277 } 1278 1279 if data.CreateAt == nil { 1280 return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.create_at_missing.error", nil, "", http.StatusBadRequest) 1281 } else if *data.CreateAt == 0 { 1282 return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.create_at_zero.error", nil, "", http.StatusBadRequest) 1283 } else if *data.CreateAt < parentCreateAt { 1284 return model.NewAppError("BulkImport", "app.import.validate_reply_import_data.create_at_before_parent.error", nil, "", http.StatusBadRequest) 1285 } 1286 1287 return nil 1288 } 1289 1290 func validatePostImportData(data *PostImportData) *model.AppError { 1291 if data.Team == nil { 1292 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.team_missing.error", nil, "", http.StatusBadRequest) 1293 } 1294 1295 if data.Channel == nil { 1296 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.channel_missing.error", nil, "", http.StatusBadRequest) 1297 } 1298 1299 if data.User == nil { 1300 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.user_missing.error", nil, "", http.StatusBadRequest) 1301 } 1302 1303 if data.Message == nil { 1304 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.message_missing.error", nil, "", http.StatusBadRequest) 1305 } else if utf8.RuneCountInString(*data.Message) > model.POST_MESSAGE_MAX_RUNES { 1306 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.message_length.error", nil, "", http.StatusBadRequest) 1307 } 1308 1309 if data.CreateAt == nil { 1310 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.create_at_missing.error", nil, "", http.StatusBadRequest) 1311 } else if *data.CreateAt == 0 { 1312 return model.NewAppError("BulkImport", "app.import.validate_post_import_data.create_at_zero.error", nil, "", http.StatusBadRequest) 1313 } 1314 1315 if data.Reactions != nil { 1316 for _, reaction := range *data.Reactions { 1317 validateReactionImportData(&reaction, *data.CreateAt) 1318 } 1319 } 1320 1321 if data.Replies != nil { 1322 for _, reply := range *data.Replies { 1323 validateReplyImportData(&reply, *data.CreateAt) 1324 } 1325 } 1326 1327 return nil 1328 } 1329 1330 func (a *App) ImportDirectChannel(data *DirectChannelImportData, dryRun bool) *model.AppError { 1331 if err := validateDirectChannelImportData(data); err != nil { 1332 return err 1333 } 1334 1335 // If this is a Dry Run, do not continue any further. 1336 if dryRun { 1337 return nil 1338 } 1339 1340 var userIds []string 1341 userMap := make(map[string]string) 1342 for _, username := range *data.Members { 1343 if result := <-a.Srv.Store.User().GetByUsername(username); result.Err == nil { 1344 user := result.Data.(*model.User) 1345 userIds = append(userIds, user.Id) 1346 userMap[username] = user.Id 1347 } else { 1348 return model.NewAppError("BulkImport", "app.import.import_direct_channel.member_not_found.error", nil, "", http.StatusBadRequest) 1349 } 1350 } 1351 1352 var channel *model.Channel 1353 1354 if len(userIds) == 2 { 1355 ch, err := a.createDirectChannel(userIds[0], userIds[1]) 1356 if err != nil && err.Id != store.CHANNEL_EXISTS_ERROR { 1357 return model.NewAppError("BulkImport", "app.import.import_direct_channel.create_direct_channel.error", nil, "", http.StatusBadRequest) 1358 } else { 1359 channel = ch 1360 } 1361 } else { 1362 ch, err := a.createGroupChannel(userIds, userIds[0]) 1363 if err != nil && err.Id != store.CHANNEL_EXISTS_ERROR { 1364 return model.NewAppError("BulkImport", "app.import.import_direct_channel.create_group_channel.error", nil, "", http.StatusBadRequest) 1365 } else { 1366 channel = ch 1367 } 1368 } 1369 1370 var preferences model.Preferences 1371 1372 for _, userId := range userIds { 1373 preferences = append(preferences, model.Preference{ 1374 UserId: userId, 1375 Category: model.PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW, 1376 Name: channel.Id, 1377 Value: "true", 1378 }) 1379 } 1380 1381 if data.FavoritedBy != nil { 1382 for _, favoriter := range *data.FavoritedBy { 1383 preferences = append(preferences, model.Preference{ 1384 UserId: userMap[favoriter], 1385 Category: model.PREFERENCE_CATEGORY_FAVORITE_CHANNEL, 1386 Name: channel.Id, 1387 Value: "true", 1388 }) 1389 } 1390 } 1391 1392 if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil { 1393 result.Err.StatusCode = http.StatusBadRequest 1394 return result.Err 1395 } 1396 1397 if data.Header != nil { 1398 channel.Header = *data.Header 1399 if result := <-a.Srv.Store.Channel().Update(channel); result.Err != nil { 1400 return model.NewAppError("BulkImport", "app.import.import_direct_channel.update_header_failed.error", nil, "", http.StatusBadRequest) 1401 } 1402 } 1403 1404 return nil 1405 } 1406 1407 func validateDirectChannelImportData(data *DirectChannelImportData) *model.AppError { 1408 if data.Members == nil { 1409 return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_required.error", nil, "", http.StatusBadRequest) 1410 } 1411 1412 if len(*data.Members) != 2 { 1413 if len(*data.Members) < model.CHANNEL_GROUP_MIN_USERS { 1414 return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_too_few.error", nil, "", http.StatusBadRequest) 1415 } else if len(*data.Members) > model.CHANNEL_GROUP_MAX_USERS { 1416 return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_too_many.error", nil, "", http.StatusBadRequest) 1417 } 1418 } 1419 1420 if data.Header != nil && utf8.RuneCountInString(*data.Header) > model.CHANNEL_HEADER_MAX_RUNES { 1421 return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.header_length.error", nil, "", http.StatusBadRequest) 1422 } 1423 1424 if data.FavoritedBy != nil { 1425 for _, favoriter := range *data.FavoritedBy { 1426 found := false 1427 for _, member := range *data.Members { 1428 if favoriter == member { 1429 found = true 1430 break 1431 } 1432 } 1433 if !found { 1434 return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.unknown_favoriter.error", map[string]interface{}{"Username": favoriter}, "", http.StatusBadRequest) 1435 } 1436 } 1437 } 1438 1439 return nil 1440 } 1441 1442 func (a *App) ImportDirectPost(data *DirectPostImportData, dryRun bool) *model.AppError { 1443 if err := validateDirectPostImportData(data); err != nil { 1444 return err 1445 } 1446 1447 // If this is a Dry Run, do not continue any further. 1448 if dryRun { 1449 return nil 1450 } 1451 1452 var userIds []string 1453 for _, username := range *data.ChannelMembers { 1454 if result := <-a.Srv.Store.User().GetByUsername(username); result.Err == nil { 1455 user := result.Data.(*model.User) 1456 userIds = append(userIds, user.Id) 1457 } else { 1458 return model.NewAppError("BulkImport", "app.import.import_direct_post.channel_member_not_found.error", nil, "", http.StatusBadRequest) 1459 } 1460 } 1461 1462 var channel *model.Channel 1463 if len(userIds) == 2 { 1464 ch, err := a.createDirectChannel(userIds[0], userIds[1]) 1465 if err != nil && err.Id != store.CHANNEL_EXISTS_ERROR { 1466 return model.NewAppError("BulkImport", "app.import.import_direct_post.create_direct_channel.error", nil, "", http.StatusBadRequest) 1467 } else { 1468 channel = ch 1469 } 1470 } else { 1471 ch, err := a.createGroupChannel(userIds, userIds[0]) 1472 if err != nil && err.Id != store.CHANNEL_EXISTS_ERROR { 1473 return model.NewAppError("BulkImport", "app.import.import_direct_post.create_group_channel.error", nil, "", http.StatusBadRequest) 1474 } else { 1475 channel = ch 1476 } 1477 } 1478 1479 var user *model.User 1480 if result := <-a.Srv.Store.User().GetByUsername(*data.User); result.Err != nil { 1481 return model.NewAppError("BulkImport", "app.import.import_direct_post.user_not_found.error", map[string]interface{}{"Username": *data.User}, "", http.StatusBadRequest) 1482 } else { 1483 user = result.Data.(*model.User) 1484 } 1485 1486 // Check if this post already exists. 1487 var posts []*model.Post 1488 if result := <-a.Srv.Store.Post().GetPostsCreatedAt(channel.Id, *data.CreateAt); result.Err != nil { 1489 return result.Err 1490 } else { 1491 posts = result.Data.([]*model.Post) 1492 } 1493 1494 var post *model.Post 1495 for _, p := range posts { 1496 if p.Message == *data.Message { 1497 post = p 1498 break 1499 } 1500 } 1501 1502 if post == nil { 1503 post = &model.Post{} 1504 } 1505 1506 post.ChannelId = channel.Id 1507 post.Message = *data.Message 1508 post.UserId = user.Id 1509 post.CreateAt = *data.CreateAt 1510 1511 post.Hashtags, _ = model.ParseHashtags(post.Message) 1512 1513 if post.Id == "" { 1514 if result := <-a.Srv.Store.Post().Save(post); result.Err != nil { 1515 return result.Err 1516 } 1517 } else { 1518 if result := <-a.Srv.Store.Post().Overwrite(post); result.Err != nil { 1519 return result.Err 1520 } 1521 } 1522 1523 if data.FlaggedBy != nil { 1524 var preferences model.Preferences 1525 1526 for _, username := range *data.FlaggedBy { 1527 var user *model.User 1528 1529 if result := <-a.Srv.Store.User().GetByUsername(username); result.Err != nil { 1530 return model.NewAppError("BulkImport", "app.import.import_direct_post.user_not_found.error", map[string]interface{}{"Username": username}, "", http.StatusBadRequest) 1531 } else { 1532 user = result.Data.(*model.User) 1533 } 1534 1535 preferences = append(preferences, model.Preference{ 1536 UserId: user.Id, 1537 Category: model.PREFERENCE_CATEGORY_FLAGGED_POST, 1538 Name: post.Id, 1539 Value: "true", 1540 }) 1541 } 1542 1543 if len(preferences) > 0 { 1544 if result := <-a.Srv.Store.Preference().Save(&preferences); result.Err != nil { 1545 return model.NewAppError("BulkImport", "app.import.import_direct_post.save_preferences.error", nil, "", http.StatusInternalServerError) 1546 } 1547 } 1548 } 1549 1550 if data.Reactions != nil { 1551 for _, reaction := range *data.Reactions { 1552 if err := a.ImportReaction(&reaction, post, dryRun); err != nil { 1553 return err 1554 } 1555 } 1556 } 1557 1558 if data.Replies != nil { 1559 for _, reply := range *data.Replies { 1560 if err := a.ImportReply(&reply, post, dryRun); err != nil { 1561 return err 1562 } 1563 } 1564 } 1565 1566 return nil 1567 } 1568 1569 func validateDirectPostImportData(data *DirectPostImportData) *model.AppError { 1570 if data.ChannelMembers == nil { 1571 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.channel_members_required.error", nil, "", http.StatusBadRequest) 1572 } 1573 1574 if len(*data.ChannelMembers) != 2 { 1575 if len(*data.ChannelMembers) < model.CHANNEL_GROUP_MIN_USERS { 1576 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.channel_members_too_few.error", nil, "", http.StatusBadRequest) 1577 } else if len(*data.ChannelMembers) > model.CHANNEL_GROUP_MAX_USERS { 1578 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.channel_members_too_many.error", nil, "", http.StatusBadRequest) 1579 } 1580 } 1581 1582 if data.User == nil { 1583 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.user_missing.error", nil, "", http.StatusBadRequest) 1584 } 1585 1586 if data.Message == nil { 1587 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.message_missing.error", nil, "", http.StatusBadRequest) 1588 } else if utf8.RuneCountInString(*data.Message) > model.POST_MESSAGE_MAX_RUNES { 1589 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.message_length.error", nil, "", http.StatusBadRequest) 1590 } 1591 1592 if data.CreateAt == nil { 1593 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.create_at_missing.error", nil, "", http.StatusBadRequest) 1594 } else if *data.CreateAt == 0 { 1595 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.create_at_zero.error", nil, "", http.StatusBadRequest) 1596 } 1597 1598 if data.FlaggedBy != nil { 1599 for _, flagger := range *data.FlaggedBy { 1600 found := false 1601 for _, member := range *data.ChannelMembers { 1602 if flagger == member { 1603 found = true 1604 break 1605 } 1606 } 1607 if !found { 1608 return model.NewAppError("BulkImport", "app.import.validate_direct_post_import_data.unknown_flagger.error", map[string]interface{}{"Username": flagger}, "", http.StatusBadRequest) 1609 } 1610 } 1611 } 1612 1613 if data.Reactions != nil { 1614 for _, reaction := range *data.Reactions { 1615 validateReactionImportData(&reaction, *data.CreateAt) 1616 } 1617 } 1618 1619 if data.Replies != nil { 1620 for _, reply := range *data.Replies { 1621 validateReplyImportData(&reply, *data.CreateAt) 1622 } 1623 } 1624 1625 return nil 1626 } 1627 1628 // 1629 // -- Old SlackImport Functions -- 1630 // Import functions are sutible for entering posts and users into the database without 1631 // some of the usual checks. (IsValid is still run) 1632 // 1633 1634 func (a *App) OldImportPost(post *model.Post) { 1635 // Workaround for empty messages, which may be the case if they are webhook posts. 1636 firstIteration := true 1637 for messageRuneCount := utf8.RuneCountInString(post.Message); messageRuneCount > 0 || firstIteration; messageRuneCount = utf8.RuneCountInString(post.Message) { 1638 firstIteration = false 1639 var remainder string 1640 if messageRuneCount > model.POST_MESSAGE_MAX_RUNES { 1641 remainder = string(([]rune(post.Message))[model.POST_MESSAGE_MAX_RUNES:]) 1642 post.Message = truncateRunes(post.Message, model.POST_MESSAGE_MAX_RUNES) 1643 } else { 1644 remainder = "" 1645 } 1646 1647 post.Hashtags, _ = model.ParseHashtags(post.Message) 1648 1649 if result := <-a.Srv.Store.Post().Save(post); result.Err != nil { 1650 l4g.Debug(utils.T("api.import.import_post.saving.debug"), post.UserId, post.Message) 1651 } 1652 1653 for _, fileId := range post.FileIds { 1654 if result := <-a.Srv.Store.FileInfo().AttachToPost(fileId, post.Id); result.Err != nil { 1655 l4g.Error(utils.T("api.import.import_post.attach_files.error"), post.Id, post.FileIds, result.Err) 1656 } 1657 } 1658 1659 post.Id = "" 1660 post.CreateAt++ 1661 post.Message = remainder 1662 } 1663 } 1664 1665 func (a *App) OldImportUser(team *model.Team, user *model.User) *model.User { 1666 user.MakeNonNil() 1667 1668 user.Roles = model.SYSTEM_USER_ROLE_ID 1669 1670 if result := <-a.Srv.Store.User().Save(user); result.Err != nil { 1671 l4g.Error(utils.T("api.import.import_user.saving.error"), result.Err) 1672 return nil 1673 } else { 1674 ruser := result.Data.(*model.User) 1675 1676 if cresult := <-a.Srv.Store.User().VerifyEmail(ruser.Id); cresult.Err != nil { 1677 l4g.Error(utils.T("api.import.import_user.set_email.error"), cresult.Err) 1678 } 1679 1680 if err := a.JoinUserToTeam(team, user, ""); err != nil { 1681 l4g.Error(utils.T("api.import.import_user.join_team.error"), err) 1682 } 1683 1684 return ruser 1685 } 1686 } 1687 1688 func (a *App) OldImportChannel(channel *model.Channel) *model.Channel { 1689 if result := <-a.Srv.Store.Channel().Save(channel, *a.Config().TeamSettings.MaxChannelsPerTeam); result.Err != nil { 1690 return nil 1691 } else { 1692 sc := result.Data.(*model.Channel) 1693 1694 return sc 1695 } 1696 } 1697 1698 func (a *App) OldImportFile(timestamp time.Time, file io.Reader, teamId string, channelId string, userId string, fileName string) (*model.FileInfo, error) { 1699 buf := bytes.NewBuffer(nil) 1700 io.Copy(buf, file) 1701 data := buf.Bytes() 1702 1703 fileInfo, err := a.DoUploadFile(timestamp, teamId, channelId, userId, fileName, data) 1704 if err != nil { 1705 return nil, err 1706 } 1707 1708 img, width, height := prepareImage(data) 1709 if img != nil { 1710 a.generateThumbnailImage(*img, fileInfo.ThumbnailPath, width, height) 1711 a.generatePreviewImage(*img, fileInfo.PreviewPath, width) 1712 } 1713 1714 return fileInfo, nil 1715 } 1716 1717 func (a *App) OldImportIncomingWebhookPost(post *model.Post, props model.StringInterface) { 1718 linkWithTextRegex := regexp.MustCompile(`<([^<\|]+)\|([^>]+)>`) 1719 post.Message = linkWithTextRegex.ReplaceAllString(post.Message, "[${2}](${1})") 1720 1721 post.AddProp("from_webhook", "true") 1722 1723 if _, ok := props["override_username"]; !ok { 1724 post.AddProp("override_username", model.DEFAULT_WEBHOOK_USERNAME) 1725 } 1726 1727 if len(props) > 0 { 1728 for key, val := range props { 1729 if key == "attachments" { 1730 if attachments, success := val.([]*model.SlackAttachment); success { 1731 parseSlackAttachment(post, attachments) 1732 } 1733 } else if key != "from_webhook" { 1734 post.AddProp(key, val) 1735 } 1736 } 1737 } 1738 1739 a.OldImportPost(post) 1740 }