HtmlDom manages the lower-level operations on a Bloom XHTML DOM. These doms can be a whole book, or just one page we're currently editing. They are actually XHTML, though when we save or send to a browser, we always convert to plain html.
Exemplo n.º 1
0
 public void BaseForRelativePaths_NoHead_NoLongerThrows()
 {
     var dom = new HtmlDom(
                   @"<html></html>");
     dom.BaseForRelativePaths = "theBase";
     Assert.AreEqual("theBase", dom.BaseForRelativePaths);
 }
        public static void AddUIDictionaryToDom(HtmlDom pageDom, CollectionSettings collectionSettings)
        {
            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>();

            d.Add(collectionSettings.Language1Iso639Code, collectionSettings.Language1Name);
            if (!String.IsNullOrEmpty(collectionSettings.Language2Iso639Code) && !d.ContainsKey(collectionSettings.Language2Iso639Code))
                d.Add(collectionSettings.Language2Iso639Code, collectionSettings.GetLanguage2Name(collectionSettings.Language2Iso639Code));
            if (!String.IsNullOrEmpty(collectionSettings.Language3Iso639Code) && !d.ContainsKey(collectionSettings.Language3Iso639Code))
                d.Add(collectionSettings.Language3Iso639Code, collectionSettings.GetLanguage3Name(collectionSettings.Language3Iso639Code));

            d.Add("vernacularLang", collectionSettings.Language1Iso639Code);//use for making the vernacular the first tab
            d.Add("{V}", collectionSettings.Language1Name);
            d.Add("{N1}", collectionSettings.GetLanguage2Name(collectionSettings.Language2Iso639Code));
            d.Add("{N2}", collectionSettings.GetLanguage3Name(collectionSettings.Language3Iso639Code));

            AddLocalizedHintContentsToDictionary(pageDom, d, collectionSettings);

            dictionaryScriptElement.InnerText = String.Format("function GetDictionary() {{ return {0};}}", JsonConvert.SerializeObject(d));

            pageDom.Head.InsertAfter(dictionaryScriptElement, pageDom.Head.LastChild);
        }
Exemplo n.º 3
0
        public static void CopyImageMetadataToWholeBook(string folderPath, HtmlDom dom, Metadata metadata, IProgress progress)
        {
            progress.WriteStatus("Starting...");

            //First update the images themselves

            int completed = 0;
            var imgElements = GetImagePaths(folderPath);
            foreach (string path in imgElements)
            {
                progress.ProgressIndicator.PercentCompleted = (int)(100.0 * (float)completed / imgElements.Count());
                progress.WriteStatus("Copying to " + Path.GetFileName(path));
                using (var image = PalasoImage.FromFile(path))
                {
                    image.Metadata = metadata;
                    image.SaveUpdatedMetadataIfItMakesSense();
                }
                ++completed;
            }

            //Now update the html attributes which echo some of it, and is used by javascript to overlay displays related to
            //whether the info is there or missing or whatever.

            foreach (XmlElement img in dom.SafeSelectNodes("//img"))
            {
                UpdateImgMetdataAttributesToMatchImage(folderPath, img, progress, metadata);
            }
        }
Exemplo n.º 4
0
        public static void CopyImageMetadataToWholeBook(string folderPath, HtmlDom dom, Metadata metadata, IProgress progress)
        {
            progress.WriteStatus("Starting...");

            //First update the images themselves

            int completed = 0;
            var imgElements = GetImagePaths(folderPath);
            foreach (string path in imgElements)
            {
                progress.ProgressIndicator.PercentCompleted = (int)(100.0 * (float)completed / imgElements.Count());
                progress.WriteStatus("Copying to " + Path.GetFileName(path));

                try
                {
                    metadata.WriteIntellectualPropertyOnly(path);
                }
                catch (TagLib.CorruptFileException e)
                {
                    NonFatalProblem.Report(ModalIf.Beta, PassiveIf.All,"Image metadata problem", "Bloom had a problem accessing the metadata portion of this image " + path+ "  ref(BL-3214)", e);
                }

                ++completed;
            }

            //Now update the html attributes which echo some of it, and is used by javascript to overlay displays related to
            //whether the info is there or missing or whatever.

            foreach (XmlElement img in dom.SafeSelectNodes("//img"))
            {
                UpdateImgMetdataAttributesToMatchImage(folderPath, img, progress, metadata);
            }
        }
Exemplo n.º 5
0
 public void SetBaseForRelativePaths_NoExistingBase_Adds()
 {
     var dom = new HtmlDom(
                   @"<html><head/></html>");
     dom.SetBaseForRelativePaths("theBase");
     AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("html/head/base[@href='theBase']", 1);
 }
Exemplo n.º 6
0
 /// <summary>Loads the requested panel into the toolbox</summary>
 public static void AppendToolboxPanel(HtmlDom domForToolbox, string fileName)
 {
     var toolbox = domForToolbox.Body.SelectSingleNode("//div[@id='toolbox']");
     var toolDom = new HtmlDom(XmlHtmlConverter.GetXmlDomFromHtmlFile(fileName));
     AddToolDependencies(toolDom);
     AppendAllChildren(toolDom.Body, toolbox);
 }
Exemplo n.º 7
0
 public void BringBookUpToDate_DomHas2ContentLanguages_PulledIntoBookProperties()
 {
     _bookDom = new HtmlDom(@"<html><head><div id='bloomDataDiv'><div data-book='contentLanguage2'>okm</div><div data-book='contentLanguage3'>kbt</div></div></head><body></body></html>");
     var book = CreateBook();
     book.BringBookUpToDate(new NullProgress());
     Assert.AreEqual("okm", book.MultilingualContentLanguage2);
     Assert.AreEqual("kbt", book.MultilingualContentLanguage3);
 }
Exemplo n.º 8
0
 public void BaseForRelativePaths_NullPath_SetsToEmpty()
 {
     var dom = new HtmlDom(
                   @"<html><head><base href='original'/></head></html>");
     dom.BaseForRelativePaths = null;
     AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("html/head/base", 0);
     Assert.AreEqual(string.Empty, dom.BaseForRelativePaths);
 }
Exemplo n.º 9
0
 /// <param name="dom">Set this parameter to, say, a page that the user just edited, to limit reading to it, so its values don't get overriden by previous pages.
 ///   Supply the whole dom if nothing has priority (which will mean the data-div will win, because it is first)</param>
 /// <param name="collectionSettings"> </param>
 /// <param name="updateImgNodeCallback">This is a callback so as not to introduce dependencies on ImageUpdater & the current folder path</param>
 public BookData(HtmlDom dom, CollectionSettings collectionSettings, Action<XmlElement> updateImgNodeCallback)
 {
     _dom = dom;
     _updateImgNode = updateImgNodeCallback;
     _collectionSettings = collectionSettings;
     GetOrCreateDataDiv();
     _dataset = GatherDataItemsFromCollectionSettings(_collectionSettings);
     GatherDataItemsFromXElement(_dataset,_dom.RawDom);
 }
Exemplo n.º 10
0
 public void BaseForRelativePaths_HasExistingBase_Removes()
 {
     var dom = new HtmlDom(
                   @"<html><head><base href='original'/></head></html>");
     AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("html/head/base[@href='original']", 1);
     dom.BaseForRelativePaths = "new";
     AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("html/head/base", 0);
     Assert.AreEqual("new", dom.BaseForRelativePaths);
 }
Exemplo n.º 11
0
 public void RemoveMetaValue_IsThere_RemovesIt()
 {
     var dom = new HtmlDom(
                   @"<html><head>
         <meta name='one' content='1'/>
         </head></html>");
     dom.RemoveMetaElement("one");
     AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//meta[@name='one']", 0);
 }
Exemplo n.º 12
0
 public void SetBaseForRelativePaths_HasExistingBase_Replaces()
 {
     var dom = new HtmlDom(
                   @"<html><head><base href='original'/></head></html>");
     AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("html/head/base[@href='original']", 1);
     dom.SetBaseForRelativePaths("new");
     AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("html/head/base[@href='original']", 0);
     AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("html/head/base[@href='new']", 1);
 }
Exemplo n.º 13
0
 public static void LoadPanelIntoToolbox(HtmlDom domForToolbox, ToolboxTool tool, List<string> checkedBoxes, string toolboxFolder)
 {
     // For all the toolbox tools, the tool name is used as the name of both the folder where the
     // assets for that tool are kept, and the name of the main htm file that represents the tool.
     var fileName = tool.ToolId + "ToolboxPanel.html";
     var path = BloomFileLocator.sTheMostRecentBloomFileLocator.LocateFile(fileName);
     Debug.Assert(!string.IsNullOrEmpty(path));
     AppendToolboxPanel(domForToolbox, path);
     checkedBoxes.Add(tool.ToolId + "Check");
 }
Exemplo n.º 14
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", "*"));
 }
Exemplo n.º 15
0
        public static IEnumerable <Layout> GetLayoutChoices(HtmlDom dom, IFileLocator fileLocator)
        {
            //here we walk through all the stylesheets, looking for one with the special style which tells us which page/orientations it supports
            foreach (XmlElement link in dom.SafeSelectNodes("//link[@rel='stylesheet']"))
            {
                var fileName = link.GetStringAttribute("href");
                if (fileName.ToLower().Contains("mode") || fileName.ToLower().Contains("page") ||
                    fileName.ToLower().Contains("matter") || fileName.ToLower().Contains("languagedisplay"))
                {
                    continue;
                }

                fileName = fileName.Replace("file://", "").Replace("%5C", "/");
                var path = fileLocator.LocateFile(fileName);
                if (string.IsNullOrEmpty(path))
                {
                    throw new ApplicationException("Could not locate " + fileName);
                }
                var contents = File.ReadAllText(path);
                var start    = contents.IndexOf("STARTLAYOUTS");
                if (start < 0)
                {
                    continue;                      //yield break; // continue;//move on to the next stylesheet
                }
                start += "STARTLAYOUTS".Length;
                var end = contents.IndexOf("ENDLAYOUTS", start);
                var s   = contents.Substring(start, end - start);

                IEnumerable <Layout> layouts = null;

                try
                {
                    layouts = Layout.GetConfigurationsFromConfigurationOptionsString(s);
                }
                catch (Exception e)
                {
                    throw new ApplicationException("Problem parsing the 'layouts' comment of " + fileName + ". The contents were\r\n" + s, e);
                }


                foreach (var p in layouts)
                {
                    yield return(p);
                }
                yield break;
            }

            //default to A5Portrait
            yield return(new Layout {
                SizeAndOrientation = FromString("A5Portrait")
            });
        }
        public static void AddUIDictionaryToDom(HtmlDom pageDom, CollectionSettings collectionSettings)
        {
            // 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>();

            d.Add(collectionSettings.Language1Iso639Code, collectionSettings.Language1Name);
            if (!String.IsNullOrEmpty(collectionSettings.Language2Iso639Code))
            {
                SafelyAddLanguage(d, collectionSettings.Language2Iso639Code,
                                  collectionSettings.GetLanguage2Name(collectionSettings.Language2Iso639Code));
            }
            if (!String.IsNullOrEmpty(collectionSettings.Language3Iso639Code))
            {
                SafelyAddLanguage(d, collectionSettings.Language3Iso639Code,
                                  collectionSettings.GetLanguage3Name(collectionSettings.Language3Iso639Code));
            }

            SafelyAddLanguage(d, "vernacularLang", collectionSettings.Language1Iso639Code);            //use for making the vernacular the first tab
            SafelyAddLanguage(d, "{V}", collectionSettings.Language1Name);
            SafelyAddLanguage(d, "{N1}", collectionSettings.GetLanguage2Name(collectionSettings.Language2Iso639Code));
            SafelyAddLanguage(d, "{N2}", collectionSettings.GetLanguage3Name(collectionSettings.Language3Iso639Code));

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

            MakePageLabelLocalizable(pageDom, d);

            AddLocalizedHintContentsToDictionary(pageDom, d, collectionSettings);

            // Hard-coded localizations for 2.0
            AddHtmlUiStrings(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);
        }
Exemplo n.º 17
0
        public void UpdateAllHtmlDataAttributesForAllImgElements_HasBothImgAndBackgroundImageElements_UpdatesBoth()
        {
            var dom = new HtmlDom("<html><body><img src='test.png'/><div style='color:orange; background-image=url(\"test.png\")'/></body></html>");

            using (var folder = new TemporaryFolder("bloom pictures test source"))
            {
                MakeSamplePngImageWithMetadata(folder.Combine("test.png"));
                ImageUpdater.UpdateAllHtmlDataAttributesForAllImgElements(folder.FolderPath, dom, new NullProgress());
            }

            AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//*[@data-copyright='Copyright 1999 by me']", 2);
            AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//*[@data-creator='joe']", 2);
            AssertThatXmlIn.Dom(dom.RawDom).HasSpecifiedNumberOfMatchesForXpath("//*[@data-license='cc-by-nd']", 2);
        }
        /// <summary>
        /// Create a Clearshare.Metadata object by reading values out of the dom's bloomDataDiv
        /// </summary>
        /// <param name="brandingNameOrFolderPath"> Normally, the branding is just a name, which we look up in the official branding folder
        //but unit tests can instead provide a path to the folder.
        /// </param>
        public static Metadata GetMetadata(HtmlDom dom, string brandingNameOrFolderPath = "")
        {
            if (ShouldSetToDefaultCopyrightAndLicense(dom))
            {
                return GetMetadataWithDefaultCopyrightAndLicense(brandingNameOrFolderPath);
            }
            var metadata = new Metadata();
            var copyright = dom.GetBookSetting("copyright");
            if (!copyright.Empty)
            {
                metadata.CopyrightNotice = WebUtility.HtmlDecode(copyright.GetFirstAlternative());
            }

            var licenseUrl = dom.GetBookSetting("licenseUrl").GetBestAlternativeString(new[] { "*", "en" });

            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.
                var licenseNotes = dom.GetBookSetting("licenseNotes");
                if (!licenseNotes.Empty)
                {
                    metadata.License = new CustomLicense { RightsStatement = WebUtility.HtmlDecode(licenseNotes.GetFirstAlternative()) };
                }
                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 (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?
                var licenseNotes = dom.GetBookSetting("licenseNotes");
                if(!licenseNotes.Empty)
                {
                    var s = WebUtility.HtmlDecode(licenseNotes.GetFirstAlternative());
                    metadata.License.RightsStatement = HtmlDom.ConvertHtmlBreaksToNewLines(s);
                }
            }
            return metadata;
        }
        public static void AddUIDictionaryToDom(HtmlDom pageDom, CollectionSettings collectionSettings)
        {
            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>();

            d.Add(collectionSettings.Language1Iso639Code, collectionSettings.Language1Name);
            if (!String.IsNullOrEmpty(collectionSettings.Language2Iso639Code))
                SafelyAddLanguage(d, collectionSettings.Language2Iso639Code,
                    collectionSettings.GetLanguage2Name(collectionSettings.Language2Iso639Code));
            if (!String.IsNullOrEmpty(collectionSettings.Language3Iso639Code))
                SafelyAddLanguage(d, collectionSettings.Language3Iso639Code,
                    collectionSettings.GetLanguage3Name(collectionSettings.Language3Iso639Code));

            SafelyAddLanguage(d, "vernacularLang", collectionSettings.Language1Iso639Code);//use for making the vernacular the first tab
            SafelyAddLanguage(d, "{V}", collectionSettings.Language1Name);
            SafelyAddLanguage(d, "{N1}", collectionSettings.GetLanguage2Name(collectionSettings.Language2Iso639Code));
            SafelyAddLanguage(d, "{N2}", collectionSettings.GetLanguage3Name(collectionSettings.Language3Iso639Code));

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

            MakePageLabelLocalizable(pageDom, 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;
        }
        private static void MakePageLabelLocalizable(HtmlDom singlePageHtmlDom, Dictionary <string, string> d)
        {
            foreach (XmlElement element in singlePageHtmlDom.RawDom.SelectNodes("//*[contains(@class, 'pageLabel')]"))
            {
                if (!element.HasAttribute("data-i18n"))
                {
                    var englishLabel = element.InnerText;
                    var key          = "EditTab.ThumbnailCaptions." + englishLabel;
                    AddTranslationToDictionaryUsingEnglishAsKey(d, key, englishLabel);

                    element.SetAttribute("data-i18n", key);
                }
            }
        }
Exemplo n.º 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, CollectionSettings collectionSettings)
        {
            CopyItemToFieldsInPages(dom, "copyright");
            CopyItemToFieldsInPages(dom, "licenseUrl");
            CopyItemToFieldsInPages(dom, "licenseDescription", languagePreferences: collectionSettings.LicenseDescriptionLanguagePriorities.ToArray());
            CopyItemToFieldsInPages(dom, "licenseNotes");
            CopyItemToFieldsInPages(dom, "licenseImage", valueAttribute: "src");
            CopyItemToFieldsInPages(dom, "originalCopyrightAndLicense");

            if (!String.IsNullOrEmpty(bookFolderPath))             //unit tests may not be interested in checking this part
            {
                UpdateBookLicenseIcon(GetMetadata(dom), bookFolderPath);
            }
        }
        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;
        }
Exemplo n.º 24
0
        public static Layout FromDom(HtmlDom dom, Layout defaultIfMissing)
        {
            var firstPage = dom.SelectSingleNode("descendant-or-self::div[contains(@class,'bloom-page')]");

            if (firstPage == null)
            {
                return(defaultIfMissing);
            }

            var layout = new Layout {
                SizeAndOrientation = defaultIfMissing.SizeAndOrientation, Style = defaultIfMissing.Style
            };

            return(FromPage(firstPage, layout));
        }
Exemplo n.º 25
0
 private static void EnsureIdsAreUnique(HtmlDom dom, string elementTag, List <string> ids, StringBuilder builder)
 {
     foreach (XmlElement element in dom.SafeSelectNodes("//" + elementTag + "[@id]"))
     {
         var id = element.GetAttribute("id");
         if (ids.Contains(id))
         {
             builder.AppendLine("The id of this " + elementTag + " must be unique, but is not: " + element.OuterXml);
         }
         else
         {
             ids.Add(id);
         }
     }
 }
Exemplo n.º 26
0
        /// <summary>
        /// We mirror several metadata tags in the html for quick access by the UI.
        /// This method makes sure they are all up to date.
        /// </summary>
        /// <param name="progress"> </param>
        public static void UpdateAllHtmlDataAttributesForAllImgElements(string folderPath, HtmlDom dom, IProgress progress)
        {
            //Update the html attributes which echo some of it, and is used by javascript to overlay displays related to
            //whether the info is there or missing or whatever.

            var imgElements = HtmlDom.SelectChildImgAndBackgroundImageElements(dom.RawDom.DocumentElement);
            int completed   = 0;

            foreach (XmlElement img in imgElements)
            {
                progress.ProgressIndicator.PercentCompleted = (int)(100.0 * (float)completed / (float)imgElements.Count);
                UpdateImgMetdataAttributesToMatchImage(folderPath, img, progress);
                completed++;
            }
        }
        /// <summary>
        /// stick in a json with various settings we want to make available to the javascript
        /// </summary>
        public static void AddUISettingsToDom(HtmlDom pageDom, CollectionSettings collectionSettings, IFileLocator fileLocator)
        {
            XmlElement element = pageDom.RawDom.SelectSingleNode("//script[@id='ui-settings']") as XmlElement;

            if (element != null)
            {
                element.ParentNode.RemoveChild(element);
            }

            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", collectionSettings.Language1Iso639Code);
            d.Add("isSourceCollection", collectionSettings.IsSourceCollection.ToString());

            d.Add("bloomBrowserUIFolder", FileLocator.GetDirectoryDistributedWithApplication("BloomBrowserUI").ToLocalhost());

            //If you modify any of these, consider modifying/updating the localization files; the localization ids for these are just the current English (which is fagile)
            //If you make changes/additions here, also synchronize with the bloomlibrary source in services.js
            var topics  = new[] { "Agriculture", "Animal Stories", "Business", "Culture", "Community Living", "Dictionary", "Environment", "Fiction", "Health", "How To", "Math", "Non Fiction", "Spiritual", "Personal Development", "Primer", "Science", "Traditional Story" };
            var builder = new StringBuilder();

            builder.Append("[");
            TopicReversal = new Dictionary <string, string>();
            foreach (var topic in topics)
            {
                var localized = LocalizationManager.GetDynamicString("Bloom", "Topics." + topic, topic, "shows in the topics chooser in the edit tab");
                TopicReversal[localized] = topic;
                builder.Append("\"" + localized + "\", ");
            }
            builder.Append("]");
            d.Add("topics", builder.ToString().Replace(", ]", "]"));
//            d.Add("topics", "['Agriculture', 'Animal Stories', 'Business', 'Culture', 'Community Living', 'Dictionary', 'Environment', 'Fiction', 'Health', 'How To', 'Math', 'Non Fiction', 'Spiritual', 'Personal Development', 'Primer', 'Science', 'Tradition']".Replace("'", "\\\""));

            element.InnerText = String.Format("function GetSettings() {{ return {0};}}", JsonConvert.SerializeObject(d));

            var head = pageDom.RawDom.SelectSingleNode("//head");

            head.InsertAfter(element, head.LastChild);
        }
Exemplo n.º 28
0
        public static void UpdateImgMetadataAttributesToMatchImage(string folderPath, XmlElement imgElement, IProgress progress, Metadata metadata)
        {
            //see also PageEditingModel.UpdateMetadataAttributesOnImage(), which does the same thing but on the browser dom
            var    url      = HtmlDom.GetImageElementUrl(new ElementProxy(imgElement));
            string fileName = url.PathOnly.NotEncoded;

            if (fileName.ToLowerInvariant() == "placeholder.png" || fileName.ToLowerInvariant() == "license.png")
            {
                return;
            }
            if (string.IsNullOrEmpty(fileName))
            {
                Logger.WriteEvent("Book.UpdateImgMetdataAttributesToMatchImage() Warning: img has no or empty src attribute");
                //Debug.Fail(" (Debug only) img has no or empty src attribute");
                return;                 // they have bigger problems, which aren't appropriate to deal with here.
            }

            if (metadata == null)
            {
                // The fileName might be URL encoded.  See https://silbloom.myjetbrains.com/youtrack/issue/BL-3901.
                var path = UrlPathString.GetFullyDecodedPath(folderPath, ref fileName);
                progress.WriteStatus("Reading metadata from " + fileName);
                if (!RobustFile.Exists(path))                 // they have bigger problems, which aren't appropriate to deal with here.
                {
                    imgElement.RemoveAttribute("data-copyright");
                    imgElement.RemoveAttribute("data-creator");
                    imgElement.RemoveAttribute("data-license");
                    Logger.WriteEvent("Book.UpdateImgMetdataAttributesToMatchImage()  Image " + path + " is missing");
                    //Debug.Fail(" (Debug only) Image " + path + " is missing");
                    return;
                }
                try
                {
                    metadata = RobustIO.MetadataFromFile(path);
                }
                catch (UnauthorizedAccessException e)
                {
                    throw new BloomUnauthorizedAccessException(path, e);
                }
            }

            progress.WriteStatus("Writing metadata to HTML for " + fileName);

            imgElement.SetAttribute("data-copyright",
                                    String.IsNullOrEmpty(metadata.CopyrightNotice) ? "" : metadata.CopyrightNotice);
            imgElement.SetAttribute("data-creator", String.IsNullOrEmpty(metadata.Creator) ? "" : metadata.Creator);
            imgElement.SetAttribute("data-license", metadata.License == null ? "" : metadata.License.ToString());
        }
Exemplo n.º 29
0
        /// <summary>
        /// Constructs by finding the file and folder of the xmatter pack, given the its key name e.g. "Factory", "SILIndonesia".
        /// The default key name is provided as a method parameter, but that can be overridden by a value from inside the book.
        /// The name of the file should be (key)-XMatter.htm. The name and the location of the folder is not our problem...
        /// we leave it to the supplied fileLocator to find it.
        /// </summary>
        /// <param name="xmatterNameFromCollectionSettings">e.g. "Factory", "SILIndonesia".  This can be overridden inside the bookDom.</param>
        /// <param name="fileLocator">The locator needs to be able tell us the path to an xmatter html file, given its name</param>
        public XMatterHelper(HtmlDom bookDom, string xmatterNameFromCollectionSettings, IFileLocator fileLocator)
        {
            string directoryPath = null;

            _bookDom = bookDom;
            var bookSpecificXMatterPack = bookDom.GetMetaValue("xmatter", null);

            if (!String.IsNullOrWhiteSpace(bookSpecificXMatterPack))
            {
                bookSpecificXMatterPack = MigrateXMatterName(bookSpecificXMatterPack);
                _nameOfXMatterPack      = bookSpecificXMatterPack;
                var errorTemplate = LocalizationManager.GetString("Errors.XMatterSpecifiedByBookNotFound",
                                                                  "This book called for a Front/Back Matter pack named '{0}', but this version of Bloom does not have it, and Bloom could not find it on this computer. The book has been changed to use the Front/Back Matter pages from the Collection Settings.");
                directoryPath = GetXMatterDirectory(_nameOfXMatterPack, fileLocator, String.Format(errorTemplate, bookSpecificXMatterPack), false);
                if (directoryPath == null)
                {
                    // Remove the xmatter specification from the DOM since it couldn't be found.
                    _bookDom.RemoveMetaElement("xmatter");
                }
            }
            if (directoryPath == null)
            {
                _nameOfXMatterPack = xmatterNameFromCollectionSettings;
                directoryPath      = GetXMatterDirectory(_nameOfXMatterPack, fileLocator, "It should not be possible to get an error here, because the collection verifies its xmatter name in CheckAndFixDependencies()", true);
            }
            var htmName = _nameOfXMatterPack + "-XMatter.html";

            PathToXMatterHtml = directoryPath.CombineForPath(htmName);
            if (!RobustFile.Exists(PathToXMatterHtml))
            {
                htmName           = _nameOfXMatterPack + "-XMatter.htm";       // pre- Bloom 3.7
                PathToXMatterHtml = directoryPath.CombineForPath(htmName);
            }
            if (!RobustFile.Exists(PathToXMatterHtml))
            {
                ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), "Could not locate the file {0} in {1} (also checked .html)", htmName, directoryPath);
                throw new ApplicationException();
            }
            PathToXMatterStylesheet = directoryPath.CombineForPath(GetStyleSheetFileName());
            if (!RobustFile.Exists(PathToXMatterHtml))
            {
                ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), "Could not locate the file {0} in {1}", GetStyleSheetFileName(), directoryPath);
                throw new ApplicationException();
            }
            XMatterDom = XmlHtmlConverter.GetXmlDomFromHtmlFile(PathToXMatterHtml, false);
        }
Exemplo n.º 30
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);
            }
        }
Exemplo n.º 31
0
        private static void CopyItemToFieldsInPages(HtmlDom dom, string key, string valueAttribute = null, string[] languagePreferences = null)
        {
            if (languagePreferences == null)
            {
                languagePreferences = new[] { "*", "en" }
            }
            ;

            MultiTextBase source = dom.GetBookSetting(key);

            foreach (XmlElement target in dom.SafeSelectNodes("//*[@data-derived='" + key + "']"))
            {
                //just put value into the text of the element
                if (string.IsNullOrEmpty(valueAttribute))
                {
                    //clear out what's there now
                    target.RemoveAttribute("lang");
                    target.InnerText = "";

                    var form = source.GetBestAlternative(languagePreferences);
                    if (form != null && !string.IsNullOrWhiteSpace(form.Form))
                    {
                        // HtmlDom.GetBookSetting(key) returns the result of XmlNode.InnerXml which will be Html encoded (&amp; &lt; etc).
                        // HtmlDom.SetElementFromUserStringPreservingLineBreaks() calls XmlNode.InnerText, which Html encodes if necessary.
                        // So we need to decode here to prevent double encoding.  See http://issues.bloomlibrary.org/youtrack/issue/BL-4585.
                        // Note that HtmlDom.SetElementFromUserStringPreservingLineBreaks() handles embedded <br/> elements, but makes no
                        // effort to handle p or div elements.
                        var decoded = System.Web.HttpUtility.HtmlDecode(form.Form);
                        HtmlDom.SetElementFromUserStringPreservingLineBreaks(target, decoded);
                        target.SetAttribute("lang", form.WritingSystemId);                         //this allows us to set the font to suit the language
                    }
                }
                else                 //Put the value into an attribute. The license image goes through this path.
                {
                    target.SetAttribute(valueAttribute, source.GetBestAlternativeString(languagePreferences));
                    if (source.Empty)
                    {
                        //if the license image is empty, make sure we don't have some alternative text
                        //about the image being missing or slow to load
                        target.SetAttribute("alt", "");
                        //over in javascript land, @alt will get set appropriately when the image url is not empty.
                    }
                }
            }
        }
Exemplo n.º 32
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");
 }
Exemplo n.º 33
0
        public void BringBookUpToDate_CoverImageHasMetaData_HtmlForCoverPageHasMetaDataAttributes()
        {
            _bookDom = new HtmlDom(@"
                <html>
                    <body>
                        <div id='bloomDataDiv'>
                            <div data-book='coverImage'>test.png</div>
                        </div>
                    </body>
                </html>");

            var book = CreateBook();
            var imagePath = book.FolderPath.CombineForPath("test.png");
            MakeSamplePngImageWithMetadata(imagePath);

            book.BringBookUpToDate(new NullProgress());
            AssertThatXmlIn.Dom(book.RawDom).HasSpecifiedNumberOfMatchesForXpath("//div/div/div/img[@data-creator='joe']",1);
        }
Exemplo n.º 34
0
        private static void CopyItemToFieldsInPages(HtmlDom dom, string key, string valueAttribute = null, string[] languagePreferences = null)
        {
            if (languagePreferences == null)
            {
                languagePreferences = new[] { "*", "en" }
            }
            ;

            MultiTextBase source = dom.GetBookSetting(key);

            var target = dom.SelectSingleNode("//*[@data-derived='" + key + "']");

            if (target == null)
            {
                return;
            }


            //just put value into the text of the element
            if (string.IsNullOrEmpty(valueAttribute))
            {
                //clear out what's there now
                target.RemoveAttribute("lang");
                target.InnerText = "";

                var form = source.GetBestAlternative(languagePreferences);
                if (form != null && !string.IsNullOrWhiteSpace(form.Form))
                {
                    HtmlDom.SetElementFromUserStringPreservingLineBreaks(target, form.Form);
                    target.SetAttribute("lang", form.WritingSystemId);                     //this allows us to set the font to suit the language
                }
            }
            else             //Put the value into an attribute. The license image goes through this path.
            {
                target.SetAttribute(valueAttribute, source.GetBestAlternativeString(languagePreferences));
                if (source.Empty)
                {
                    //if the license image is empty, make sure we don't have some alternative text
                    //about the image being missing or slow to load
                    target.SetAttribute("alt", "");
                    //over in javascript land, @alt will get set appropriately when the image url is not empty.
                }
            }
        }
        public static void UpdateBook(HtmlDom dom, string language1Iso639Code)
        {
            int page = 0;
            foreach (XmlElement pageDiv in dom.SafeSelectNodes("/html/body//div[contains(@class,'bloom-page')]"))
            {
                var term = pageDiv.SelectSingleNode("//div[contains(@data-book,'term')]").InnerText.Trim();
                XmlNode weekDataNode = pageDiv.SelectSingleNode("//div[contains(@data-book,'week')]");
                if(weekDataNode==null)
                    continue; // term intro books don't have weeks

                var week = weekDataNode.InnerText.Trim();
                // TODO: need a better way to identify thumbnails, like a class that is always there, lest  we replace some other img that we don't want to replace
                foreach (XmlElement thumbnailContainer in pageDiv.SafeSelectNodes(".//img"))
                {
                    ++page;
                    thumbnailContainer.SetAttribute("src", language1Iso639Code + "-t" + term + "-w" + week + "-p" + page + ".png");
                }
            }
        }
 private static void AddLocalizedHintContentsToDictionary(HtmlDom singlePageHtmlDom, Dictionary <string, string> dictionary, CollectionSettings collectionSettings)
 {
     /*  Disabling this, generic data-hint localization at the moment, as it is interfering with the primary factory-supplied ones.
      * when we bring it back, lets think of ways to get nice ids in there that don't rely on the english. E.g., we could do
      * something like this: data-hint="[ColorBook.ColorPrompt]What color do you want?" and then we could take that id and prepend something
      * like "BookEdit.MiscBooks." so we end up with BookEdit.MiscBooks.ColorBook.ColorPrompt
      *
      * var nameOfXMatterPack = singlePageHtmlDom.GetMetaValue("xMatter", collectionSettings.XMatterPackName);
      *
      *
      * string idPrefix = "";
      * var pageElement = singlePageHtmlDom.RawDom.SelectSingleNode("//div") as XmlElement;
      * if (XMatterHelper.IsFrontMatterPage(pageElement))
      * {
      *      idPrefix = "FrontMatter." + nameOfXMatterPack + ".";
      * }
      * else if (XMatterHelper.IsBackMatterPage(pageElement))
      * {
      *      idPrefix = "BackMatter." + nameOfXMatterPack + ".";
      * }
      * foreach (XmlElement element in singlePageHtmlDom.RawDom.SelectNodes("//*[@data-hint]"))
      * {
      *      //why aren't we just doing: element.SetAttribute("data-hint", translation);  instead of bothering to write out a dictionary?
      *      //because (especially since we're currently just assuming it is in english), we would later save it with the translation, and then next time try to translate that, and poplute the
      *      //list of strings that we tell people to translate
      *      var key = element.GetAttribute("data-hint");
      *      if (!dictionary.ContainsKey(key))
      *      {
      *              string translation;
      *              var id = idPrefix + key;
      *              if (key.Contains("{lang}"))
      *              {
      *                      translation = LocalizationManager.GetDynamicString("Bloom", id, key, "Put {lang} in your translation, so it can be replaced by the language name.");
      *              }
      *              else
      *              {
      *                      translation = LocalizationManager.GetDynamicString("Bloom", id, key);
      *              }
      *              dictionary.Add(key, translation);
      *      }
      * }
      */
 }
Exemplo n.º 37
0
 private static void CopyStringToFieldsInPages(HtmlDom dom, string key, string val, string lang)
 {
     foreach (XmlElement target in dom.SafeSelectNodes("//*[@data-derived='" + key + "']"))
     {
         if (target == null)                 // don't think this can happen, but something like it seemed to in one test...
         {
             continue;
         }
         if (string.IsNullOrEmpty(val))
         {
             target.RemoveAttribute("lang");
             target.InnerText = "";
         }
         else
         {
             HtmlDom.SetElementFromUserStringSafely(target, val);
             target.SetAttribute("lang", lang);
         }
     }
 }
Exemplo n.º 38
0
        /// <summary>
        /// Constructs by finding the file and folder of the xmatter pack, given the its key name e.g. "Factory", "SILIndonesia".
        /// The name of the file should be (key)-XMatter.htm. The name and the location of the folder is not our problem...
        /// we leave it to the supplied fileLocator to find it.
        /// </summary>
        /// <param name="nameOfXMatterPack">e.g. "Factory", "SILIndonesia"</param>
        /// <param name="fileLocator">The locator needs to be able tell us the path to an xmater html file, given its name</param>
        public XMatterHelper(HtmlDom bookDom, string nameOfXMatterPack, IFileLocator fileLocator)
        {
            _bookDom = bookDom;
            _nameOfXMatterPack = nameOfXMatterPack;

            string directoryName = nameOfXMatterPack + "-XMatter";
            string directoryPath;
            try
            {
                directoryPath = fileLocator.LocateDirectoryWithThrow(directoryName);
            }
            catch(ApplicationException error)
            {
                var errorTemplate = LocalizationManager.GetString("Errors.XMatterNotFound",
                    "This Book called for Front/Back Matter pack named '{0}', but Bloom couldn't find that on this computer. You can either install a Bloom Pack that will give you '{0}', or go to Settings:Book Making and change to another Front/Back Matter Pack.");
                var msg = string.Format(errorTemplate, nameOfXMatterPack);

                ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), msg);
                //NB: we don't want to put up a dialog for each one; one failure here often means 20 more are coming as the other books are loaded!
                throw new ApplicationException(msg);
            }
            var htmName = nameOfXMatterPack + "-XMatter.html";
            PathToXMatterHtml = directoryPath.CombineForPath(htmName);
            if(!RobustFile.Exists(PathToXMatterHtml))
            {
                htmName = nameOfXMatterPack + "-XMatter.htm"; // pre- Bloom 3.7
                PathToXMatterHtml = directoryPath.CombineForPath(htmName);
            }
            if (!RobustFile.Exists(PathToXMatterHtml))
            {
                ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), "Could not locate the file {0} in {1} (also checked .html)", htmName, directoryPath);
                throw new ApplicationException();
            }
            PathToStyleSheetForPaperAndOrientation = directoryPath.CombineForPath(GetStyleSheetFileName());
            if (!RobustFile.Exists(PathToXMatterHtml))
            {
                ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), "Could not locate the file {0} in {1}", GetStyleSheetFileName(), directoryPath);
                throw new ApplicationException();
            }
            XMatterDom = XmlHtmlConverter.GetXmlDomFromHtmlFile(PathToXMatterHtml, false);
        }
        //TODO: make this be a real extension
        public static void UpdateBook(HtmlDom dom, string language1Iso639Code)
        {
            int day = 0;
            foreach (XmlElement pageDiv in dom.SafeSelectNodes("/html/body/div[contains(@class,'bloom-page')]"))
            {
                var term = pageDiv.SelectSingleNode("//div[contains(@data-book,'term')]").InnerText.Trim();
                var week = pageDiv.SelectSingleNode("//div[contains(@data-book,'week')]").InnerText.Trim();

                var thumbnailHolders = pageDiv.SafeSelectNodes(".//img");
                if (thumbnailHolders.Count == 2)
                {
                    ++day;
                    ((XmlElement)thumbnailHolders[0]).SetAttribute("src", language1Iso639Code+"-t"+term + "-w" + week + "-d" + day + ".png");
                    ++day;
                    ((XmlElement)thumbnailHolders[1]).SetAttribute("src", language1Iso639Code + "-t" + term + "-w" + week + "-d" + day + ".png");
                }
                //day1Thumbnail day2Thumbnail day4Thumbnail
                //unfortunately Day3 went out with  an img container just copied from day1, with erroneous "day1Thumbnail" class

            }
        }
Exemplo n.º 40
0
        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);
        }
Exemplo n.º 41
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);
        }
Exemplo n.º 42
0
        /// <summary>
        /// Constructs by finding the file and folder of the xmatter pack, given the its key name e.g. "Factory", "SILIndonesia".
        /// The name of the file should be (key)-XMatter.htm. The name and the location of the folder is not our problem...
        /// we leave it to the supplied fileLocator to find it.
        /// </summary>
        /// <param name="nameOfXMatterPack">e.g. "Factory", "SILIndonesia"</param>
        /// <param name="fileLocator">The locator needs to be able tell use the path to an xmater html file, given its name</param>
        public XMatterHelper(HtmlDom bookDom, string nameOfXMatterPack, IFileLocator fileLocator)
        {
            _bookDom = bookDom;
            _nameOfXMatterPack = nameOfXMatterPack;

            string directoryName = nameOfXMatterPack + "-XMatter";
            var directoryPath = fileLocator.LocateDirectory(directoryName, "xmatter pack directory named " + directoryName);
            string htmName = nameOfXMatterPack + "-XMatter.htm";
            PathToXMatterHtml = directoryPath.CombineForPath(htmName);
            if(!File.Exists(PathToXMatterHtml))
            {
                ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), "Could not locate the file {0} in {1}", htmName, directoryPath);
                throw new ApplicationException();
            }
            PathToStyleSheetForPaperAndOrientation = directoryPath.CombineForPath(GetStyleSheetFileName());
            if (!File.Exists(PathToXMatterHtml))
            {
                ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), "Could not locate the file {0} in {1}", GetStyleSheetFileName(), directoryPath);
                throw new ApplicationException();
            }
            XMatterDom = XmlHtmlConverter.GetXmlDomFromHtmlFile(PathToXMatterHtml, false);
        }
        /// <summary>
        /// Adds a script to the page that triggers i18n after the page is fully loaded.
        /// </summary>
        /// <param name="pageDom"></param>
        private static void AddLocalizationTriggerToDom(HtmlDom pageDom)
        {
            XmlElement i18nScriptElement = pageDom.RawDom.SelectSingleNode("//script[@id='ui-i18n']") as XmlElement;

            if (i18nScriptElement != null)
            {
                i18nScriptElement.ParentNode.RemoveChild(i18nScriptElement);
            }

            i18nScriptElement = pageDom.RawDom.CreateElement("script");
            i18nScriptElement.SetAttribute("type", "text/javascript");
            i18nScriptElement.SetAttribute("id", "ui-i18n");

            // Explanation of the JavaScript:
            //   $(document).ready(function() {...}) tells the browser to run the code inside the braces after the document has completed loading.
            //   $('body') is a jQuery function that selects the contents of the body tag.
            //   .find('*[data-i18n]') instructs jQuery to return a collection of all elements inside the body tag that have a "data-i18n" attribute.
            //   .localize() runs the jQuery.fn.localize() method, which loops through the above collection of elements and attempts to localize the text.
            i18nScriptElement.InnerText = "$(document).ready(function() { $('body').find('*[data-i18n]').localize(); });";

            pageDom.Head.InsertAfter(i18nScriptElement, pageDom.Head.LastChild);
        }
        private static void AddLocalizedHintContentsToDictionary(HtmlDom singlePageHtmlDom, Dictionary <string, string> dictionary, CollectionSettings collectionSettings)
        {
            var nameOfXMatterPack = singlePageHtmlDom.GetMetaValue("xMatter", collectionSettings.XMatterPackName);


            string idPrefix    = "";
            var    pageElement = singlePageHtmlDom.RawDom.SelectSingleNode("//div") as XmlElement;

            if (XMatterHelper.IsFrontMatterPage(pageElement))
            {
                idPrefix = "FrontMatter." + nameOfXMatterPack + ".";
            }
            else if (XMatterHelper.IsBackMatterPage(pageElement))
            {
                idPrefix = "BackMatter." + nameOfXMatterPack + ".";
            }
            foreach (XmlElement element in singlePageHtmlDom.RawDom.SelectNodes("//*[@data-hint]"))
            {
                //why aren't we just doing: element.SetAttribute("data-hint", translation);  instead of bothering to write out a dictionary?
                //because (especially since we're currently just assuming it is in english), we would later save it with the translation, and then next time try to translate that, and poplute the
                //list of strings that we tell people to translate
                var key = element.GetAttribute("data-hint");
                if (!dictionary.ContainsKey(key))
                {
                    string translation;
                    var    id = idPrefix + key;
                    if (key.Contains("{lang}"))
                    {
                        translation = LocalizationManager.GetDynamicString("Bloom", id, key, "Put {lang} in your translation, so it can be replaced by the language name.");
                    }
                    else
                    {
                        translation = LocalizationManager.GetDynamicString("Bloom", id, key);
                    }
                    dictionary.Add(key, translation);
                }
            }
        }
        /// <summary>
        /// stick in a json with various settings we want to make available to the javascript
        /// </summary>
        public static void AddUISettingsToDom(HtmlDom pageDom, CollectionSettings collectionSettings, IFileLocator fileLocator)
        {
            XmlElement element = pageDom.RawDom.SelectSingleNode("//script[@id='ui-settings']") as XmlElement;
            if (element != null)
                element.ParentNode.RemoveChild(element);

            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", collectionSettings.Language1Iso639Code);

            d.Add("bloomBrowserUIFolder", FileLocator.GetDirectoryDistributedWithApplication("BloomBrowserUI"));

            var topics = new[] { "Agriculture", "Animal Stories", "Business", "Culture", "Community Living", "Dictionary", "Environment", "Fiction", "Health", "How To", "Math", "Non Fiction", "Spiritual", "Personal Development", "Primer", "Science", "Traditional Story" };
            var builder = new StringBuilder();
            builder.Append("[");
            foreach (var topic in topics)
            {
                var localized = LocalizationManager.GetDynamicString("Bloom", "Topics." + topic, topic, "shows in the topics chooser in the edit tab");
                builder.Append("\""+localized+"\", ");
            }
            builder.Append("]");
            d.Add("topics", builder.ToString().Replace(", ]","]"));
            //            d.Add("topics", "['Agriculture', 'Animal Stories', 'Business', 'Culture', 'Community Living', 'Dictionary', 'Environment', 'Fiction', 'Health', 'How To', 'Math', 'Non Fiction', 'Spiritual', 'Personal Development', 'Primer', 'Science', 'Tradition']".Replace("'", "\\\""));

            element.InnerText = String.Format("function GetSettings() {{ return {0};}}", JsonConvert.SerializeObject(d));

            var head = pageDom.RawDom.SelectSingleNode("//head");
            head.InsertAfter(element, head.LastChild);
        }
Exemplo n.º 46
0
 /// <summary>
 /// Find every place in the html file where an img element is nested inside a div with class bloom-imageContainer.
 /// Convert the img into a background image of the image container div.
 /// Specifically, make the following changes:
 /// - Copy any data-x attributes from the img element to the div
 /// - Convert the src attribute of the img to style="background-image:url('...')" (with the same source) on the div
 ///    (any pre-existing style attribute on the div is lost)
 /// - Add the class bloom-backgroundImage to the div
 /// - delete the img element
 /// (See oldImg and newImg in unit test CompressBookForDevice_ImgInImgContainer_ConvertedToBackground for an example).
 /// </summary>
 /// <param name="wholeBookHtml"></param>
 /// <returns></returns>
 private static void ConvertImagesToBackground(XmlDocument dom)
 {
     foreach (var imgContainer in dom.SafeSelectNodes("//div[contains(@class, 'bloom-imageContainer')]").Cast <XmlElement>().ToArray())
     {
         var img = imgContainer.ChildNodes.Cast <XmlNode>().FirstOrDefault(n => n is XmlElement && n.Name == "img");
         if (img == null || img.Attributes["src"] == null)
         {
             continue;
         }
         // The filename should be already urlencoded since src is a url.
         var src = img.Attributes["src"].Value;
         HtmlDom.SetImageElementUrl(new ElementProxy(imgContainer), UrlPathString.CreateFromUrlEncodedString(src));
         foreach (XmlAttribute attr in img.Attributes)
         {
             if (attr.Name.StartsWith("data-"))
             {
                 imgContainer.SetAttribute(attr.Name, attr.Value);
             }
         }
         imgContainer.SetAttribute("class", imgContainer.Attributes["class"].Value + " bloom-backgroundImage");
         imgContainer.RemoveChild(img);
     }
 }
Exemplo n.º 47
0
        /// <summary>
        /// Constructs by finding the file and folder of the xmatter pack, given the its key name e.g. "Factory", "SILIndonesia".
        /// The name of the file should be (key)-XMatter.htm. The name and the location of the folder is not our problem...
        /// we leave it to the supplied fileLocator to find it.
        /// </summary>
        /// <param name="nameOfXMatterPack">e.g. "Factory", "SILIndonesia"</param>
        /// <param name="fileLocator">The locator needs to be able tell use the path to an xmater html file, given its name</param>
        public XMatterHelper(HtmlDom bookDom, string nameOfXMatterPack, IFileLocator fileLocator)
        {
            _bookDom           = bookDom;
            _nameOfXMatterPack = nameOfXMatterPack;

            string directoryName = nameOfXMatterPack + "-XMatter";
            var    directoryPath = fileLocator.LocateDirectory(directoryName, "xmatter pack directory named " + directoryName);
            string htmName       = nameOfXMatterPack + "-XMatter.htm";

            PathToXMatterHtml = directoryPath.CombineForPath(htmName);
            if (!File.Exists(PathToXMatterHtml))
            {
                ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), "Could not locate the file {0} in {1}", htmName, directoryPath);
                throw new ApplicationException();
            }
            PathToStyleSheetForPaperAndOrientation = directoryPath.CombineForPath(GetStyleSheetFileName());
            if (!File.Exists(PathToXMatterHtml))
            {
                ErrorReport.NotifyUserOfProblem(new ShowOncePerSessionBasedOnExactMessagePolicy(), "Could not locate the file {0} in {1}", GetStyleSheetFileName(), directoryPath);
                throw new ApplicationException();
            }
            XMatterDom = XmlHtmlConverter.GetXmlDomFromHtmlFile(PathToXMatterHtml, false);
        }
Exemplo n.º 48
0
        public static string GetMessageIfVersionIsIncompatibleWithThisBloom(HtmlDom dom)
        {
            //var dom = new HtmlDom(XmlHtmlConverter.GetXmlDomFromHtmlFile(path, false));//with throw if there are errors

            var versionString = dom.GetMetaValue("BloomFormatVersion", "").Trim();

            if (string.IsNullOrEmpty(versionString))
            {
                return("");               // "This file lacks the following required element: <meta name='BloomFormatVersion' content='x.y'>";
            }
            float versionFloat = 0;

            if (!float.TryParse(versionString, out versionFloat))
            {
                return("This file claims a version number that isn't really a number: " + versionString);
            }

            if (versionFloat > float.Parse(kBloomFormatVersion))
            {
                return(string.Format("This book or template was made with a newer version of Bloom. Download the latest version of Bloom from bloomlibrary.org (format {0} vs. {1})", versionString, kBloomFormatVersion));
            }

            return(null);
        }
Exemplo n.º 49
0
        public static void SetBaseForRelativePaths(HtmlDom dom, string folderPath, bool pointAtEmbeddedServer)
        {
            string path = "";

            if (!string.IsNullOrEmpty(folderPath))
            {
                if (pointAtEmbeddedServer && Settings.Default.ImageHandler == "http" && ImageServer.IsAbleToUsePort)
                {
                    //this is only used by relative paths, and only img src's are left relative.
                    //we are redirecting through our build-in httplistener in order to shrink
                    //big images before giving them to gecko which has trouble with really hi-res ones
                    var uri = folderPath + Path.DirectorySeparatorChar;
                    uri  = uri.Replace(":", "%3A");
                    uri  = uri.Replace('\\', '/');
                    uri  = ImageServer.PathEndingInSlash + uri;
                    path = uri;
                }
                else
                {
                    path = "file://" + folderPath + Path.DirectorySeparatorChar;
                }
            }
            dom.SetBaseForRelativePaths(path);
        }
Exemplo n.º 50
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(), "*");
        }
Exemplo n.º 51
0
        // NB: make sure you assigned HtmlDom.BaseForRelativePaths if the temporary document might
        // contain references to files in the directory of the original HTML file it is derived from,
        // 'cause that provides the information needed
        // to fake out the browser about where the 'file' is so internal references work.
        public void Navigate(HtmlDom htmlDom, HtmlDom htmlEditDom = null, bool setAsCurrentPageForDebugging = false)
        {
            if (InvokeRequired)
            {
                Invoke(new Action<HtmlDom, HtmlDom, bool>(Navigate), htmlDom, htmlEditDom, setAsCurrentPageForDebugging);
                return;
            }

            XmlDocument dom = htmlDom.RawDom;
            XmlDocument editDom = htmlEditDom == null ? null : htmlEditDom.RawDom;

            _rootDom = dom;//.CloneNode(true); //clone because we want to modify it a bit
            _pageEditDom = editDom ?? dom;

            XmlHtmlConverter.MakeXmlishTagsSafeForInterpretationAsHtml(dom);
            var fakeTempFile = EnhancedImageServer.MakeSimulatedPageFileInBookFolder(htmlDom, setAsCurrentPageForDebugging: setAsCurrentPageForDebugging);
            SetNewDependent(fakeTempFile);
            _url = fakeTempFile.Key;
            UpdateDisplay();
        }
Exemplo n.º 52
0
        /// <summary>
        /// Internal for testing because it's not yet clear this is the appropriate public routine.
        /// Probably some API gets a list of BloomInfo objects from the parse.com data, and we pass one of
        /// them as the argument for the public method.
        /// </summary>
        /// <param name="bucket"></param>
        /// <param name="s3BookId"></param>
        /// <param name="dest"></param>
        /// <returns></returns>
        internal string DownloadBook(string bucket, string s3BookId, string dest)
        {
            var destinationPath = _s3Client.DownloadBook(bucket, s3BookId, dest, _progressDialog);
            if (BookDownLoaded != null)
            {
                var bookInfo = new BookInfo(destinationPath, false); // A downloaded book is a template, so never editable.
                BookDownLoaded(this, new BookDownloadedEventArgs() {BookDetails = bookInfo});
            }
            // Books in the library should generally show as locked-down, so new users are automatically in localization mode.
            // Occasionally we may want to upload a new authoring template, that is, a 'book' that is suitableForMakingShells.
            // Such books should not be locked down.
            // So, we try to lock it. What we want to do is Book.RecordedAsLockedDown = true; Book.Save().
            // But all kinds of things have to be set up before we can create a Book. So we duplicate a few bits of code.
            var htmlFile = BookStorage.FindBookHtmlInFolder(destinationPath);
            if (htmlFile == "")
                return destinationPath; //argh! we can't lock it.
            var xmlDomFromHtmlFile = XmlHtmlConverter.GetXmlDomFromHtmlFile(htmlFile, false);
            var dom = new HtmlDom(xmlDomFromHtmlFile);
            if (!BookMetaData.FromString(MetaDataText(destinationPath)).IsSuitableForMakingShells)
            {
                Book.Book.RecordAsLockedDown(dom, true);
                XmlHtmlConverter.SaveDOMAsHtml5(dom.RawDom, htmlFile);
            }

            return destinationPath;
        }
Exemplo n.º 53
0
        public string UploadBook(string bookFolder, IProgress progress, out string parseId, string pdfToInclude = null)
        {
            // Books in the library should generally show as locked-down, so new users are automatically in localization mode.
            // Occasionally we may want to upload a new authoring template, that is, a 'book' that is suitableForMakingShells.
            // Such books must never be locked.
            // So, typically we will try to lock it. What we want to do is Book.RecordedAsLockedDown = true; Book.Save().
            // But all kinds of things have to be set up before we can create a Book. So we duplicate a few bits of code.
            var htmlFile = BookStorage.FindBookHtmlInFolder(bookFolder);
            bool wasLocked = false;
            bool allowLocking = false;
            HtmlDom domForLocking = null;
            var metaDataText = MetaDataText(bookFolder);
            var metadata = BookMetaData.FromString(metaDataText);
            if (!string.IsNullOrEmpty(htmlFile))
            {
                var xmlDomFromHtmlFile = XmlHtmlConverter.GetXmlDomFromHtmlFile(htmlFile, false);
                domForLocking = new HtmlDom(xmlDomFromHtmlFile);
                wasLocked = Book.Book.HtmlHasLockedDownFlag(domForLocking);
                allowLocking = !metadata.IsSuitableForMakingShells;
                if (allowLocking && !wasLocked)
                {
                    Book.Book.RecordAsLockedDown(domForLocking, true);
                    XmlHtmlConverter.SaveDOMAsHtml5(domForLocking.RawDom, htmlFile);
                }
            }
            string s3BookId;
            try
            {
                // In case we somehow have a book with no ID, we must have one to upload it.
                if (string.IsNullOrEmpty(metadata.Id))
                {
                    metadata.Id = Guid.NewGuid().ToString();
                }
                // And similarly it should have SOME title.
                if (string.IsNullOrEmpty(metadata.Title))
                {
                    metadata.Title = Path.GetFileNameWithoutExtension(bookFolder);
                }
                metadata.SetUploader(UserId);
                s3BookId = S3BookId(metadata);
                metadata.DownloadSource = s3BookId;
                // Any updated ID at least needs to become a permanent part of the book.
                // The file uploaded must also contain the correct DownloadSource data, so that it can be used
                // as an 'order' to download the book.
                // It simplifies unit testing if the metadata file is also updated with the uploadedBy value.
                // Not sure if there is any other reason to do it (or not do it).
                // For example, do we want to send/receive who is the latest person to upload?
                metadata.WriteToFolder(bookFolder);
                // The metadata is also a book order...but we need it on the server with the desired file name,
                // because we can't rename on download. The extension must be the one Bloom knows about,
                // and we want the file name to indicate which book, so use the name of the book folder.
                var metadataPath = BookMetaData.MetaDataPath(bookFolder);
                var orderPath = Path.Combine(bookFolder, Path.GetFileName(bookFolder) + BookOrderExtension);
                RobustFile.Copy(metadataPath, orderPath, true);
                parseId = "";
                try
                {
                    _s3Client.UploadBook(s3BookId, bookFolder, progress, pdfToInclude: pdfToInclude);
                    metadata.BaseUrl = _s3Client.BaseUrl;
                    metadata.BookOrder = _s3Client.BookOrderUrlOfRecentUpload;
                    progress.WriteStatus(LocalizationManager.GetString("PublishTab.Upload.UploadingBookMetadata", "Uploading book metadata", "In this step, Bloom is uploading things like title, languages, and topic tags to the BloomLibrary.org database."));
                    // Do this after uploading the books, since the ThumbnailUrl is generated in the course of the upload.
                    var response = _parseClient.SetBookRecord(metadata.WebDataJson);
                    parseId = response.ResponseUri.LocalPath;
                    int index = parseId.LastIndexOf('/');
                    parseId = parseId.Substring(index + 1);
                    if (parseId == "books")
                    {
                        // For NEW books the response URL is useless...need to do a new query to get the ID.
                        var json = _parseClient.GetSingleBookRecord(metadata.Id);
                        parseId = json.objectId.Value;
                    }
                    //   if (!UseSandbox) // don't make it seem like there are more uploads than their really are if this a tester pushing to the sandbox
                    {
                        Analytics.Track("UploadBook-Success", new Dictionary<string, string>() { { "url", metadata.BookOrder }, { "title", metadata.Title } });
                    }
                }
                catch (WebException e)
                {
                    DisplayNetworkUploadProblem(e, progress);
                    if (!UseSandbox) // don't make it seem like there are more upload failures than their really are if this a tester pushing to the sandbox
                        Analytics.Track("UploadBook-Failure", new Dictionary<string, string>() { { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message } });
                    return "";
                }
                catch (AmazonS3Exception e)
                {
                    if (e.Message.Contains("The difference between the request time and the current time is too large"))
                    {
                        progress.WriteError(LocalizationManager.GetString("PublishTab.Upload.TimeProblem",
                            "There was a problem uploading your book. This is probably because your computer is set to use the wrong timezone or your system time is badly wrong. See http://www.di-mgt.com.au/wclock/help/wclo_setsysclock.html for how to fix this."));
                        if (!UseSandbox)
                            Analytics.Track("UploadBook-Failure-SystemTime");
                    }
                    else
                    {
                        DisplayNetworkUploadProblem(e, progress);
                        if (!UseSandbox)
                            // don't make it seem like there are more upload failures than their really are if this a tester pushing to the sandbox
                            Analytics.Track("UploadBook-Failure",
                                new Dictionary<string, string>() { { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message } });
                    }
                    return "";
                }
                catch (AmazonServiceException e)
                {
                    DisplayNetworkUploadProblem(e, progress);
                    if (!UseSandbox) // don't make it seem like there are more upload failures than their really are if this a tester pushing to the sandbox
                        Analytics.Track("UploadBook-Failure", new Dictionary<string, string>() { { "url", metadata.BookOrder }, { "title", metadata.Title }, { "error", e.Message } });
                    return "";
                }
                catch (Exception e)
                {
                    progress.WriteError(LocalizationManager.GetString("PublishTab.Upload.UploadProblemNotice",
                        "There was a problem uploading your book. You may need to restart Bloom or get technical help."));
                    progress.WriteError(e.Message.Replace("{", "{{").Replace("}", "}}"));
                    progress.WriteVerbose(e.StackTrace);
                    if (!UseSandbox) // don't make it seem like there are more upload failures than their really are if this a tester pushing to the sandbox
                        Analytics.Track("UploadBook-Failure", new Dictionary<string, string>() {{"url", metadata.BookOrder}, {"title", metadata.Title}, {"error", e.Message}});
                    return "";
                }
            }
            finally
            {
                if (domForLocking != null && allowLocking && !wasLocked)
                {
                    Book.Book.RecordAsLockedDown(domForLocking, false);
                    XmlHtmlConverter.SaveDOMAsHtml5(domForLocking.RawDom, htmlFile);
                }

            }
            return s3BookId;
        }
Exemplo n.º 54
0
 public static Metadata GetOriginalMetadata(HtmlDom dom, BookData bookData)
 {
     return(CreateMetadata(dom.GetBookSetting("originalCopyright"), dom.GetBookSetting("originalLicenseUrl").GetExactAlternative("*"), dom.GetBookSetting("originalLicenseNotes"), bookData));
 }
Exemplo n.º 55
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());
        }
Exemplo n.º 56
0
 public static void LogMetdata(HtmlDom dom)
 {
     Logger.WriteEvent("LicenseUrl: " + dom.GetBookSetting("licenseUrl"));
     Logger.WriteEvent("LicenseNotes: " + dom.GetBookSetting("licenseNotes"));
     Logger.WriteEvent("");
 }
Exemplo n.º 57
0
 public void GetThumbnailAsync(int width, int height, HtmlDom dom,Action<Image> onReady ,Action<Exception> onError)
 {
     var thumbnailOptions = new HtmlThumbNailer.ThumbnailOptions()
     {
         BackgroundColor = Color.White,
         BorderStyle = HtmlThumbNailer.ThumbnailOptions.BorderStyles.None,
         CenterImageUsingTransparentPadding = false,
         Height = height,
         Width = width
     };
     dom.UseOriginalImages = true; // apparently these thumbnails can be big...anyway we want printable images.
     _thumbNailer.HtmlThumbNailer.GetThumbnailAsync(String.Empty, string.Empty, dom, thumbnailOptions,onReady, onError);
 }
Exemplo n.º 58
0
 private static void EnsureIdsAreUnique(HtmlDom dom, string elementTag, List<string> ids, StringBuilder builder)
 {
     foreach(XmlElement element in dom.SafeSelectNodes("//" + elementTag + "[@id]"))
     {
         var id = element.GetAttribute("id");
         if(ids.Contains(id))
             builder.AppendLine("The id of this " + elementTag + " must be unique, but is not: " + element.OuterXml);
         else
             ids.Add(id);
     }
 }
Exemplo n.º 59
0
        public static void AddStylesheetFromAnotherBook(HtmlDom sourceBookDom, HtmlDom targetBookDom)
        {
            var addedModifiedStyleSheets = new List<string>();
            //This was refactored from book, where there was these notes:
            //     NB: at this point this code can't handle the "userModifiedStyles" from children, it'll ignore them (they would conflict with each other)
            //     NB: at this point custom styles (e.g. larger/smaller font rules) from children will be lost.

            //At this point, this addedModifiedStyleSheets is just used as a place to track which stylesheets we already have
            foreach(string sheetName in sourceBookDom.GetTemplateStyleSheets())
            {
                if(!addedModifiedStyleSheets.Contains(sheetName))
                    //nb: if two books have stylesheets with the same name, we'll only be grabbing the 1st one.
                {
                    addedModifiedStyleSheets.Add(sheetName);
                    targetBookDom.AddStyleSheetIfMissing(sheetName);
                }
            }
        }
Exemplo n.º 60
0
 public void UpdatePageToTemplate(HtmlDom pageDom, XmlElement templatePageDiv, string pageId)
 {
     var pageDiv = pageDom.SafeSelectNodes("//body/div[@id='" + pageId + "']").Cast<XmlElement>().FirstOrDefault();
     if(pageDiv != null)
     {
         var idAttr = templatePageDiv.Attributes["id"];
         var templateId = idAttr == null ? "" : idAttr.Value;
         var oldLineage = MigrateEditableData(pageDiv, templatePageDiv, templateId);
         var props = new Dictionary<string, string>();
         props["newLayout"] = templateId;
         props["oldLineage"] = oldLineage;
         Analytics.Track("Change Page Layout", props);
     }
 }