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);
     }
 }
Example #2
0
 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", "*"));
 }
Example #3
0
        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);
        }
Example #8
0
        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());
        }
Example #9
0
 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");
 }
Example #10
0
        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));
        }
Example #11
0
        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)
            }
        }
Example #13
0
        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);
            }
        }
Example #15
0
        /// <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);
        }
Example #17
0
 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);
 }
Example #18
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);
 }
Example #19
0
        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;
        }
Example #20
0
        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);
        }
Example #21
0
        /// <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);
            }
        }
Example #22
0
        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);
        }
Example #23
0
 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"));
 }
Example #24
0
 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");
 }
Example #25
0
 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"]);
 }
Example #26
0
 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");
 }
Example #27
0
 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");
 }
Example #28
0
        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);
        }
Example #30
0
        /// <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.
//
//			}
        }
Example #31
0
 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);
 }
Example #32
0
 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"));
 }
Example #33
0
 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", "*"));
 }
Example #34
0
        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
            //	        {
            //
            //	        }
        }
Example #35
0
 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);
 }
Example #36
0
 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"));
 }
Example #37
0
 public static Metadata GetOriginalMetadata(HtmlDom dom, BookData bookData)
 {
     return(CreateMetadata(dom.GetBookSetting("originalCopyright"), dom.GetBookSetting("originalLicenseUrl").GetExactAlternative("*"), dom.GetBookSetting("originalLicenseNotes"), bookData));
 }
Example #38
0
        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);
        }
Example #39
0
 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());
 }
Example #40
0
 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);
 }
Example #41
0
 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);
        }
Example #43
0
 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);
        }
Example #45
0
        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);
        }
Example #46
0
 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);
 }
Example #47
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();
 }
Example #48
0
        /// <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);
        }
Example #49
0
 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", "*"));
 }
Example #50
0
 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);
 }
Example #51
0
 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);
 }
Example #52
0
        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);
        }
Example #53
0
 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);
 }