github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/app/export.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  	"encoding/json"
     8  	"io"
     9  	"net/http"
    10  	"strings"
    11  
    12  	"github.com/vnforks/kid/v5/mlog"
    13  	"github.com/vnforks/kid/v5/model"
    14  )
    15  
    16  // We use this map to identify the exportable preferences.
    17  // Here we link the preference category and name, to the name of the relevant field in the import struct.
    18  var exportablePreferences = map[ComparablePreference]string{{
    19  	Category: model.PREFERENCE_CATEGORY_THEME,
    20  	Name:     "",
    21  }: "Theme", {
    22  	Category: model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS,
    23  	Name:     "feature_enabled_markdown_preview",
    24  }: "UseMarkdownPreview", {
    25  	Category: model.PREFERENCE_CATEGORY_ADVANCED_SETTINGS,
    26  	Name:     "formatting",
    27  }: "UseFormatting", {
    28  	Category: model.PREFERENCE_CATEGORY_SIDEBAR_SETTINGS,
    29  	Name:     "show_unread_section",
    30  }: "ShowUnreadSection", {
    31  	Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS,
    32  	Name:     model.PREFERENCE_NAME_USE_MILITARY_TIME,
    33  }: "UseMilitaryTime", {
    34  	Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS,
    35  	Name:     model.PREFERENCE_NAME_COLLAPSE_SETTING,
    36  }: "CollapsePreviews", {
    37  	Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS,
    38  	Name:     model.PREFERENCE_NAME_MESSAGE_DISPLAY,
    39  }: "MessageDisplay", {
    40  	Category: model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS,
    41  	Name:     "class_display_mode",
    42  }: "ClassDisplayMode", {
    43  	Category: model.PREFERENCE_CATEGORY_TUTORIAL_STEPS,
    44  	Name:     "",
    45  }: "TutorialStep", {
    46  	Category: model.PREFERENCE_CATEGORY_NOTIFICATIONS,
    47  	Name:     model.PREFERENCE_NAME_EMAIL_INTERVAL,
    48  }: "EmailInterval",
    49  }
    50  
    51  func (a *App) BulkExport(writer io.Writer, file string, pathToEmojiDir string, dirNameToExportEmoji string) *model.AppError {
    52  	mlog.Info("Bulk export: exporting version")
    53  	if err := a.exportVersion(writer); err != nil {
    54  		return err
    55  	}
    56  
    57  	mlog.Info("Bulk export: exporting branches")
    58  	if err := a.exportAllBranches(writer); err != nil {
    59  		return err
    60  	}
    61  
    62  	mlog.Info("Bulk export: exporting classes")
    63  	if err := a.exportAllClasses(writer); err != nil {
    64  		return err
    65  	}
    66  
    67  	mlog.Info("Bulk export: exporting users")
    68  	if err := a.exportAllUsers(writer); err != nil {
    69  		return err
    70  	}
    71  
    72  	return nil
    73  }
    74  
    75  func (a *App) exportWriteLine(writer io.Writer, line *LineImportData) *model.AppError {
    76  	b, err := json.Marshal(line)
    77  	if err != nil {
    78  		return model.NewAppError("BulkExport", "app.export.export_write_line.json_marshall.error", nil, "err="+err.Error(), http.StatusBadRequest)
    79  	}
    80  
    81  	if _, err := writer.Write(append(b, '\n')); err != nil {
    82  		return model.NewAppError("BulkExport", "app.export.export_write_line.io_writer.error", nil, "err="+err.Error(), http.StatusBadRequest)
    83  	}
    84  
    85  	return nil
    86  }
    87  
    88  func (a *App) exportVersion(writer io.Writer) *model.AppError {
    89  	version := 1
    90  	versionLine := &LineImportData{
    91  		Type:    "version",
    92  		Version: &version,
    93  	}
    94  
    95  	return a.exportWriteLine(writer, versionLine)
    96  }
    97  
    98  func (a *App) exportAllBranches(writer io.Writer) *model.AppError {
    99  	afterId := strings.Repeat("0", 26)
   100  	for {
   101  		branches, err := a.Srv().Store.Branch().GetAllForExportAfter(1000, afterId)
   102  
   103  		if err != nil {
   104  			return err
   105  		}
   106  
   107  		if len(branches) == 0 {
   108  			break
   109  		}
   110  
   111  		for _, branch := range branches {
   112  			afterId = branch.Id
   113  
   114  			// Skip deleted.
   115  			if branch.DeleteAt != 0 {
   116  				continue
   117  			}
   118  
   119  			branchLine := ImportLineFromBranch(branch)
   120  			if err := a.exportWriteLine(writer, branchLine); err != nil {
   121  				return err
   122  			}
   123  		}
   124  	}
   125  
   126  	return nil
   127  }
   128  
   129  func (a *App) exportAllClasses(writer io.Writer) *model.AppError {
   130  	afterId := strings.Repeat("0", 26)
   131  	for {
   132  		classes, err := a.Srv().Store.Class().GetAllClassesForExportAfter(1000, afterId)
   133  
   134  		if err != nil {
   135  			return err
   136  		}
   137  
   138  		if len(classes) == 0 {
   139  			break
   140  		}
   141  
   142  		for _, class := range classes {
   143  			afterId = class.Id
   144  
   145  			// Skip deleted.
   146  			if class.DeleteAt != 0 {
   147  				continue
   148  			}
   149  
   150  			classLine := ImportLineFromClass(class)
   151  			if err := a.exportWriteLine(writer, classLine); err != nil {
   152  				return err
   153  			}
   154  		}
   155  	}
   156  
   157  	return nil
   158  }
   159  
   160  func (a *App) exportAllUsers(writer io.Writer) *model.AppError {
   161  	afterId := strings.Repeat("0", 26)
   162  	for {
   163  		users, err := a.Srv().Store.User().GetAllAfter(1000, afterId)
   164  
   165  		if err != nil {
   166  			return err
   167  		}
   168  
   169  		if len(users) == 0 {
   170  			break
   171  		}
   172  
   173  		for _, user := range users {
   174  			afterId = user.Id
   175  
   176  			// Gathering here the exportable preferences to pass them on to ImportLineFromUser
   177  			exportedPrefs := make(map[string]*string)
   178  			allPrefs, err := a.GetPreferencesForUser(user.Id)
   179  			if err != nil {
   180  				return err
   181  			}
   182  			for _, pref := range allPrefs {
   183  				// We need to manage the special cases
   184  				// Here we manage Tutorial steps
   185  				if pref.Category == model.PREFERENCE_CATEGORY_TUTORIAL_STEPS {
   186  					pref.Name = ""
   187  					// Then the email interval
   188  				} else if pref.Category == model.PREFERENCE_CATEGORY_NOTIFICATIONS && pref.Name == model.PREFERENCE_NAME_EMAIL_INTERVAL {
   189  					switch pref.Value {
   190  					case model.PREFERENCE_EMAIL_INTERVAL_NO_BATCHING_SECONDS:
   191  						pref.Value = model.PREFERENCE_EMAIL_INTERVAL_IMMEDIATELY
   192  					case model.PREFERENCE_EMAIL_INTERVAL_FIFTEEN_AS_SECONDS:
   193  						pref.Value = model.PREFERENCE_EMAIL_INTERVAL_FIFTEEN
   194  					case model.PREFERENCE_EMAIL_INTERVAL_HOUR_AS_SECONDS:
   195  						pref.Value = model.PREFERENCE_EMAIL_INTERVAL_HOUR
   196  					case "0":
   197  						pref.Value = ""
   198  					}
   199  				}
   200  				id, ok := exportablePreferences[ComparablePreference{
   201  					Category: pref.Category,
   202  					Name:     pref.Name,
   203  				}]
   204  				if ok {
   205  					prefPtr := pref.Value
   206  					if prefPtr != "" {
   207  						exportedPrefs[id] = &prefPtr
   208  					} else {
   209  						exportedPrefs[id] = nil
   210  					}
   211  				}
   212  			}
   213  
   214  			userLine := ImportLineFromUser(user, exportedPrefs)
   215  
   216  			userLine.User.NotifyProps = a.buildUserNotifyProps(user.NotifyProps)
   217  
   218  			// Do the Branch Memberships.
   219  			members, err := a.buildUserBranchAndClassMemberships(user.Id)
   220  			if err != nil {
   221  				return err
   222  			}
   223  
   224  			userLine.User.Branches = members
   225  
   226  			if err := a.exportWriteLine(writer, userLine); err != nil {
   227  				return err
   228  			}
   229  		}
   230  	}
   231  
   232  	return nil
   233  }
   234  
   235  func (a *App) buildUserBranchAndClassMemberships(userId string) (*[]UserBranchImportData, *model.AppError) {
   236  	var memberships []UserBranchImportData
   237  
   238  	members, err := a.Srv().Store.Branch().GetBranchMembersForExport(userId)
   239  
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	for _, member := range members {
   245  		// Skip deleted.
   246  		if member.DeleteAt != 0 {
   247  			continue
   248  		}
   249  
   250  		memberData := ImportUserBranchDataFromBranchMember(member)
   251  
   252  		// Do the Class Memberships.
   253  		classMembers, err := a.buildUserClassMemberships(userId, member.BranchId)
   254  		if err != nil {
   255  			return nil, err
   256  		}
   257  
   258  		// Get the user theme
   259  		themePreference, err := a.Srv().Store.Preference().Get(member.UserId, model.PREFERENCE_CATEGORY_THEME, member.BranchId)
   260  		if err == nil {
   261  			memberData.Theme = &themePreference.Value
   262  		}
   263  
   264  		memberData.Classes = classMembers
   265  
   266  		memberships = append(memberships, *memberData)
   267  	}
   268  
   269  	return &memberships, nil
   270  }
   271  
   272  func (a *App) buildUserClassMemberships(userId string, branchId string) (*[]UserClassImportData, *model.AppError) {
   273  	var memberships []UserClassImportData
   274  
   275  	members, err := a.Srv().Store.Class().GetClassMembersForExport(userId, branchId)
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  
   280  	category := model.PREFERENCE_CATEGORY_FAVORITE_CLASS
   281  	preferences, err := a.GetPreferenceByCategoryForUser(userId, category)
   282  	if err != nil && err.StatusCode != http.StatusNotFound {
   283  		return nil, err
   284  	}
   285  
   286  	for _, member := range members {
   287  		memberships = append(memberships, *ImportUserClassDataFromClassMemberAndPreferences(member, &preferences))
   288  	}
   289  	return &memberships, nil
   290  }
   291  
   292  func (a *App) buildUserNotifyProps(notifyProps model.StringMap) *UserNotifyPropsImportData {
   293  
   294  	getProp := func(key string) *string {
   295  		if v, ok := notifyProps[key]; ok {
   296  			return &v
   297  		}
   298  		return nil
   299  	}
   300  
   301  	return &UserNotifyPropsImportData{
   302  		Desktop:          getProp(model.DESKTOP_NOTIFY_PROP),
   303  		DesktopSound:     getProp(model.DESKTOP_SOUND_NOTIFY_PROP),
   304  		Email:            getProp(model.EMAIL_NOTIFY_PROP),
   305  		Mobile:           getProp(model.PUSH_NOTIFY_PROP),
   306  		MobilePushStatus: getProp(model.PUSH_STATUS_NOTIFY_PROP),
   307  		ClassTrigger:     getProp(model.PUSH_NOTIFY_PROP),
   308  		CommentsTrigger:  getProp(model.COMMENTS_NOTIFY_PROP),
   309  		MentionKeys:      getProp(model.MENTION_KEYS_NOTIFY_PROP),
   310  	}
   311  }