Пример #1
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));
        }
Пример #2
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);
        }
        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");
        }
        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
        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.
        }
Пример #6
0
        private static string GetMetaJsonModfiedForTemplate(string path)
        {
            var meta = BookMetaData.FromString(RobustFile.ReadAllText(path));

            meta.IsSuitableForMakingShells = true;
            return(meta.Json);
        }
Пример #7
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));
        }
Пример #8
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);
        }
Пример #9
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));
        }
Пример #10
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);
        }
Пример #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);
            bool needToSave = false;

            // If the book is downloaded from Bloom Library, we don't want to treat it as though
            // it were directly created from a Reader bloomPack.  So relax the formatting lock.
            // See https://issues.bloomlibrary.org/youtrack/issue/BL-9996.
            if (dom.HasMetaElement("lockFormatting"))
            {
                dom.RemoveMetaElement("lockFormatting");
                needToSave = true;
            }
            if (!BookMetaData.FromString(MetaDataText(destinationPath)).IsSuitableForMakingShells)
            {
                dom.RecordAsLockedDown(true);
                needToSave = true;
            }
            if (needToSave)
            {
                XmlHtmlConverter.SaveDOMAsHtml5(dom.RawDom, htmlFile);
            }

            return(destinationPath);
        }
Пример #12
0
        private void GetUrlAndTitle(string bucket, string s3orderKey, ref string url, ref string title)
        {
            int index = s3orderKey.IndexOf('/');

            if (index > 0)
            {
                index = s3orderKey.IndexOf('/', index + 1);                 // second slash
            }
            if (index > 0)
            {
                url = s3orderKey.Substring(0, index);
            }
            if (url == "unknown" || string.IsNullOrWhiteSpace(title) || title == "unknown")
            {
                // not getting the info we want in the expected way. This old algorithm may work.
                var metadata = BookMetaData.FromString(_s3Client.DownloadFile(s3orderKey, bucket));
                url   = metadata.DownloadSource;
                title = metadata.Title;
            }
        }
Пример #13
0
        public void WebDataJson_IncludesCorrectFields()
        {
            var meta = new BookMetaData()
            {
                Id = "myId",
                IsSuitableForMakingShells      = true,
                IsSuitableForVernacularLibrary = false,
                IsExperimental             = true,
                Title                      = "myTitle",
                AllTitles                  = "abc,\"def",
                BaseUrl                    = "http://some/unlikely/url",
                Isbn                       = "123-456-78-9",
                DownloadSource             = "http://some/amazon/url",
                License                    = "ccby",
                FormatVersion              = "1.0",
                Credits                    = "JohnT",
                Summary                    = "A very nice book\\ in a very nice nook",
                Tags                       = new [] { "Animals" },
                CurrentTool                = "mytool",
                BookletMakingIsAppropriate = false, PageCount = 7,
                LanguageTableReferences    = new [] { new ParseDotComObjectPointer()
                                                      {
                                                          ClassName = "Language", ObjectId = "23456"
                                                      } },
                Uploader = new ParseDotComObjectPointer()
                {
                    ClassName = "User", ObjectId = "12345"
                },
                Tools = new List <ToolboxTool>(new [] { new DecodableReaderTool() }),
                AllowUploadingToBloomLibrary = false
            };
            var result = meta.WebDataJson;
            var meta2  = BookMetaData.FromString(result);

            Assert.That(meta2.Id, Is.EqualTo("myId"));
            Assert.That(meta2.IsSuitableForMakingShells, Is.True);
            Assert.That(meta2.IsSuitableForVernacularLibrary, Is.False);
            Assert.That(meta2.IsExperimental, Is.True);
            Assert.That(meta2.Title, Is.EqualTo("myTitle"));
            Assert.That(meta2.AllTitles, Is.EqualTo("abc,\"def"));
            Assert.That(meta2.BaseUrl, Is.EqualTo("http://some/unlikely/url"));
            Assert.That(meta2.Isbn, Is.EqualTo("123-456-78-9"));

            Assert.That(meta2.License, Is.EqualTo("ccby"));
            Assert.That(meta2.FormatVersion, Is.EqualTo("1.0"));
            Assert.That(meta2.Credits, Is.EqualTo("JohnT"));
            Assert.That(meta2.Tags, Has.Length.EqualTo(1));
            Assert.That(meta2.Summary, Is.EqualTo("A very nice book\\ in a very nice nook"));
            Assert.That(meta2.PageCount, Is.EqualTo(7));
            Assert.That(meta2.LanguageTableReferences, Has.Length.EqualTo(1));
            Assert.That(meta2.LanguageTableReferences[0].ObjectId, Is.EqualTo("23456"));
            Assert.That(meta2.Uploader, Is.Not.Null);
            Assert.That(meta2.Uploader.ObjectId, Is.EqualTo("12345"));

            // These properties (and various others) should not be in the serialization data.
            // Since AllowUploadingToBloomLibrary defaults true, that should be its value if not set by json
            Assert.That(meta2.AllowUploadingToBloomLibrary, Is.True, "AllowUploadingtoBloomLibrary was unexpectedly serialized");
            Assert.That(meta2.DownloadSource, Is.Null);
            Assert.That(meta2.CurrentTool, Is.Null);
            Assert.That(meta2.Tools, Is.Null);
            Assert.That(meta2.BookletMakingIsAppropriate, Is.True);             // default value
        }
Пример #14
0
        public string DownloadFromOrderUrl(string orderUrl, string destPath)
        {
            var decoded     = HttpUtilityFromMono.UrlDecode(orderUrl);
            var bucketStart = decoded.IndexOf(_s3Client.BucketName, StringComparison.InvariantCulture);

            if (bucketStart == -1)
            {
#if DEBUG
                if (decoded.StartsWith(("BloomLibraryBooks")))
                {
                    Palaso.Reporting.ErrorReport.NotifyUserOfProblem(
                        "The book is from bloomlibrary.org, but you are running the DEBUG version of Bloom, which can only use dev.bloomlibrary.org.");
                }
                else
                {
                    throw new ApplicationException("Can't match URL of bucket of the book being downloaded, and I don't know why.");
                }
#else
                if (decoded.StartsWith(("BloomLibraryBooks-Sandbox")))
                {
                    Palaso.Reporting.ErrorReport.NotifyUserOfProblem(
                        "The book is from the testing version of the bloomlibrary, but you are running the RELEASE version of Bloom. The RELEASE build cannot use the 'dev.bloomlibrary.org' site. If you need to do that for testing purposes, set the windows Environment variable 'BloomSandbox' to 'true'.", decoded);
                }
                else
                {
                    throw new ApplicationException(string.Format("Can't match URL of bucket of the book being downloaded {0}, and I don't know why.", decoded));
                }
#endif
                return(null);
            }

            var    s3orderKey = decoded.Substring(bucketStart + _s3Client.BucketName.Length + 1);
            string url        = "unknown";
            string title      = "unknown";
            try
            {
                var metadata = BookMetaData.FromString(_s3Client.DownloadFile(s3orderKey));
                url   = metadata.DownloadSource;
                title = metadata.Title;
                if (_progressDialog != null)
                {
                    _progressDialog.Invoke((Action)(() => { _progressDialog.Progress = 1; }));
                }
                // downloading the metadata is considered step 1.
                var destinationPath = DownloadBook(metadata.DownloadSource, destPath);
                LastBookDownloadedPath = destinationPath;

                Analytics.Track("DownloadedBook-Success",
                                new Dictionary <string, string>()
                {
                    { "url", url }, { "title", title }
                });
                return(destinationPath);
            }
            catch (WebException e)
            {
                DisplayNetworkDownloadProblem(e);
                Analytics.Track("DownloadedBook-Failure",
                                new Dictionary <string, string>()
                {
                    { "url", url }, { "title", title }
                });
                Analytics.ReportException(e);
                return("");
            }
            catch (AmazonServiceException e)
            {
                DisplayNetworkDownloadProblem(e);
                Analytics.Track("DownloadedBook-Failure",
                                new Dictionary <string, string>()
                {
                    { "url", url }, { "title", title }
                });
                Analytics.ReportException(e);
                return("");
            }
            catch (Exception e)
            {
                ShellWindow.Invoke((Action)(() =>
                                            Palaso.Reporting.ErrorReport.NotifyUserOfProblem(e,
                                                                                             LocalizationManager.GetString("PublishTab.Upload.DownloadProblem",
                                                                                                                           "There was a problem downloading your book. You may need to restart Bloom or get technical help."))));
                Analytics.Track("DownloadedBook-Failure",
                                new Dictionary <string, string>()
                {
                    { "url", url }, { "title", title }
                });
                Analytics.ReportException(e);
                return("");
            }
        }
Пример #15
0
        public bool IsBookOnServer(string bookPath)
        {
            var metadata = BookMetaData.FromString(File.ReadAllText(bookPath.CombineForPath(BookInfo.MetaDataFileName)));

            return(_parseClient.GetSingleBookRecord(metadata.Id) != null);
        }
Пример #16
0
        public string UploadBook(string bookFolder, IProgress progress, out string parseId)
        {
            var metaDataText = MetaDataText(bookFolder);
            var metadata     = BookMetaData.FromString(metaDataText);

            // In case we somehow have a book with no ID, we must have one to upload it.
            if (string.IsNullOrEmpty(metadata.Id))
            {
                metadata.Id = Guid.NewGuid().ToString();
            }
            // And similarly it should have SOME title.
            if (string.IsNullOrEmpty(metadata.Title))
            {
                metadata.Title = Path.GetFileNameWithoutExtension(bookFolder);
            }
            metadata.SetUploader(UserId);
            var s3BookId = S3BookId(metadata);

            metadata.DownloadSource = s3BookId;
            // Any updated ID at least needs to become a permanent part of the book.
            // The file uploaded must also contain the correct DownloadSource data, so that it can be used
            // as an 'order' to download the book.
            // It simplifies unit testing if the metadata file is also updated with the uploadedBy value.
            // Not sure if there is any other reason to do it (or not do it).
            // For example, do we want to send/receive who is the latest person to upload?
            metadata.WriteToFolder(bookFolder);
            // The metadata is also a book order...but we need it on the server with the desired file name,
            // because we can't rename on download. The extension must be the one Bloom knows about,
            // and we want the file name to indicate which book, so use the name of the book folder.
            var metadataPath = BookMetaData.MetaDataPath(bookFolder);
            var orderPath    = Path.Combine(bookFolder, Path.GetFileName(bookFolder) + BookOrderExtension);

            File.Copy(metadataPath, orderPath, true);
            parseId = "";
            try
            {
                _s3Client.UploadBook(s3BookId, bookFolder, progress);
                metadata.BaseUrl   = _s3Client.BaseUrl;
                metadata.BookOrder = _s3Client.BookOrderUrl;
                progress.WriteStatus(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."));
                // Do this after uploading the books, since the ThumbnailUrl is generated in the course of the upload.
                var response = _parseClient.SetBookRecord(metadata.Json);
                parseId = response.ResponseUri.LocalPath;
                int index = parseId.LastIndexOf('/');
                parseId = parseId.Substring(index + 1);
                if (parseId == "books")
                {
                    // For NEW books the response URL is useless...need to do a new query to get the ID.
                    var json = _parseClient.GetSingleBookRecord(metadata.Id);
                    parseId = json.objectId.Value;
                }
                //   if (!UseSandbox) // don't make it seem like there are more uploads than their really are if this a tester pushing to the sandbox
                {
                    Analytics.Track("UploadBook-Success", new Dictionary <string, string>()
                    {
                        { "url", metadata.BookOrder }, { "title", metadata.Title }
                    });
                }
            }
            catch (WebException e)
            {
                DisplayNetworkUploadProblem(e, progress);
                if (!UseSandbox)                 // don't make it seem like there are more upload failures than their really are if this a tester pushing to the sandbox
                {
                    Analytics.Track("UploadBook-Failure", new Dictionary <string, string>()
                    {
                        { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message }
                    });
                }
                return("");
            }
            catch (AmazonServiceException e)
            {
                DisplayNetworkUploadProblem(e, progress);
                if (!UseSandbox)                 // don't make it seem like there are more upload failures than their really are if this a tester pushing to the sandbox
                {
                    Analytics.Track("UploadBook-Failure", new Dictionary <string, string>()
                    {
                        { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message }
                    });
                }
                return("");
            }
            catch (Exception e)
            {
                progress.WriteError(LocalizationManager.GetString("PublishTab.Upload.UploadProblemNotice",
                                                                  "There was a problem uploading your book. You may need to restart Bloom or get technical help."));
                progress.WriteError(e.Message.Replace("{", "{{").Replace("}", "}}"));
                progress.WriteVerbose(e.StackTrace);
                if (!UseSandbox)                 // don't make it seem like there are more upload failures than their really are if this a tester pushing to the sandbox
                {
                    Analytics.Track("UploadBook-Failure", new Dictionary <string, string>()
                    {
                        { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message }
                    });
                }
                return("");
            }
            return(s3BookId);
        }
Пример #17
0
        private string UploadBook(string bookFolder, IProgress progress, out string parseId, string pdfToInclude = null, bool excludeAudio = true)
        {
            // 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 must never be locked.
            // So, typically we will 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(bookFolder);
            bool    wasLocked     = false;
            bool    allowLocking  = false;
            HtmlDom domForLocking = null;
            var     metaDataText  = MetaDataText(bookFolder);
            var     metadata      = BookMetaData.FromString(metaDataText);

            if (!string.IsNullOrEmpty(htmlFile))
            {
                var xmlDomFromHtmlFile = XmlHtmlConverter.GetXmlDomFromHtmlFile(htmlFile, false);
                domForLocking = new HtmlDom(xmlDomFromHtmlFile);
                wasLocked     = domForLocking.RecordedAsLockedDown;
                allowLocking  = !metadata.IsSuitableForMakingShells;
                if (allowLocking && !wasLocked)
                {
                    domForLocking.RecordAsLockedDown(true);
                    XmlHtmlConverter.SaveDOMAsHtml5(domForLocking.RawDom, htmlFile);
                }
            }
            string s3BookId;

            try
            {
                // In case we somehow have a book with no ID, we must have one to upload it.
                if (string.IsNullOrEmpty(metadata.Id))
                {
                    metadata.Id = Guid.NewGuid().ToString();
                }
                // And similarly it should have SOME title.
                if (string.IsNullOrEmpty(metadata.Title))
                {
                    metadata.Title = Path.GetFileNameWithoutExtension(bookFolder);
                }
                metadata.SetUploader(UserId);
                s3BookId = S3BookId(metadata);
                metadata.DownloadSource = s3BookId;
                // Any updated ID at least needs to become a permanent part of the book.
                // The file uploaded must also contain the correct DownloadSource data, so that it can be used
                // as an 'order' to download the book.
                // It simplifies unit testing if the metadata file is also updated with the uploadedBy value.
                // Not sure if there is any other reason to do it (or not do it).
                // For example, do we want to send/receive who is the latest person to upload?
                metadata.WriteToFolder(bookFolder);
                // The metadata is also a book order...but we need it on the server with the desired file name,
                // because we can't rename on download. The extension must be the one Bloom knows about,
                // and we want the file name to indicate which book, so use the name of the book folder.
                var metadataPath = BookMetaData.MetaDataPath(bookFolder);
                var orderPath    = Path.Combine(bookFolder, Path.GetFileName(bookFolder) + BookOrderExtension);
                RobustFile.Copy(metadataPath, orderPath, true);
                parseId = "";
                try
                {
                    _s3Client.UploadBook(s3BookId, bookFolder, progress, pdfToInclude, excludeAudio);
                    metadata.BaseUrl   = _s3Client.BaseUrl;
                    metadata.BookOrder = _s3Client.BookOrderUrlOfRecentUpload;
                    progress.WriteStatus(LocalizationManager.GetString("PublishTab.Upload.UploadingBookMetadata", "Uploading book metadata", "In this step, Bloom is uploading things like title, languages, and topic tags to the BloomLibrary.org database."));
                    // Do this after uploading the books, since the ThumbnailUrl is generated in the course of the upload.
                    var response = _parseClient.SetBookRecord(metadata.WebDataJson);
                    parseId = response.ResponseUri.LocalPath;
                    int index = parseId.LastIndexOf('/');
                    parseId = parseId.Substring(index + 1);
                    if (parseId == "books")
                    {
                        // For NEW books the response URL is useless...need to do a new query to get the ID.
                        var json = _parseClient.GetSingleBookRecord(metadata.Id);
                        parseId = json.objectId.Value;
                    }
                    //   if (!UseSandbox) // don't make it seem like there are more uploads than their really are if this a tester pushing to the sandbox
                    {
                        Analytics.Track("UploadBook-Success", new Dictionary <string, string>()
                        {
                            { "url", metadata.BookOrder }, { "title", metadata.Title }
                        });
                    }
                }
                catch (WebException e)
                {
                    DisplayNetworkUploadProblem(e, progress);
                    if (!UseSandbox)                     // don't make it seem like there are more upload failures than their really are if this a tester pushing to the sandbox
                    {
                        Analytics.Track("UploadBook-Failure", new Dictionary <string, string>()
                        {
                            { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message }
                        });
                    }
                    return("");
                }
                catch (AmazonS3Exception e)
                {
                    if (e.Message.Contains("The difference between the request time and the current time is too large"))
                    {
                        progress.WriteError(LocalizationManager.GetString("PublishTab.Upload.TimeProblem",
                                                                          "There was a problem uploading your book. This is probably because your computer is set to use the wrong timezone or your system time is badly wrong. See http://www.di-mgt.com.au/wclock/help/wclo_setsysclock.html for how to fix this."));
                        if (!UseSandbox)
                        {
                            Analytics.Track("UploadBook-Failure-SystemTime");
                        }
                    }
                    else
                    {
                        DisplayNetworkUploadProblem(e, progress);
                        if (!UseSandbox)
                        {
                            // don't make it seem like there are more upload failures than their really are if this a tester pushing to the sandbox
                            Analytics.Track("UploadBook-Failure",
                                            new Dictionary <string, string>()
                            {
                                { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message }
                            });
                        }
                    }
                    return("");
                }
                catch (AmazonServiceException e)
                {
                    DisplayNetworkUploadProblem(e, progress);
                    if (!UseSandbox)                     // don't make it seem like there are more upload failures than their really are if this a tester pushing to the sandbox
                    {
                        Analytics.Track("UploadBook-Failure", new Dictionary <string, string>()
                        {
                            { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message }
                        });
                    }
                    return("");
                }
                catch (Exception e)
                {
                    progress.WriteError(LocalizationManager.GetString("PublishTab.Upload.UploadProblemNotice",
                                                                      "There was a problem uploading your book. You may need to restart Bloom or get technical help."));
                    progress.WriteError(e.Message.Replace("{", "{{").Replace("}", "}}"));
                    progress.WriteVerbose(e.StackTrace);
                    if (!UseSandbox)                     // don't make it seem like there are more upload failures than their really are if this a tester pushing to the sandbox
                    {
                        Analytics.Track("UploadBook-Failure", new Dictionary <string, string>()
                        {
                            { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message }
                        });
                    }
                    return("");
                }
            }
            finally
            {
                if (domForLocking != null && allowLocking && !wasLocked)
                {
                    domForLocking.RecordAsLockedDown(false);
                    XmlHtmlConverter.SaveDOMAsHtml5(domForLocking.RawDom, htmlFile);
                }
            }
            return(s3BookId);
        }
Пример #18
0
        private string UploadBook(string bookFolder, IProgress progress, out string parseId,
                                  string pdfToInclude = null, ISet <string> audioFilesToInclude = null, IEnumerable <string> videoFilesToInclude = null, string[] languages = null,
                                  CollectionSettings collectionSettings = null)
        {
            // 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 must never be locked.
            // So, typically we will 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(bookFolder);
            bool    wasLocked     = false;
            bool    allowLocking  = false;
            HtmlDom domForLocking = null;
            var     metaDataText  = MetaDataText(bookFolder);
            var     metadata      = BookMetaData.FromString(metaDataText);

            if (!String.IsNullOrEmpty(htmlFile))
            {
                var xmlDomFromHtmlFile = XmlHtmlConverter.GetXmlDomFromHtmlFile(htmlFile, false);
                domForLocking = new HtmlDom(xmlDomFromHtmlFile);
                wasLocked     = domForLocking.RecordedAsLockedDown;
                allowLocking  = !metadata.IsSuitableForMakingShells;
                if (allowLocking && !wasLocked)
                {
                    domForLocking.RecordAsLockedDown(true);
                    XmlHtmlConverter.SaveDOMAsHtml5(domForLocking.RawDom, htmlFile);
                }
            }
            string s3BookId;

            try
            {
                // In case we somehow have a book with no ID, we must have one to upload it.
                if (String.IsNullOrEmpty(metadata.Id))
                {
                    metadata.Id = Guid.NewGuid().ToString();
                }
                // And similarly it should have SOME title.
                if (String.IsNullOrEmpty(metadata.Title))
                {
                    metadata.Title = Path.GetFileNameWithoutExtension(bookFolder);
                }
                metadata.SetUploader(UserId);
                s3BookId = S3BookId(metadata);
#if DEBUG
                // S3 URL can be reasonably deduced, as long as we have the S3 ID, so print that out in Debug mode.
                // Format: $"https://s3.amazonaws.com/BloomLibraryBooks{isSandbox}/{s3BookId}/{title}"
                // Example: https://s3.amazonaws.com/BloomLibraryBooks-Sandbox/[email protected]/8d0d9043-a1bb-422d-aa5b-29726cdcd96a/AutoSplit+Timings
                var msgBookId = "s3BookId: " + s3BookId;
                progress.WriteMessage(msgBookId);
#endif
                metadata.DownloadSource = s3BookId;
                // If the collection has a default bookshelf, make sure the book has that tag.
                // Also make sure it doesn't have any other bookshelf tags (which would typically be
                // from a previous default bookshelf upload), including a duplicate of the one
                // we may be about to add.
                var tags = (metadata.Tags ?? new string[0]).Where(t => !t.StartsWith("bookshelf:"));

                if (!String.IsNullOrEmpty(collectionSettings?.DefaultBookshelf))
                {
                    tags = tags.Concat(new [] { "bookshelf:" + collectionSettings.DefaultBookshelf });
                }
                metadata.Tags = tags.ToArray();

                // Any updated ID at least needs to become a permanent part of the book.
                // The file uploaded must also contain the correct DownloadSource data, so that it can be used
                // as an 'order' to download the book.
                // It simplifies unit testing if the metadata file is also updated with the uploadedBy value.
                // Not sure if there is any other reason to do it (or not do it).
                // For example, do we want to send/receive who is the latest person to upload?
                metadata.WriteToFolder(bookFolder);
                // The metadata is also a book order...but we need it on the server with the desired file name,
                // because we can't rename on download. The extension must be the one Bloom knows about,
                // and we want the file name to indicate which book, so use the name of the book folder.
                var metadataPath = BookMetaData.MetaDataPath(bookFolder);
                RobustFile.Copy(metadataPath, BookInfo.BookOrderPath(bookFolder), true);
                parseId = "";
                try
                {
                    _s3Client.UploadBook(s3BookId, bookFolder, progress, pdfToInclude, audioFilesToInclude, videoFilesToInclude, languages);
                    metadata.BaseUrl   = _s3Client.BaseUrl;
                    metadata.BookOrder = _s3Client.BookOrderUrlOfRecentUpload;
                    var metaMsg = LocalizationManager.GetString("PublishTab.Upload.UploadingBookMetadata", "Uploading book metadata", "In this step, Bloom is uploading things like title, languages, and topic tags to the BloomLibrary.org database.");
                    if (IsDryRun)
                    {
                        metaMsg = "(Dry run) Would upload book metadata";                               // TODO: localize?
                    }
                    progress.WriteStatus(metaMsg);
                    // Do this after uploading the books, since the ThumbnailUrl is generated in the course of the upload.
                    if (!IsDryRun)
                    {
                        var response = ParseClient.SetBookRecord(metadata.WebDataJson);
                        parseId = response.ResponseUri.LocalPath;
                        int index = parseId.LastIndexOf('/');
                        parseId = parseId.Substring(index + 1);
                        if (parseId == "books")
                        {
                            // For NEW books the response URL is useless...need to do a new query to get the ID.
                            var json = ParseClient.GetSingleBookRecord(metadata.Id);
                            parseId = json.objectId.Value;
                        }
                        //   if (!UseSandbox) // don't make it seem like there are more uploads than their really are if this a tester pushing to the sandbox
                        {
                            Analytics.Track("UploadBook-Success", new Dictionary <string, string>()
                            {
                                { "url", metadata.BookOrder }, { "title", metadata.Title }
                            });
                        }
                    }
                }
                catch (WebException e)
                {
                    DisplayNetworkUploadProblem(e, progress);
                    if (IsProductionRun)                     // don't make it seem like there are more upload failures than their really are if this a tester pushing to the sandbox
                    {
                        Analytics.Track("UploadBook-Failure", new Dictionary <string, string>()
                        {
                            { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message }
                        });
                    }
                    return("");
                }
                catch (AmazonS3Exception e)
                {
                    if (e.Message.Contains("The difference between the request time and the current time is too large"))
                    {
                        progress.WriteError(LocalizationManager.GetString("PublishTab.Upload.TimeProblem",
                                                                          "There was a problem uploading your book. This is probably because your computer is set to use the wrong timezone or your system time is badly wrong. See http://www.di-mgt.com.au/wclock/help/wclo_setsysclock.html for how to fix this."));
                        if (IsProductionRun)
                        {
                            Analytics.Track("UploadBook-Failure-SystemTime");
                        }
                    }
                    else
                    {
                        DisplayNetworkUploadProblem(e, progress);
                        if (IsProductionRun)
                        {
                            // don't make it seem like there are more upload failures than there really are if this a tester pushing to the sandbox
                            Analytics.Track("UploadBook-Failure",
                                            new Dictionary <string, string>()
                            {
                                { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message }
                            });
                        }
                    }
                    return("");
                }
                catch (AmazonServiceException e)
                {
                    DisplayNetworkUploadProblem(e, progress);
                    if (IsProductionRun)                     // don't make it seem like there are more upload failures than there really are if this a tester pushing to the sandbox
                    {
                        Analytics.Track("UploadBook-Failure", new Dictionary <string, string>()
                        {
                            { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message }
                        });
                    }
                    return("");
                }
                catch (Exception e)
                {
                    var msg1 = LocalizationManager.GetString("PublishTab.Upload.UploadProblemNotice",
                                                             "There was a problem uploading your book. You may need to restart Bloom or get technical help.");
                    var msg2 = e.Message.Replace("{", "{{").Replace("}", "}}");
                    progress.WriteError(msg1);
                    progress.WriteError(msg2);
                    progress.WriteVerbose(e.StackTrace);

                    if (IsProductionRun)                     // don't make it seem like there are more upload failures than there really are if this a tester pushing to the sandbox
                    {
                        Analytics.Track("UploadBook-Failure", new Dictionary <string, string>()
                        {
                            { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message }
                        });
                    }
                    return("");
                }
            }
            finally
            {
                if (domForLocking != null && allowLocking && !wasLocked)
                {
                    domForLocking.RecordAsLockedDown(false);
                    XmlHtmlConverter.SaveDOMAsHtml5(domForLocking.RawDom, htmlFile);
                }
            }
            return(s3BookId);
        }