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);
}