예제 #1
0
        public IRestResponse SetBookRecord(string metadataJson)
        {
            if (!LoggedIn)
            {
                throw new ApplicationException("BloomParseClient got SetBookRecord, but the user is not logged in.");
            }
            if (BookUpload.IsDryRun)
            {
                throw new ApplicationException("Should not call SetBookRecord during dry run!");
            }
            var metadata = BookMetaData.FromString(metadataJson);
            var book     = GetSingleBookRecord(metadata.Id);

            metadataJson = ChangeJsonBeforeCreatingOrModifyingBook(metadataJson);
            if (book == null)
            {
                return(CreateBookRecord(metadataJson));
            }

            var request = MakePutRequest("classes/books/" + book.objectId);

            request.AddParameter("application/json", metadataJson, ParameterType.RequestBody);
            var response = Client.Execute(request);

            if (response.StatusCode != HttpStatusCode.OK)
            {
                throw new ApplicationException("BloomParseClient.SetBookRecord: " + response.StatusDescription + " " + response.Content);
            }
            return(response);
        }
예제 #2
0
        /// <summary>
        /// This method assumes we just did IsBookOnServer() and got a positive response.
        /// </summary>
        public dynamic GetBookOnServer(string bookPath)
        {
            var metadata = BookMetaData.FromString(RobustFile.ReadAllText(bookPath.CombineForPath(BookInfo.MetaDataFileName)));

            // 'true' parameter tells the query to include language information so we can get the names.
            return(ParseClient.GetSingleBookRecord(metadata.Id, true));
        }
예제 #3
0
        private void FixupHeaderForWriting(BBeB book)
        {
            if (book.MetaData == null)
            {
                // TODO - This is just because we don't have any metadata yet
                Debug.WriteLineIf(s_bDebugMode, "Book has no metadata - Mocking some up for testing");

                MemoryStream  metaStream    = new MemoryStream();
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(BookMetaData));
                BookMetaData  md            = new BookMetaData();
                xmlSerializer.Serialize(metaStream, md);
                metaStream.Seek(0, SeekOrigin.Begin);
                book.MetaData = md;                 //.Load(metaStream);
            }

            if (book.Header.wVersion >= 800)
            {
                if (book.ThumbnailData == null)
                {
                    throw new InvalidBookException("Don't have a thumbnail image");
                }

                book.Header.dwThumbSize = (uint)book.ThumbnailData.Length;
            }

            book.Header.NumberOfObjects = (ulong)book.Objects.Count;
        }
        public IRestResponse SetBookRecord(string metadataJson)
        {
            if (!LoggedIn)
            {
                throw new ApplicationException();
            }
            var metadata = BookMetaData.FromString(metadataJson);
            var book     = GetSingleBookRecord(metadata.Id);

            if (book == null)
            {
                return(CreateBookRecord(metadataJson));
            }

            var request = MakePutRequest("classes/books/" + book.objectId);

            request.AddParameter("application/json", metadataJson, ParameterType.RequestBody);
            var response = _client.Execute(request);

            if (response.StatusCode != HttpStatusCode.OK)
            {
                throw new ApplicationException(response.StatusDescription + " " + response.Content);
            }
            return(response);
        }
예제 #5
0
        private static string GetMetaJsonModfiedForTemplate(string path)
        {
            var meta = BookMetaData.FromString(RobustFile.ReadAllText(path));

            meta.IsSuitableForMakingShells = true;
            return(meta.Json);
        }
예제 #6
0
        public void HandleBookOrder(string bookOrderPath, string projectPath)
        {
            var metadata = BookMetaData.FromString(File.ReadAllText(bookOrderPath));
            var s3BookId = metadata.DownloadSource;

            _s3Client.DownloadBook(s3BookId, Path.GetDirectoryName(projectPath));
        }
예제 #7
0
        public void FeaturesSetter_OverallFeaturesOnly_ConvertBackGetsSameResult()
        {
            var input    = new string[] { "blind", "talkingBook", "signLanguage", "quiz", "motion", "comic", "activity", "widget" };
            var metadata = new BookMetaData();

            // System under test
            metadata.Features = input;                      // Run the setter
            string[] convertBackResult = metadata.Features; // Run the getter

            // Verify that converting back gets the same result (We don't care about the order they're in, though))
            CollectionAssert.AreEqual(input.OrderBy(x => x), convertBackResult.OrderBy(x => x));

            // Verify individual other properties too
            Assert.AreEqual(true, metadata.Feature_Blind, "Blind");
            Assert.AreEqual(true, metadata.Feature_TalkingBook, "TalkingBook");
            Assert.AreEqual(true, metadata.Feature_SignLanguage, "SignLanguage");
            Assert.AreEqual(true, metadata.Feature_Quiz, "Quiz");
            Assert.AreEqual(true, metadata.Feature_Motion, "Motion");
            Assert.AreEqual(true, metadata.Feature_Comic, "Comic");
            Assert.AreEqual(true, metadata.Feature_Activity, "Activity");
            Assert.AreEqual(true, metadata.Feature_Widget, "Widget");

            string[] expectedResult = new string[] { "" };
            CollectionAssert.AreEqual(expectedResult, metadata.Feature_Blind_LangCodes, "Blind Language Codes");
            CollectionAssert.AreEqual(expectedResult, metadata.Feature_TalkingBook_LangCodes, "TB Language Codes");
            CollectionAssert.AreEqual(expectedResult, metadata.Feature_SignLanguage_LangCodes, "SL Language Codes");
        }
예제 #8
0
        /// <summary>
        /// Read the metadata using the supplied reader.
        /// </summary>
        /// <param name="reader">The reader to use to read the metadata with. It is
        /// already positioned at the first byte of metadata.</param>
        /// <param name="nCompressedLen">The length of the compressed metadata. The first
        /// four bytes is the size of the uncompressed data.</param>
        public BookMetaData DeserializeMetaData(BinaryReader reader, int nCompressedLen)
        {
            Debug.WriteLineIf(s_bDebugMode, "Reading metadata");

            byte[] byUncompressedData = ZLib.Decompress(reader, nCompressedLen);

            Debug.WriteLineIf(s_bDebugMode, "Parsing metadata");
            MemoryStream xmlStream = new MemoryStream(byUncompressedData);

            XmlSerializer serializer = new XmlSerializer(typeof(BookMetaData));
            XmlTextReader xreader    = new XmlTextReader(xmlStream);
            BookMetaData  metaData   = null;

            try
            {
                metaData = (BookMetaData)serializer.Deserialize(xreader);
            }
            catch (Exception e)
            {
                Debug.WriteLineIf(s_bDebugMode, e.ToString());
            }
            xreader.Close();

            return(metaData);
        }
예제 #9
0
        public void MetaData_WithUnknownTool_DiscardsIt()
        {
            var json     = "{ \"bookInstanceId\":\"bee9496b-00c2-42f2-83c5-7e2f61f50a33\",\"tools\":[{\"name\":\"decodableReader\",\"enabled\":true,\"state\":\"stage:1;sort:alphabetic\"},{\"name\":\"leveledReader\",\"enabled\":true,\"state\":\"1\"},{\"name\":\"talkingBook\",\"enabled\":true,\"state\":null}, {\"name\":\"rubbish\",\"enabled\":true,\"state\":null}],\"currentTool\":\"leveledReaderTool\",\"toolboxIsOpen\":true}";
            var metadata = BookMetaData.FromString(json);

            Assert.That(metadata.Tools, Has.Count.EqualTo(3));             // Unknown tool should not show up.
        }
        public void UploadBook_FillsInMetaData()
        {
            var bookFolder = MakeBook("My incomplete book", "", "", "data");

            File.WriteAllText(Path.Combine(bookFolder, "thumbnail.png"), @"this should be a binary picture");

            Login();
            string s3Id = _transfer.UploadBook(bookFolder, new NullProgress());

            _transfer.WaitUntilS3DataIsOnServer(bookFolder);
            var dest = _workFolderPath.CombineForPath("output");

            Directory.CreateDirectory(dest);
            var newBookFolder = _transfer.DownloadBook(s3Id, dest);
            var metadata      = BookMetaData.FromString(File.ReadAllText(Path.Combine(newBookFolder, BookInfo.MetaDataFileName)));

            Assert.That(string.IsNullOrEmpty(metadata.Id), Is.False, "should have filled in missing ID");
            Assert.That(metadata.Uploader.ObjectId, Is.EqualTo(_parseClient.UserId), "should have set uploader to id of logged-in user");
            Assert.That(metadata.DownloadSource, Is.EqualTo(s3Id));

            var    record  = _parseClient.GetSingleBookRecord(metadata.Id);
            string baseUrl = record.baseUrl;

            Assert.That(baseUrl.StartsWith("https://s3.amazonaws.com/BloomLibraryBooks"), "baseUrl should start with s3 prefix");

            string order = record.bookOrder;

            Assert.That(order, Is.StringContaining("My+incomplete+book.BloomBookOrder"), "order url should include correct file name");
            Assert.That(order.StartsWith(BloomLinkArgs.kBloomUrlPrefix + BloomLinkArgs.kOrderFile + "="), "order url should start with Bloom URL prefix");

            Assert.That(File.Exists(Path.Combine(newBookFolder, "My incomplete book.BloomBookOrder")), "Should have created, uploaded and downloaded the book order");
        }
예제 #11
0
        /// <summary>
        /// Internal for testing because it's not yet clear this is the appropriate public routine.
        /// Probably some API gets a list of BloomInfo objects from the parse.com data, and we pass one of
        /// them as the argument for the public method.
        /// </summary>
        /// <param name="bucket"></param>
        /// <param name="s3BookId"></param>
        /// <param name="dest"></param>
        /// <returns></returns>
        internal string DownloadBook(string bucket, string s3BookId, string dest)
        {
            var destinationPath = _s3Client.DownloadBook(bucket, s3BookId, dest, _progressDialog);

            if (BookDownLoaded != null)
            {
                var bookInfo = new BookInfo(destinationPath, false);                 // A downloaded book is a template, so never editable.
                BookDownLoaded(this, new BookDownloadedEventArgs()
                {
                    BookDetails = bookInfo
                });
            }
            // Books in the library should generally show as locked-down, so new users are automatically in localization mode.
            // Occasionally we may want to upload a new authoring template, that is, a 'book' that is suitableForMakingShells.
            // Such books should not be locked down.
            // So, we try to lock it. What we want to do is Book.RecordedAsLockedDown = true; Book.Save().
            // But all kinds of things have to be set up before we can create a Book. So we duplicate a few bits of code.
            var htmlFile = BookStorage.FindBookHtmlInFolder(destinationPath);

            if (htmlFile == "")
            {
                return(destinationPath);                //argh! we can't lock it.
            }
            var xmlDomFromHtmlFile = XmlHtmlConverter.GetXmlDomFromHtmlFile(htmlFile, false);
            var dom = new HtmlDom(xmlDomFromHtmlFile);

            if (!BookMetaData.FromString(MetaDataText(destinationPath)).IsSuitableForMakingShells)
            {
                dom.RecordAsLockedDown(true);
                XmlHtmlConverter.SaveDOMAsHtml5(dom.RawDom, htmlFile);
            }

            return(destinationPath);
        }
예제 #12
0
        public void HandleBookOrder(string bookOrderPath, string projectPath)
        {
            var metadata = BookMetaData.FromString(RobustFile.ReadAllText(bookOrderPath));
            var s3BookId = metadata.DownloadSource;
            var bucket   = BloomS3Client.ProductionBucketName;           //TODO

            _s3Client.DownloadBook(bucket, s3BookId, Path.GetDirectoryName(projectPath));
        }
예제 #13
0
        private string S3BookId(BookMetaData metadata)
        {
            // It's tempting to use '/' so that S3 tools will treat all the books with the same ID as a folder.
            // But this complicates things because that character is taken as a path separator (even in Windows),
            // which gives us an extra level of folder in our temp folder...too much trouble for now, anyway.
            // So use a different separator.
            var s3BookId = _parseClient.Account + "/" + metadata.Id;

            return(s3BookId);
        }
예제 #14
0
        public void CreateBookOnDiskFromTemplateStarter_IsTemplate_ButNotTemplateFactory()
        {
            var source = BloomFileLocator.GetFactoryBookTemplateDirectory("Template Starter");

            var path        = _starter.CreateBookOnDiskFromTemplate(source, _projectFolder.Path);
            var newMetaData = BookMetaData.FromFolder(path);

            Assert.That(newMetaData.IsSuitableForMakingShells, Is.True);
            Assert.That(newMetaData.IsSuitableForMakingTemplates, Is.False);
        }
        /// <summary>
        /// Creates the .bloomd and bloomdigital folders
        /// </summary>
        private static CreateArtifactsExitCode CreateBloomDigitalArtifacts(string bookPath, string creator, string zippedBloomDOutputPath, string unzippedBloomDigitalOutputPath)
        {
#if DEBUG
            // Useful for allowing debugging of Bloom while running the harvester
            //MessageBox.Show("Attach debugger now");
#endif
            var exitCode = CreateArtifactsExitCode.Success;

            using (var tempBloomD = TempFile.CreateAndGetPathButDontMakeTheFile())
            {
                if (String.IsNullOrEmpty(zippedBloomDOutputPath))
                {
                    zippedBloomDOutputPath = tempBloomD.Path;
                }

                BookServer bookServer = _projectContext.BookServer;

                var  metadata       = BookMetaData.FromFolder(bookPath);
                bool isTemplateBook = metadata.IsSuitableForMakingShells;

                using (var folderForUnzipped = new TemporaryFolder("BloomCreateArtifacts_Unzipped"))
                {
                    // Ensure directory exists, just in case.
                    Directory.CreateDirectory(Path.GetDirectoryName(zippedBloomDOutputPath));
                    // Make the bloomd
                    string unzippedPath = Publish.Android.BloomPubMaker.CreateBloomPub(
                        zippedBloomDOutputPath,
                        bookPath,
                        bookServer,
                        System.Drawing.Color.Azure,                 // TODO: What should this be?
                        new Bloom.web.NullWebSocketProgress(),
                        folderForUnzipped,
                        creator,
                        isTemplateBook);

                    // Currently the zipping process does some things we actually need, like making the cover picture
                    // transparent (BL-7437). Eventually we plan to separate the preparation and zipping steps (BL-7445).
                    // Until that is done, the most reliable way to get an unzipped BloomD for our preview is to actually
                    // unzip the BloomD.
                    if (!String.IsNullOrEmpty(unzippedBloomDigitalOutputPath))
                    {
                        SIL.IO.RobustIO.DeleteDirectory(unzippedBloomDigitalOutputPath, recursive: true);                           // In case the folder isn't already empty

                        // Ensure directory exists, just in case.
                        Directory.CreateDirectory(Path.GetDirectoryName(unzippedBloomDigitalOutputPath));

                        ZipFile.ExtractToDirectory(zippedBloomDOutputPath, unzippedBloomDigitalOutputPath);

                        exitCode |= RenameBloomDigitalFiles(unzippedBloomDigitalOutputPath);
                    }
                }
            }

            return(exitCode);
        }
예제 #16
0
        public void CreateBookOnDiskFromTemplate_OriginalIsTemplate_CopyIsNotTemplate()
        {
            var source           = BloomFileLocator.GetFactoryBookTemplateDirectory("Basic Book");
            var originalMetaData = BookMetaData.FromFolder(source);

            Assert.That(originalMetaData.IsSuitableForMakingShells);
            var path        = _starter.CreateBookOnDiskFromTemplate(source, _projectFolder.Path);
            var newMetaData = BookMetaData.FromFolder(path);

            Assert.That(newMetaData.IsSuitableForMakingShells, Is.False);
        }
예제 #17
0
        public void FeaturesGetter_NeitherQuizNorWidgetSet_ThenActivityIsFalse()
        {
            var metadata = new BookMetaData();

            metadata.Feature_Quiz = metadata.Feature_Widget = false;

            // System under test
            string[] result = metadata.Features;

            Assert.IsFalse(result.Contains("activity"));
        }
예제 #18
0
 public void InsertOrUpdateMetaData(BookMetaData entity)
 {
     if (entity.Id == default(int))
     {
         Context.BookMetaData.Add(entity);
     }
     else
     {
         Context.Entry(entity).State = EntityState.Modified;
     }
 }
예제 #19
0
        public void FeaturesGetter_Comic(bool containsComic)
        {
            var metadata = new BookMetaData();

            metadata.Feature_Comic = containsComic;

            // System under test
            string[] result = metadata.Features;

            string[] expectedResult = containsComic ? new string[] { "comic" } : new string[0];
            Assert.AreEqual(expectedResult, result);
        }
예제 #20
0
        public void FeaturesGetter_Activity(bool containsActivity)
        {
            var metadata = new BookMetaData();

            metadata.Feature_Activity = containsActivity;

            // System under test
            string[] result = metadata.Features;

            string[] expectedResult = containsActivity ? new string[] { "activity" } : new string[0];
            Assert.AreEqual(expectedResult, result);
        }
예제 #21
0
        public void FeaturesGetter_Motion(bool containsMotion)
        {
            var metadata = new BookMetaData();

            metadata.Feature_Motion = containsMotion;

            // System under test
            string[] result = metadata.Features;

            string[] expectedResult = containsMotion ? new string[] { "motion" } : new string[0];
            Assert.AreEqual(expectedResult, result);
        }
예제 #22
0
        public void FeaturesGetter_IfQuizOrWidgetSet_ThenActivityIsTrue(bool containsQuiz, bool containsWidget)
        {
            var metadata = new BookMetaData();

            metadata.Feature_Quiz   = containsQuiz;
            metadata.Feature_Widget = containsWidget;

            // System under test
            string[] result = metadata.Features;

            Assert.IsTrue(result.Contains("activity"));
        }
예제 #23
0
        public void FeaturesGetter_Quiz(bool containsQuiz)
        {
            var metadata = new BookMetaData();

            metadata.Feature_Quiz = containsQuiz;

            // System under test
            string[] result = metadata.Features;

            string[] expectedResult = containsQuiz ? new string[] { "quiz" } : new string[0];
            Assert.AreEqual(expectedResult, result);
        }
예제 #24
0
        public void FeaturesGetter_Blind(IEnumerable <string> langCodes, string[] featuresExpected)
        {
            var metadata = new BookMetaData();

            metadata.Feature_Blind_LangCodes = langCodes;

            // System under test
            string[] featuresResult     = metadata.Features;
            bool     featureBlindResult = metadata.Feature_Blind;

            Assert.AreEqual(featuresExpected, featuresResult, "Features");
            Assert.AreEqual(featuresExpected.Any(), featureBlindResult, "Feature_Blind");
        }
예제 #25
0
        public void FeaturesGetter_SignLanguage(IEnumerable <string> langCodes, string[] featuresExpected)
        {
            var metadata = new BookMetaData();

            metadata.Feature_SignLanguage_LangCodes = langCodes;

            // System under test
            string[] result = metadata.Features;
            bool     featureSignLanguageResult = metadata.Feature_SignLanguage;

            Assert.AreEqual(featuresExpected, result, "Features");
            Assert.AreEqual(featuresExpected.Any(), featureSignLanguageResult, "Feature_SignLanguage");
        }
예제 #26
0
        public void FeaturesGetter_Widget(bool containsWidget)
        {
            var metadata = new BookMetaData();

            metadata.Feature_Widget = containsWidget;

            // System under test
            string[] result = metadata.Features;

            bool expectedResult = containsWidget;

            Assert.AreEqual(expectedResult, result.Contains("widget"));
        }
예제 #27
0
        public void AudioLangsToPublishForBloomReader_GivenNonDefaultJson_DeserializesProperly()
        {
            var json = "{ \"audioLangsToPublish\": { \"bloomPUB\": { \"en\": \"Include\" } } }";

            // System under test
            var metadata = BookMetaData.FromString(json);

            // Verification
            var expected = new Dictionary <string, Bloom.Publish.InclusionSetting>();

            expected.Add("en", Bloom.Publish.InclusionSetting.Include);
            CollectionAssert.AreEquivalent(expected, metadata.AudioLangsToPublish.ForBloomPUB);
        }
        /// <summary>
        /// Review: this is fragile and expensive. We're doing real internet traffic and creating real objects on S3 and parse.com
        /// which (to a very small extent) costs us real money. This will be slow. Also, under S3 eventual consistency rules,
        /// there is no guarantee that the data we just created will actually be retrievable immediately.
        /// </summary>
        /// <param name="bookName"></param>
        /// <param name="id"></param>
        /// <param name="uploader"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public Tuple <string, string> UploadAndDownLoadNewBook(string bookName, string id, string uploader, string data, bool isTemplate = false)
        {
            //  Create a book folder with meta.json that includes an uploader and id and some other files.
            var originalBookFolder = MakeBook(bookName, id, uploader, data, true);

            if (isTemplate)
            {
                var metadata = BookMetaData.FromFolder(originalBookFolder);
                metadata.IsSuitableForMakingShells = true;
                metadata.WriteToFolder(originalBookFolder);
            }
            // The files that actually get uploaded omit some of the ones in the folder.
            // The only omitted one that messes up current unit tests is meta.bak
            var filesToUpload = Directory.GetFiles(originalBookFolder).Where(p => !p.EndsWith(".bak") && !p.Contains(BookStorage.PrefixForCorruptHtmFiles));
            int fileCount     = filesToUpload.Count();

            Login();
            //HashSet<string> notifications = new HashSet<string>();

            var progress = new SIL.Progress.StringBuilderProgress();
            var s3Id     = _uploader.UploadBook(originalBookFolder, progress);

            var uploadMessages = progress.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);

            var expectedFileCount = fileCount + 2;             // should get one per file, plus one for metadata, plus one for book order

#if DEBUG
            ++expectedFileCount;                // and if in debug mode, then plus one for S3 ID
#endif
            Assert.That(uploadMessages.Length, Is.EqualTo(expectedFileCount), "Uploaded file counts do not match");
            Assert.That(progress.Text, Does.Contain(
                            LocalizationManager.GetString("PublishTab.Upload.UploadingBookMetadata", "Uploading book metadata",
                                                          "In this step, Bloom is uploading things like title, languages, & topic tags to the bloomlibrary.org database.")));
            Assert.That(progress.Text, Does.Contain(Path.GetFileName(filesToUpload.First())));

            _uploader.WaitUntilS3DataIsOnServer(BloomS3Client.UnitTestBucketName, originalBookFolder);
            var dest = _workFolderPath.CombineForPath("output");
            Directory.CreateDirectory(dest);
            _downloadedBooks.Clear();
            var url           = BookUpload.BloomS3UrlPrefix + BloomS3Client.UnitTestBucketName + "/" + s3Id;
            var newBookFolder = _downloader.HandleDownloadWithoutProgress(url, dest);

            Assert.That(Directory.GetFiles(newBookFolder).Length, Is.EqualTo(fileCount + 1), "Book order was not added during upload");             // book order is added during upload

            Assert.That(_downloadedBooks.Count, Is.EqualTo(1));
            Assert.That(_downloadedBooks[0].FolderPath, Is.EqualTo(newBookFolder));
            // Todo: verify that metadata was transferred to Parse.com
            return(new Tuple <string, string>(originalBookFolder, newBookFolder));
        }
예제 #29
0
        public void TopicsList_SetsTopicsWhileLeavingOtherTagsIntact(string jsonTagsList, string topicsListToSet, string expectedTopicsList, string[] expectedTags)
        {
            var jsonPath = Path.Combine(_folder.Path, BookInfo.MetaDataFileName);

            File.WriteAllText(jsonPath, @"{'tags':[" + jsonTagsList + @"]}");
            var bi = new BookInfo(_folder.Path, true);

            //SUT
            bi.TopicsList = topicsListToSet;

            Assert.AreEqual(expectedTopicsList, bi.TopicsList);

            BookMetaData metadata = (BookMetaData)ReflectionHelper.GetField(bi, "_metadata");

            Assert.AreEqual(expectedTags, metadata.Tags);
        }
예제 #30
0
        // Wait (up to three seconds) for data uploaded to become available.
        // Currently only used in unit testing.
        // I have no idea whether 3s is an adequate time to wait for 'eventual consistency'. So far it seems to work.
        internal void WaitUntilS3DataIsOnServer(string bookPath)
        {
            var s3Id  = S3BookId(BookMetaData.FromFolder(bookPath));
            var count = Directory.GetFiles(bookPath).Length;

            for (int i = 0; i < 30; i++)
            {
                var uploaded = _s3Client.GetBookFileCount(s3Id);
                if (uploaded >= count)
                {
                    return;
                }
                Thread.Sleep(100);
            }
            throw new ApplicationException("S3 is very slow today");
        }