Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions DistFiles/localization/en/Bloom.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -2117,6 +2117,11 @@
<source xml:lang="en">Bloom had trouble saving a page. Please click Details below and report this to us. Then quit Bloom, run it again, and check to see if the page you just edited is missing anything. Sorry!</source>
<note>ID: Errors.CouldNotSavePage</note>
</trans-unit>
<trans-unit id="Errors.CurrentRunningVersion">
<source xml:lang="en">You are running Bloom {0}</source>
<note>ID: Errors.CurrentRunningVersion</note>
<note>The placeholder {0} represents the current version of Bloom. For example, {0} might be replaced with "4.3"</note>
</trans-unit>
<trans-unit id="Errors.DefenderFolderProtection">
<source xml:lang="en">This might be caused by Windows Defender "Controlled Folder Access" or some other virus protection.</source>
<note>ID: Errors.DefenderFolderProtection</note>
Expand All @@ -2131,6 +2136,11 @@
<source xml:lang="en">Your computer denied Bloom access to the book. You may need technical help in setting the operating system permissions for this file.</source>
<note>ID: Errors.DeniedAccess</note>
</trans-unit>
<trans-unit id="Errors.DownloadLatestVersion">
<source xml:lang="en">Upgrade to the latest Bloom (requires Internet connection)</source>
<note>ID: Errors.DownloadLatestVersion</note>
<note>This is a link that will take them to the Downloads (Installers) web page</note>
</trans-unit>
<trans-unit id="Errors.ErrorSelecting">
<source xml:lang="en">There was a problem selecting the book. Restarting Bloom may fix the problem. If not, please click the 'Details' button and report the problem to the Bloom Developers.</source>
<note>ID: Errors.ErrorSelecting</note>
Expand All @@ -2141,12 +2151,28 @@
<note>ID: Errors.ErrorUpdating</note>
<note xml:lang="en">OLD TEXT (before 3.9, added spaces): There was a problem updating the book. Restarting Bloom may fix the problem. If not, please click the 'Details' button and report the problem to the Bloom Developers.</note>
</trans-unit>
<trans-unit id="Errors.FeatureRequiresNewerVersionPlural">
<source xml:lang="en">This book requires Bloom {0} or greater because it uses the following features:</source>
<note>ID: Errors.FeatureRequiresNewerVersionPlural</note>
<note>The placeholder {0} represents the required version of Bloom. For example, {0} might be replaced with "4.4"</note>
<note>A bulleted list of features (in English) will follow underneath this text</note>
</trans-unit>
<trans-unit id="Errors.FeatureRequiresNewerVersionSingular">
<source xml:lang="en">This book requires Bloom {0} or greater because it uses the feature \"{1}\".</source>
<note>ID: Errors.FeatureRequiresNewerVersionSingular</note>
<note>The placeholder {0} represents the required version of Bloom. For example, {0} might be replaced with "4.4"</note>
<note>The placeholder {1} represents a short description of the feature For example, {1} might be replaced with "Whole Text Audio"</note>
</trans-unit>
<trans-unit id="Errors.NeedNewerVersion">
<source xml:lang="en">{0} requires a newer version of Bloom. Download the latest version of Bloom from {1}</source>
<note>ID: Errors.NeedNewerVersion</note>
<note>{0} will get the name of the book, {1} will give a link to open the Bloom Library Web page.</note>
<note xml:lang="en">OLD TEXT (before 3.9, removed period): {0} requires a newer version of Bloom. Download the latest version of Bloom from {1}.</note>
</trans-unit>
<trans-unit id="Errors.NewVersionNeededHeader">
<source xml:lang="en">"This book needs a new version of Bloom.</source>
<note>ID: Errors.NewVersionNeededHeader</note>
</trans-unit>
<trans-unit id="Errors.ProblemDeletingFile">
<source xml:lang="en">Bloom had a problem deleting this file: {0}</source>
<note>ID: Errors.ProblemDeletingFile</note>
Expand Down
1 change: 1 addition & 0 deletions src/BloomExe/BloomExe.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@
<Compile Include="Book\BloomReaderFileMaker.cs" />
<Compile Include="Book\BookSelectionChangedEventArgs.cs" />
<Compile Include="Book\QuestionGroup.cs" />
<Compile Include="Book\VersionRequirements.cs" />
<Compile Include="CLI\ChangeLayoutCommand.cs" />
<Compile Include="Collection\BrandingProject.cs" />
<Compile Include="MiscUI\DefenderFolderProtectionCheck.cs" />
Expand Down
16 changes: 10 additions & 6 deletions src/BloomExe/Book/Book.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
Expand Down Expand Up @@ -565,7 +565,7 @@ private HtmlDom GetBookDomWithStyleSheets(params string[] cssFileNames)
private HtmlDom GetErrorDom(string extraMessages="")
{
var builder = new StringBuilder();
builder.Append("<html><body style='font-family:arial,sans'>");
builder.Append("<html><head><meta charset=\"UTF-8\" /></head><body style='font-family:arial,sans'>");

if(_storage != null)
{
Expand All @@ -578,14 +578,17 @@ private HtmlDom GetErrorDom(string extraMessages="")

// often GetBrokenBookRecommendation and FatalErrorDescription both come from _storage.ErrorMessagesHtml.
// Try not to say the same thing twice.
if (!builder.ToString().Contains(FatalErrorDescription))
if (FatalErrorDescription != null && !builder.ToString().Contains(FatalErrorDescription))
builder.Append(FatalErrorDescription);

builder.Append("<p>"+ WebUtility.HtmlEncode(extraMessages)+"</p>");

var message = LocalizationManager.GetString("Errors.ReportThisProblemButton", "Report this problem to Bloom Support");
builder.AppendFormat(
"<input type='button' value='"+message+"' href='ReportProblem'></input>");
if (_storage.ErrorAllowsReporting)
{
var message = LocalizationManager.GetString("Errors.ReportThisProblemButton", "Report this problem to Bloom Support");
builder.AppendFormat(
"<input type='button' value='" + message + "' href='ReportProblem'></input>");
}

builder.Append("</body></html>");

Expand Down Expand Up @@ -719,6 +722,7 @@ public virtual HtmlDom GetPreviewHtmlFileForWholeBook()
{
return GetErrorDom(_storage.GetValidateErrors());
}

var previewDom= GetBookDomWithStyleSheets("previewMode.css", "origami.css");
AddCreationTypeAttribute(previewDom);

Expand Down
120 changes: 115 additions & 5 deletions src/BloomExe/Book/BookStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public interface IBookStorage
HtmlDom MakeDomRelocatable(HtmlDom dom);
string SaveHtml(HtmlDom bookDom);
void SetBookName(string name);
string GetHtmlMessageIfFeatureIncompatibility();
string GetValidateErrors();
void CheckBook(IProgress progress,string pathToFolderOfReplacementImages = null);
void UpdateBookFileAndFolderName(CollectionSettings settings);
Expand All @@ -65,6 +66,7 @@ public interface IBookStorage
BookInfo BookInfo { get; set; }
string NormalBaseForRelativepaths { get; }
string InitialLoadErrors { get; }
bool ErrorAllowsReporting { get; }
void UpdateSupportFiles();
void Update(string fileName, string factoryPath = "");
string Duplicate();
Expand Down Expand Up @@ -101,8 +103,10 @@ public class BookStorage : IBookStorage

public event EventHandler FolderPathChanged;


// Returns any errors reported while loading the book (during 'expensive initialization').
public string InitialLoadErrors { get; private set; }
public bool ErrorAllowsReporting { get; private set; } // True if we want to display a Report to Bloom Support button

public BookInfo BookInfo
{
Expand Down Expand Up @@ -217,9 +221,9 @@ public bool TryGetPremadeThumbnail(string fileName, out Image image)

public HtmlDom Dom { get; private set; }

public static string GetHtmlMessageIfVersionIsIncompatibleWithThisBloom(HtmlDom dom,string path)
public static string GetHtmlMessageIfVersionIsIncompatibleWithThisBloom(HtmlDom dom, string path)
{
var versionString = dom.GetMetaValue("BloomFormatVersion", "").Trim();
var versionString = dom.GetMetaValue("BloomFormatVersion", "").Trim();
if (string.IsNullOrEmpty(versionString))
return "";// "This file lacks the following required element: <meta name='BloomFormatVersion' content='x.y'>";

Expand All @@ -230,15 +234,70 @@ public static string GetHtmlMessageIfVersionIsIncompatibleWithThisBloom(HtmlDom
if (versionFloat > float.Parse(kBloomFormatVersion, CultureInfo.InvariantCulture))
{
var msg = LocalizationManager.GetString("Errors.NeedNewerVersion",
"{0} requires a newer version of Bloom. Download the latest version of Bloom from {1}","{0} will get the name of the book, {1} will give a link to open the Bloom Library Web page.");
"{0} requires a newer version of Bloom. Download the latest version of Bloom from {1}", "{0} will get the name of the book, {1} will give a link to open the Bloom Library Web page.");
msg = string.Format(msg, path, string.Format("<a href='{0}'>BloomLibrary.org</a>", UrlLookup.LookupUrl(UrlType.LibrarySite)));
msg += string.Format(". (Format {0} vs. {1})",versionString, kBloomFormatVersion);
msg += string.Format(". (Format {0} vs. {1})", versionString, kBloomFormatVersion);
return msg;
}

return null;
}

// Returns HTML with error message for any features that this book contains which cannot be opened by this version of Bloom.
// Note that although we don't allow the user to open the book (because if this version opens and saves the book, it will cause major problems for a later version of Bloom),
// here isn't actually any corruption or malformed data or anything particularly wrong with the book storage. So, we need to handle these kind of errors differently than validation errors.
public string GetHtmlMessageIfFeatureIncompatibility()
{
// Check if there are any features in this file format (which is readable), but which won't be supported (and have effects bad enough to warrant blocking opening) in this version.
string featureVersionRequirementJson = Dom.GetMetaValue("FeatureRequirement", "");
if (String.IsNullOrEmpty(featureVersionRequirementJson))
{
return "";
}
VersionRequirement[] featureVersionRequirementList = (VersionRequirement[])JsonConvert.DeserializeObject(featureVersionRequirementJson, typeof(VersionRequirement[]));

if (featureVersionRequirementList != null && featureVersionRequirementList.Length >= 1)
{
string currentBloomDesktopVersion = typeof(BookStorage).Assembly?.GetName()?.Version?.ToString() ?? "1.0";
var breakingFeatureRequirements = featureVersionRequirementList.Where(x => VersionComparer.IsLessThanVersion(currentBloomDesktopVersion, x.BloomDesktopMinVersion));

// Note: even though versionRequirements is guaranated non-empty by now, the ones that actually break our current version of Bloom DESKTOP could be empty.
if (breakingFeatureRequirements.Count() >= 1)
{
string messageNewVersionNeededHeader = LocalizationManager.GetString("Errors.NewVersionNeededHeader", "This book needs a new version of Bloom.");
string messageCurrentRunningVersion = String.Format(LocalizationManager.GetString("Errors.CurrentRunningVersion", "You are running Bloom {0}"), currentBloomDesktopVersion);
string messageDownloadLatestVersion = LocalizationManager.GetString("Errors.DownloadLatestVersion", "Upgrade to the latest Bloom (requires Internet connection)");

string messageFeatureRequiresNewerVersion;
if (breakingFeatureRequirements.Count() == 1)
{
var requirement = breakingFeatureRequirements.First();
messageFeatureRequiresNewerVersion = String.Format(LocalizationManager.GetString("Errors.FeatureRequiresNewerVersionSingular", "This book requires Bloom {0} or greater because it uses the feature \"{1}\"."), requirement.BloomDesktopMinVersion, requirement.FeaturePhrase) + "<br/>";
}
else
{
var sortedRequirements = breakingFeatureRequirements.OrderByDescending(x => x.BloomDesktopMinVersion, new VersionComparer<string>());
var highestVersionRequired = sortedRequirements.First().BloomDesktopMinVersion;

messageFeatureRequiresNewerVersion = String.Format(LocalizationManager.GetString("Errors.FeatureRequiresNewerVersionPlural", "This book requires Bloom {0} or greater because it uses the following features:"), highestVersionRequired);

string listItemsHtml = String.Join("", sortedRequirements.Select(x => $"<li>{x.FeaturePhrase}</li>"));
messageFeatureRequiresNewerVersion += $"<ul>{listItemsHtml}</ul>";
}

string message =
$"<strong>{messageNewVersionNeededHeader}</strong><br/><br/><br/>" +
$"{messageCurrentRunningVersion}. {messageFeatureRequiresNewerVersion}<br/><br/>" +
$"<a href='{UrlLookup.LookupUrl(UrlType.LibrarySite)}/installers'>{messageDownloadLatestVersion}</a>"; // Enhance: is there a market-specific version of Bloom Library? If so, ideal to link to it somehow.

return message;
}
}

return "";
}


public bool GetLooksOk()
{
return RobustFile.Exists(PathToExistingHtml) && String.IsNullOrEmpty(ErrorMessagesHtml);
Expand All @@ -259,6 +318,20 @@ public void Save()
Dom.UpdateMetaElement("BloomFormatVersion", kBloomFormatVersion);
}
BookInfo.FormatVersion = kBloomFormatVersion;

VersionRequirement[] requiredVersions = GetRequiredVersions(Dom);
if (requiredVersions != null && requiredVersions.Length >= 1)
{
string json = JsonConvert.SerializeObject(requiredVersions);
Dom.UpdateMetaElement("FeatureRequirement", json);
}
else
{
// TODO: Unit test for this if possible
// Might be necessary if you duplicated a book, or modified a book such that it no longer needs this
Dom.RemoveMetaElement("FeatureRequirement");
}

var watch = Stopwatch.StartNew();
string tempPath = SaveHtml(Dom);
watch.Stop();
Expand Down Expand Up @@ -294,6 +367,26 @@ public void Save()
BookInfo.Save();
}

// Determines which features will have serious breaking effects if not opened in the proper version of any relevant Bloom products
// Note: This should include not only BloomDesktop considerations, but needs to insert enough information for things like BloomReader to be able to figure it out too
public static VersionRequirement[] GetRequiredVersions(HtmlDom dom)
{
var reqList = new List<VersionRequirement>();

if (dom.DoesContainNarrationAudioRecordedUsingWholeTextBox())
{
reqList.Add(new VersionRequirement()
{
FeatureId = "wholeTextBoxAudio",
FeaturePhrase = "Whole Text Box Audio",
BloomDesktopMinVersion = "4.4",
BloomReaderMinVersion = "1.0"
});
}

return reqList.OrderByDescending(x => x.BloomDesktopMinVersion, new VersionComparer<string>()).ToArray();
}

public const string BackupFilename = "bookhtml.bak"; // need to know this in BookCollection too.

private string GetBackupFilePath()
Expand Down Expand Up @@ -1014,6 +1107,7 @@ private void ExpensiveInitialization(bool forSelectedBook = false)
else
{
ErrorMessagesHtml = WebUtility.HtmlEncode(error.Message);
ErrorAllowsReporting = true;
Logger.WriteEvent("*** ERROR in " + PathToExistingHtml);
Logger.WriteEvent("*** ERROR: " + error.Message.Replace("{", "{{").Replace("}", "}}"));
return;
Expand Down Expand Up @@ -1065,11 +1159,25 @@ private void ExpensiveInitialization(bool forSelectedBook = false)
ErrorMessagesHtml = incompatibleVersionMessage;
Logger.WriteEvent("*** ERROR: " + incompatibleVersionMessage);
_errorAlreadyContainsInstructions = true;
ErrorAllowsReporting = true;
return;
}
else
{
Logger.WriteEvent("BookStorage Loading Dom from {0}", PathToExistingHtml);
var incompatibleFeatureMessage = GetHtmlMessageIfFeatureIncompatibility();
if (!String.IsNullOrWhiteSpace(incompatibleFeatureMessage))
{
ErrorMessagesHtml = incompatibleFeatureMessage;
Logger.WriteEvent("*** ERROR: " + incompatibleFeatureMessage);
_errorAlreadyContainsInstructions = true;
ErrorAllowsReporting = false; // This doesn't any corruption or bugs in the code, so no reporting button needed.
return;
}

else
{
Logger.WriteEvent("BookStorage Loading Dom from {0}", PathToExistingHtml);
}
}
}

Expand Down Expand Up @@ -1143,6 +1251,7 @@ private void ProcessAccessDeniedError(UnauthorizedAccessException error)
message += "<br></br>" + String.Format(seeAlso, "<a href='" + helpUrl + "'>" + helpUrl + "</a>");
ErrorMessagesHtml = message;
_errorAlreadyContainsInstructions = true;
ErrorAllowsReporting = true;
}

/// <summary>
Expand Down Expand Up @@ -1187,6 +1296,7 @@ public void UpdateSupportFiles()
catch (Exception error)
{
ErrorMessagesHtml = WebUtility.HtmlEncode(error.Message);
ErrorAllowsReporting = true;
}

CopyBrandingFiles();
Expand Down
6 changes: 6 additions & 0 deletions src/BloomExe/Book/HtmlDom.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1825,6 +1825,12 @@ public static XmlNodeList SelectAudioSentenceElementsWithRecordingMd5(XmlElement
return element.SafeSelectNodes("descendant-or-self::node()[contains(@class,'audio-sentence') and @recordingmd5]");
}

public bool DoesContainNarrationAudioRecordedUsingWholeTextBox()
{
var nodes = _dom.SafeSelectNodes("//*[@data-audiorecordingmode='TextBox']");
return nodes?.Count >= 1;
}

public static bool IsImgOrSomethingWithBackgroundImage(XmlElement element)
{
return element.SelectNodes("self::img | self::*[contains(@style,'background-image')]").Count == 1;
Expand Down
Loading