public static Result Export(BubbleGroup group, CancellationTokenSource cancellationToken, Options options) { var untilTime = Options.ConvertToUnixTimestamp(options.ExportTime); var outputLocation = Path.Combine(Platform.GetSettingsPath(), "ConversationExport"); var cache = BubbleGroupCacheManager.Load(group); if (cache == null) { Utils.DebugPrint("Could not relate the bubblegroup to a cache"); return(new Result { Case = Result.State.Failure }); } //Null out what we dont't need to export cache = Utils.Clone(cache); cache.Photo = null; if (cache.Participants != null) { foreach (var participant in cache.Participants) { participant.Photo = null; } } var finalZip = MediaManager.GenerateFileLocation((cache.Name ?? "Unknown").Replace("/", "_") + ".zip"); // Should never happen, as GenerateFileLocation ensures it's a new file. But just in case. if (File.Exists(finalZip)) { File.Delete(finalZip); } try { Monitor.Enter(_exportLock); if (cancellationToken.Token.IsCancellationRequested) { cancellationToken.Token.ThrowIfCancellationRequested(); } if (Directory.Exists(outputLocation)) { Directory.Delete(outputLocation, true); } Directory.CreateDirectory(outputLocation); using (var ms = Platform.GetConversationExportAssetsArchiveStream()) { ExtractZip(ms, outputLocation); } var bubblesJs = Path.Combine(outputLocation, "js", "bubbles.js"); using (var fs = File.OpenWrite(bubblesJs)) { using (var sw = new StreamWriter(fs)) { using (var writer = new JsonTextWriter(sw)) { sw.Write("angular.module(\"app\").service('exportedBubbles', function () {"); sw.Write("\n"); sw.Write("this.bubbles = "); writer.WriteStartArray(); var cursor = new BubbleGroupFactory.Cursor(group, new BubbleGroupFactory.Cursor.Selection()); while (true) { var bubbles = cursor.FetchNext(); if (bubbles == null || !bubbles.Any()) { goto End; } foreach (var bubble in bubbles) { if (cancellationToken.Token.IsCancellationRequested) { cancellationToken.Token.ThrowIfCancellationRequested(); } if (bubble.Time < untilTime) { goto End; } if (!SupportsBubble(bubble, options)) { continue; } CopyBubbleFilesToTargetIfNeeded(bubble, outputLocation, options); var jobject = JObject.FromObject(bubble); jobject.Add("ID", bubble.ID); jobject.Add("Type", bubble.GetType().Name); jobject.Add("Service", bubble.Service.Information.ServiceName); writer.WriteRawValue(jobject.ToString(Formatting.None)); } } End: writer.WriteEndArray(); sw.Write(";"); sw.Write("\n"); sw.Write("});"); } } } var cacheJs = Path.Combine(outputLocation, "js", "cache.js"); using (var fs = File.OpenWrite(cacheJs)) { using (var sw = new StreamWriter(fs)) { using (var writer = new JsonTextWriter(sw)) { sw.Write("angular.module(\"app\").service('exportedBubblesCache', function () {"); sw.Write("\n"); sw.Write("this.cache = "); var jobject = JObject.FromObject(cache); writer.WriteRawValue(jobject.ToString(Formatting.None)); sw.Write(";"); sw.Write("\n"); sw.Write("});"); } } } using (var fs = File.OpenWrite(finalZip)) { ArchiveZip(fs, outputLocation); } return(new Result { Case = Result.State.Success, OutputLocation = finalZip, }); } catch (Exception ex) { // Insights.Report(ex); Utils.DebugPrint("Failed to export conversation: " + ex); } finally { try { Directory.Delete(outputLocation, true); } catch { // fall-through } try { Monitor.Exit(_exportLock); } catch { // fall-through } } return(new Result { Case = Result.State.Failure }); }
internal static void LoadAllPartiallyIfPossible(int bubblesPerGroup = 100) { var corruptedGroups = new List <string>(); var bubbleGroupsLocations = Directory.GetFiles(BubbleGroupDatabase.GetBaseLocation(), "*.*", SearchOption.AllDirectories); var bubbleGroupsLocationsSorted = bubbleGroupsLocations.OrderByDescending(x => Time.GetUnixTimestamp(File.GetLastWriteTime(x))).ToList(); foreach (var bubbleGroupLocation in bubbleGroupsLocationsSorted) { String groupId = null; try { var groupHeader = Path.GetFileNameWithoutExtension(bubbleGroupLocation); var groupDelimeter = groupHeader.IndexOf("^", StringComparison.Ordinal); var serviceName = groupHeader.Substring(0, groupDelimeter); groupId = groupHeader.Substring(groupDelimeter + 1); var service = ServiceManager.GetByName(serviceName); if (service == null) { Utils.DebugPrint("Service " + serviceName + " not found in AllServices!"); continue; } var deserializedBubbleGroup = LoadPartiallyIfPossible(bubbleGroupLocation, service, groupId, bubblesPerGroup); if (deserializedBubbleGroup == null) { throw new Exception("DeserializedBubbleGroup is nothing."); } BubbleGroupManager.BubbleGroupsAdd(deserializedBubbleGroup); } catch (Exception ex) { Utils.DebugPrint("Group " + bubbleGroupLocation + " is corrupt. Deleting. " + ex); File.Delete(bubbleGroupLocation); if (groupId != null) { corruptedGroups.Add(groupId); } } } var migrationResaveNeeded = false; var corruptedUnifiedGroups = new List <SimpleDatabase <UnifiedBubbleGroup, DisaUnifiedBubbleGroupEntry> .Container>(); var removeFromRuntime = new List <SimpleDatabase <UnifiedBubbleGroup, DisaUnifiedBubbleGroupEntry> .Container>(); foreach (var group in UnifiedBubbleGroupsDatabase) { var innerGroupCorrupt = false; var innerGroups = new List <BubbleGroup>(); foreach (var innerGroupId in @group.Serializable.GroupIds) { var innerGroup = BubbleGroupManager.Find(innerGroupId); if (innerGroup == null) { Utils.DebugPrint("Unified group, inner group " + innerGroupId + " could not be related."); if (corruptedGroups.Contains(innerGroupId)) { Utils.DebugPrint( "It was detected that this inner group was corrupted and deleted. Will delete unified group."); innerGroupCorrupt = true; corruptedUnifiedGroups.Add(@group); } } else { innerGroups.Add(innerGroup); } } if (!innerGroups.Any()) { Utils.DebugPrint("Yuck. This unified group has no inner groups. Removing from runtime."); removeFromRuntime.Add(@group); continue; } if (innerGroupCorrupt) { continue; } var primaryGroup = innerGroups.FirstOrDefault(x => x.ID == @group.Serializable.PrimaryGroupId); if (primaryGroup == null) { Utils.DebugPrint("Unified group, primary group " + @group.Serializable.PrimaryGroupId + " could not be related. Removing from runtime."); removeFromRuntime.Add(@group); continue; } var id = @group.Serializable.Id; var unifiedGroup = CreateUnifiedInternal(innerGroups, primaryGroup, id); if (id == null) { migrationResaveNeeded = true; @group.Serializable.Id = unifiedGroup.ID; } var sendingGroup = innerGroups.FirstOrDefault(x => x.ID == @group.Serializable.SendingGroupId); if (sendingGroup != null) { unifiedGroup.SendingGroup = sendingGroup; } @group.Object = unifiedGroup; BubbleGroupManager.BubbleGroupsAdd(unifiedGroup); } if (removeFromRuntime.Any()) { foreach (var group in removeFromRuntime) { UnifiedBubbleGroupsDatabase.Remove(@group); } } if (corruptedUnifiedGroups.Any()) { foreach (var group in corruptedUnifiedGroups) { UnifiedBubbleGroupsDatabase.Remove(@group); } } if (migrationResaveNeeded) { Utils.DebugPrint("It was detected that we need to save migration changes for UnifiedBubbleGroups."); UnifiedBubbleGroupsDatabase.SaveChanges(); } try { foreach (var groupCache in BubbleGroupCacheManager.Load()) { var associatedGroup = BubbleGroupManager.Find(groupCache.Guid); if (associatedGroup == null) { continue; } var unifiedGroup = associatedGroup as UnifiedBubbleGroup; if (unifiedGroup != null) { associatedGroup = unifiedGroup.PrimaryGroup; } associatedGroup.Title = groupCache.Name; associatedGroup.Photo = groupCache.Photo; associatedGroup.IsPhotoSetInitiallyFromCache = true; if (groupCache.Participants != null) { associatedGroup.Participants = groupCache.Participants.ToSynchronizedCollection(); foreach (var participant in associatedGroup.Participants) { participant.IsPhotoSetInitiallyFromCache = true; } } } } catch (Exception ex) { Utils.DebugPrint("Failed to load bubble groups!: " + ex); } BubbleGroupSettingsManager.Load(); }