private static void Merge(Options.MergeOptions options)
        {
            RobustIO.DeleteDirectoryAndContents(options.WorkDir);
            Settings.TempDir = options.WorkDir;

            // restore previous data
            var restoreOptions = new Options.RestoreOptions(options);

            RestoreLanguageDepot(restoreOptions);
            RestoreMongoDb(restoreOptions);

            // run merge
            LfMergeHelper.Run($"--project {options.Project} --clone --action=Synchronize");
            Console.WriteLine("Successfully merged test data");

            // save merged data
            var saveOptions = new Options.SaveOptions(options)
            {
                WorkDir   = Path.Combine(options.WorkDir, "LanguageDepot"),
                CommitMsg = options.CommitMsg ?? "Merged test data"
            };

            SaveLanguageDepot(saveOptions);
            SaveLanguageDepotNoOpPatchIfNecessary(options.ModelVersion, options.LanguageDepotVersion + 1);
            SaveMongoDb(saveOptions);
        }
        public void RefreshPatches(string modelVersion)
        {
            // Import the initial patch file in a temporary directory
            var sourceMongoSource = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());

            InitSourceDir(sourceMongoSource);

            var destMongoSource = GetMongoSourceDir(modelVersion);

            InitSourceDir(destMongoSource);

            var mongoPatchDir = GetMongoPatchDir(modelVersion);
            var patchFiles    = Directory.GetFiles(mongoPatchDir, "*.patch");

            Array.Sort(patchFiles);
            foreach (var file in patchFiles)
            {
                Console.WriteLine($"\tProcessing {Path.GetFileName(file)}");
                var patchNoStr = Path.GetFileName(file).Substring(0, 4);
                var patchNo    = int.Parse(patchNoStr);
                TestHelper.Run(Git, $"am --ignore-whitespace {file}", sourceMongoSource);
                TestHelper.Run(Git, $"tag r{patchNo}", sourceMongoSource);

                CopyFiles(sourceMongoSource, destMongoSource, true);

                RestoreDatabaseInternal(modelVersion);

                SaveDatabaseInternal(mongoPatchDir, modelVersion, ExtractSubject(file),
                                     patchNo, destMongoSource);
            }

            RobustIO.DeleteDirectoryAndContents(sourceMongoSource);
        }
        public void HandleModifiedFile_NoLocalBook_DoesNothing()
        {
            // Setup //
            // Simulate that a book appeared remotely. We should eventually get a created notice.
            // Sometimes, for reasons we don't fully understand, we get a modified notice
            // first. Or the book might be modified again before we fetch it. In any case,
            // we don't need modify messages until we fetch a local copy.
            const string bookFolderName = "My book";
            var          bookBuilder    = new BookFolderBuilder()
                                          .WithRootFolder(_collectionFolder.FolderPath)
                                          .WithTitle(bookFolderName)
                                          .Build(); // Writes the book to disk based on the above specified values
            string bookFolderPath = bookBuilder.BuiltBookFolderPath;

            var status = _collection.PutBook(bookFolderPath);

            // pretending we had nothing local before the change.
            RobustIO.DeleteDirectory(bookFolderPath, true);

            // Anticipate verification
            var prevMessages = _tcLog.Messages.Count;

            _mockTcManager.Setup(m => m.RaiseBookStatusChanged(It.IsAny <BookStatusChangeEventArgs>()))
            .Throws(new ArgumentException("RaiseBookStatus should not be called"));

            // System Under Test //
            _collection.HandleModifiedFile(new BookRepoChangeEventArgs()
            {
                BookFileName = $"{bookFolderName}.bloom"
            });

            // Verification
            Assert.That(_tcLog.Messages.Count, Is.EqualTo(prevMessages));
        }
Beispiel #4
0
 public static void DeleteFolderThatMayBeInUse(string folder)
 {
     if (Directory.Exists(folder))
     {
         try
         {
             RobustIO.DeleteDirectory(folder, true);
         }
         catch (Exception e)
         {
             try
             {
                 Debug.WriteLine(e.Message);
                 //maybe we can at least clear it out a bit
                 string[] files = Directory.GetFiles(folder, "*.*", SearchOption.AllDirectories);
                 foreach (string s in files)
                 {
                     try
                     {
                         RobustFile.Delete(s);
                     }
                     catch (Exception)
                     {
                     }
                 }
                 //sleep and try again (in case some other thread will  let go of them)
                 Thread.Sleep(1000);
                 RobustIO.DeleteDirectory(folder, true);
             }
             catch (Exception)
             {
             }
         }
     }
 }
Beispiel #5
0
        public void MigrateOldConfigurationsIfNeeded_MatchesLabelsWhenUIIsLocalized()
        {
            // Localize a Part's label to German (sufficient to cause a mismatched nodes crash if one config's labels are localized)
            var localizedPartLabels = new Dictionary <string, string>();

            localizedPartLabels["Main Entry"] = "Haupteintrag";
            var pathsToL10NStrings = (Dictionary <string, Dictionary <string, string> >)ReflectionHelper.GetField(StringTable.Table, "m_pathsToStrings");

            pathsToL10NStrings["group[@id = 'LocalizedAttributes']/"] = localizedPartLabels;

            var configSettingsDir = LcmFileHelper.GetConfigSettingsDir(Path.GetDirectoryName(Cache.ProjectId.Path));
            var newConfigFilePath = Path.Combine(configSettingsDir, DictionaryConfigurationListener.DictionaryConfigurationDirectoryName,
                                                 "Lexeme" + DictionaryConfigurationModel.FileExtension);

            Assert.False(File.Exists(newConfigFilePath), "should not yet be migrated");
            Directory.CreateDirectory(configSettingsDir);
            File.WriteAllLines(Path.Combine(configSettingsDir, "Test.fwlayout"), new[] {
                @"<layoutType label='Lexeme-based (complex forms as main entries)' layout='publishStem'><configure class='LexEntry' label='Main Entry' layout='publishStemEntry' />",
                @"<configure class='LexEntry' label='Minor Entry' layout='publishStemMinorEntry' hideConfig='true' /></layoutType>'"
            });
            var migrator = new DictionaryConfigurationMigrator(m_propertyTable, m_mediator);

            Assert.DoesNotThrow(() => migrator.MigrateOldConfigurationsIfNeeded(), "ArgumentException indicates localized labels.");             // SUT
            var updatedConfigModel = new DictionaryConfigurationModel(newConfigFilePath, Cache);

            Assert.AreEqual(2, updatedConfigModel.Parts.Count, "Should have 2 top-level nodes");
            Assert.AreEqual("Main Entry", updatedConfigModel.Parts[0].Label);
            RobustIO.DeleteDirectoryAndContents(configSettingsDir);
        }
        public static void SimulateRename(Bloom.TeamCollection.TeamCollection tc, string oldName, string newName)
        {
            var oldPath = Path.Combine(tc.LocalCollectionFolder, oldName);
            var newPath = Path.Combine(tc.LocalCollectionFolder, newName);

            RobustIO.MoveDirectory(oldPath, newPath);
            RobustFile.Move(Path.Combine(newPath, oldName + ".htm"), Path.Combine(newPath, newName + ".htm"));
            tc.HandleBookRename(oldName, newName);
        }
 public void AsyncLocalCheckIn_NoPreviousRepoCreation_Throws()
 {
     Assert.Throws <InvalidOperationException>(() =>
     {
         //simulate not having previously created a repository
         RobustIO.DeleteDirectoryAndContents(_pathToTestRoot.CombineForPath(".hg"));
         _model.AsyncLocalCheckIn("testing", null);
     });
 }
Beispiel #8
0
 public override void FixtureTeardown()
 {
     RobustIO.DeleteDirectoryAndContents(Cache.ProjectId.Path);
     base.FixtureTeardown();
     m_application.Dispose();
     m_window.Dispose();
     m_mediator.Dispose();
     FwRegistrySettings.Release();
 }
Beispiel #9
0
 public void LoadXElement_WithNRFileName_DoesNotCrash()
 {
     using (var folder = new TemporaryFolder("Balangao کتابونه"))
     {
         var input    = new XElement("root");
         var fileName = Path.Combine(folder.Path, "Balangao کتابونه.xml");
         input.Save(fileName);
         Assert.DoesNotThrow(() => RobustIO.LoadXElement(fileName));
     }
 }
Beispiel #10
0
 public void DeleteDirectoryAndContents_NoOverrideContainsReadOnlyFile_ReturnsFalse()
 {
     using (var tempDir = new TemporaryFolder("DeleteDirectoryAndContents_NoOverrideContainsReadOnlyFile_ReturnsFalse"))
     {
         var fileName = tempDir.Combine("tempFile.txt");
         File.WriteAllText(fileName, @"Some test text");
         new FileInfo(fileName).IsReadOnly = true;
         Assert.IsFalse(RobustIO.DeleteDirectoryAndContents(tempDir.Path, overrideReadOnly: false));
         Assert.IsTrue(Directory.Exists(tempDir.Path), "Did not expect it to delete directory because of the readonly file");
     }
 }
Beispiel #11
0
 public void DeleteDirectoryAndContents_ContainsReadOnlyFile_StillRemoves()
 {
     using (var tempDir = new TemporaryFolder("DeleteDirectoryAndContents_ContainsReadOnlyFile_StillRemoves"))
     {
         var fileName = tempDir.Combine("tempFile.txt");
         File.WriteAllText(fileName, @"Some test text");
         new FileInfo(fileName).IsReadOnly = true;
         RobustIO.DeleteDirectoryAndContents(tempDir.Path);
         Assert.IsFalse(Directory.Exists(tempDir.Path), "Did not delete directory");
     }
 }
Beispiel #12
0
 public void SaveXml()
 {
     using (var temp = new TempFile())
     {
         var doc = new XmlDocument();
         doc.LoadXml("<?xml version='1.0' encoding='utf-8'?><root>This is a test</root>");
         RobustIO.SaveXml(doc, temp.Path);
         // Not a great test, since it captures some non-essential features of how XmlDocument writes its content.
         // But this is at least a reasonable result to get from doing the above.
         Assert.That(File.ReadAllText(temp.Path, Encoding.UTF8), Is.EqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>" + Environment.NewLine + "<root>This is a test</root>"));
     }
 }
Beispiel #13
0
        public static void UpdateImgMetadataAttributesToMatchImage(string folderPath, XmlElement imgElement, IProgress progress, Metadata metadata)
        {
            //see also PageEditingModel.UpdateMetadataAttributesOnImage(), which does the same thing but on the browser dom
            var    url      = HtmlDom.GetImageElementUrl(new ElementProxy(imgElement));
            string fileName = url.PathOnly.NotEncoded;

            if (fileName.ToLowerInvariant() == "placeholder.png" || fileName.ToLowerInvariant() == "license.png")
            {
                return;
            }
            if (string.IsNullOrEmpty(fileName))
            {
                Logger.WriteEvent("Book.UpdateImgMetdataAttributesToMatchImage() Warning: img has no or empty src attribute");
                //Debug.Fail(" (Debug only) img has no or empty src attribute");
                return;                 // they have bigger problems, which aren't appropriate to deal with here.
            }

            if (metadata == null)
            {
                // The fileName might be URL encoded.  See https://silbloom.myjetbrains.com/youtrack/issue/BL-3901.
                var path = UrlPathString.GetFullyDecodedPath(folderPath, ref fileName);
                progress.WriteStatus("Reading metadata from " + fileName);
                if (!RobustFile.Exists(path))                 // they have bigger problems, which aren't appropriate to deal with here.
                {
                    imgElement.RemoveAttribute("data-copyright");
                    imgElement.RemoveAttribute("data-creator");
                    imgElement.RemoveAttribute("data-license");
                    Logger.WriteEvent("Book.UpdateImgMetdataAttributesToMatchImage()  Image " + path + " is missing");
                    //Debug.Fail(" (Debug only) Image " + path + " is missing");
                    return;
                }
                try
                {
                    metadata = RobustIO.MetadataFromFile(path);
                }
                catch (UnauthorizedAccessException e)
                {
                    throw new BloomUnauthorizedAccessException(path, e);
                }
            }

            progress.WriteStatus("Writing metadata to HTML for " + fileName);

            imgElement.SetAttribute("data-copyright",
                                    String.IsNullOrEmpty(metadata.CopyrightNotice) ? "" : metadata.CopyrightNotice);
            imgElement.SetAttribute("data-creator", String.IsNullOrEmpty(metadata.Creator) ? "" : metadata.Creator);
            imgElement.SetAttribute("data-license", metadata.License == null ? "" : metadata.License.ToString());
        }
        public static void InitializeNewProject(string modelVersion, string initialPatchFile)
        {
            // Import the initial patch file in a temporary directory
            var tempMongoSource = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());

            InitSourceDir(tempMongoSource);
            TestHelper.Run(Git, $"am --ignore-whitespace {initialPatchFile}", tempMongoSource);

            // Then copy the files to the real mongoSourceDir
            var mongoSourceDir = GetMongoSourceDir(modelVersion);

            InitSourceDir(mongoSourceDir);
            CopyFiles(tempMongoSource, mongoSourceDir);

            RobustIO.DeleteDirectoryAndContents(tempMongoSource);
        }
Beispiel #15
0
        private static void RestoreLanguageDepot(Options.RestoreOptions options)
        {
            var dir = Path.Combine(Settings.TempDir, "LanguageDepot");

            RobustIO.DeleteDirectoryAndContents(dir);
            using (var ld = new LanguageDepotHelper(true))
            {
                for (var i = 0; i <= options.LanguageDepotVersion.Value; i++)
                {
                    ld.ApplySinglePatch(options.ModelVersion, i);
                }
            }

            // Now in FLEx get project from colleague from USB
            Console.WriteLine("Successfully restored languagedepot test data for model " +
                              $"{options.ModelVersion}");
        }
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (!disposing)
            {
                return;
            }

            if (string.IsNullOrEmpty(RepoDir))
            {
                return;
            }

            RobustIO.DeleteDirectoryAndContents(RepoDir);
            RepoDir = null;
        }
Beispiel #17
0
        public void MigrateOldConfigurationsIfNeeded_BringsPreHistoricFileToCurrentVersion()
        {
            var configSettingsDir = LcmFileHelper.GetConfigSettingsDir(Path.GetDirectoryName(Cache.ProjectId.Path));
            var newConfigFilePath = Path.Combine(configSettingsDir, DictionaryConfigurationListener.DictionaryConfigurationDirectoryName,
                                                 "Lexeme" + DictionaryConfigurationModel.FileExtension);

            Assert.False(File.Exists(newConfigFilePath), "should not yet be migrated");
            Directory.CreateDirectory(configSettingsDir);
            File.WriteAllLines(Path.Combine(configSettingsDir, "Test.fwlayout"), new[] {
                @"<layoutType label='Lexeme-based (complex forms as main entries)' layout='publishStem'><configure class='LexEntry' label='Main Entry' layout='publishStemEntry' />",
                @"<configure class='LexEntry' label='Minor Entry' layout='publishStemMinorEntry' hideConfig='true' /></layoutType>'"
            });
            var migrator = new DictionaryConfigurationMigrator(m_propertyTable, m_mediator);

            migrator.MigrateOldConfigurationsIfNeeded();             // SUT
            var updatedConfigModel = new DictionaryConfigurationModel(newConfigFilePath, Cache);

            Assert.AreEqual(DictionaryConfigurationMigrator.VersionCurrent, updatedConfigModel.Version);
            RobustIO.DeleteDirectoryAndContents(configSettingsDir);
        }
Beispiel #18
0
        public void EnchantDictionaryMigrated()
        {
            // make temp folder with enchant dictionary in it
            var outputFolder = Path.Combine(Path.GetTempPath(), "EnchantDictionaryMigrationTestOut");
            var inputFolder  = Path.Combine(Path.GetTempPath(), "EnchantDictionaryMigrationTestIn");

            RobustIO.DeleteDirectoryAndContents(outputFolder);
            RobustIO.DeleteDirectoryAndContents(inputFolder);
            Directory.CreateDirectory(outputFolder);
            Directory.CreateDirectory(inputFolder);
            // ReSharper disable LocalizableElement
            File.AppendAllText(Path.Combine(inputFolder, "test.dic"), "nil");
            File.AppendAllText(Path.Combine(inputFolder, "test.exc"), "nil");
            // ReSharper restore LocalizableElement
            // SUT
            Assert.DoesNotThrow(() => SpellingHelper.AddAnySpellingExceptionsFromBackup(inputFolder, outputFolder));
            // Verify that the correct files were copied
            Assert.True(File.Exists(Path.Combine(outputFolder, "test.exc")), "The exception file should have been copied.");
            Assert.False(File.Exists(Path.Combine(outputFolder, "test.dic")), "The .dic file should not have been copied.");
        }
        private void HandleOkButtonClick(object sender, EventArgs e)
        {
            if (m_model.RecordingProjectName != RecordingProjectName && File.Exists(Project.GetProjectFilePath(IsoCode, PublicationId, RecordingProjectName)))
            {
                var msg = string.Format(LocalizationManager.GetString("DialogBoxes.ProjectSettingsDlg.OverwriteProjectPrompt",
                                                                      "A {0} project with an ID of {1} and a Recording Project Name of {2} already exists for this language. Do you want to overwrite it?"),
                                        ProductName, m_txtPublicationId.Text, RecordingProjectName);
                if (MessageBox.Show(this, msg, ProductName, MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) != DialogResult.Yes)
                {
                    DialogResult = DialogResult.None;
                    return;
                }

                var existingProjectPath = Project.GetProjectFolderPath(IsoCode, PublicationId, RecordingProjectName);
                if (!RobustIO.DeleteDirectoryAndContents(existingProjectPath))
                {
                    var failedMsg = string.Format(LocalizationManager.GetString("DialogBoxes.ProjectSettingsDlg.OverwriteProjectFailed",
                                                                                "{0} was unable to delete all of the files in {1}. You can try to clean this up manually and then re-attempt saving these changes."),
                                                  ProductName, existingProjectPath);
                    MessageBox.Show(this, failedMsg, ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }
            }

            m_model.RecordingProjectName     = RecordingProjectName;
            m_model.AudioStockNumber         = AudioStockNumber;
            m_model.ChapterAnnouncementStyle = ChapterAnnouncementStyle;
            m_model.SkipChapterAnnouncementForFirstChapter = !m_chkChapterOneAnnouncements.Checked;
            m_model.Project.ReferenceTextProxy             = ((KeyValuePair <string, ReferenceTextProxy>)m_ReferenceText.SelectedItem).Value;

            m_model.Project.DramatizationPreferences.BookIntroductionsDramatization   = (ExtraBiblicalMaterialSpeakerOption)m_bookIntro.SelectedValue;
            m_model.Project.DramatizationPreferences.SectionHeadDramatization         = (ExtraBiblicalMaterialSpeakerOption)m_sectionHeadings.SelectedValue;
            m_model.Project.DramatizationPreferences.BookTitleAndChapterDramatization = (ExtraBiblicalMaterialSpeakerOption)m_titleChapters.SelectedValue;

            m_model.Project.ProjectSettingsStatus = ProjectSettingsStatus.Reviewed;
            DialogResult = DialogResult.OK;
            Close();
        }
Beispiel #20
0
        public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection)
        {
            lock (LockObject)
            {
                // We need to forget any cached version of the XML. Otherwise, when more than one lot of settings
                // is saved in the same file, the provider that is doing the save for one of them may have stale
                // (or missing) settings for the other. We want to write the dirty properties over a current
                // version of everything else that has been saved in the file.
                _settingsXml = null;

                //Iterate through the settings to be stored, only dirty settings for this provider are in collection
                foreach (SettingsPropertyValue propval in collection)
                {
                    var groupName = context["GroupName"].ToString();
                    var groupNode = SettingsXml.SelectSingleNode("/configuration/userSettings/" + context["GroupName"]);
                    if (groupNode == null)
                    {
                        var parentNode = SettingsXml.SelectSingleNode("/configuration/userSettings");
                        groupNode = SettingsXml.CreateElement(groupName);
                        parentNode.AppendChild(groupNode);
                    }
                    var section = (XmlElement)SettingsXml.SelectSingleNode("/configuration/configSections/sectionGroup/section");
                    if (section == null)
                    {
                        var parentNode = SettingsXml.SelectSingleNode("/configuration/configSections/sectionGroup");
                        section = SettingsXml.CreateElement("section");
                        section.SetAttribute("name", groupName);
                        section.SetAttribute("type", String.Format("{0}, {1}", typeof(ClientSettingsSection), Assembly.GetAssembly(typeof(ClientSettingsSection))));
                        parentNode.AppendChild(section);
                    }
                    SetValue(groupNode, propval);
                }
                Directory.CreateDirectory(UserConfigLocation);
                RobustIO.SaveXml(SettingsXml, Path.Combine(UserConfigLocation, UserConfigFileName));
            }
        }
Beispiel #21
0
        public void MigrateOldConfigurationsIfNeeded_PreservesOrderOfBibliographies()
        {
            var configSettingsDir = LcmFileHelper.GetConfigSettingsDir(Path.GetDirectoryName(Cache.ProjectId.Path));
            var newConfigFilePath = Path.Combine(configSettingsDir, DictionaryConfigurationListener.ReversalIndexConfigurationDirectoryName,
                                                 "AllReversalIndexes" + DictionaryConfigurationModel.FileExtension);

            Assert.False(File.Exists(newConfigFilePath), "should not yet be migrated");
            Directory.CreateDirectory(configSettingsDir);
            File.WriteAllLines(Path.Combine(configSettingsDir, "Test.fwlayout"), new[] {
                @"<layoutType label='All Reversal Indexes' layout='publishReversal'>",
                @"<configure class='ReversalIndexEntry' label='Reversal Entry' layout='publishReversalEntry' /></layoutType>'"
            });
            var migrator = new DictionaryConfigurationMigrator(m_propertyTable, m_mediator);

            migrator.MigrateOldConfigurationsIfNeeded();             // SUT
            var updatedConfigModel = new DictionaryConfigurationModel(newConfigFilePath, Cache);
            var refdSenseChildren  = updatedConfigModel.Parts[0].Children.Find(n => n.Label == "Referenced Senses").Children;
            var bibCount           = 0;

            for (var i = 0; i < refdSenseChildren.Count; i++)
            {
                var bibNode = refdSenseChildren[i];
                if (!bibNode.Label.StartsWith("Bibliography"))
                {
                    continue;
                }
                StringAssert.StartsWith("Bibliography (", bibNode.Label, "Should specify (entry|sense), lest we never know");
                Assert.False(bibNode.IsCustomField, bibNode.Label + " should not be custom.");
                // Rough test to ensure Bibliography nodes aren't bumped to the end of the list. In the defaults, the later Bibliography
                // node is a little more than five nodes from the end
                Assert.LessOrEqual(i, refdSenseChildren.Count - 5, "Bibliography nodes should not have been bumped to the end of the list");
                ++bibCount;
            }
            Assert.AreEqual(2, bibCount, "Should be exactly two Bibliography nodes (sense and entry)");
            RobustIO.DeleteDirectoryAndContents(configSettingsDir);
        }
Beispiel #22
0
        public void CompressExportedFiles_IncludesAcceptableMediaTypes()
        {
            var view = new MockWebonaryDlg();

            var tempDirectoryToCompress = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());

            Directory.CreateDirectory(tempDirectoryToCompress);
            try
            {
                var zipFileToUpload = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());

                // TIFF
                var tiffFilename    = Path.GetFileName(Path.GetTempFileName() + ".tif");
                var tiffPath        = Path.Combine(tempDirectoryToCompress, tiffFilename);
                var tiffMagicNumber = new byte[] { 0x49, 0x49, 0x2A };
                File.WriteAllBytes(tiffPath, tiffMagicNumber);

                // JPEG
                var jpegFilename    = Path.GetFileName(Path.GetTempFileName() + ".jpg");
                var jpegPath        = Path.Combine(tempDirectoryToCompress, jpegFilename);
                var jpegMagicNumber = new byte[] { 0xff, 0xd8 };
                File.WriteAllBytes(jpegPath, jpegMagicNumber);

                // MP4
                var mp4Filename    = Path.GetFileName(Path.GetTempFileName() + ".mp4");
                var mp4Path        = Path.Combine(tempDirectoryToCompress, mp4Filename);
                var mp4MagicNumber = new byte[] { 0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32 };
                File.WriteAllBytes(mp4Path, mp4MagicNumber);

                var xhtmlFilename = Path.GetFileName(Path.GetTempFileName() + ".xhtml");
                var xhtmlPath     = Path.Combine(tempDirectoryToCompress, xhtmlFilename);
                var xhtmlContent  = "<xhtml/>";
                File.WriteAllText(xhtmlPath, xhtmlContent);

                // SUT
                UploadToWebonaryController.CompressExportedFiles(tempDirectoryToCompress, zipFileToUpload, view);

                // Verification
                const string unsupported      = ".*nsupported.*";
                const string unsupportedRegex = ".*{0}" + unsupported;
                using (var uploadZip = new ZipFile(zipFileToUpload))
                {
                    Assert.False(uploadZip.EntryFileNames.Contains(tiffFilename), "Should not have included unsupported TIFF file in file to upload.");
                    Assert.True(uploadZip.EntryFileNames.Contains(jpegFilename), "Should have included supported JPEG file in file to upload.");
                    Assert.True(uploadZip.EntryFileNames.Contains(mp4Filename), "Should have included supported MP4 file in file to upload.");
                }

                var query = string.Format(unsupportedRegex, tiffFilename);
                Assert.True(view.StatusStrings.Exists(statusString => Regex.Matches(statusString, query).Count == 1), "Lack of support for the tiff file should have been reported to the user.");
                query = string.Format(unsupportedRegex, jpegFilename);
                Assert.False(view.StatusStrings.Exists(statusString => Regex.Matches(statusString, query).Count == 1), "Should not have reported lack of support for the jpeg file.");
                query = string.Format(unsupportedRegex, mp4Filename);
                Assert.False(view.StatusStrings.Exists(statusString => Regex.Matches(statusString, query).Count == 1), "Should not have reported lack of support for the mp4 file.");

                Assert.That(view.StatusStrings.Count(statusString => Regex.Matches(statusString, unsupported).Count > 0), Is.EqualTo(1), "Too many unsupported files reported.");
            }
            finally
            {
                RobustIO.DeleteDirectoryAndContents(tempDirectoryToCompress);
            }
        }
Beispiel #23
0
        ///<summary>
        /// Perform the migration
        ///</summary>
        ///<exception cref="InvalidOperationException"></exception>
        public virtual void Migrate()
        {
            var problems = new List <FolderMigratorProblem>();

            // Clean up root backup path
            if (Directory.Exists(MigrationPath))
            {
                if (!RobustIO.DeleteDirectoryAndContents(MigrationPath))
                {
                    //for user, ah well
                    //for programmer, tell us
                    Debug.Fail("(Debug mode only) Couldn't delete the migration folder");
                }
            }

            //check if there is anything to migrate. If not, don't do anything
            if (!Directory.Exists(SourcePath))
            {
                return;
            }


            if (GetLowestVersionInFolder(SourcePath) == ToVersion)
            {
                return;
            }

            // Backup current folder to backup path under backup root
            CopyDirectory(SourcePath, BackupPath, MigrationPath);

            string currentPath = BackupPath;
            int    lowestVersionInFolder;
            int    lowestVersoinInFolder1 = -1;

            while ((lowestVersionInFolder = GetLowestVersionInFolder(currentPath)) != ToVersion)
            {
                //This guards against an empty Folder
                if (lowestVersionInFolder == int.MaxValue)
                {
                    break;
                }
                if (lowestVersionInFolder == lowestVersoinInFolder1)
                {
                    var fileNotMigrated = _versionCache.First(info => info.Version == lowestVersionInFolder).FileName;
                    throw new ApplicationException(
                              String.Format("The migration strategy for {0} failed to migrate the file '{1} from version {2}",
                                            SearchPattern, fileNotMigrated,
                                            lowestVersoinInFolder1)
                              );
                }
                int currentVersion = lowestVersionInFolder;
                // Get all files in folder with this version
                var fileNamesToMigrate = GetFilesOfVersion(currentVersion, currentPath);

                // Find a strategy to migrate this version
                IMigrationStrategy strategy = MigrationStrategies.Find(s => s.FromVersion == currentVersion);
                if (strategy == null)
                {
                    throw new InvalidOperationException(
                              String.Format("No migration strategy could be found for version {0}", currentVersion)
                              );
                }
                string destinationPath = Path.Combine(
                    MigrationPath, String.Format("{0}_{1}", strategy.FromVersion, strategy.ToVersion)
                    );
                Directory.CreateDirectory(destinationPath);
                // Migrate all the files of the current version
                foreach (var filePath in Directory.GetFiles(currentPath, SearchPattern))
                {
                    string fileName = Path.GetFileName(filePath);
                    if (fileName == null)
                    {
                        continue;
                    }
                    string sourceFilePath = Path.Combine(currentPath, fileName);
                    string targetFilePath = Path.Combine(destinationPath, fileName);
                    if (fileNamesToMigrate.Contains(sourceFilePath))
                    {
                        strategy.Migrate(sourceFilePath, targetFilePath);
                    }
                    else
                    {
                        File.Copy(sourceFilePath, targetFilePath);
                    }
                }

                try
                {
                    strategy.PostMigrate(currentPath, destinationPath);
                }
                catch (Exception e)
                {
                    problems.Add(new FolderMigratorProblem {
                        Exception = e, FilePath = currentPath
                    });
                }

                // Setup for the next iteration
                currentPath            = destinationPath;
                lowestVersoinInFolder1 = lowestVersionInFolder;
            }

            // Delete all the files in SourcePath matching SearchPattern
            foreach (var filePath in Directory.GetFiles(SourcePath, SearchPattern))
            {
                File.Delete(filePath);
            }

            // Copy the migration results into SourcePath
            CopyDirectory(currentPath, SourcePath, "");
            if (!RobustIO.DeleteDirectoryAndContents(MigrationPath))
            {
                //for user, ah well
                //for programmer, tell us
                Debug.Fail("(Debug mode only) Couldn't delete the migration folder");
            }

            // Call the problem handler if we encountered problems during migration.
            if (problems.Count > 0)
            {
                _problemHandler(problems);
            }
        }
Beispiel #24
0
		public static void Cleanup()
		{
			RobustIO.DeleteDirectoryAndContents(BaseDir);
		}
Beispiel #25
0
        private static void Wizard(Options.WizardOptions wizardOptions)
        {
            var workdir = wizardOptions.WorkDir;

            MongoHelper.AddTestUser(Settings.DataDir);

            for (var modelVersion = wizardOptions.MinModel;
                 modelVersion <= wizardOptions.MaxModel;
                 modelVersion++)
            {
                if (modelVersion == 7000071)
                {
                    continue;
                }

                Console.WriteLine(
                    "--------------------------------------------------------------");
                Console.WriteLine($"Processing model version {modelVersion}");

                if (string.IsNullOrEmpty(workdir))
                {
                    wizardOptions.WorkDir =
                        Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
                    Settings.TempDir = wizardOptions.WorkDir;
                    Directory.CreateDirectory(wizardOptions.WorkDir);
                }

                wizardOptions.ModelVersion = modelVersion;
                var patchDir = Path.Combine(Settings.DataDir, modelVersion.ToString());
                if (!Directory.Exists(patchDir))
                {
                    Directory.CreateDirectory(patchDir);
                }
                var mongoDir = Path.Combine(patchDir, "mongo");
                if (!Directory.Exists(mongoDir))
                {
                    Directory.CreateDirectory(mongoDir);
                }

                var outputDir = Path.Combine(wizardOptions.FwRoot, $"Output{modelVersion}");
                if (!Directory.Exists(outputDir))
                {
                    Console.WriteLine($"Can't find FW output directory {outputDir}");
                    continue;
                }

                var sourceEnviron = modelVersion < 7000072 ? "fwenviron" : "fwenviron9";
                var targetEnviron = Path.Combine(outputDir, "fwenviron");
                if (!File.Exists(targetEnviron))
                {
                    File.Copy(
                        Path.Combine(Path.GetDirectoryName(typeof(Program).Assembly.Location),
                                     sourceEnviron),
                        targetEnviron);
                }

                // LanguageDepot
                if (wizardOptions.NewProject)
                {
                    wizardOptions.LanguageDepotVersion = 0;
                    wizardOptions.MongoVersion         = 0;
                    // Delete FW project
                    RobustIO.DeleteDirectoryAndContents(
                        Path.Combine(wizardOptions.GetFwProjectDirectory(modelVersion),
                                     $"test-{modelVersion}"));
                    RobustIO.DeleteDirectoryAndContents(
                        Path.Combine(wizardOptions.UsbDirectory, $"test-{modelVersion}"));

                    Console.WriteLine(
                        $"Create a new project 'test-{modelVersion}' and send this project for the first time to the USB stick");
                    Run("/bin/bash",
                        $"-i -c \"cd {outputDir} && . fwenviron && cd Output_$(uname -m)/Debug && mono --debug FieldWorks.exe > /dev/null\"",
                        outputDir, true);
                }
                else
                {
                    Console.WriteLine($"Restoring version {wizardOptions.LanguageDepotVersion} " +
                                      $"of chorus repo for model {modelVersion}");
                    RestoreLanguageDepot(new Options.RestoreOptions(wizardOptions));
                    DirectoryHelper.Copy(Path.Combine(wizardOptions.WorkDir,
                                                      "LanguageDepot", ".hg"),
                                         Path.Combine(wizardOptions.UsbDirectory, $"test-{modelVersion}", ".hg"),
                                         true);
                    // Delete FW project
                    RobustIO.DeleteDirectoryAndContents(
                        Path.Combine(wizardOptions.GetFwProjectDirectory(modelVersion),
                                     $"test-{modelVersion}"));

                    Console.WriteLine(
                        $"Now get project 'test-{modelVersion}' from USB stick and make changes and afterwards do a s/r with the USB stick.");
                    // for whatever reason we have to pass -i, otherwise 7000069 won't be able to bring
                    // up the s/r dialog!
                    Run("/bin/bash",
                        $"-i -c \"cd {outputDir} && . fwenviron && cd Output_$(uname -m)/Debug && mono --debug FieldWorks.exe > /dev/null\"",
                        outputDir, true);
                }

                Console.WriteLine($"Saving chorus repo test data for {modelVersion}");
                DirectoryHelper.Copy(
                    Path.Combine(wizardOptions.UsbDirectory, $"test-{modelVersion}", ".hg"),
                    Path.Combine(wizardOptions.WorkDir, "LanguageDepot", ".hg"), true);
                SaveLanguageDepot(new Options.SaveOptions(wizardOptions)
                {
                    WorkDir   = null,
                    CommitMsg = wizardOptions.CommitMsg ?? "New test data"
                });

                SaveLanguageDepotNoOpPatchIfNecessary(modelVersion,
                                                      wizardOptions.LanguageDepotVersion + 1);

                // Mongo
                if (wizardOptions.NewProject)
                {
                    // Since this is a new project, we copy the first patch for mongo from an
                    // older model version, if we can find it
                    int tmpModelVersion;
                    var mongoPatchFile = string.Empty;
                    for (tmpModelVersion = Settings.MinModelVersion;
                         tmpModelVersion <= Settings.MaxModelVersion;
                         tmpModelVersion++)
                    {
                        var dirInfo    = new DirectoryInfo(Path.Combine(Settings.DataDir, tmpModelVersion.ToString(), "mongo"));
                        var patchFiles = dirInfo.GetFiles("0001-*.patch");
                        if (patchFiles.Length != 1)
                        {
                            continue;
                        }

                        mongoPatchFile = patchFiles[0].FullName;
                        break;
                    }

                    if (tmpModelVersion > Settings.MaxModelVersion)
                    {
                        Console.WriteLine(
                            "ERROR: Can't find first mongo patch. Please create mongo project some other way.");
                        return;
                    }

                    MongoHelper.InitializeNewProject(modelVersion.ToString(), mongoPatchFile);

                    Console.WriteLine($"Saving LanguageForge test data for {modelVersion}");
                    SaveMongoDb(new Options.SaveOptions(wizardOptions)
                    {
                        CommitMsg = "Add empty project"
                    });
                }
                else
                {
                    Console.WriteLine(
                        $"Restoring LanguageForge mongo data version {wizardOptions.MongoVersion} for model {modelVersion}");
                    RestoreMongoDb(new Options.RestoreOptions(wizardOptions));

                    Console.WriteLine(
                        $"Now make the changes to '{wizardOptions.Project}' in your local" +
                        " LanguageForge, then press return. Don't do a send/receive!");

                    Console.WriteLine(
                        "(You might have to empty the cached IndexedDB data in your " +
                        "browser's developer tools)");
                    Console.WriteLine("Login as user 'test' with password 'passwordfortest'");
                    Run("/bin/bash", "-c \"xdg-open http://languageforge.local/app/projects\"",
                        outputDir);
                    Console.ReadLine();

                    Console.WriteLine($"Saving LanguageForge test data for {modelVersion}");
                    SaveMongoDb(new Options.SaveOptions(wizardOptions)
                    {
                        CommitMsg = wizardOptions.CommitMsg ?? "New test data"
                    });

                    // Merge the data we just created
                    Console.WriteLine($"Merge test data for {modelVersion}");
                    Merge(new Options.MergeOptions(wizardOptions)
                    {
                        LanguageDepotVersion = wizardOptions.LanguageDepotVersion + 1,
                        MongoVersion         = wizardOptions.MongoVersion + 1
                    });
                }

                if (string.IsNullOrEmpty(workdir))
                {
                    // we created a temporary workdir, so delete it again
                    RobustIO.DeleteDirectoryAndContents(wizardOptions.WorkDir);
                }
            }
        }
 public static void Cleanup()
 {
     RobustIO.DeleteDirectoryAndContents(Path.Combine(Settings.TempDir, "patches"));
 }
        public void OneTimeSetup()
        {
            _repoFolder       = new TemporaryFolder("SyncAtStartup_Repo");
            _collectionFolder = new TemporaryFolder("SyncAtStartup_Local");
            FolderTeamCollection.CreateTeamCollectionLinkFile(_collectionFolder.FolderPath,
                                                              _repoFolder.FolderPath);
            _mockTcManager           = new Mock <ITeamCollectionManager>();
            _tcLog                   = new TeamCollectionMessageLog(TeamCollectionManager.GetTcLogPathFromLcPath(_collectionFolder.FolderPath));
            _collection              = new FolderTeamCollection(_mockTcManager.Object, _collectionFolder.FolderPath, _repoFolder.FolderPath, tcLog: _tcLog);
            _collection.CollectionId = Bloom.TeamCollection.TeamCollection.GenerateCollectionId();
            TeamCollectionManager.ForceCurrentUserForTests("*****@*****.**");

            // Simulate a book that was once shared, but has been deleted from the repo folder (has a tombstone).
            MakeBook("Should be deleted", "This should be deleted as it has local status but is not shared", true);
            var bookFolderPath = Path.Combine(_collectionFolder.FolderPath, "Should be deleted");

            _collection.DeleteBookFromRepo(bookFolderPath);

            // Simulate a book that was once shared, but has been deleted from the repo folder. But there is no tombstone.
            // (Despite the name, it is only converted to a new local in the default case. When we do a First Time Join,
            // it just gets copied into the repo.)
            MakeBook("Should be converted to new local", "This should become a new local (no status) book as it has local status but is not in the repo", true);
            var delPath = Path.Combine(_repoFolder.FolderPath, "Books", "Should be converted to new local.bloom");

            RobustFile.Delete(delPath);

            // Simulate a book newly created locally. Not in repo, but should not be deleted.
            MakeBook("A book", "This should survive as it has no local status", false);
            // By the way, like most new books, it got renamed early in life...twice
            SimulateRename(_collection, "A book", "An early name");
            SimulateRename(_collection, "An early name", "New book");

            // Simulate a book that needs nothing done to it. It's the same locally and on the repo.
            MakeBook("Keep me", "This needs nothing done to it");

            // Simulate a book that is checked out locally to the current user, but the file has
            // been deleted on the repo.
            MakeBook("Keep me too", "This also needs nothing done", false);
            _collection.WriteLocalStatus("Keep me too", new BookStatus().WithLockedBy("*****@*****.**"));

            // Simlulate a book that is only in the team repo
            MakeBook("Add me", "Fetch to local");
            var delPathAddMe = Path.Combine(_collectionFolder.FolderPath, "Add me");

            SIL.IO.RobustIO.DeleteDirectoryAndContents(delPathAddMe);

            // Simulate a book that was checked in, then checked out again and renamed,
            // but not yet checked in. Both "A renamed book" folder and content and "An old name.bloom"
            // should survive. (Except for an obscure reason when joining a TC...see comment in the test.)
            MakeBook("An old name", "Should be kept in both places with different names");
            _collection.AttemptLock("An old name", "*****@*****.**");
            SimulateRename(_collection, "An old name", "an intermediate name");
            SimulateRename(_collection, "an intermediate name", "A renamed book");

            // Simulate a book that is not checked out locally and has been modified elsewhere
            MakeBook("Update me", "Needs to be become this locally");
            UpdateLocalBook("Update me", "This is supposed to be an older value, not edited locally");

            // Simulate a book that is checked out locally but not in the repo, and where the saved local
            // checksum equals the repo checksum, and it is not checked out in the repo. This would
            // typically indicate that someone remote forced a checkout, perhaps while this user was
            // offline, but checked in again without making changes.
            // Also pretend it has been modified locally.
            // Test result: collection is updated to indicate the local checkout. Local changes are not lost.
            MakeBook("Check me out", "Local and remote checksums correspond to this");
            UpdateLocalBook("Check me out", "This is supposed to be a newer value from local editing", false);
            var oldLocalStatus = _collection.GetLocalStatus("Check me out");
            var newLocalStatus = oldLocalStatus.WithLockedBy(Bloom.TeamCollection.TeamCollectionManager.CurrentUser);

            _checkMeOutOriginalChecksum = oldLocalStatus.checksum;
            _collection.WriteLocalStatus("Check me out", newLocalStatus);

            // Simulate a book that appears newly-created locally (no local status) but is also in the
            // repo. This would indicate two people coincidentally creating a book with the same name.
            // Test result: the local book should get renamed (both folder and htm).
            // When merging while joining a new TC, this case is treated as a conflict and the
            // local book is moved to Lost and Found.
            MakeBook("Rename local", "This content is on the server");
            _collection.AttemptLock("Rename local", "*****@*****.**");
            UpdateLocalBook("Rename local", "This is a new book created independently");
            var statusFilePath = Bloom.TeamCollection.TeamCollection.GetStatusFilePath("Rename local", _collectionFolder.FolderPath);

            RobustFile.Delete(statusFilePath);

            // Simulate a book that is checked out locally but also checked out, to a different user
            // or machine, on the repo. This would indicate some sort of manual intervention, perhaps
            // while this user was long offline. The book has not been modified locally, but the local
            // status is out of date.
            // Test result: local status is updated to reflect the remote checkout, book content updated to repo.
            MakeBook("Update and undo checkout", "This content is everywhere");
            _collection.AttemptLock("Update and undo checkout", "*****@*****.**");
            _collection.WriteLocalStatus("Update and undo checkout", _collection.GetStatus("Update and undo checkout").WithLockedBy(Bloom.TeamCollection.TeamCollectionManager.CurrentUser));

            // Simulate a book that is checked out locally and not on the server, but the repo and (old)
            // local checksums are different. The book has not been edited locally.
            // Test result: book is updated to match repo. Local and remote status should match...review: which wins?
            MakeBook("Update and checkout", "This content is on the server");
            UpdateLocalBook("Update and checkout", "This simulates older content changed remotely but not locally");
            _collection.WriteLocalStatus("Update and checkout", _collection.GetLocalStatus("Update and checkout").WithLockedBy(Bloom.TeamCollection.TeamCollectionManager.CurrentUser));

            // Simulate a book that is checked out and modified locally, but has also been modified
            // remotely.
            // Test result: current local state is saved in lost-and-found. Repo version of book and state
            // copied to local. Warning to user.
            MakeBook("Update content and status and warn", "This simulates new content on server");
            _collection.AttemptLock("Update content and status and warn", "*****@*****.**");
            UpdateLocalBook("Update content and status and warn", "This is supposed to be the newest value from local editing");
            var newStatus = _collection.GetStatus("Update content and status and warn").WithLockedBy(Bloom.TeamCollection.TeamCollectionManager.CurrentUser)
                            .WithChecksum("different from either");

            _collection.WriteLocalStatus("Update content and status and warn", newStatus);

            // Simulate a book that is checked out and modified locally, but is also checked out by another
            // user or machine in the repo. It has not (yet) been modified remotely.
            // Test result: current local state is saved in lost-and-found. Repo version of book and state
            // copied to local. Warning to user.
            MakeBook("Update content and status and warn2", "This simulates new content on server");
            _collection.AttemptLock("Update content and status and warn2", "*****@*****.**");
            UpdateLocalBook("Update content and status and warn2", "This is supposed to be the newest value from local editing", false);
            newStatus = _collection.GetStatus("Update content and status and warn2").WithLockedBy(Bloom.TeamCollection.TeamCollectionManager.CurrentUser);
            _collection.WriteLocalStatus("Update content and status and warn2", newStatus);

            // Simulate a book which has no local status, but for which the computed checksum matches
            // the repo one. This could happen if a user obtained the same book independently,
            // or during initial merging of a local and team collection, where much of the material
            // was previously duplicated.
            // Test result: status is copied to local
            MakeBook("copy status", "Same content in both places");
            _collection.AttemptLock("copy status", "*****@*****.**");
            statusFilePath = Bloom.TeamCollection.TeamCollection.GetStatusFilePath("copy status", _collectionFolder.FolderPath);
            RobustFile.Delete(statusFilePath);

            // Simulate a book that was copied from another TC, using File Explorer.
            // It therefore has a book.status file, but with a different guid.
            // Test result: it should survive, and on a new collection sync get copied into the repo
            var copiedEx = "copied with Explorer";

            MakeBook(copiedEx, "This content is only local", false);
            _collection.WriteLocalStatus(copiedEx, new BookStatus(), collectionId: Bloom.TeamCollection.TeamCollection.GenerateCollectionId());

            // Simulate a book that appeared in DropBox when their software found a conflict.
            // It should NOT be copied locally, but instead moved to Lost and Found, with a report.
            MakeBook(kConflictName, "This content is only on the repo, apart from conflicting copies");
            var conflictFolderPath = Path.Combine(_collectionFolder.FolderPath, kConflictName);

            SIL.IO.RobustIO.DeleteDirectoryAndContents(conflictFolderPath);

            _collection.WriteLocalStatus(copiedEx, new BookStatus(), collectionId: Bloom.TeamCollection.TeamCollection.GenerateCollectionId());

            // Simulate a corrupt zip file, only in the repo
            File.WriteAllText(Path.Combine(_repoFolder.FolderPath, "Books", "new corrupt book.bloom"), "This is not a valid zip!");

            // Simulate a corrupt zip file that corresponds to a local book.
            var badZip = "has a bad zip in repo";

            MakeBook(badZip, "This book seems to be in both places, but the repo is corrupt");
            File.WriteAllText(Path.Combine(_repoFolder.FolderPath, "Books", badZip + ".bloom"), "This is also not a valid zip!");

            // Simulate a book that was renamed remotely. That is, there's a local book Old Name, with local status,
            // and there's no repo book by that name, but there's a repo book New Name (and no such local book).
            // The book's meta.json indicates they are the same book.
            // We'll initially make both, with the new name and new content.
            MakeBook(kNewNameForRemoteRename, "This is the new book content after remote editing and rename");
            var oldFolder = Path.Combine(_collectionFolder.FolderPath, kBookRenamedRemotely);
            var newFolder = Path.Combine(_collectionFolder.FolderPath, kNewNameForRemoteRename);

            RobustIO.MoveDirectory(newFolder, oldFolder);              // made at new path, simulate still at old.
            var oldPath = Path.Combine(_collectionFolder.FolderPath, kBookRenamedRemotely,
                                       kBookRenamedRemotely + ".htm"); // simulate old book name and content

            RobustFile.WriteAllText(oldPath, "This is the simulated original book content");
            RobustFile.Delete(Path.Combine(_collectionFolder.FolderPath, kBookRenamedRemotely,
                                           kNewNameForRemoteRename + ".htm")); // get rid of the 'new' content

            // Simulate a book that is in the repo, where there is a local book that has no status, a different name,
            // and the same ID. This might indicate (a) that it was renamed by someone else after this user's pre-TC
            // copy of the collection diverged; (b) that it was renamed by this user after the divergence;
            // (c) that they were independently copied from some common-ID source.
            // We will treat this as a conflict, moving the local version to lost and found, even on a first time join.
            MakeBook(kRepoNameForIdConflict, "This is the repo version of a book that has a no-status local book with the same ID.");
            // Move the local version to a new folder
            var oldFolder2 = Path.Combine(_collectionFolder.FolderPath, kRepoNameForIdConflict);
            var newFolder2 = Path.Combine(_collectionFolder.FolderPath, kLocalNameForIdConflict);

            RobustIO.MoveDirectory(oldFolder2, newFolder2);
            var localStatusPath =
                Bloom.TeamCollection.TeamCollection.GetStatusFilePath(kLocalNameForIdConflict,
                                                                      _collectionFolder.FolderPath);

            RobustFile.Delete(localStatusPath);

            // Make a couple of folders that are legitimately present, but not books.
            var allowedWords = Path.Combine(_collectionFolder.FolderPath, "Allowed Words");

            Directory.CreateDirectory(allowedWords);
            File.WriteAllText(Path.Combine(allowedWords, "some sample.txt"), "This a fake word list");
            var sampleTexts = Path.Combine(_collectionFolder.FolderPath, "Sample Texts");

            Directory.CreateDirectory(sampleTexts);
            File.WriteAllText(Path.Combine(sampleTexts, "a sample.txt"), "This a fake sample text");

            _progressSpy = new ProgressSpy();

            // sut for the whole suite!
            Assert.That(_collection.SyncAtStartup(_progressSpy, FirstTimeJoin()), Is.True);
        }
Beispiel #28
0
        private static bool UpgradeToCurrentDataFormatVersion(ApplicationMetadata info)
        {
            if (info.DataVersion >= Settings.Default.DataFormatVersion)
            {
                return(false);
            }

            bool retVal = true;

            Analytics.Track("DataVersionUpgrade", new Dictionary <string, string>
            {
                { "old", info.DataVersion.ToString(CultureInfo.InvariantCulture) },
                { "new", Settings.Default.DataFormatVersion.ToString(CultureInfo.InvariantCulture) }
            });

            switch (info.DataVersion)
            {
            case 0:
                foreach (var publicationFolder in Project.AllPublicationFolders)
                {
                    var filesToMove = Directory.GetFiles(publicationFolder);
                    if (!filesToMove.Any())
                    {
                        continue;
                    }

                    var projectFilePath = Directory.GetFiles(publicationFolder, "*" + kOldProjectExtension).FirstOrDefault();
                    if (projectFilePath != null)
                    {
                        Exception exception;
                        var       metadata = GlyssenDblTextMetadata.Load <GlyssenDblTextMetadata>(projectFilePath, out exception);
                        string    recordingProjectName;
                        if (exception != null)
                        {
                            // Just add a directory layer and don't worry about it for now.
                            recordingProjectName = Path.GetFileName(publicationFolder);
                        }
                        else
                        {
                            if (metadata.Identification != null && !string.IsNullOrEmpty(metadata.Identification.Name))
                            {
                                recordingProjectName = metadata.Identification.Name;
                            }
                            else
                            {
                                recordingProjectName = metadata.Id;
                            }
                        }
                        recordingProjectName = Project.GetDefaultRecordingProjectName(recordingProjectName);
                        var recordingProjectFolder = Path.Combine(publicationFolder, recordingProjectName);
                        Directory.CreateDirectory(recordingProjectFolder);
                        foreach (var file in filesToMove)
                        {
                            File.Move(file, Path.Combine(recordingProjectFolder, Path.GetFileName(file)));
                        }
                        if (Settings.Default.CurrentProject == projectFilePath)
                        {
                            Settings.Default.CurrentProject = Path.Combine(recordingProjectFolder,
                                                                           Path.GetFileName(projectFilePath));
                        }
                    }
                }
                goto case 1;

            case 1:
                foreach (var recordingProjectFolder in Project.AllRecordingProjectFolders.ToList())
                {
                    var versificationPath = Path.Combine(recordingProjectFolder, DblBundleFileUtils.kVersificationFileName);
                    if (!File.Exists(versificationPath))
                    {
                        var projectFilePath = Directory.GetFiles(recordingProjectFolder, "*" + kOldProjectExtension).FirstOrDefault();

                        if (projectFilePath != null)
                        {
                            if (projectFilePath.Equals(SampleProject.SampleProjectFilePath, StringComparison.OrdinalIgnoreCase))
                            {
                                File.WriteAllText(versificationPath, Resources.EnglishVersification);
                            }
                            else
                            {
                                Exception exception;
                                var       metadata       = GlyssenDblTextMetadata.Load <GlyssenDblTextMetadata>(projectFilePath, out exception);
                                var       origBundlePath = metadata.OriginalReleaseBundlePath;
                                if (string.IsNullOrEmpty(origBundlePath))
                                {
                                    // Note: We didn't support Paratext-based projects until settings version 3 (Glyssen 1.1),
                                    // so for this step in the migration process (going from 0 to 1), any project without
                                    // OriginalReleaseBundlePath set is invalid (possibly from a really early version of Glyssen
                                    // or some g;itch arising from development activity or external mangling of the file). So
                                    // we should be able to safely blow this away.
                                    try
                                    {
                                        Project.DeleteProjectFolderAndEmptyContainingFolders(recordingProjectFolder, true);
                                    }
                                    catch (Exception)
                                    {
                                        // Oh, well, we tried. Not the end of the world.
                                    }
                                }
                                else
                                {
                                    var bundle       = new GlyssenBundle(origBundlePath);
                                    var errorlogPath = Path.Combine(recordingProjectFolder, "errorlog.txt");
                                    bundle.CopyVersificationFile(versificationPath);
                                    try
                                    {
                                        ProjectBase.LoadVersification(versificationPath);
                                    }
                                    catch (InvalidVersificationLineException ex)
                                    {
                                        var msg = string.Format(LocalizationManager.GetString("DataMigration.InvalidVersificationFile",
                                                                                              "Invalid versification file encountered during data migration. Errors must be fixed or subsequent " +
                                                                                              "attempts to open this project will fail.\r\n" +
                                                                                              "Project: {0}\r\n" +
                                                                                              "Text release Bundle: {1}\r\n" +
                                                                                              "Versification file: {2}\r\n" +
                                                                                              "Error: {3}"),
                                                                projectFilePath, origBundlePath, versificationPath, ex.Message);
                                        MessageBox.Show(msg, GlyssenInfo.kProduct, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                                        File.WriteAllText(errorlogPath, msg);
                                    }
                                }
                            }
                        }
                    }
                }
                goto case 2;

            case 2:
                foreach (var pgProjFile in Project.AllRecordingProjectFolders.SelectMany(d => Directory.GetFiles(d, "*" + kOldProjectExtension)))
                {
                    var newName = Path.ChangeExtension(pgProjFile, Constants.kProjectFileExtension);
                    File.Move(pgProjFile, newName);
                    if (Settings.Default.CurrentProject == pgProjFile)
                    {
                        Settings.Default.CurrentProject = newName;
                    }
                }
                break;                         // No need to go to case 3, since the problem it fixes would only have been caused by a version of Glyssen with data version 3

            case 3:
                try
                {
                    RobustIO.DeleteDirectory(Path.GetDirectoryName(SampleProject.SampleProjectFilePath) + " Audio", true);
                }
                catch (IOException e)
                {
                    Logger.WriteError("Unable to clean up superfluous sample Audio Audio folder.", e);
                }
                var safeReplacements   = new List <Tuple <string, string> >();
                var unsafeReplacements = new List <Tuple <string, string> >();
                foreach (var folder in Project.AllRecordingProjectFolders.Where(d => d.EndsWith(" Audio Audio")))
                {
                    // Because of the way this bug (PG-1192) worked, the most likely thing is that the "correct"
                    // version of the project will have been initially created but then all the actual work will
                    // have gotten saved into the "incorrect" version. If this looks to be the case, we can
                    // probably safely delete the correct one and then rename the incorrect one to have the correct
                    // name.
                    var baseFolder = Path.GetDirectoryName(folder);
                    Debug.Assert(baseFolder != null);
                    var languageFolder = Path.GetDirectoryName(baseFolder);
                    Debug.Assert(languageFolder != null);
                    var langCode = Path.GetFileName(languageFolder);
                    Debug.Assert(langCode != null);
                    var incorrectProjName = Path.GetFileName(folder);
                    Debug.Assert(incorrectProjName != null);
                    var correctProjectName   = incorrectProjName.Substring(0, incorrectProjName.Length - " Audio".Length);
                    var correctProjectFolder = Path.Combine(baseFolder, correctProjectName);
                    if (Directory.Exists(correctProjectFolder))
                    {
                        var glyssenProjFilename      = langCode + Constants.kProjectFileExtension;
                        var incorrectProjectFilePath = Path.Combine(folder, glyssenProjFilename);
                        var correctProjectFilePath   = Path.Combine(correctProjectFolder, glyssenProjFilename);
                        var finfoIncorrectProject    = new FileInfo(incorrectProjectFilePath);
                        var finfoCorrectProject      = new FileInfo(correctProjectFilePath);
                        if (finfoCorrectProject.Exists && finfoIncorrectProject.Exists)
                        {
                            if (finfoCorrectProject.LastWriteTimeUtc < finfoIncorrectProject.LastWriteTimeUtc)
                            {
                                var books = Directory.GetFiles(correctProjectFolder, "???.xml").Where(b => Canon.IsBookIdValid(Path.GetFileNameWithoutExtension(b)))
                                            .Select(XmlSerializationHelper.DeserializeFromFile <BookScript>);
                                foreach (var book in books)
                                {
                                    // If book == null, there was a problem loading it. It may be locked or be in some incompatible format.
                                    // In any case, we shouldn't risk assuming we can safely replace it.
                                    if (book == null || book.GetScriptBlocks().Any(b => b.UserConfirmed || b.MatchesReferenceText))
                                    {
                                        unsafeReplacements.Add(new Tuple <string, string>(folder, correctProjectFolder));
                                        break;
                                    }
                                }
                                if (unsafeReplacements.LastOrDefault()?.Item1 == folder)
                                {
                                    continue;
                                }
                                try
                                {
                                    var projToBackUp = Project.Load(correctProjectFilePath);
                                    projToBackUp.CreateBackup("Overwritten by migration 3-4");
                                }
                                catch (Exception e)
                                {
                                    Logger.WriteError("Unable to load project and create backup", e);
                                    safeReplacements.Add(new Tuple <string, string>(folder, correctProjectFolder));
                                    continue;
                                }
                                try
                                {
                                    RobustIO.DeleteDirectory(correctProjectFolder, true);
                                    RobustIO.MoveDirectory(folder, correctProjectFolder);
                                }
                                catch (IOException e)
                                {
                                    Logger.WriteError("Unable to replace project after making backup", e);
                                    Console.WriteLine(e);
                                    unsafeReplacements.Add(new Tuple <string, string>(folder, correctProjectFolder));
                                }
                            }
                            else
                            {
                                unsafeReplacements.Add(new Tuple <string, string>(folder, correctProjectFolder));
                            }
                        }
                    }
                }

                if (safeReplacements.Any())
                {
                    string fmt;
                    if (safeReplacements.Count == 1)
                    {
                        fmt = LocalizationManager.GetString("DataMigration.ConfirmReplacementOfAudioAudio",
                                                            "Doing this will replace the existing project by the same name, which was originally created by {0}. " +
                                                            "Since none of the blocks in the project to be overwritten have any user decisions recorded, this seems " +
                                                            "to be safe, but since {0} failed to make a backup, you need to confirm this. If you choose not to confirm " +
                                                            "this action, you can either clean up the problem project yourself or verify that is is safe and then restart " +
                                                            "{0}. You will be asked about this each time you start the program as long as this problem remains unresolved.\r\n" +
                                                            "Confirm overwriting?",
                                                            "Param: \"Glyssen\" (product name); " +
                                                            "This follows the \"AudioAudioProblemPreambleSingle\".");
                    }
                    else
                    {
                        fmt = LocalizationManager.GetString("DataMigration.ConfirmReplacementsOfAudioAudio",
                                                            "Doing this will replace the existing projects by the same name, which were originally created by {0}. " +
                                                            "Since none of the blocks in the projects to be overwritten have any user decisions recorded, this seems " +
                                                            "to be safe, but since {0} failed to make a backup, you need to confirm this. If you choose not to confirm " +
                                                            "this action, you can either clean up the problem projects yourself or verify that is is safe and then restart " +
                                                            "{0}. You will be asked about this each time you start the program as long as these problems remains unresolved.\r\n" +
                                                            "Confirm overwriting?",
                                                            "Param: \"Glyssen\" (product name); " +
                                                            "This follows the \"AudioAudioProblemPreambleMultiple\".");
                    }
                    var msg = GetAudioAudioProblemPreamble(safeReplacements.Count) +
                              String.Join(Environment.NewLine, safeReplacements.Select(r => r.Item1)) + Environment.NewLine + Environment.NewLine +
                              String.Format(fmt, GlyssenInfo.kProduct);
                    if (DialogResult.Yes == MessageBox.Show(msg, GlyssenInfo.kProduct, MessageBoxButtons.YesNo,
                                                            MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2))
                    {
                        foreach (var replacement in safeReplacements)
                        {
                            RobustIO.DeleteDirectory(replacement.Item2, true);
                            RobustIO.MoveDirectory(replacement.Item1, replacement.Item2);
                        }
                        safeReplacements.Clear();
                    }
                }
                if (unsafeReplacements.Any())
                {
                    string fmt;
                    if (unsafeReplacements.Count == 1)
                    {
                        fmt = LocalizationManager.GetString("DataMigration.NoticeToManuallyFixAudioAudioProject",
                                                            "However, doing this would replace the existing project by the same name. " +
                                                            "Since {0} was unable to determine whether this was safe or otherwise failed to do the replacement, it is recommended " +
                                                            "that you clean up the problem project yourself. You are encouraged to contact a local support person if needed or " +
                                                            "seek help on {1}. You will be reminded about this each time you start the program as long as this problem remains unresolved.",
                                                            "Param 0: \"Glyssen\" (product name); " +
                                                            "Param 1: \"https://community.scripture.software.sil.org/\"" +
                                                            "This follows the \"AudioAudioProblemPreambleSingle\".");
                    }
                    else
                    {
                        fmt = LocalizationManager.GetString("DataMigration.NoticeToManuallyFixAudioAudioProjects",
                                                            "However, doing this would replace the existing projects by the same name. " +
                                                            "Since {0} was unable to determine whether this was safe or otherwise failed to do the replacements, it is recommended " +
                                                            "that you clean up the problem projects yourself. You are encouraged to contact a local support person if needed or " +
                                                            "seek help on {1}. You will be reminded about this each time you start the program as long as these problems remains unresolved.",
                                                            "Param 0: \"Glyssen\" (product name); " +
                                                            "Param 1: \"https://community.scripture.software.sil.org/\"" +
                                                            "This follows the \"AudioAudioProblemPreambleMultiple\".");
                    }
                    var msg = GetAudioAudioProblemPreamble(unsafeReplacements.Count) +
                              String.Join(Environment.NewLine, unsafeReplacements.Select(r => r.Item1)) + Environment.NewLine + Environment.NewLine +
                              String.Format(fmt, GlyssenInfo.kProduct, Constants.kSupportSite);
                    MessageBox.Show(msg, GlyssenInfo.kProduct, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
                if (unsafeReplacements.Any() || safeReplacements.Any())
                {
                    retVal = false;
                }
                break;

            default:
                throw new Exception("No migration found from the existing data version!");
            }

            if (retVal)
            {
                info.DataVersion = Settings.Default.DataFormatVersion;
            }
            return(retVal);
        }
Beispiel #29
0
        [RequestSizeLimit(250_000_000)]  // 250MB.
        public async Task <IActionResult> UploadLiftFile(string projectId, [FromForm] FileUpload fileUpload)
        {
            if (!await _permissionService.HasProjectPermission(HttpContext, Permission.ImportExport))
            {
                return(Forbid());
            }

            // Sanitize projectId
            if (!Sanitization.SanitizeId(projectId))
            {
                return(new UnsupportedMediaTypeResult());
            }

            // Ensure Lift file has not already been imported.
            if (!await _projRepo.CanImportLift(projectId))
            {
                return(BadRequest("A Lift file has already been uploaded."));
            }

            var liftStoragePath = FileStorage.GenerateLiftImportDirPath(projectId);

            // Clear out any files left by a failed import
            RobustIO.DeleteDirectoryAndContents(liftStoragePath);

            var file = fileUpload.File;

            if (file is null)
            {
                return(BadRequest("Null File"));
            }

            // Ensure file is not empty
            if (file.Length == 0)
            {
                return(BadRequest("Empty File"));
            }

            // Copy zip file data to a new temporary file
            fileUpload.FilePath = Path.GetTempFileName();
            await using (var fs = new FileStream(fileUpload.FilePath, FileMode.OpenOrCreate))
            {
                await file.CopyToAsync(fs);
            }

            // Make temporary destination for extracted files
            var extractDir = FileOperations.GetRandomTempDir();

            // Extract the zip to new created directory.
            FileOperations.ExtractZipFile(fileUpload.FilePath, extractDir, true);

            // Check number of directories extracted
            var directoriesExtracted = Directory.GetDirectories(extractDir);
            var extractedDirPath     = "";

            switch (directoriesExtracted.Length)
            {
            // If there was one directory, we're good
            case 1:
            {
                extractedDirPath = directoriesExtracted.First();
                break;
            }

            // If there were two, and there was a __MACOSX directory, ignore it
            case 2:
            {
                var numDirs = 0;
                foreach (var dir in directoriesExtracted)
                {
                    if (dir.EndsWith("__MACOSX"))
                    {
                        Directory.Delete(dir, true);
                    }
                    else         // This directory probably matters
                    {
                        extractedDirPath = dir;
                        numDirs++;
                    }
                }
                // Both directories seemed important
                if (numDirs == 2)
                {
                    return(BadRequest("Your zip file should have one directory."));
                }
                break;
            }

            // There were 0 or more than 2 directories
            default:
            {
                return(BadRequest(
                           "Your zip file structure has the wrong number of directories."));
            }
            }

            // Copy the extracted contents into the persistent storage location for the project.
            FileOperations.CopyDirectory(extractedDirPath, liftStoragePath);
            Directory.Delete(extractDir, true);

            // Search for the lift file within the extracted files
            var extractedLiftNameArr = Directory.GetFiles(liftStoragePath);
            var extractedLiftPath    = Array.FindAll(extractedLiftNameArr, x => x.EndsWith(".lift"));

            if (extractedLiftPath.Length > 1)
            {
                return(BadRequest("More than one .lift file detected."));
            }
            if (extractedLiftPath.Length == 0)
            {
                return(BadRequest("No lift files detected."));
            }

            int liftParseResult;
            // Sets the projectId of our parser to add words to that project
            var liftMerger = _liftService.GetLiftImporterExporter(projectId, _wordRepo);

            try
            {
                // Add character set to project from ldml file
                var proj = await _projRepo.GetProject(projectId);

                if (proj is null)
                {
                    return(NotFound(projectId));
                }

                _liftService.LdmlImport(
                    Path.Combine(liftStoragePath, "WritingSystems"),
                    proj.VernacularWritingSystem.Bcp47, _projRepo, proj);

                var parser = new LiftParser <LiftObject, LiftEntry, LiftSense, LiftExample>(liftMerger);

                // Import words from lift file
                liftParseResult = parser.ReadLiftFile(extractedLiftPath.FirstOrDefault());
                await liftMerger.SaveImportEntries();
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"Error importing lift file {fileUpload.Name} into project {projectId}.");
                return(BadRequest("Error processing the lift data. Contact support for help."));
            }

            // Store that we have imported Lift data already for this project to signal the frontend
            // not to attempt to import again.
            var project = await _projRepo.GetProject(projectId);

            if (project is null)
            {
                return(NotFound(projectId));
            }

            project.LiftImported = true;
            await _projRepo.Update(projectId, project);

            return(Ok(liftParseResult));
        }