private static void UpdateRightToLeftSetting(BookData bookData, XmlElement e, string lang) { HtmlDom.RemoveRtlDir(e); if ((lang == bookData.Language1IsoCode && bookData.Language1.IsRightToLeft) || (lang == bookData.Language2IsoCode && bookData.Language2.IsRightToLeft) || (lang == bookData.Language3IsoCode && bookData.Language3.IsRightToLeft) || (lang == bookData.MetadataLanguage1IsoCode && bookData.MetadataLanguage1.IsRightToLeft)) { HtmlDom.AddRtlDir(e); } }
public void Constructor_CollectionSettingsHasCountrProvinceDistrict_LanguageLocationFilledIn() { // var dom = new HtmlDom(@"<html><head><div id='bloomDataDiv'> // <div data-book='country'>the country</div> // <div data-book='province'>the province</div> // <div data-book='district'>the district</div> // </div></head><body></body></html>"); var dom = new HtmlDom(); var data = new BookData(dom, new CollectionSettings(){Country="the country", Province = "the province", District= "the district"}, null); Assert.AreEqual("the district, the province<br/>the country", data.GetVariableOrNull("languageLocation", "*")); }
public static void SetupPage(XmlElement pageDiv, BookData bookData) //, bool inShellMode) { TranslationGroupManager.PrepareElementsInPageOrDocument(pageDiv, bookData); SetLanguageForElementsWithMetaLanguage(pageDiv, bookData); // a page might be "extra" as far as the template is concerned, but // once a page is inserted into book (which may become a shell), it's // just a normal page pageDiv.SetAttribute("data-page", pageDiv.GetAttribute("data-page").Replace("extra", "").Trim()); ClearAwayDraftText(pageDiv); ClearAwayPageDescription(pageDiv); }
static void PrepareSpecialLanguageGroups(XmlNode pageDiv, BookData bookData) { foreach ( XmlElement groupElement in pageDiv.SafeSelectNodes("descendant-or-self::*[contains(@class,'bloom-translationGroup')]")) { var dataDefaultLangs = groupElement.Attributes["data-default-languages"]?.Value; if (dataDefaultLangs != null && dataDefaultLangs.Contains("N2")) { MakeElementWithLanguageForOneGroup(groupElement, bookData.MetadataLanguage2IsoCode); } } }
public static void AddUIDictionaryToDom(HtmlDom pageDom, BookData bookData) { CheckDynamicStrings(); // add dictionary script to the page XmlElement dictionaryScriptElement = pageDom.RawDom.SelectSingleNode("//script[@id='ui-dictionary']") as XmlElement; if (dictionaryScriptElement != null) { dictionaryScriptElement.ParentNode.RemoveChild(dictionaryScriptElement); } dictionaryScriptElement = pageDom.RawDom.CreateElement("script"); dictionaryScriptElement.SetAttribute("type", "text/javascript"); dictionaryScriptElement.SetAttribute("id", "ui-dictionary"); var d = new Dictionary <string, string>(); foreach (var lang in bookData.GetAllBookLanguages()) { SafelyAddLanguage(d, lang.Iso639Code, lang.GetNameInLanguage(lang.Iso639Code)); } SafelyAddLanguage(d, "vernacularLang", bookData.Language1.Iso639Code); //use for making the vernacular the first tab SafelyAddLanguage(d, "{V}", bookData.Language1.Name); SafelyAddLanguage(d, "{N1}", bookData.MetadataLanguage1.GetNameInLanguage(bookData.MetadataLanguage1IsoCode)); if (!string.IsNullOrEmpty(bookData.Language3IsoCode)) { SafelyAddLanguage(d, "{N2}", bookData.MetadataLanguage2.GetNameInLanguage(bookData.MetadataLanguage2IsoCode)); } // TODO: Eventually we need to look through all .bloom-translationGroup elements on the current page to determine // whether there is text in a language not yet added to the dictionary. // For now, we just add a few we know we need AddSomeCommonNationalLanguages(d); // Hard-coded localizations for 2.0 AddHtmlUiStrings(d); // Do this last, on the off-chance that the page contains a localizable string that matches // a language code. AddLanguagesUsedInPage(pageDom.RawDom, d); dictionaryScriptElement.InnerText = String.Format("function GetInlineDictionary() {{ return {0};}}", JsonConvert.SerializeObject(d)); // add i18n initialization script to the page //AddLocalizationTriggerToDom(pageDom); pageDom.Head.InsertAfter(dictionaryScriptElement, pageDom.Head.LastChild); _collectDynamicStrings = false; }
/// <summary> /// stick in a json with various settings we want to make available to the javascript /// </summary> public static void AddUISettingsToDom(HtmlDom pageDom, BookData bookData, IFileLocator fileLocator) { CheckDynamicStrings(); XmlElement existingElement = pageDom.RawDom.SelectSingleNode("//script[@id='ui-settings']") as XmlElement; XmlElement element = pageDom.RawDom.CreateElement("script"); element.SetAttribute("type", "text/javascript"); element.SetAttribute("id", "ui-settings"); var d = new Dictionary <string, string>(); //d.Add("urlOfUIFiles", "file:///" + fileLocator.LocateDirectory("ui", "ui files directory")); if (!String.IsNullOrEmpty(Settings.Default.LastSourceLanguageViewed)) { d.Add("defaultSourceLanguage", Settings.Default.LastSourceLanguageViewed); } d.Add("languageForNewTextBoxes", bookData.Language1.Iso639Code); d.Add("isSourceCollection", bookData.CollectionSettings.IsSourceCollection.ToString()); // BL-2357 To aid in smart ordering of source languages in source bubble if (!String.IsNullOrEmpty(bookData.Language2IsoCode)) { d.Add("currentCollectionLanguage2", bookData.Language2IsoCode); } if (!String.IsNullOrEmpty(bookData.Language3IsoCode)) { d.Add("currentCollectionLanguage3", bookData.Language3IsoCode); } d.Add("browserRoot", FileLocationUtilities.GetDirectoryDistributedWithApplication(BloomFileLocator.BrowserRoot).ToLocalhost()); element.InnerText = String.Format("function GetSettings() {{ return {0};}}", JsonConvert.SerializeObject(d)); var head = pageDom.RawDom.SelectSingleNode("//head"); if (existingElement != null) { head.ReplaceChild(element, existingElement); } else { head.InsertAfter(element, head.LastChild); } _collectDynamicStrings = false; }
/// <summary> /// This is used when a book is first created from a source; without it, if the shell maker left the book as trilingual when working on it, /// then everytime someone created a new book based on it, it too would be trilingual. /// </summary> public static void SetInitialMultilingualSetting(BookData bookData, int oneTwoOrThreeContentLanguages, CollectionSettings collectionSettings) { //var multilingualClass = new string[]{"bloom-monolingual", "bloom-bilingual","bloom-trilingual"}[oneTwoOrThreeContentLanguages-1]; if (oneTwoOrThreeContentLanguages < 3) bookData.RemoveAllForms("contentLanguage3"); if (oneTwoOrThreeContentLanguages < 2) bookData.RemoveAllForms("contentLanguage2"); bookData.Set("contentLanguage1", collectionSettings.Language1Iso639Code, false); if (oneTwoOrThreeContentLanguages > 1) bookData.Set("contentLanguage2", collectionSettings.Language2Iso639Code, false); if (oneTwoOrThreeContentLanguages > 2 && !string.IsNullOrEmpty(collectionSettings.Language3Iso639Code)) bookData.Set("contentLanguage3", collectionSettings.Language3Iso639Code, false); }
internal static string GetOriginalCopyrightAndLicenseNotice(BookData bookData, HtmlDom dom) { var originalMetadata = GetOriginalMetadata(dom, bookData); // As of BL-7898, we are using the existence of an original copyright/license to determine if we are working with a derivative. if (!IsDerivative(originalMetadata)) { return(null); } // The originalTitle strategy used here is not ideal. We would prefer to have a placeholder specifically for it // in both EditTab.FrontMatter.OriginalCopyrightSentence and EditTab.FrontMatter.OriginalHadNoCopyrightSentence. // But we don't want to require a new set of translations if we can avoid it. var encodedTitle = dom.GetBookSetting("originalTitle")?.GetExactAlternative("*"); var originalTitle = HttpUtility.HtmlDecode(encodedTitle); var titleCitation = "<cite data-book=\"originalTitle\"" + (string.IsNullOrEmpty(originalTitle) ? " class=\"missingOriginalTitle\">" : ">") + originalTitle + "</cite>"; var languagePriorityIdsNotLang1 = bookData.GetLanguagePrioritiesForLocalizedTextOnPage(false); var originalLicenseSentence = GetOriginalLicenseSentence(languagePriorityIdsNotLang1, originalMetadata.License, out string licenseOnly); var rawCopyright = originalMetadata.CopyrightNotice; // If we have all the pieces available, we want to use this one. // At the very least it's easier to localize into the format the language wants to use. var fullFormatString = LocalizationManager.GetString( "EditTab.FrontMatter.FullOriginalCopyrightLicenseSentence", "Adapted from original, {0}, {1}. Licensed under {2}.", "On the Credits page of a book being translated, Bloom shows the original copyright. {0} is original title, {1} is original copyright, and {2} is license information.", languagePriorityIdsNotLang1, out string langUsed); // The last condition here (langUsed ==...) is meant to detect if the string has been translated // into the current language or not. if (!string.IsNullOrEmpty(originalTitle) && !string.IsNullOrEmpty(rawCopyright) && !string.IsNullOrEmpty(licenseOnly) && langUsed == languagePriorityIdsNotLang1.First()) { return(string.Format(fullFormatString, titleCitation, rawCopyright, licenseOnly)); } var copyrightNotice = GetOriginalCopyrightSentence( languagePriorityIdsNotLang1, rawCopyright, titleCitation).Trim(); return((copyrightNotice + " " + originalLicenseSentence).Trim()); }
public static void TransformCreditPageData(HtmlDom dom, BookData bookData, CollectionSettings collectionSettings, BookStorage storage, bool makingTranslation) { // If we're deriving a translation from an existing book, // we should save the original copyright and license of that book. if (makingTranslation) { SetOriginalCopyrightAndLicense(dom, bookData, collectionSettings); } // a new book should never have the copyright holder set, whether it's a template, shell, or translation bookData.RemoveAllForms("copyright"); // RemoveAllForms does modify the dom storage.BookInfo.Copyright = null; // this might be redundant but let's play safe // This is a place to put who it was translated by, usually in a national language. // Doesn't apply to templates or (usually) to shells; but a translation can serve again as a shell. // In that case, we expect it to be filled in with the new translator's information. // Keeping the previous translator's details there is confusing (BL-6271) bookData.RemoveAllForms("versionAcknowledgments"); }
private static string GetBestMultiTextBaseValue(MultiTextBase multiTextBase, BookData bookData) { string alternative = multiTextBase.GetFirstAlternative(); if (bookData != null) { var langs = new List <string>(); langs.AddRange(bookData.GetAllBookLanguageCodes()); langs.Add("*"); langs.Add("en"); var bestAltString = multiTextBase.GetBestAlternativeString(langs); if (!string.IsNullOrEmpty(bestAltString)) { alternative = bestAltString; } } return(DecodeAlternative(alternative)); }
private void SetBookTitle(BookStorage storage, BookData bookData, bool usingTemplate) { //This is what we were trying to do: there was a defaultNameForDerivedBooks meta tag in the html //which had no language code. It worked fine for English, e.g., naming new English books //"My Book" or "My Dictionary" or whatever. //But in other cases, it actually hurt because that English name would be hidden down in the new //book, where the author wouldn't see it. But some consumer, using English, would see it, and //"My Book" is a pretty dumb name for the carefully prepared book to be listed under. // //Now, if we are making this book from a shell book, we can keep whatever (title,language) pairs it has. //Those will be just fine, for example, if we have English as one of our national languages and so get // "vaccinations" for free without having to type that in again. // //But if we are making this from a *template*, then we *don't* want to keep the various ways to say the //name of the template. Seeing "Basic Book" as the name of a resulting shell is not helpful. //We just don't have a use for this at all anymore: nice idea, doesn't really work: storage.Dom.RemoveMetaElement("defaultNameForDerivedBooks"); // Clear these out let other code set again when there is a real title. storage.BookInfo.Title = ""; storage.Dom.Title = ""; //If we're making a book from a template, remove all the titles in all languages if (usingTemplate) { bookData.RemoveAllForms("bookTitle"); } // If we're making a Template, we really want its title to include Template // (in hopes the user will keep it at the end so the pages can be used in Add Page) if (storage.BookInfo.IsSuitableForMakingShells) { storage.BookInfo.Title = "My Template"; storage.Dom.Title = "My Template"; storage.Dom.SetBookSetting("bookTitle", "en", "My Template"); // Yes, we want the English word Template in the vernacular Title. Ugly, but that's // what determines the file name, and that's what determines whether Add Page will // include it. storage.Dom.SetBookSetting("bookTitle", _collectionSettings.Language1Iso639Code, "My Template"); } }
/// <summary> /// Here, "normally" means unless the user overrides via a .bloom-visibility-user-on/off /// </summary> internal static bool ShouldNormallyShowEditable(string lang, string[] dataDefaultLanguages, string contentLanguageIso2, string contentLanguageIso3, // these are effected by the multilingual settings for this book BookData bookData) // use to get the collection's current N1 and N2 in xmatter or other template pages that specify default languages { if (string.IsNullOrEmpty(lang)) { return(false); // if by any bizarre chance we have a block with an empty language code, we don't want to show it! } // Note: There is code in bloom-player that is modeled after this code. // If this function changes, you should check in bloom-player's bloom-player-core.tsx file, function shouldNormallyShowEditable(). // It may benefit from being updated too. if (dataDefaultLanguages == null || dataDefaultLanguages.Length == 0 || String.IsNullOrWhiteSpace(dataDefaultLanguages[0]) || dataDefaultLanguages[0].Equals("auto", StringComparison.InvariantCultureIgnoreCase)) { return(lang == bookData.Language1.Iso639Code || lang == contentLanguageIso2 || lang == contentLanguageIso3); } else { // Note there are (perhaps unfortunately) two different labeling systems, but they have a more-or-less 1-to-1 correspondence: // The V/N1/N2 system feels natural in vernacular book contexts // The L1/L2/L3 system is more natural in source book contexts. // But, the new model makes L2 and L3 mean the second and third checked languages, while N1 and N2 are the // second and third metadata languages, currently locked to the collection L2 and L3. // V and L1 both mean the first checked language. // Changes here should result in consistent ones in Book.IsLanguageWanted and BookData.GatherDataItemsFromXElement // and RuntimeInformationInjector.AddUIDictionaryToDom and I18ApiHandleI18nRequest return((lang == bookData.Language1.Iso639Code && dataDefaultLanguages.Contains("V")) || (lang == bookData.Language1.Iso639Code && dataDefaultLanguages.Contains("L1")) || (lang == bookData.MetadataLanguage1IsoCode && dataDefaultLanguages.Contains("N1")) || (lang == bookData.Language2IsoCode && dataDefaultLanguages.Contains("L2")) || (lang == bookData.MetadataLanguage2IsoCode && dataDefaultLanguages.Contains("N2")) || (lang == bookData.Language3IsoCode && dataDefaultLanguages.Contains("L3")) || dataDefaultLanguages.Contains(lang)); // a literal language id, e.g. "en" (used by template starter) } }
public Book(BookInfo info, IBookStorage storage, ITemplateFinder templateFinder, CollectionSettings collectionSettings, HtmlThumbNailer thumbnailProvider, PageSelection pageSelection, PageListChangedEvent pageListChangedEvent, BookRefreshEvent bookRefreshEvent) { BookInfo = info; Guard.AgainstNull(storage,"storage"); _storage = storage; //this is a hack to keep these two in sync (in one direction) _storage.FolderPathChanged +=(x,y)=>BookInfo.FolderPath = _storage.FolderPath; _templateFinder = templateFinder; _collectionSettings = collectionSettings; _thumbnailProvider = thumbnailProvider; _pageSelection = pageSelection; _pageListChangedEvent = pageListChangedEvent; _bookRefreshEvent = bookRefreshEvent; _bookData = new BookData(OurHtmlDom, _collectionSettings, UpdateImageMetadataAttributes); if (IsEditable && !HasFatalError) { _bookData.SynchronizeDataItemsThroughoutDOM(); WriteLanguageDisplayStyleSheet(); //NB: if you try to do this on a file that's in program files, access will be denied OurHtmlDom.AddStyleSheet(@"languageDisplay.css"); } FixBookIdAndLineageIfNeeded(_storage.Dom); _storage.Dom.RemoveExtraContentTypesMetas(); Guard.Against(OurHtmlDom.RawDom.InnerXml=="","Bloom could not parse the xhtml of this document"); }
/// <summary> /// This is used when a book is first created from a source; without it, if the shell maker left the book as trilingual when working on it, /// then everytime someone created a new book based on it, it too would be trilingual. /// </summary> public static void SetInitialMultilingualSetting(BookData bookData, int oneTwoOrThreeContentLanguages, CollectionSettings collectionSettings) { //var multilingualClass = new string[]{"bloom-monolingual", "bloom-bilingual","bloom-trilingual"}[oneTwoOrThreeContentLanguages-1]; if (oneTwoOrThreeContentLanguages < 3) { bookData.RemoveAllForms("contentLanguage3"); } if (oneTwoOrThreeContentLanguages < 2) { bookData.RemoveAllForms("contentLanguage2"); } bookData.Set("contentLanguage1", collectionSettings.Language1Iso639Code, false); if (oneTwoOrThreeContentLanguages > 1) { bookData.Set("contentLanguage2", collectionSettings.Language2Iso639Code, false); } if (oneTwoOrThreeContentLanguages > 2 && !string.IsNullOrEmpty(collectionSettings.Language3Iso639Code)) { bookData.Set("contentLanguage3", collectionSettings.Language3Iso639Code, false); } }
/// <summary> /// Copy the copyright & license info to the originalCopyrightAndLicense, /// then remove the copyright so the translator can put in their own if they /// want. We retain the license, but the translator is allowed to change that. /// If the source is already a translation (already has original copyright or license) /// we keep them unchanged. /// </summary> public static void SetOriginalCopyrightAndLicense(HtmlDom dom, BookData bookData, CollectionSettings collectionSettings) { // If it already has some of this information, just keep it. if (bookData.BookIsDerivative()) { return; //leave the original there. } // If there's no copyright information in a source-collection book, we're presumably making // a new original book, and shouldn't try to record any original copyright and license information. // This is somewhat redundant with the check in BookStarter.SetupNewDocumentContents(), the one // non-unit-test current caller of this method, that doesn't call this at all if the source is // a template book. I was trying for a minimal reasonable change for BL-5131, and therefore // put in this extra check, since previously this method was simply NEVER called in a source // collection. var copyrightNotice = BookCopyrightAndLicense.GetMetadata(dom).CopyrightNotice; if (String.IsNullOrEmpty(copyrightNotice) && collectionSettings.IsSourceCollection) { return; } bookData.Set("originalLicenseUrl", BookCopyrightAndLicense.GetLicenseUrl(dom), "*"); bookData.Set("originalCopyright", System.Web.HttpUtility.HtmlEncode(copyrightNotice), "*"); bookData.Set("originalLicenseNotes", dom.GetBookSetting("licenseNotes").GetFirstAlternative(), "*"); }
private static void UpdateContentLanguageClassesOnElement(XmlElement e, Dictionary <string, string> contentLanguages, BookData bookData, string contentLanguageIso2, string contentLanguageIso3, string[] dataDefaultLanguages) { HtmlDom.RemoveClassesBeginingWith(e, "bloom-content"); var lang = e.GetAttribute("lang"); //These bloom-content* classes are used by some stylesheet rules, primarily to boost the font-size of some languages. //Enhance: this is too complex; the semantics of these overlap with each other and with bloom-visibility-code-on, and with data-language-order. //It would be better to have non-overlapping things; 1 for order, 1 for visibility, one for the lang's role in this collection. string orderClass; if (contentLanguages.TryGetValue(lang, out orderClass)) { HtmlDom.AddClass(e, orderClass); //bloom-content1, bloom-content2, bloom-content3 } //Enhance: it's even more likely that we can get rid of these by replacing them with bloom-content2, bloom-content3 if (lang == bookData.MetadataLanguage1IsoCode) { HtmlDom.AddClass(e, "bloom-contentNational1"); } // It's not clear that this class should be applied to blocks where lang == bookData.Language3IsoCode. // I (JohnT) added lang == bookData.MetadataLanguage2IsoCode while dealing with BL-10893 // but am reluctant to remove the old code as something might depend on it. I believe it is (nearly?) // always true that if we have Language3IsoCode at all, it will be equal to MetadataLanguage2IsoCode, // so at least for now it probably makes no difference. In our next major reworking of language codes, // hopefully we can make this distinction clearer and remove Language3IsoCode here. if (lang == bookData.Language3IsoCode || lang == bookData.MetadataLanguage2IsoCode) { HtmlDom.AddClass(e, "bloom-contentNational2"); } HtmlDom.RemoveClassesBeginingWith(e, "bloom-visibility-code"); if (ShouldNormallyShowEditable(lang, dataDefaultLanguages, contentLanguageIso2, contentLanguageIso3, bookData)) { HtmlDom.AddClass(e, "bloom-visibility-code-on"); } UpdateRightToLeftSetting(bookData, e, lang); }
public void SetMultilingualContentLanguages_ThirdContentLangTurnedOff_RemovedFromDataDiv() { var dom = new HtmlDom(@"<html><head><div id='bloomDataDiv'><div data-book='contentLanguage2'>xyz</div><div data-book='contentLanguage3'>kbt</div></div></head><body></body></html>"); var data = new BookData(dom, new CollectionSettings(), null); data.SetMultilingualContentLanguages(null,null); AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//div[@id='bloomDataDiv']/div[@data-book='contentLanguage3']", 0); }
public void SetMultilingualContentLanguage_ContentLanguageSpecifiedInHtml_ReadsIt() { var dom = new HtmlDom(@"<html ><head></head><body> <div id='bloomDataDiv'> <div data-book='contentLanguage2'>fr</div> </div> </body></html>"); var collectionSettings = new CollectionSettings(); var data = new BookData(dom, collectionSettings, null); data.SetMultilingualContentLanguages("en", "de"); Assert.AreEqual("en", data.MultilingualContentLanguage2); Assert.AreEqual("de", data.MultilingualContentLanguage3); }
private string SetupNewDocumentContents(string sourceFolderPath, string initialPath) { var storage = _bookStorageFactory(initialPath); var bookData = new BookData(storage.Dom, _collectionSettings, null); UpdateEditabilityMetadata(storage);//Path.GetFileName(initialPath).ToLower().Contains("template")); //NB: for a new book based on a page template, I think this should remove *everything*, because the rest is in the xmatter // for shells, we'll still have pages. //Remove from the new book any div-pages labelled as "extraPage" foreach (XmlElement initialPageDiv in storage.Dom.SafeSelectNodes("/html/body/div[contains(@data-page,'extra')]")) { initialPageDiv.ParentNode.RemoveChild(initialPageDiv); } XMatterHelper.RemoveExistingXMatter(storage.Dom); bookData.RemoveAllForms("ISBN");//ISBN number of the original doesn't apply to derivatives var sizeAndOrientation = Layout.FromDom(storage.Dom, Layout.A5Portrait); //Note that we do this *before* injecting frontmatter, which is more likely to have a good reason for having English //Useful for things like Primers. Note that Lorem Ipsum and prefixing all text with "_" also work. // if ("true"==GetMetaValue(storage.Dom.RawDom, "removeTranslationsWhenMakingNewBook", "false")) // { // ClearAwayAllTranslations(storage.Dom.RawDom); // } InjectXMatter(initialPath, storage, sizeAndOrientation); SetLineageAndId(storage); SetBookTitle(storage, bookData); //Few sources will have this set at all. A template picture dictionary is one place where we might expect it to call for, say, bilingual int multilingualLevel = int.Parse(GetMetaValue(storage.Dom.RawDom, "defaultMultilingualLevel", "1")); TranslationGroupManager.SetInitialMultilingualSetting(bookData, multilingualLevel, _collectionSettings); var sourceDom = XmlHtmlConverter.GetXmlDomFromHtmlFile(sourceFolderPath.CombineForPath(Path.GetFileName(GetPathToHtmlFile(sourceFolderPath))), false); //If this is a shell book, make elements to hold the vernacular foreach (XmlElement div in storage.Dom.RawDom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")) { XmlElement sourceDiv = sourceDom.SelectSingleNode("//div[@id='"+div.GetAttribute("id")+"']") as XmlElement; SetupIdAndLineage(sourceDiv, div); SetupPage(div, _collectionSettings, null, null); } ClearAwayDraftText(storage.Dom.RawDom); storage.Save(); //REVIEW this actually undoes the setting of the intial files name: // storage.UpdateBookFileAndFolderName(_librarySettings); return storage.FolderPath; }
private string SetupNewDocumentContents(string sourceFolderPath, string initialPath) { var storage = _bookStorageFactory(initialPath); bool usingTemplate = storage.MetaData.IsSuitableForMakingShells; bool makingTemplate = storage.MetaData.IsSuitableForMakingTemplates; var bookData = new BookData(storage.Dom, _collectionSettings, null); UpdateEditabilityMetadata(storage); //Path.GetFileName(initialPath).ToLower().Contains("template")); // NB: For a new book based on a page template, I think this should remove *everything*, // because the rest is in the xmatter. // For shells, we'll still have pages. //Remove from the new book any div-pages labeled as "extraPage" for (var initialPageDivs = storage.Dom.SafeSelectNodes("/html/body/div[contains(@data-page,'extra')]"); initialPageDivs.Count > 0; initialPageDivs = storage.Dom.SafeSelectNodes("/html/body/div[contains(@data-page,'extra')]")) { initialPageDivs[0].ParentNode.RemoveChild(initialPageDivs[0]); } XMatterHelper.RemoveExistingXMatter(storage.Dom); bookData.RemoveAllForms("ISBN"); //ISBN number of the original doesn't apply to derivatives var sizeAndOrientation = Layout.FromDomAndChoices(storage.Dom, Layout.A5Portrait, _fileLocator); //Note that we do this *before* injecting frontmatter, which is more likely to have a good reason for having English //Useful for things like Primers. Note that Lorem Ipsum and prefixing all text with "_" also work. // if ("true"==GetMetaValue(storage.Dom.RawDom, "removeTranslationsWhenMakingNewBook", "false")) // { // ClearAwayAllTranslations(storage.Dom.RawDom); // } ProcessXMatterMetaTags(storage); // If we are making a shell (from a template, as opposed to making a translation of a shell), // it should not have a pre-determined license. A default will be filled in later. // (But, if we're MAKING a template, we want to keep the CC0 from Template Starter.) if (usingTemplate && !makingTemplate) { BookCopyrightAndLicense.RemoveLicense(storage); } InjectXMatter(initialPath, storage, sizeAndOrientation); SetLineageAndId(storage, sourceFolderPath); SetBookTitle(storage, bookData, usingTemplate); if (!usingTemplate && // when people add a book to a source collection, they are assumed *editing* the book, not making a derivative (BL-4497) !_collectionSettings.IsSourceCollection) { BookCopyrightAndLicense.SetOriginalCopyrightAndLicense(storage.Dom, bookData, _collectionSettings); } //Few sources will have this set at all. A template picture dictionary is one place where we might expect it to call for, say, bilingual int multilingualLevel = int.Parse(GetMetaValue(storage.Dom.RawDom, "defaultMultilingualLevel", "1")); TranslationGroupManager.SetInitialMultilingualSetting(bookData, multilingualLevel, _collectionSettings); var sourceDom = XmlHtmlConverter.GetXmlDomFromHtmlFile(sourceFolderPath.CombineForPath(Path.GetFileName(GetPathToHtmlFile(sourceFolderPath))), false); //If this is a shell book, make elements to hold the vernacular foreach (XmlElement div in storage.Dom.RawDom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")) { XmlElement sourceDiv = sourceDom.SelectSingleNode("//div[@id='" + div.GetAttribute("id") + "']") as XmlElement; SetupIdAndLineage(sourceDiv, div); SetupPage(div, _collectionSettings, null, null); } ClearAwayDraftText(storage.Dom.RawDom); storage.Save(); //REVIEW this actually undoes the setting of the initial files name: // storage.UpdateBookFileAndFolderName(_librarySettings); return(storage.FolderPath); }
/// <summary> /// Propagating the copyright and license information in the bloomDataDiv to template fields /// found in the pages of the book (normally just the credits page). /// </summary> /// <remarks>This is "internal" just as a convention, that it is accessible for testing purposes only</remarks> internal static void UpdateDomFromDataDiv(HtmlDom dom, string bookFolderPath, BookData bookData, bool useOriginalCopyright) { CopyItemToFieldsInPages(dom, "copyright"); CopyItemToFieldsInPages(dom, "licenseUrl"); CopyItemToFieldsInPages(dom, "licenseDescription", languagePreferences: bookData.GetLanguagePrioritiesForLocalizedTextOnPage().ToArray()); CopyItemToFieldsInPages(dom, "licenseNotes"); CopyItemToFieldsInPages(dom, "licenseImage", valueAttribute: "src"); // If we're using the original copyright, we don't need to show it separately. // See https://issues.bloomlibrary.org/youtrack/issue/BL-7381. CopyStringToFieldsInPages(dom, "originalCopyrightAndLicense", useOriginalCopyright ? null : GetOriginalCopyrightAndLicenseNotice(bookData, dom), "*"); if (!String.IsNullOrEmpty(bookFolderPath)) //unit tests may not be interested in checking this part { UpdateBookLicenseIcon(GetMetadata(dom, bookData), bookFolderPath); } }
public void UpdateFieldsAndVariables_BookTitleInSpanOnSecondPage_UpdatesH2OnFirstWithCurrentNationalLang() { var dom = new HtmlDom(@"<html ><head></head><body> <div class='bloom-page titlePage'> <div class='pageContent'> <h2 data-book='bookTitle' lang='N1'>{national book title}</h2> </div> </div> <div class='bloom-page verso'> <div class='pageContent'> (<span lang='en' data-book='bookTitle'>Vaccinations</span><span lang='tpi' data-book='bookTitle'>Tambu Sut</span>) <br /> </div> </div> </body></html>"); var collectionSettings = new CollectionSettings() { Language1Iso639Code = "etr" }; var data = new BookData(dom, collectionSettings, null); data.SynchronizeDataItemsThroughoutDOM(); XmlElement nationalTitle = (XmlElement)dom.SelectSingleNodeHonoringDefaultNS("//h2[@data-book='bookTitle']"); Assert.AreEqual("Vaccinations", nationalTitle.InnerText); //now switch the national language to Tok Pisin collectionSettings.Language2Iso639Code = "tpi"; data.SynchronizeDataItemsThroughoutDOM(); nationalTitle = (XmlElement)dom.SelectSingleNodeHonoringDefaultNS("//h2[@data-book='bookTitle']"); Assert.AreEqual("Tambu Sut", nationalTitle.InnerText); }
public void SynchronizeDataItemsThroughoutDOM_HasOnlyEnglishContributorsInDataDivButFrenchInBody_DoesNotCopyEnglishIntoFrenchSlot() { var dom = new HtmlDom(@"<html ><head></head><body> <div id='bloomDataDiv'> <div data-book='originalContributions' lang='en'>the contributions</div> </div> <div class='bloom-page verso'> <div id='originalContributions' class='bloom-translationGroup'> <div data-book='originalContributions' lang='fr'>les contributeurs</div> <div data-book='originalContributions' lang='xyz'></div> </div> </div> </body></html>"); var collectionSettings = new CollectionSettings() { Language1Iso639Code = "etr", Language2Iso639Code = "fr" }; var data = new BookData(dom, collectionSettings, null); data.SynchronizeDataItemsThroughoutDOM(); XmlElement frenchContributions = (XmlElement)dom.SelectSingleNodeHonoringDefaultNS("//*[@data-book='originalContributions' and @lang='fr']"); Assert.AreEqual("les contributeurs", frenchContributions.InnerText, "Should not touch existing French Contributions"); //Assert.IsFalse(frenchContributions.HasAttribute("bloom-languageBloomHadToCopyFrom")); }
public void SynchronizeDataItemsThroughoutDOM_HasOnlyEnglishContributorsButEnglishIsLang3_CopiesEnglishIntoNationalLanguageSlot() { var dom = new HtmlDom(@"<html ><head></head><body> <div id='bloomDataDiv'> <div data-book='originalContributions' lang='en'>the contributions</div> </div> <div class='bloom-page verso'> <div id='originalContributions' class='bloom-translationGroup'> <div class='bloom-copyFromOtherLanguageIfNecessary' data-book='originalContributions' lang='fr'></div> <div class='bloom-copyFromOtherLanguageIfNecessary' data-book='originalContributions' lang='en'></div> </div> </div> </body></html>"); var collectionSettings = new CollectionSettings() { Language1Iso639Code = "etr", Language2Iso639Code = "fr" }; var data = new BookData(dom, collectionSettings, null); data.SynchronizeDataItemsThroughoutDOM(); XmlElement englishContributions = (XmlElement)dom.SelectSingleNodeHonoringDefaultNS("//*[@data-book='originalContributions' and @lang='en']"); Assert.AreEqual("the contributions", englishContributions.InnerText, "Should copy English into body of course, as normal"); XmlElement frenchContributions = (XmlElement)dom.SelectSingleNodeHonoringDefaultNS("//*[@data-book='originalContributions' and @lang='fr']"); Assert.AreEqual("the contributions", frenchContributions.InnerText, "Should copy English into French Contributions becuase it's better than just showing nothing"); //Assert.AreEqual("en",frenchContributions.GetAttribute("bloom-languageBloomHadToCopyFrom"),"Should have left a record that we did this dubious 'borrowing' from English"); }
public void Constructor_CollectionSettingsHasISO639Code_DataSetContainsProperV() { var dom = new HtmlDom(); var data = new BookData(dom, new CollectionSettings() { Language1Iso639Code = "xyz" }, null); Assert.AreEqual("xyz", data.GetWritingSystemCodes()["V"]); }
public void SynchronizeDataItemsThroughoutDOM_HasOnlyEdoloContributors_CopiesItIntoL2ButNotL1() { var dom = new HtmlDom(@"<html ><head></head><body> <div id='bloomDataDiv'> <div data-book='originalContributions' lang='etr'>the contributions</div> </div> <div class='bloom-page verso'> <div id='originalContributions' class='bloom-translationGroup'> <div class='bloom-copyFromOtherLanguageIfNecessary' data-book='originalContributions' lang='fr'></div> <div class='bloom-copyFromOtherLanguageIfNecessary' data-book='originalContributions' lang='xyz'></div> </div> </div> </body></html>"); var collectionSettings = new CollectionSettings() { Language1Iso639Code = "xyz", Language2Iso639Code = "fr" }; var data = new BookData(dom, collectionSettings, null); data.SynchronizeDataItemsThroughoutDOM(); XmlElement frenchContributions = (XmlElement)dom.SelectSingleNodeHonoringDefaultNS("//*[@data-book='originalContributions' and @lang='fr']"); Assert.AreEqual("the contributions", frenchContributions.InnerText, "Should copy Edolo into French Contributions becuase it's better than just showing nothing"); XmlElement vernacularContributions = (XmlElement)dom.SelectSingleNodeHonoringDefaultNS("//*[@data-book='originalContributions' and @lang='xyz']"); Assert.AreEqual("", vernacularContributions.InnerText, "Should not copy Edolo into Vernacualr Contributions. Only national language fields get this treatment"); }
public void SynchronizeDataItemsThroughoutDOM_HasFrenchAndEnglishContributorsInDataDiv_DoesNotCopyEnglishIntoFrenchSlot() { var dom = new HtmlDom(@"<html ><head></head><body> <div id='bloomDataDiv'> <div data-book='originalContributions' lang='en'>the contributions</div> <div data-book='originalContributions' lang='fr'>les contributeurs</div> </div> <div class='bloom-page verso'> <div id='originalContributions' class='bloom-translationGroup'> <div data-book='originalContributions' lang='fr'></div> <div data-book='originalContributions' lang='xyz'></div> </div> </div> </body></html>"); var collectionSettings = new CollectionSettings() { Language1Iso639Code = "xyz", Language2Iso639Code = "fr" }; var data = new BookData(dom, collectionSettings, null); data.SynchronizeDataItemsThroughoutDOM(); XmlElement frenchContributions = (XmlElement)dom.SelectSingleNodeHonoringDefaultNS("//*[@data-book='originalContributions' and @lang='fr']"); Assert.AreEqual("les contributeurs", frenchContributions.InnerText, "Should use the French, not the English even though the French in the body was empty"); XmlElement vernacularContributions = (XmlElement)dom.SelectSingleNodeHonoringDefaultNS("//*[@data-book='originalContributions' and @lang='xyz']"); Assert.AreEqual("", vernacularContributions.InnerText, "Should not copy Edolo into Vernacualr Contributions. Only national language fields get this treatment"); }
public void SuckInDataFromEditedDom_NoDataDIvTitleChanged_NewTitleInCache() { HtmlDom bookDom = new HtmlDom(@"<html ><head></head><body> <div class='bloom-page' id='guid2'> <textarea lang='xyz' data-book='bookTitle'>original</textarea> </div> </body></html>"); var data = new BookData(bookDom, _collectionSettings, null); Assert.AreEqual("original", data.GetVariableOrNull("bookTitle", "xyz")); HtmlDom editedPageDom = new HtmlDom(@"<html ><head></head><body> <div class='bloom-page' id='guid2'> <textarea lang='xyz' data-book='bookTitle'>changed</textarea> </div> </body></html>"); data.SuckInDataFromEditedDom(editedPageDom); Assert.AreEqual("changed", data.GetVariableOrNull("bookTitle", "xyz")); }
private static void UpdateContentLanguageClassesOnElement(XmlElement e, Dictionary <string, string> contentLanguages, BookData bookData, string contentLanguageIso2, string contentLanguageIso3, string[] dataDefaultLanguages) { HtmlDom.RemoveClassesBeginingWith(e, "bloom-content"); var lang = e.GetAttribute("lang"); //These bloom-content* classes are used by some stylesheet rules, primarily to boost the font-size of some languages. //Enhance: this is too complex; the semantics of these overlap with each other and with bloom-visibility-code-on, and with data-language-order. //It would be better to have non-overlapping things; 1 for order, 1 for visibility, one for the lang's role in this collection. string orderClass; if (contentLanguages.TryGetValue(lang, out orderClass)) { HtmlDom.AddClass(e, orderClass); //bloom-content1, bloom-content2, bloom-content3 } //Enhance: it's even more likely that we can get rid of these by replacing them with bloom-content2, bloom-content3 if (lang == bookData.MetadataLanguage1IsoCode) { HtmlDom.AddClass(e, "bloom-contentNational1"); } if (lang == bookData.Language3IsoCode) { HtmlDom.AddClass(e, "bloom-contentNational2"); } HtmlDom.RemoveClassesBeginingWith(e, "bloom-visibility-code"); if (ShouldNormallyShowEditable(lang, dataDefaultLanguages, contentLanguageIso2, contentLanguageIso3, bookData)) { HtmlDom.AddClass(e, "bloom-visibility-code-on"); } UpdateRightToLeftSetting(bookData, e, lang); }
/// <summary> /// In xmatter, text fields are normally labeled with a "meta" language code, like "N1" for first national language. /// This method detects those and then looks them up, returning the actual language code in use at the moment. /// </summary> /// <remarks>This is a little uncomfortable in this class, as this feature is not currently used in any /// bloom-translationGroup elements. /// </remarks> public static void SetLanguageForElementsWithMetaLanguage(XmlNode elementOrDom, BookData bookData) { // foreach (XmlElement element in elementOrDom.SafeSelectNodes(".//*[@data-metalanguage]")) // { // string lang = ""; // string metaLanguage = element.GetStringAttribute("data-metalanguage").Trim(); // switch (metaLanguage) // { // case "V": // lang = settings.Language1Iso639Code; // break; // case "N1": // lang = settings.Language2Iso639Code; // break; // case "N2": // lang = settings.Language3Iso639Code; // break; // default: // var msg = "Element called for meta language '" + metaLanguage + "', which is unrecognized."; // Debug.Fail(msg); // Logger.WriteEvent(msg); // continue; // break; // } // element.SetAttribute("lang", lang); // // // As an aside: if the field also has a class "bloom-copyFromOtherLanguageIfNecessary", then elsewhere we will copy from the old // // national language (or regional, or whatever) to this one if necessary, so as not to lose what they had before. // // } }
public void UpdateFieldsAndVariables_CustomLibraryVariable_CopiedToOtherElement() { var dom=new HtmlDom(@"<html ><head></head><body> <div class='bloom-page' id='guid3'> <p> <textarea lang='xyz' id='copyOfVTitle' data-book='bookTitle'>tree</textarea> <textarea lang='xyz' id='1' data-collection='testLibraryVariable'>aa</textarea> <textarea lang='xyz' id='2' data-collection='testLibraryVariable'>bb</textarea> </p> </div> </body></html>"); var data = new BookData(dom, _collectionSettings, null); data.UpdateVariablesAndDataDivThroughDOM(); var textarea2 = dom.SelectSingleNodeHonoringDefaultNS("//textarea[@id='2']"); Assert.AreEqual("aa", textarea2.InnerText); }
public void Set_Null_Removes() { var htmlDom = new HtmlDom(); var data = new BookData(htmlDom, new CollectionSettings(), null); data.Set("1", "one", "en"); data.Set("1", null, "en"); Assert.AreEqual(null, data.GetVariableOrNull("1", "en")); AssertThatXmlIn.Dom(htmlDom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='en']", 0); var roundTripData = new BookData(htmlDom, new CollectionSettings(), null); Assert.IsNull(roundTripData.GetVariableOrNull("1", "en")); }
public void Constructor_CollectionSettingsHasISO639Code_iso639CodeFilledIn() { var dom = new HtmlDom(); var data = new BookData(dom, new CollectionSettings() { Language1Iso639Code = "xyz" }, null); Assert.AreEqual("xyz", data.GetVariableOrNull("iso639Code", "*")); }
private static void SetBookTitle(BookStorage storage, BookData bookData) { //NB: no multi-lingual name suggestion ability yet //otherwise, the case where there is no defaultNameForDerivedBooks, we just want to use the names //that the shell used, e.g. "Vaccinations". //We don't have to do anything special to get that. string kdefaultName = null; var nameSuggestion = storage.Dom.GetMetaValue("defaultNameForDerivedBooks", kdefaultName); // var nameSuggestion = storage.Dom.SafeSelectNodes("//head/meta[@name='defaultNameForDerivedBooks']"); if(nameSuggestion!=null) bookData.Set("bookTitle",nameSuggestion,"en"); storage.Dom.RemoveMetaElement("defaultNameForDerivedBooks"); // //var name = "New Book"; //shouldn't rarel show up, because it will be overriden by the meta tag // if (nameSuggestion.Count > 0) // { // var metaTag = (XmlElement) nameSuggestion[0]; // var name = metaTag.GetAttribute("content"); // bookData.SetDataDivBookVariable("bookTitle", name, "en"); // metaTag.ParentNode.RemoveChild(metaTag); // } // else // { // // } }
public void UpdateFieldsAndVariables_OneDataItemChanges_ItemsWithThatLanguageAlsoUpdated() { var dom = new HtmlDom(@"<html ><head></head><body> <div class='bloom-page' id='guid1'> <p> <textarea lang='en' id='1' data-book='bookTitle'>EnglishTitle</textarea> <textarea lang='xyz' id='2' data-book='bookTitle'>xyzTitle</textarea> </p> </div> <div class='bloom-page' id='guid3'> <p> <textarea lang='xyz' id='3' data-book='bookTitle'>xyzTitle</textarea> </p> </div> </body></html>"); AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@lang='en' and @id='1' and text()='EnglishTitle']", 1); AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@lang='xyz' and @id='2' and text()='xyzTitle']", 1); var textarea2 = dom.SelectSingleNodeHonoringDefaultNS("//textarea[@id='2']"); textarea2.InnerText = "newXyzTitle"; var data = new BookData(dom, new CollectionSettings() { Language1Iso639Code = "etr" }, null); data.SynchronizeDataItemsThroughoutDOM(); var textarea3 = dom.SelectSingleNodeHonoringDefaultNS("//textarea[@id='3']"); Assert.AreEqual("newXyzTitle", textarea3.InnerText); AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@id='1' and text()='EnglishTitle']", 1); }
public void Set_AddTwoForms_BothAdded() { var htmlDom = new HtmlDom(); var data = new BookData(htmlDom, new CollectionSettings(), null); data.Set("1", "one", "en"); data.Set("1", "uno", "es"); var roundTripData = new BookData(htmlDom, new CollectionSettings(), null); Assert.AreEqual("one", roundTripData.GetVariableOrNull("1", "en")); Assert.AreEqual("uno", roundTripData.GetVariableOrNull("1", "es")); }
public static Metadata GetOriginalMetadata(HtmlDom dom, BookData bookData) { return(CreateMetadata(dom.GetBookSetting("originalCopyright"), dom.GetBookSetting("originalLicenseUrl").GetExactAlternative("*"), dom.GetBookSetting("originalLicenseNotes"), bookData)); }
private string SetupNewDocumentContents(string sourceFolderPath, string initialPath) { var storage = _bookStorageFactory(initialPath); bool usingTemplate = storage.BookInfo.IsSuitableForMakingShells; bool makingTemplate = storage.BookInfo.IsSuitableForMakingTemplates; // If we're not making it from a template or making a template, we're deriving a translation from an existing book var makingTranslation = !usingTemplate && !makingTemplate; var bookData = new BookData(storage.Dom, _collectionSettings, null); UpdateEditabilityMetadata(storage); //Path.GetFileName(initialPath).ToLower().Contains("template")); // BL-7614 We don't want a derivative of a book downloaded from a "bookshelf" to have the same bookshelf storage.BookInfo.ClearBookshelf(); // NB: For a new book based on a page template, I think this should remove *everything*, // because the rest is in the xmatter. // For shells, we'll still have pages. // BL-6108: But if this is a template and we remove all the pages and xmatter, // there won't be anything left to tell us what the template's preferred layout was, // so we'll save that first. Layout templateLayout = null; if (usingTemplate) { templateLayout = Layout.FromDom(storage.Dom, Layout.A5Portrait); } // Remove from the new book any pages labeled as "extra". // Typically pages in a template are marked "extra" to indicate that they are options to insert with "Add Page" // but not pages (which a few templates have) that should be automatically inserted into every book // made from the template. // Originally we removed 'extra' pages in all cases, but we haven't actually used the capability // of deleting 'extra' pages from translations of shell books, and on the other hand, we did briefly release // a version of Bloom that incorrectly left shell book pages so marked. Something like 73 books in our library // may have this problem (BL-6392). Since we don't actually need the capability for making translations // of shell books, we decided to simply disable it: all pages in a shell book, even those marked // 'extra', will be kept in the translation. if (!makingTranslation) { for (var initialPageDivs = storage.Dom.SafeSelectNodes("/html/body/div[contains(@data-page,'extra')]"); initialPageDivs.Count > 0; initialPageDivs = storage.Dom.SafeSelectNodes("/html/body/div[contains(@data-page,'extra')]")) { initialPageDivs[0].ParentNode.RemoveChild(initialPageDivs[0]); } } else { // When making a translation of an original move the 'publisher' (if there is one) to 'originalPublisher'. storage.BookInfo.MovePublisherToOriginalPublisher(); } XMatterHelper.RemoveExistingXMatter(storage.Dom); // BL-4586 Some old books ended up with background-image urls containing XML img tags // in the HTML-encoded string. This happened because the coverImage data-book element // contained an img tag instead of a bare filename. // Normally such a thing would get fixed on loading the book, but if the "old book" in question // is a shell downloaded from BloomLibrary.org, Bloom is not allowed to modify the book, // so if such a thing exists in this copied book here we will strip it out and replace it with the // filename in the img src attribute. Book.RemoveImgTagInDataDiv(storage.Dom); bookData.RemoveAllForms("ISBN"); //ISBN number of the original doesn't apply to derivatives var sizeAndOrientation = Layout.FromDomAndChoices(storage.Dom, templateLayout ?? Layout.A5Portrait, _fileLocator); //Note that we do this *before* injecting frontmatter, which is more likely to have a good reason for having English //Useful for things like Primers. Note that Lorem Ipsum and prefixing all text with "_" also work. // if ("true"==GetMetaValue(storage.Dom.RawDom, "removeTranslationsWhenMakingNewBook", "false")) // { // ClearAwayAllTranslations(storage.Dom.RawDom); // } ProcessXMatterMetaTags(storage); // If we are making a shell (from a template, as opposed to making a translation of a shell), // it should not have a pre-determined license. A default will be filled in later. // (But, if we're MAKING a template, we want to keep the CC0 from Template Starter.) if (usingTemplate && !makingTemplate) { BookCopyrightAndLicense.RemoveLicense(storage); } InjectXMatter(initialPath, storage, sizeAndOrientation); SetLineageAndId(storage, sourceFolderPath); if (makingTranslation) { storage.EnsureOriginalTitle(); // Before SetBookTitle, so we definitely won't use this book's new empty title } SetBookTitle(storage, bookData, usingTemplate); TransformCreditPageData(storage.Dom, bookData, _collectionSettings, storage, makingTranslation); //Few sources will have this set at all. A template picture dictionary is one place where we might expect it to call for, say, bilingual int multilingualLevel = int.Parse(GetMetaValue(storage.Dom.RawDom, "defaultMultilingualLevel", "1")); TranslationGroupManager.SetInitialMultilingualSetting(bookData, multilingualLevel, _collectionSettings); var sourceDom = XmlHtmlConverter.GetXmlDomFromHtmlFile(sourceFolderPath.CombineForPath(Path.GetFileName(GetPathToHtmlFile(sourceFolderPath))), false); //If this is a shell book, make elements to hold the vernacular foreach (XmlElement div in storage.Dom.RawDom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")) { XmlElement sourceDiv = sourceDom.SelectSingleNode("//div[@id='" + div.GetAttribute("id") + "']") as XmlElement; SetupIdAndLineage(sourceDiv, div); SetupPage(div, _collectionSettings, null, null); } ClearAwayDraftText(storage.Dom.RawDom); storage.UpdateSupportFiles(); // Copy branding files etc. storage.Save(); //REVIEW this actually undoes the setting of the initial files name: // storage.UpdateBookFileAndFolderName(_librarySettings); return(storage.FolderPath); }
public void Set_CalledTwiceWithDIfferentLangs_HasBoth() { var htmlDom = new HtmlDom(); var data = new BookData(htmlDom, new CollectionSettings(), null); data.Set("1", "one", "en"); data.Set("1", "uno", "es"); Assert.AreEqual(2,data.GetMultiTextVariableOrEmpty("1").Forms.Count()); }
public void SetMultilingualContentLanguages_HasTrilingualLanguages_AddsToDataDiv() { var dom = new HtmlDom(@"<html><head></head><body></body></html>"); var data = new BookData(dom, new CollectionSettings(), null); data.SetMultilingualContentLanguages("okm", "kbt"); AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//div[@id='bloomDataDiv']/div[@data-book='contentLanguage2' and text()='okm']", 1); AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//div[@id='bloomDataDiv']/div[@data-book='contentLanguage3' and text()='kbt']", 1); }
public void UpdateVariablesAndDataDivThroughDOM_HasDataLibraryValues_LibraryValuesNotPutInDataDiv() { var dom = new HtmlDom(@"<html><head></head><body><div data-book='someVariable' lang='en'>hi</div><div data-collection='user' lang='en'>john</div></body></html>"); var data = new BookData(dom, new CollectionSettings(), null); data.UpdateVariablesAndDataDivThroughDOM(); AssertThatXmlIn.Dom(dom.RawDom).HasNoMatchForXpath("//div[@id='bloomDataDiv']/div[@data-book='user']"); AssertThatXmlIn.Dom(dom.RawDom).HasNoMatchForXpath("//div[@id='bloomDataDiv']/div[@data-collection]"); }
/// <summary> /// For each group of editable elements in the div which have lang attributes (normally, a .bloom-translationGroup div), /// make a new element with the lang code of the vernacular (normally, a .bloom-editable). /// Also enable/disable editing as warranted (e.g. in shell mode or not) /// </summary> public static void PrepareElementsInPageOrDocument(XmlNode pageOrDocumentNode, BookData bookData) { GenerateEditableDivsWithPreTranslatedContent(pageOrDocumentNode); foreach (var code in bookData.GetBasicBookLanguageCodes()) { PrepareElementsOnPageOneLanguage(pageOrDocumentNode, code); } FixGroupStyleSettings(pageOrDocumentNode); }
public void Constructor_CollectionSettingsHasLanguage1Name_LanguagenameOfNationalLanguage1FilledIn() { var dom = new HtmlDom(); var data = new BookData(dom, new CollectionSettings() { Language1Name = "foobar" }, null); Assert.AreEqual("foobar", data.GetVariableOrNull("nameOfLanguage", "*")); }
/// <summary> /// For each group of editable elements in the div which have lang attributes (normally, a .bloom-translationGroup div), /// make sure we have child elements with the lang codes we need (most often, a .bloom-editable in the vernacular /// is added). /// Also enable/disable editing as warranted (e.g. in shell mode or not) /// </summary> public static void PrepareElementsInPageOrDocument(XmlNode pageOrDocumentNode, BookData bookData) { GenerateEditableDivsWithPreTranslatedContent(pageOrDocumentNode); foreach (var code in bookData.GetBasicBookLanguageCodes()) { PrepareElementsOnPageOneLanguage(pageOrDocumentNode, code); } // I'm not sure exactly why, but GetBasicBookLanguageCodes() returns // the languages identified as L1 (and possibly, if turned on, L2 and L3) // and M1. I'm nervous about changing that. But it's now possible for // a group to specify (using N2 in data-default-languages) that it should // have an M2 block, and M2 may not be any of the GetBasicBookLanguageCodes() // languages (unless we're in trilingual mode). So we need another method // to handle any special languages this block needs that are not considered // 'basic' for this book. PrepareSpecialLanguageGroups(pageOrDocumentNode, bookData); FixGroupStyleSettings(pageOrDocumentNode); }
public void UpdateVariablesAndDataDivThroughDOM_NewLangAdded_AddedToDataDiv() { var dom = new HtmlDom(@"<html><head></head><body><div data-book='someVariable' lang='en'>hi</div></body></html>"); var e = dom.RawDom.CreateElement("div"); e.SetAttribute("data-book", "someVariable"); e.SetAttribute("lang", "fr"); e.InnerText = "bonjour"; dom.RawDom.SelectSingleNode("//body").AppendChild(e); var data = new BookData(dom, new CollectionSettings(), null); data.UpdateVariablesAndDataDivThroughDOM(); AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//body/div[1][@id='bloomDataDiv']", 1);//NB microsoft uses 1 as the first. W3c uses 0. AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//div[@id='bloomDataDiv']/div[@data-book='someVariable' and @lang='en' and text()='hi']", 1); AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//div[@id='bloomDataDiv']/div[@data-book='someVariable' and @lang='fr' and text()='bonjour']", 1); }
public void UpdateVariablesAndDataDivThroughDOM_VariableIsNull_DataDivForItRemoved() { var htmlDom = new HtmlDom(); var data = new BookData(htmlDom, new CollectionSettings(), null); data.Set("1","one","en"); data.Set("1", null, "es"); data.UpdateVariablesAndDataDivThroughDOM(); AssertThatXmlIn.Dom(htmlDom.RawDom).HasSpecifiedNumberOfMatchesForXpath("html/body/div/div[@lang='en']",1); AssertThatXmlIn.Dom(htmlDom.RawDom).HasSpecifiedNumberOfMatchesForXpath("html/body/div/div[@lang='es']", 0); }
private static Metadata GetMetadata(string dataDivContent) { var dom = new HtmlDom(@"<html><head><div id='bloomDataDiv'>" + dataDivContent + "</div></head><body></body></html>"); var data = new BookData(dom, new CollectionSettings(), null); return data.GetLicenseMetadata(); }
/// <summary> /// Call this when we have a new set of metadata to use. It /// 1) sets the bloomDataDiv with the data, /// 2) causes any template fields in the book to get the new values /// 3) updates the license image on disk /// </summary> public static void SetMetadata(Metadata metadata, HtmlDom dom, string bookFolderPath, BookData bookData, bool useOriginalCopyright) { dom.SetBookSetting("copyright", "*", ConvertNewLinesToHtmlBreaks(metadata.CopyrightNotice)); dom.SetBookSetting("licenseUrl", "*", metadata.License.Url); // This is for backwards compatibility. The book may have licenseUrl in 'en' created by an earlier version of Bloom. // For backwards compatibility, GetMetaData will read that if it doesn't find a '*' license first. So now that we're // setting a licenseUrl for '*', we must make sure the 'en' one is gone, because if we're setting a non-CC license, // the new URL will be empty and the '*' one will go away, possibly exposing the 'en' one to be used by mistake. // See BL-3166. dom.SetBookSetting("licenseUrl", "en", null); string languageUsedForDescription; //This part is unfortunate... the license description, which is always localized, doesn't belong in the datadiv; it //could instead just be generated when we update the page. However, for backwards compatibility (prior to 3.6), //we localize it and place it in the datadiv. dom.RemoveBookSetting("licenseDescription"); var description = metadata.License.GetDescription(bookData.GetLanguagePrioritiesForLocalizedTextOnPage(), out languageUsedForDescription); dom.SetBookSetting("licenseDescription", languageUsedForDescription, ConvertNewLinesToHtmlBreaks(description)); // Book may have old licenseNotes, typically in 'en'. This can certainly show up again if licenseNotes in '*' is removed, // and maybe anyway. Safest to remove it altogether if we are setting it using the new scheme. dom.RemoveBookSetting("licenseNotes"); dom.SetBookSetting("licenseNotes", "*", ConvertNewLinesToHtmlBreaks(metadata.License.RightsStatement)); // we could do away with licenseImage in the bloomDataDiv, since the name is always the same, but we keep it for backward compatibility if (metadata.License is CreativeCommonsLicense) { dom.SetBookSetting("licenseImage", "*", "license.png"); } else { //CC licenses are the only ones we know how to show an image for dom.RemoveBookSetting("licenseImage"); } UpdateDomFromDataDiv(dom, bookFolderPath, bookData, useOriginalCopyright); }
public void Constructor_CollectionSettingsHasLanguage3Iso639Code_nameOfNationalLanguage2FilledIn() { var dom = new HtmlDom(); var data = new BookData(dom, new CollectionSettings() { Language3Iso639Code = "tpi" }, null); Assert.AreEqual("Tok Pisin", data.GetVariableOrNull("nameOfNationalLanguage2", "*")); }
public void UpdateFieldsAndVariables_VernacularTitleChanged_TitleCopiedToParagraphAnotherPage() { var dom = new HtmlDom(@"<html ><head></head><body> <div class='bloom-page' id='guid2'> <p> <textarea lang='xyz' data-book='bookTitle'>original</textarea> </p> </div> <div class='bloom-page' id='0a99fad3-0a17-4240-a04e-86c2dd1ec3bd'> <p class='centered' lang='xyz' data-book='bookTitle' id='P1'>originalButNoExactlyCauseItShouldn'tMatter</p> </div> </body></html>"); var data = new BookData(dom, _collectionSettings, null); var textarea1 = dom.SelectSingleNodeHonoringDefaultNS("//textarea[@data-book='bookTitle' and @lang='xyz']"); textarea1.InnerText = "peace"; data.SynchronizeDataItemsThroughoutDOM(); var paragraph = dom.SelectSingleNodeHonoringDefaultNS("//p[@data-book='bookTitle' and @lang='xyz']"); Assert.AreEqual("peace", paragraph.InnerText); }
public void Set_DidNotHaveForm_Added() { var htmlDom = new HtmlDom(); var data = new BookData(htmlDom, new CollectionSettings(), null); data.Set("1", "one", "en"); Assert.AreEqual("one", data.GetVariableOrNull("1", "en")); AssertThatXmlIn.Dom(htmlDom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='en']",1); var roundTripData = new BookData(htmlDom, new CollectionSettings(), null); var t = roundTripData.GetVariableOrNull("1", "en"); Assert.AreEqual("one", t); }
public static Metadata CreateMetadata(MultiTextBase copyright, string licenseUrl, MultiTextBase licenseNotes, BookData bookData) { var metadata = new Metadata(); if (!copyright.Empty) { metadata.CopyrightNotice = GetBestMultiTextBaseValue(copyright, bookData); } if (string.IsNullOrWhiteSpace(licenseUrl)) { //NB: we are mapping "RightsStatement" (which comes from XMP-dc:Rights) to "LicenseNotes" in the html. //custom licenses live in this field, so if we have notes (and no URL) it is a custom one. if (!licenseNotes.Empty) { metadata.License = new CustomLicense { RightsStatement = GetBestMultiTextBaseValue(licenseNotes, bookData) }; } else { // The only remaining current option is a NullLicense metadata.License = new NullLicense(); //"contact the copyright owner } } else // there is a licenseUrl, which means it is a CC license { try { metadata.License = CreativeCommonsLicense.FromLicenseUrl(licenseUrl); } catch (IndexOutOfRangeException) { // Need to handle urls which do not end with the version number. // Simply set it to the default version. if (!licenseUrl.EndsWith("/")) { licenseUrl += "/"; } licenseUrl += CreativeCommonsLicense.kDefaultVersion; metadata.License = CreativeCommonsLicense.FromLicenseUrl(licenseUrl); } catch (Exception e) { throw new ApplicationException("Bloom had trouble parsing this license url: '" + licenseUrl + "'. (ref BL-4108)", e); } //are there notes that go along with that? if (!licenseNotes.Empty) { metadata.License.RightsStatement = GetBestMultiTextBaseValue(licenseNotes, bookData); } } return(metadata); }
public void UpdateVariablesAndDataDivThroughDOM_DoesNotExist_MakesOne() { var dom = new HtmlDom(@"<html><head></head><body><div data-book='someVariable'>world</div></body></html>"); var data = new BookData(dom, new CollectionSettings(), null); data.UpdateVariablesAndDataDivThroughDOM(); AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//body/div[1][@id='bloomDataDiv']", 1);//NB microsoft uses 1 as the first. W3c uses 0. AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//div[@id='bloomDataDiv']/div[@data-book='someVariable' and text()='world']", 1); }