SelectSingleNode() public method

public SelectSingleNode ( string xpath ) : XmlElement
xpath string
return System.Xml.XmlElement
Ejemplo n.º 1
0
        /// <summary>
        /// Some book layouts rely on the first page facing the second page. A Wall Calendar is one example.
        /// Here we check if the first content page has this requirement and, if so, we insert a blank "flyleaf"
        /// page.
        /// </summary>
        private void InjectFlyleafIfNeeded(Layout layout)
        {
            // the "inside" here means "not counting the cover"
            var numberOfFrontMatterPagesInside   = XMatterDom.SafeSelectNodes("//div[contains(@class,'bloom-frontMatter')]").Count - 1;
            var firstPageWouldNotBePartOfASpread = numberOfFrontMatterPagesInside % 2 != 0;

            if (firstPageWouldNotBePartOfASpread)
            {
                var lastFrontMatterPage = _bookDom.SelectSingleNode("//div[contains(@class,'bloom-frontMatter')][last()]");

                var firstContentPageAndAlsoStartsSpread = _bookDom.SelectSingleNode(
                    "//div[contains(@class,'bloom-frontMatter')][last()]"                     // last frontmatter page
                    + "/following-sibling::div[contains(@data-page, 'spread-start')]");
                // page after that says it needs to be facing the next page
                if (firstContentPageAndAlsoStartsSpread != null)
                {
                    var flyDom = new XmlDocument();
                    flyDom.LoadXml(@"
						<div class='bloom-flyleaf bloom-frontMatter bloom-page' data-page='required singleton'>
							<div class='pageLabel'>Flyleaf</div>
							<div style='height: 100px; width:100%'
								data-hint='This page was automatically inserted because the following page is marked as part of a two page spread.'>
							</div>
						</div>"                        );
                    var flyleaf = _bookDom.RawDom.ImportNode(flyDom.FirstChild, true) as XmlElement;
                    flyleaf.SetAttribute("id", Guid.NewGuid().ToString());
                    lastFrontMatterPage.ParentNode.InsertAfter(flyleaf, lastFrontMatterPage);
                    SizeAndOrientation.UpdatePageSizeAndOrientationClasses(flyleaf, layout);
                }
            }
        }
Ejemplo n.º 2
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
            };

            foreach (var part in firstPage.GetStringAttribute("class").SplitTrimmed(' '))
            {
                if (part.ToLowerInvariant().Contains("portrait") || part.ToLowerInvariant().Contains("landscape"))
                {
                    layout.SizeAndOrientation = SizeAndOrientation.FromString(part);
                }
                if (part.ToLowerInvariant().Contains("layout-style-"))
                {
                    int startIndex = "layout-style-".Length;
                    layout.Style = part.Substring(startIndex, part.Length - startIndex);                        //reivew: this might let us suck up a style that is no longer listed in any css
                }
            }
            return(layout);
        }
Ejemplo n.º 3
0
        // This overload is much more convenient for testing.
        public string CheckBook(HtmlDom dom, string[] languages, Book book = null)
        {
            var meta = dom.SelectSingleNode("//meta[@name='bloom-licensed-content-id' and @content]");

            if (meta == null)
            {
                return(null);
            }
            bool didCheck;
            var  problems = GetProblemLanguages(languages, meta.GetStringAttribute("content"), out didCheck);

            if (problems.Count() == 0)
            {
                return(null);
            }
            if (!didCheck)
            {
                return(LocalizationManager.GetString("PublishTab.Android.CantGetLicenseInfo",
                                                     kCannotReachLicenseServerMessage));
            }
            var template = LocalizationManager.GetString("PublishTab.Android.UnlicensedLanguages",
                                                         kUnlicenseLanguageMessage,
                                                         "{0} will be a language name or a list of them");
            // In real life we always have a book and can get nicer names. I put the fallback in for testing.
            var langs = string.Join(CultureInfo.CurrentCulture.TextInfo.ListSeparator + " ",
                                    problems.Select(x => book == null
                        ? WritingSystem.LookupIsoCode.GetLocalizedLanguageName(x, "")
                        : book.PrettyPrintLanguage(x)));

            return(string.Format(template, langs));
        }
Ejemplo n.º 4
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));
        }
Ejemplo n.º 5
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.
                }
            }
        }
        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.
                }
            }
        }
Ejemplo n.º 7
0
        public void Save_UpdatesMetadataTitle()
        {
            _bookDom = new HtmlDom(
                @"<html>
                <head>
                    <meta content='text/html; charset=utf-8' http-equiv='content-type' />
                   <title>Test Shell</title>
                    <link rel='stylesheet' href='Basic Book.css' type='text/css' />
                    <link rel='stylesheet' href='../../previewMode.css' type='text/css' />;
                </head>
                <body>
                    <div class='bloom-page'>
                        <div class='bloom-page' id='guid3'>
                            <textarea lang='en' data-book='bookTitle'>original</textarea>
                        </div>
                    </div>
                </body></html>");

            var book = CreateBook();

            var titleElt = _bookDom.SelectSingleNode("//textarea");
            titleElt.InnerText = "changed & <mangled>";
            book.Save();
            Assert.That(_metadata.Title, Is.EqualTo("changed & <mangled>"));
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Return the top-level document that should be displayed in the browser for the current page.
        /// </summary>
        /// <returns></returns>
        public HtmlDom GetXmlDocumentForEditScreenWebPage()
        {
            var path = FileLocator.GetFileDistributedWithApplication(Path.Combine(BloomFileLocator.BrowserRoot, "bookEdit", "EditViewFrame.html"));
            // {simulatedPageFileInBookFolder} is placed in the template file where we want the source file for the 'page' iframe.
            // We don't really make a file for the page, the contents are just saved in our local server.
            // But we give it a url that makes it seem to be in the book folder so local urls work.
            // See EnhancedImageServer.MakeSimulatedPageFileInBookFolder() for more details.
            var frameText = RobustFile.ReadAllText(path, Encoding.UTF8).Replace("{simulatedPageFileInBookFolder}", _currentPage.Key);
            var dom = new HtmlDom(XmlHtmlConverter.GetXmlDomFromHtml(frameText));

            if (_currentlyDisplayedBook.BookInfo.ToolboxIsOpen)
            {
                // Make the toolbox initially visible.
                // What we have to do to accomplish this is pretty non-intutive. It's a consequence of the way
                // the pure-drawer CSS achieves the open/close effect. This input is a check-box, so clicking it
                // changes the state of things in a way that all the other CSS can depend on.
                var toolboxCheckBox = dom.SelectSingleNode("//input[@id='pure-toggle-right']");
                if (toolboxCheckBox != null)
                    toolboxCheckBox.SetAttribute("checked", "true");
            }

            return dom;
        }
Ejemplo n.º 9
0
        public void Save_UpdatesMetadataCreditsRemovingP()
        {
            _bookDom = new HtmlDom(
                @"<html>
                <head>
                    <meta content='text/html; charset=utf-8' http-equiv='content-type' />
                   <title>Test Shell</title>
                    <link rel='stylesheet' href='Basic Book.css' type='text/css' />
                    <link rel='stylesheet' href='../../previewMode.css' type='text/css' />;
                </head>
                <body>
                    <div class='bloom-page'>
                        <div class='bloom-page' id='guid3'>
                            <textarea lang='en' data-book='originalAcknowledgments'><p>original</p></textarea>
                        </div>
                    </div>
                </body></html>");

            var book = CreateBook();

            var acksElt = _bookDom.SelectSingleNode("//textarea");
            #if __MonoCS__	// may not be needed for Mono 4.x
            acksElt.OwnerDocument.PreserveWhitespace = true;	// Does not preserve newlines on Linux without this
            #endif
            acksElt.InnerXml = "<p>changed</p>" + Environment.NewLine + "<p>more changes</p>";
            book.Save();
            Assert.That(_metadata.Credits, Is.EqualTo("changed" + Environment.NewLine + "more changes"));
        }
Ejemplo n.º 10
0
        public void Save_UpdatesMetadataCreditsRemovingBreaks()
        {
            _bookDom = new HtmlDom(
                @"<html>
                <head>
                    <meta content='text/html; charset=utf-8' http-equiv='content-type' />
                   <title>Test Shell</title>
                    <link rel='stylesheet' href='Basic Book.css' type='text/css' />
                    <link rel='stylesheet' href='../../previewMode.css' type='text/css' />;
                </head>
                <body>
                    <div class='bloom-page'>
                        <div class='bloom-page' id='guid3'>
                            <textarea lang='en' data-book='originalAcknowledgments'>original</textarea>
                        </div>
                    </div>
                </body></html>");

            var book = CreateBook();

            var acksElt = _bookDom.SelectSingleNode("//textarea");
            acksElt.InnerXml = "changed" + Environment.NewLine + "<br />more changes";
            book.Save();
            Assert.That(_metadata.Credits, Is.EqualTo("changed" + Environment.NewLine + "more changes"));
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Earlier, we handed out a single-page version of the document. Now it has been edited,
        /// so we now we need to fold changes back in
        /// </summary>
        public void SavePage(HtmlDom editedPageDom)
        {
            Debug.Assert(IsEditable);
            try
            {
                // This is needed if the user did some ChangeLayout (origami) manipulation. This will populate new
                // translationGroups with .bloom-editables and set the proper classes on those editables to match the current multilingual settings.
                UpdateEditableAreasOfElement(editedPageDom);

                //replace the corresponding page contents in our DOM with what is in this PageDom
                XmlElement pageFromEditedDom = editedPageDom.SelectSingleNodeHonoringDefaultNS("//div[contains(@class, 'bloom-page')]");
                string pageId = pageFromEditedDom.GetAttribute("id");
                var pageFromStorage = GetPageFromStorage(pageId);

                HtmlDom.ProcessPageAfterEditing(pageFromStorage, pageFromEditedDom);

                _bookData.SuckInDataFromEditedDom(editedPageDom); //this will do an updatetitle
                // When the user edits the styles on a page, the new or modified rules show up in a <style/> element with title "userModifiedStyles". Here we copy that over to the book DOM.
                var userModifiedStyles = editedPageDom.SelectSingleNode("html/head/style[@title='userModifiedStyles']");
                if (userModifiedStyles != null)
                {
                    GetOrCreateUserModifiedStyleElementFromStorage().InnerXml = userModifiedStyles.InnerXml;
                    //Debug.WriteLine("Incoming User Modified Styles:   " + userModifiedStyles.OuterXml);
                }
                Save();

                _storage.UpdateBookFileAndFolderName(_collectionSettings);
                //review used to have   UpdateBookFolderAndFileNames(data);

                //Enhance: if this is only used to re-show the thumbnail, why not limit it to if this is the cover page?
                //e.g., look for the class "cover"
                InvokeContentsChanged(null); //enhance: above we could detect if anything actually changed
            }
            catch (Exception error)
            {
                var msg = LocalizationManager.GetString("Errors.CouldNotSavePage",
                    "Bloom had trouble saving a page. Please click Details below and report this to us. Then quit Bloom, run it again, and check to see if the page you just edited is missing anything. Sorry!");
                ErrorReport.NotifyUserOfProblem(error, msg);
            }
        }
Ejemplo n.º 12
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};

            foreach (var part in firstPage.GetStringAttribute("class").SplitTrimmed(' '))
            {
                if (part.ToLower().Contains("portrait") || part.ToLower().Contains("landscape"))
                {
                    layout.SizeAndOrientation = SizeAndOrientation.FromString(part);
                }
                if (part.ToLower().Contains("layout-style-"))
                {
                    int startIndex = "layout-style-".Length;
                    layout.Style = part.Substring(startIndex, part.Length-startIndex);	//reivew: this might let us suck up a style that is no longer listed in any css
                }
            }
            return layout;
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Earlier, we handed out a single-page version of the document. Now it has been edited,
        /// so we now we need to fold changes back in
        /// </summary>
        public void SavePage(HtmlDom editedPageDom)
        {
            Debug.Assert(IsEditable);
            try
            {
                //replace the corresponding page contents in our DOM with what is in this PageDom
                XmlElement divElement = editedPageDom.SelectSingleNodeHonoringDefaultNS("//div[contains(@class, 'bloom-page')]");
                string pageDivId = divElement.GetAttribute("id");
                var page = GetPageFromStorage(pageDivId);
                /*
                 * there are too many non-semantic variations that are introduced by various processes (e.g. self closing of empy divs, handling of non-ascii)
                 * var selfClosingVersion = divElement.InnerXml.Replace("\"></div>", "\"/>");
                    if (page.InnerXml == selfClosingVersion)
                    {
                        return;
                    }
                 */

                page.InnerXml = divElement.InnerXml;

                 _bookData.SuckInDataFromEditedDom(editedPageDom);//this will do an updatetitle
                // When the user edits the styles on a page, the new or modified rules show up in a <style/> element with id "customStyles". Here we copy that over to the book DOM.
                var customStyles = editedPageDom.SelectSingleNode("html/head/style[@id='customStyles']");
                if (customStyles != null)
                {
                    GetOrCreateCustomStyleElementFromStorage().InnerXml = customStyles.InnerXml;
                    Debug.WriteLine("Incoming CustomStyles:   " + customStyles.OuterXml);
                }
                //Debug.WriteLine("CustomBookStyles:   " + GetOrCreateCustomStyleElementFromStorage().OuterXml);
                try
                {
                    _storage.Save();
                }
                catch (Exception error)
                {
                    ErrorReport.NotifyUserOfProblem(error, "There was a problem saving");
                }

                _storage.UpdateBookFileAndFolderName(_collectionSettings);
                //review used to have   UpdateBookFolderAndFileNames(data);

                //Enhance: if this is only used to re-show the thumbnail, why not limit it to if this is the cover page?
                //e.g., look for the class "cover"
                InvokeContentsChanged(null); //enhance: above we could detect if anything actually changed
            }
            catch (Exception error)
            {
                Palaso.Reporting.ErrorReport.NotifyUserOfProblem(error, "Bloom had trouble saving a page. Please click Details below and report this to us. Then quit Bloom, run it again, and check to see if the page you just edited is missing anything. Sorry!");
            }
        }
        private void ThumbnailReady(string exportFolder, HtmlDom dom, Image image)
        {
            string term;
            string week;
            try
            {
                term = dom.SelectSingleNode("//div[contains(@data-book,'term')]").InnerText.Trim();
                week = dom.SelectSingleNode("//div[contains(@data-book,'week')]").InnerText.Trim();
            }
            catch (Exception e)
            {
                Debug.Fail("Book missing either term or week variable");
                throw new ApplicationException("This page is lacking either a term or week data-book variable.");
            }
            //the selector for day one is different because it doesn't have @data-* attribute
            XmlElement dayNode = dom.SelectSingleNode("//div[contains(@class,'DayStyle')]");
            string page="?";
            // many pupil books don't have a specific day per page

            if (dom.SelectSingleNode("//div[contains(@class,'day5Left')]") != null) // in P2, we have 2 pages for day 5, so we can't use the 'DayStyle' to differentiate them
            {
                page = "5";
            }
            else if (dom.SelectSingleNode("//div[contains(@class,'day5Right')]") != null)
            {
                page = "6";
            }
            else if (dayNode != null)
            {
                page = dayNode.InnerText.Trim();
            }
            else
            {
                if (dom.SelectSingleNode("//div[contains(@class,'page1') or contains(@class,'storyPageLeft')]") != null)
                {
                    page = "1";
                }
                else if (dom.SelectSingleNode("//div[contains(@class,'page2') or contains(@class,'storyPageRight')]") != null)
                {
                    page = "2";
                }
                else if (dom.SelectSingleNode("//div[contains(@class,'page3') or contains(@class,'thirdPage')]") != null)
                {
                    page = "3";
                }
                else if (dom.SelectSingleNode("//div[contains(@class,'page4') or contains(@class,'fourthPage')]") != null)
                {
                    page = "4";
                }
                else
                {
                    Debug.Fail("Couldn't figure out what page this is.");
                }
            }
            var fileName = Language1Iso639Code + "-t" + term + "-w" + week + "-p" + page + ".png";
            //just doing image.Save() works for .bmp and .jpg, but not .png
            using (var b = new Bitmap(image))
            {
                SIL.IO.RobustIO.SaveImage(b, Path.Combine(exportFolder, fileName));
            }
        }
 private void ThumbnailReady(string exportFolder, HtmlDom dom, Image image)
 {
     var term = dom.SelectSingleNode("//div[contains(@data-book,'term')]").InnerText.Trim();
     var week = dom.SelectSingleNode("//div[contains(@data-book,'week')]").InnerText.Trim();
     //the selector for day one is different because it doesn't have @data-* attribute
     var day = dom.SelectSingleNode("//div[contains(@class,'DayStyle')]").InnerText.Trim();
     var fileName = Language1Iso639Code + "-t" + term + "-w" + week + "-d" + day + ".png";
     //just doing image.Save() works for .bmp and .jpg, but not .png
     using (var b = new Bitmap(image))
     {
         b.Save(Path.Combine(exportFolder, fileName));
     }
 }
Ejemplo n.º 16
0
        public void Save_UpdatesMetadataIsbnAndPageCount()
        {
            _bookDom = new HtmlDom(
                @"<html>
                <head>
                    <meta content='text/html; charset=utf-8' http-equiv='content-type' />
                   <title>Test Shell</title>
                    <link rel='stylesheet' href='Basic Book.css' type='text/css' />
                    <link rel='stylesheet' href='../../previewMode.css' type='text/css' />;
                </head>
                <body>
                    <div class='bloom-page' id='guid3'>
                        <textarea lang='en' data-book='ISBN'>original</textarea>
                    </div>
                </body></html>");

            var book = CreateBook();

            var isbnElt = _bookDom.SelectSingleNode("//textarea");
            isbnElt.InnerText = "978-0-306-40615-7";
            book.Save();
            Assert.That(book.BookInfo.Isbn, Is.EqualTo("978-0-306-40615-7"));

            var dom = book.GetEditableHtmlDomForPage(book.GetPages().First());
            isbnElt = dom.SelectSingleNode("//textarea");
            isbnElt.InnerText = " ";
            book.SavePage(dom);
            book.Save();
            Assert.That(_metadata.Isbn, Is.EqualTo(""));
        }
Ejemplo n.º 17
0
        public void Save_UpdatesMetadataIsbn()
        {
            _bookDom = new HtmlDom(
                @"<html>
                <head>
                    <meta content='text/html; charset=utf-8' http-equiv='content-type' />
                   <title>Test Shell</title>
                    <link rel='stylesheet' href='Basic Book.css' type='text/css' />
                    <link rel='stylesheet' href='../../previewMode.css' type='text/css' />;
                </head>
                <body>
                    <div class='bloom-page'>
                        <div class='bloom-page' id='guid3'>
                            <textarea lang='en' data-book='ISBN'>original</textarea>
                        </div>
                    </div>
                </body></html>");

            var book = CreateBook();

            var isbnElt = _bookDom.SelectSingleNode("//textarea");
            isbnElt.InnerText = "978-0-306-40615-7";
            book.Save();
            Assert.That(book.BookInfo.Isbn, Is.EqualTo("978-0-306-40615-7"));

            // todo: reinstate this when this bug is fixed: https://trello.com/c/CaUlk8kN/546-clearing-isbn-does-not-clear-data-div.
            //isbnElt.InnerText = " ";
            //book.Save();
            //Assert.That(_metadata.volumeInfo.industryIdentifiers.Length, Is.EqualTo(0));
        }