public void RemoveMetaValue_IsThere_RemovesIt() { var dom = new HtmlDom( @"<html><head> <meta name='one' content='1'/> </head></html>"); dom.RemoveMetaElement("one"); AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//meta[@name='one']", 0); }
/// <summary> /// Constructs by finding the file and folder of the xmatter pack, given the its key name e.g. "Factory", "SILIndonesia". /// The default key name is provided as a method parameter, but that can be overridden by a value from inside the book. /// The name of the file should be (key)-XMatter.htm. The name and the location of the folder is not our problem... /// we leave it to the supplied fileLocator to find it. /// </summary> /// <param name="bookDom">The book's DOM</param> /// <param name="xmatterNameFromCollectionSettings">e.g. "Factory", "SILIndonesia". This can be overridden inside the bookDom.</param> /// <param name="fileLocator">The locator needs to be able tell us the path to an xmatter html file, given its name</param> /// <param name="useDeviceVersionIfAvailable">If true, use a pack-specific device xmatter, or "Device" if none found. E.g. ABC => ABC-Device</param> public XMatterHelper(HtmlDom bookDom, string xmatterNameFromCollectionSettings, IFileLocator fileLocator, bool useDeviceVersionIfAvailable = false) { string directoryPath = null; _bookDom = bookDom; var bookSpecificXMatterPack = bookDom.GetMetaValue("xmatter", null); if (!String.IsNullOrWhiteSpace(bookSpecificXMatterPack)) { bookSpecificXMatterPack = MigrateXMatterName(bookSpecificXMatterPack); _nameOfXMatterPack = bookSpecificXMatterPack; if (useDeviceVersionIfAvailable) { _nameOfXMatterPack = GetBestDeviceXMatterAvailable(_nameOfXMatterPack, fileLocator); } var errorTemplate = LocalizationManager.GetString("Errors.XMatterSpecifiedByBookNotFound", "This book called for a Front/Back Matter pack named '{0}', but this version of Bloom does not have it, and Bloom could not find it on this computer. The book has been changed to use the Front/Back Matter pages from the Collection Settings."); directoryPath = GetXMatterDirectory(_nameOfXMatterPack, fileLocator, String.Format(errorTemplate, bookSpecificXMatterPack), false); if (directoryPath == null) { // Remove the xmatter specification from the DOM since it couldn't be found. _bookDom.RemoveMetaElement("xmatter"); } } if (directoryPath == null) { _nameOfXMatterPack = xmatterNameFromCollectionSettings; if (useDeviceVersionIfAvailable) { _nameOfXMatterPack = GetBestDeviceXMatterAvailable(_nameOfXMatterPack, fileLocator); } directoryPath = GetXMatterDirectory(_nameOfXMatterPack, fileLocator, "It should not be possible to get an error here, because the collection verifies its xmatter name in CheckAndFixDependencies()", true); } var htmName = _nameOfXMatterPack + "-XMatter.html"; PathToXMatterHtml = directoryPath.CombineForPath(htmName); if (!RobustFile.Exists(PathToXMatterHtml)) { htmName = _nameOfXMatterPack + "-XMatter.htm"; // pre- Bloom 3.7 PathToXMatterHtml = directoryPath.CombineForPath(htmName); } if (!RobustFile.Exists(PathToXMatterHtml)) { ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), "Could not locate the file {0} in {1} (also checked .html)", htmName, directoryPath); throw new ApplicationException(); } PathToXMatterStylesheet = directoryPath.CombineForPath(GetStyleSheetFileName()); if (!RobustFile.Exists(PathToXMatterStylesheet)) { ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), "Could not locate the file {0} in {1}", GetStyleSheetFileName(), directoryPath); throw new ApplicationException(); } XMatterDom = XmlHtmlConverter.GetXmlDomFromHtmlFile(PathToXMatterHtml, false); }
public void RemoveMetaValue_NotThere_OK() { var dom = new HtmlDom(); dom.RemoveMetaElement("notthere"); }
public string HandleRetiredXMatterPacks(HtmlDom dom, string nameOfXMatterPack) { // Bloom 3.7 retired the BigBook xmatter pack. // If we ever create another xmatter pack called BigBook (or rename the Factory pack) we'll need to redo this. string[] retiredPacks = { "BigBook" }; const string xmatterSuffix = "-XMatter.css"; if (retiredPacks.Contains(nameOfXMatterPack)) { EnsureDoesntHaveLinkToStyleSheet(dom, nameOfXMatterPack + xmatterSuffix); nameOfXMatterPack = "Factory"; EnsureHasLinkToStyleSheet(dom, nameOfXMatterPack + xmatterSuffix); // Since HtmlDom.GetMetaValue() is always called with the collection's xmatter pack as default, // we can just remove this wrong meta element. dom.RemoveMetaElement("xmatter"); } return nameOfXMatterPack; }
public static void RecordAsLockedDown(HtmlDom dom, bool locked) { if (locked) { dom.UpdateMetaElement("lockedDownAsShell", "true"); } else { dom.RemoveMetaElement("lockedDownAsShell"); } }
/// <summary> /// As the bloom format evolves, including structure and classes and other attributes, this /// makes changes to old books. It needs to be very fast, because currently we dont' have /// a real way to detect the need for migration. So we do it all the time. /// /// Yes, we have format version number, but, for example, one overhaul of the common xmatter /// html introduced a new class, "frontCover". Hardly enough to justify bumping the version number /// and making older Blooms unable to read new books. But because this is run, the xmatter will be /// migrated to the new template. /// </summary> /// <param name="bookDOM"></param> /// <param name="progress"></param> private void BringBookUpToDate(HtmlDom bookDOM /* may be a 'preview' version*/, IProgress progress) { if (Title.Contains("allowSharedUpdate")) { // Original version of this code that suffers BL_3166 progress.WriteStatus("Updating Front/Back Matter..."); BringXmatterHtmlUpToDate(bookDOM); progress.WriteStatus("Gathering Data..."); TranslationGroupManager.PrepareElementsInPageOrDocument(bookDOM.RawDom, _collectionSettings); progress.WriteStatus("Updating Data..."); InjectStringListingActiveLanguagesOfBook(); //hack if (bookDOM == OurHtmlDom) //we already have a data for this { _bookData.SynchronizeDataItemsThroughoutDOM(); // I think we should only mess with tags if we are updating the book for real. var oldTagsPath = Path.Combine(_storage.FolderPath, "tags.txt"); if (RobustFile.Exists(oldTagsPath)) { ConvertTagsToMetaData(oldTagsPath, BookInfo); RobustFile.Delete(oldTagsPath); } } else //used for making a preview dom { var bd = new BookData(bookDOM, _collectionSettings, UpdateImageMetadataAttributes); bd.SynchronizeDataItemsThroughoutDOM(); } // get any license info into the json and restored in the replaced front matter. BookCopyrightAndLicense.SetMetadata(GetLicenseMetadata(), bookDOM, FolderPath, CollectionSettings); bookDOM.RemoveMetaElement("bloomBookLineage", () => BookInfo.BookLineage, val => BookInfo.BookLineage = val); bookDOM.RemoveMetaElement("bookLineage", () => BookInfo.BookLineage, val => BookInfo.BookLineage = val); // BookInfo will always have an ID, the constructor makes one even if there is no json file. // To allow migration, pretend it has no ID if there is not yet a meta.json. bookDOM.RemoveMetaElement("bloomBookId", () => (RobustFile.Exists(BookInfo.MetaDataPath) ? BookInfo.Id : null), val => BookInfo.Id = val); // Title should be replicated in json //if (!string.IsNullOrWhiteSpace(Title)) // check just in case we somehow have more useful info in json. // bookDOM.Title = Title; // Bit of a kludge, but there's no way to tell whether a boolean is already set in the JSON, so we fake that it is not, // thus ensuring that if something is in the metadata we use it. // If there is nothing there the default of true will survive. bookDOM.RemoveMetaElement("SuitableForMakingVernacularBooks", () => null, val => BookInfo.IsSuitableForVernacularLibrary = val == "yes" || val == "definitely"); UpdateTextsNewlyChangedToRequiresParagraph(bookDOM); //we've removed and possible added pages, so our page cache is invalid _pagesCache = null; } else { // New version that we hope prevents BL_3166 if (_doingBookUpdate) MessageBox.Show("Caught Bloom doing two updates at once! Possible BL-3166 is being prevented"); lock (_updateLock) { _doingBookUpdate = true; progress.WriteStatus("Updating Front/Back Matter..."); // Nothing in the update process should change the license info, so save what is current before we mess with // anything (may fix BL-3166). var licenseMetadata = GetLicenseMetadata(); BringXmatterHtmlUpToDate(bookDOM); progress.WriteStatus("Gathering Data..."); TranslationGroupManager.PrepareElementsInPageOrDocument(bookDOM.RawDom, _collectionSettings); progress.WriteStatus("Updating Data..."); InjectStringListingActiveLanguagesOfBook(); //hack if (bookDOM == OurHtmlDom) //we already have a data for this { _bookData.SynchronizeDataItemsThroughoutDOM(); // I think we should only mess with tags if we are updating the book for real. var oldTagsPath = Path.Combine(_storage.FolderPath, "tags.txt"); if (RobustFile.Exists(oldTagsPath)) { ConvertTagsToMetaData(oldTagsPath, BookInfo); RobustFile.Delete(oldTagsPath); } } else //used for making a preview dom { var bd = new BookData(bookDOM, _collectionSettings, UpdateImageMetadataAttributes); bd.SynchronizeDataItemsThroughoutDOM(); } // get any license info into the json and restored in the replaced front matter. BookCopyrightAndLicense.SetMetadata(licenseMetadata, bookDOM, FolderPath, CollectionSettings); bookDOM.RemoveMetaElement("bloomBookLineage", () => BookInfo.BookLineage, val => BookInfo.BookLineage = val); bookDOM.RemoveMetaElement("bookLineage", () => BookInfo.BookLineage, val => BookInfo.BookLineage = val); // BookInfo will always have an ID, the constructor makes one even if there is no json file. // To allow migration, pretend it has no ID if there is not yet a meta.json. bookDOM.RemoveMetaElement("bloomBookId", () => (RobustFile.Exists(BookInfo.MetaDataPath) ? BookInfo.Id : null), val => BookInfo.Id = val); // Title should be replicated in json //if (!string.IsNullOrWhiteSpace(Title)) // check just in case we somehow have more useful info in json. // bookDOM.Title = Title; // Bit of a kludge, but there's no way to tell whether a boolean is already set in the JSON, so we fake that it is not, // thus ensuring that if something is in the metadata we use it. // If there is nothing there the default of true will survive. bookDOM.RemoveMetaElement("SuitableForMakingVernacularBooks", () => null, val => BookInfo.IsSuitableForVernacularLibrary = val == "yes" || val == "definitely"); UpdateTextsNewlyChangedToRequiresParagraph(bookDOM); //we've removed and possible added pages, so our page cache is invalid _pagesCache = null; _doingBookUpdate = false; } } }
private void BringBookUpToDate(HtmlDom bookDOM /* may be a 'preview' version*/, IProgress progress) { progress.WriteStatus("Gathering Data..."); //by default, this comes from the collection, but the book can select one, including "null" to select the factory-supplied empty xmatter var nameOfXMatterPack = OurHtmlDom.GetMetaValue("xMatter", _collectionSettings.XMatterPackName); var helper = new XMatterHelper(bookDOM, nameOfXMatterPack, _storage.GetFileLocator()); XMatterHelper.RemoveExistingXMatter(bookDOM); Layout layout = Layout.FromDom(bookDOM, Layout.A5Portrait); //enhance... this is currently just for the whole book. would be better page-by-page, somehow... progress.WriteStatus("Injecting XMatter..."); helper.InjectXMatter(_bookData.GetWritingSystemCodes(), layout); TranslationGroupManager.PrepareElementsInPageOrDocument(bookDOM.RawDom, _collectionSettings); progress.WriteStatus("Updating Data..."); //hack if(bookDOM == OurHtmlDom)//we already have a data for this { _bookData.SynchronizeDataItemsThroughoutDOM(); // I think we should only mess with tags if we are updating the book for real. var oldTagsPath = Path.Combine(_storage.FolderPath, "tags.txt"); if (File.Exists(oldTagsPath)) { ConvertTagsToMetaData(oldTagsPath, BookInfo); File.Delete(oldTagsPath); } } else //used for making a preview dom { var bd = new BookData(bookDOM, _collectionSettings, UpdateImageMetadataAttributes); bd.SynchronizeDataItemsThroughoutDOM(); } bookDOM.RemoveMetaElement("bloomBookLineage", () => BookInfo.BookLineage, val => BookInfo.BookLineage = val); bookDOM.RemoveMetaElement("bookLineage", () => BookInfo.BookLineage, val => BookInfo.BookLineage = val); // BookInfo will always have an ID, the constructor makes one even if there is no json file. // To allow migration, pretend it has no ID if there is not yet a meta.json. bookDOM.RemoveMetaElement("bloomBookId", () => (File.Exists(BookInfo.MetaDataPath) ? BookInfo.Id : null), val => BookInfo.Id = val); // Title should be replicated in json //if (!string.IsNullOrWhiteSpace(Title)) // check just in case we somehow have more useful info in json. // bookDOM.Title = Title; // Bit of a kludge, but there's no way to tell whether a boolean is already set in the JSON, so we fake that it is not, // thus ensuring that if something is in the metadata we use it. bookDOM.RemoveMetaElement("SuitableForMakingShells", () => null, val => BookInfo.IsSuitableForMakingShells = val == "yes" || val == "definitely"); // If there is nothing there the default of true will survive. bookDOM.RemoveMetaElement("SuitableForMakingVernacularBooks", () => null, val => BookInfo.IsSuitableForVernacularLibrary = val == "yes" || val == "definitely"); }