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 }