diff --git a/src/BloomExe/Program.cs b/src/BloomExe/Program.cs index 2b20e1b6f46d..aedb5d91d59a 100644 --- a/src/BloomExe/Program.cs +++ b/src/BloomExe/Program.cs @@ -373,7 +373,7 @@ static int Main(string[] args1) if (FolderTeamCollection.IsJoinTeamCollectionFile(args)) { SetUpErrorHandling(); - string newCollection; + string newCollection = null; // When we're spinning up a fake local collection in "projectName", we don't want // any chance of copying FROM there to the repo. TeamCollectionManager.ForceNextSyncToLocal = true; @@ -394,69 +394,77 @@ static int Main(string[] args1) Path.GetDirectoryName(args[0]), fakeProjectFolder.FolderPath ); - using ( - var projectContext = _applicationContainer.CreateProjectContext( - fakeCollectionPath, - true - ) - ) + if (fakeCollectionPath != null) { - if (!UniqueToken.AcquireTokenQuietly(_mutexId)) - { - var msg = LocalizationManager.GetString( - "TeamCollection.QuitOtherBloom", - "Please close Bloom before joining a Team Collection" - ); - BloomMessageBox.ShowInfo(msg); - return 1; - } - _gotUniqueToken = true; - - if ( - projectContext.TeamCollectionManager.CurrentCollection - == null + using ( + var projectContext = + _applicationContainer.CreateProjectContext( + fakeCollectionPath, + true + ) ) { - if ( - projectContext - .TeamCollectionManager - .CurrentCollectionEvenIfDisconnected != null - ) + if (!UniqueToken.AcquireTokenQuietly(_mutexId)) { var msg = LocalizationManager.GetString( - "TeamCollection.ConnectToJoin", - "Bloom cannot currently join this collection." + "TeamCollection.QuitOtherBloom", + "Please close Bloom before joining a Team Collection" ); - // This is a bit of a kludge, but when we make a disconnected collection it's pretty - // consistently true that the last message just says "you'll be disconnected till you fix this", - // but the previous one actually says what's wrong. So we'll reuse this in this (hopefully) - // rare case. - var messages = - projectContext.TeamCollectionManager.CurrentCollectionEvenIfDisconnected.MessageLog.GetProgressMessages(); + BloomMessageBox.ShowInfo(msg); + return 1; + } + + _gotUniqueToken = true; + + if ( + projectContext.TeamCollectionManager.CurrentCollection + == null + ) + { if ( - messages.Length > 1 - && messages[messages.Length - 2].progressKind - == "Error" + projectContext + .TeamCollectionManager + .CurrentCollectionEvenIfDisconnected != null ) { - msg += - Environment.NewLine - + messages[messages.Length - 2].message; + var msg = LocalizationManager.GetString( + "TeamCollection.ConnectToJoin", + "Bloom cannot currently join this collection." + ); + // This is a bit of a kludge, but when we make a disconnected collection it's pretty + // consistently true that the last message just says "you'll be disconnected till you fix this", + // but the previous one actually says what's wrong. So we'll reuse this in this (hopefully) + // rare case. + var messages = + projectContext.TeamCollectionManager.CurrentCollectionEvenIfDisconnected.MessageLog.GetProgressMessages(); + if ( + messages.Length > 1 + && messages[messages.Length - 2].progressKind + == "Error" + ) + { + msg += + Environment.NewLine + + messages[messages.Length - 2].message; + } + + ErrorReport.NotifyUserOfProblem(msg); } - ErrorReport.NotifyUserOfProblem(msg); + + return 1; // something went wrong processing it, hopefully already reported. } - return 1; // something went wrong processing it, hopefully already reported. + + newCollection = + FolderTeamCollection.ShowJoinCollectionTeamDialog( + args[0], + projectContext.TeamCollectionManager + ); } - newCollection = - FolderTeamCollection.ShowJoinCollectionTeamDialog( - args[0], - projectContext.TeamCollectionManager - ); } } } - if (newCollection == null) // user canceled + if (newCollection == null) // user canceled, or catastrophic problem already reported return 1; args = new string[] { }; // continue to open, but without args. diff --git a/src/BloomExe/TeamCollection/FolderTeamCollection.cs b/src/BloomExe/TeamCollection/FolderTeamCollection.cs index e79d0857b949..3969e6e01b88 100644 --- a/src/BloomExe/TeamCollection/FolderTeamCollection.cs +++ b/src/BloomExe/TeamCollection/FolderTeamCollection.cs @@ -13,6 +13,7 @@ using Bloom.web; using L10NSharp; using SIL.IO; +using SIL.Reporting; namespace Bloom.TeamCollection { @@ -550,6 +551,9 @@ protected override void CopyRepoCollectionFilesToLocalImpl(string destFolder) // allows writing it but not moving/deleting it. But it seems the way our zip // utility overwrites files involves something that is not allowed. We need to free // it up while we do this. + // Note: I'm ignoring the remote possibility that CopyRepoCollectionFilesTo + // might fail or return false. If the repo files are for some reason not available, + // we will just carry on with the local ones as are they are (having notified the user). _collectionLock.UnlockFor(() => CopyRepoCollectionFilesTo(destFolder, _repoFolderPath)); ExtractFolder(destFolder, _repoFolderPath, "Allowed Words"); ExtractFolder(destFolder, _repoFolderPath, "Sample Texts"); @@ -631,11 +635,23 @@ private static string MergeColorPaletteValues(string repoValues, string localVal return string.Join(" ", localList); } - private static void CopyRepoCollectionFilesTo(string destFolder, string repoFolder) + // Returns true if it was able to do the copy. If false is returned, the user has been notified of the problem. + // Some callers may simply exit the program at that point, if not obtaining a local collection file is + // catastrophic, like when joining a team collection. Others may choose to ignore it. + private static bool CopyRepoCollectionFilesTo(string destFolder, string repoFolder) { var collectionZipPath = GetRepoProjectFilesZipPath(repoFolder); if (!RobustFile.Exists(collectionZipPath)) - return; + { + // For now, TC messages are not being localized. + var msg = string.Format( + "This Team Collection is missing \"{0}\". See https://docs.bloomlibrary.org/team-collections-problems/", + collectionZipPath + ); + ErrorReport.NotifyUserOfProblem(msg); + return false; + } + try { RobustZip.ExtractFolderFromZip( @@ -654,7 +670,10 @@ private static void CopyRepoCollectionFilesTo(string destFolder, string repoFold "Bloom could not unpack the collection files in your Team Collection", exception: e ); + return false; } + + return true; } private static void SyncColorPaletteFileWithRepo( @@ -1335,7 +1354,8 @@ public static string MissingTcPieces(string joinCollectionPath) /// /// Called when the user clicks the Join{ and Merge} button in the dialog. /// - /// an indication of the type of join for analytics + /// an indication of the type of join for analytics (or null if something + /// went catastrophically wrong and the join should be aborted) public static string JoinCollectionTeam() { var repoFolder = Path.GetDirectoryName(_joinCollectionPath); @@ -1376,6 +1396,8 @@ public static string JoinCollectionTeam() repoFolder, localCollectionFolder ); + if (string.IsNullOrEmpty(_newCollectionToJoin)) + return null; // Soon we will open the new collection, and do a SyncAtStartup. We want that to have some // special behavior, but only if joining for the first time. if (firstTimeJoin) @@ -1388,6 +1410,7 @@ public static string JoinCollectionTeam() /// in the specified repoFolder. (We could get away without unpacking more than the .bloomCollection /// file, but we'll want the others soon, and typically it's not a lot.) /// + /// path to collection file, or null if something went catastrophically wrong public static string SetupMinimumLocalCollectionFilesForRepo( string repoFolder, string localCollectionFolder @@ -1395,7 +1418,8 @@ string localCollectionFolder { Directory.CreateDirectory(localCollectionFolder); CreateTeamCollectionLinkFile(localCollectionFolder, repoFolder); - CopyRepoCollectionFilesTo(localCollectionFolder, repoFolder); + if (!CopyRepoCollectionFilesTo(localCollectionFolder, repoFolder)) + return null; return CollectionPath(localCollectionFolder); } diff --git a/src/BloomExe/TeamCollection/TeamCollectionApi.cs b/src/BloomExe/TeamCollection/TeamCollectionApi.cs index afd6ef446086..21870d01096c 100644 --- a/src/BloomExe/TeamCollection/TeamCollectionApi.cs +++ b/src/BloomExe/TeamCollection/TeamCollectionApi.cs @@ -360,18 +360,28 @@ private void HandleJoinTeamCollection(ApiRequest request) { var joinType = FolderTeamCollection.JoinCollectionTeam(); ReactDialog.CloseCurrentModal(); - - Analytics.Track( - "TeamCollectionJoin", - new Dictionary() - { - { "CollectionId", _settings?.CollectionId }, - { "CollectionName", _settings?.CollectionName }, - { "Backend", _tcManager?.CurrentCollection?.GetBackendType() }, - { "User", CurrentUser }, - { "JoinType", joinType }, // create, open, or merge - } - ); + // If this is null, something went badly wrong, and should have already been reported. + // We haven't joined the TC, so don't track it. + // This check was added in connection with problems when the TC we're trying to join + // doesn't have an "other" folder, or it doesn't have the zip that contains the .bloomCollection + // file. Such a problem will usually be detected and reported long before we open the + // dialog, so there's no likely path to this happening. The file would have to + // disappear between when we open the dialog and when we click the button to join. + // But we may as well handle the situation properly. + if (!string.IsNullOrEmpty(joinType)) + { + Analytics.Track( + "TeamCollectionJoin", + new Dictionary() + { + { "CollectionId", _settings?.CollectionId }, + { "CollectionName", _settings?.CollectionName }, + { "Backend", _tcManager?.CurrentCollection?.GetBackendType() }, + { "User", CurrentUser }, + { "JoinType", joinType }, // create, open, or merge + } + ); + } request.PostSucceeded(); }