public void CanRetrieveContentOfFakeTempFile_ButOnlyUntilDisposed() { using (var server = CreateImageServer()) { var html = @"<html ><head></head><body>here it is</body></html>"; var dom = new HtmlDom(html); dom.BaseForRelativePaths = _folder.Path.ToLocalhost(); string url; using (var fakeTempFile = EnhancedImageServer.MakeSimulatedPageFileInBookFolder(dom)) { url = fakeTempFile.Key; var transaction = new PretendRequestInfo(url); // Execute server.MakeReply(transaction); // Verify // Whitespace inserted by CreateHtml5StringFromXml seems to vary across versions and platforms. // I would rather verify the actual output, but don't want this test to be fragile, and the // main point is that we get a file with the DOM content. Assert.That(transaction.ReplyContents, Is.EqualTo(TempFileUtils.CreateHtml5StringFromXml(dom.RawDom))); } var transactionFail = new PretendRequestInfo(url); // Execute server.MakeReply(transactionFail); // Verify Assert.That(transactionFail.StatusCode, Is.EqualTo(404)); } }
public static string MakeToolboxContent(Book.Book book) { var path = BloomFileLocator.GetBrowserFile("bookEdit/toolbox", "toolbox.html"); var toolboxFolder = Path.GetDirectoryName(path); var domForToolbox = new HtmlDom(XmlHtmlConverter.GetXmlDomFromHtmlFile(path)); var toolsToDisplay = GetToolsToDisplay(book, IdsOfToolsThisVersionKnowsAbout); // get additional tools to load var checkedBoxes = new List <string>(); foreach (var tool in toolsToDisplay) { LoadPanelIntoToolbox(domForToolbox, tool, checkedBoxes, toolboxFolder); } // Load settings into the toolbox panel AppendToolboxPanel(domForToolbox, BloomFileLocator.GetFileDistributedWithApplication(Path.Combine(toolboxFolder, "settings", "Settings.html"))); // check the appropriate boxes foreach (var checkBoxId in checkedBoxes) { // Review: really? we have to use a special character to make a check? can't we add "checked" attribute? var node = domForToolbox.Body.SelectSingleNode("//div[@id='" + checkBoxId + "']"); if (node != null) { node.InnerXml = "✔"; } } XmlHtmlConverter.MakeXmlishTagsSafeForInterpretationAsHtml(domForToolbox.RawDom); return(TempFileUtils.CreateHtml5StringFromXml(domForToolbox.RawDom)); }
public static string MakeToolboxContent(Book.Book book) { var path = BloomFileLocator.GetBrowserFile(false, "bookEdit/toolbox", "toolbox.html"); var domForToolbox = new HtmlDom(XmlHtmlConverter.GetXmlDomFromHtmlFile(path)); XmlHtmlConverter.MakeXmlishTagsSafeForInterpretationAsHtml(domForToolbox.RawDom); return(TempFileUtils.CreateHtml5StringFromXml(domForToolbox.RawDom)); }
public void SetupServerWithCurrentPageIframeContents() { _domForCurrentPage = _bookSelection.CurrentSelection.GetEditableHtmlDomForPage(_pageSelection.CurrentSelection); XmlHtmlConverter.MakeXmlishTagsSafeForInterpretationAsHtml(_domForCurrentPage.RawDom); _server.CurrentPageContent = TempFileUtils.CreateHtml5StringFromXml(_domForCurrentPage.RawDom); _server.AccordionContent = MakeAccordionContent(); _server.CurrentBook = _currentlyDisplayedBook; }
public void CanRetrieveContentOfFakeTempFile_WhenFolderContainsAmpersand_NotViaJavaScript() { var dom = SetupDomWithAmpersandInTitle(); var transaction = CreateServerMakeSimPageMakeReply(dom); // Verify // Whitespace inserted by CreateHtml5StringFromXml seems to vary across versions and platforms. // I would rather verify the actual output, but don't want this test to be fragile, and the // main point is that we get a file with the DOM content. Assert.That(transaction.ReplyContents, Is.EqualTo(TempFileUtils.CreateHtml5StringFromXml(dom.RawDom))); }
/// <summary> /// This code sets things up so that we can edit (or make a thumbnail of, etc.) one page of a book. /// This is tricky because we have to satisfy several constraints: /// - We need to make this page content the 'src' of an iframe in a browser. So it has to be /// locatable by url. /// - It needs to appear to the browser to be a document in the book's folder. This allows local /// hrefs (e.g., src of images) that are normally relative to the whole-book file to locate /// the images. (We previously did this by making a file elsewhere and setting the 'base' /// for interpreting urls. But this fails for internal hrefs (starting with #)). /// - We don't want to risk leaving junk page files in the real book folder if anything goes wrong. /// - There may be several of these simulated pages around at the same time (e.g., when the thumbnailer is /// working on several threads). /// - The simulated files need to hang around for an unpredictable time (until the browser is done /// with them). /// The solution we have adopted is to make this server simulate files in the book folder. /// That is, the src for the page iframe is set to a localhost: url which maps to a file in the /// book folder. This means that any local hrefs (e.g., to images) will become server requests /// for the right file in the right folder. However, the page file never exists as a real file /// system file; instead, a request for the page file itself will be intercepted, and this server /// simply returns the content it has remembered. /// To manage the lifetime of the page data, we use a SimulatedPageFile object, which the Browser /// disposes of when it is no longer looking at that URL. Its dispose method tells this class /// to discard the simulated page data. /// To handle the need for multiple simulated page files and quickly check whether a particular /// url is one of them, we have a dictionary in which the urls are keys. /// A marker is inserted into the generated urls if the input HtmlDom wants to use original images. /// </summary> /// <param name="dom"></param> /// <param name="isCurrentPageContent">If this is true, the url will be inserted by JavaScript into /// a src attr for an IFrame. We need to account for this because un-escaped quotation marks in the /// URL can cause errors in JavaScript strings. Also, we want to use the same name each time /// for current page content, so Open Page in Browser works even after changing pages.</param> /// <returns></returns> public static SimulatedPageFile MakeSimulatedPageFileInBookFolder(HtmlDom dom, bool isCurrentPageContent = false, bool setAsCurrentPageForDebugging = false, string source = "") { var simulatedPageFileName = Path.ChangeExtension((isCurrentPageContent ? "currentPage" : Guid.NewGuid().ToString()) + SimulatedFileUrlMarker + source, ".html"); var pathToSimulatedPageFile = simulatedPageFileName; // a default, if there is no special folder if (dom.BaseForRelativePaths != null) { pathToSimulatedPageFile = Path.Combine(dom.BaseForRelativePaths, simulatedPageFileName).Replace('\\', '/'); } if (File.Exists(pathToSimulatedPageFile)) { // Just in case someone perversely calls a book "currentPage" we will use another name. // (We want one that does NOT conflict with anything really in the folder.) // We only allow one HTML file per folder so we shouldn't need multiple attempts. pathToSimulatedPageFile = Path.Combine(dom.BaseForRelativePaths, "X" + simulatedPageFileName).Replace('\\', '/'); } // FromLocalHost is smart about doing nothing if it is not a localhost url. In case it is, we // want the OriginalImageMarker (if any) after the localhost stuff. pathToSimulatedPageFile = pathToSimulatedPageFile.FromLocalhost(); if (dom.UseOriginalImages) { pathToSimulatedPageFile = OriginalImageMarker + "/" + pathToSimulatedPageFile; } var url = pathToSimulatedPageFile.ToLocalhost(); var key = pathToSimulatedPageFile.Replace('\\', '/'); if (isCurrentPageContent) { // We need to UrlEncode the single and double quote characters, and the space character, // so they will play nicely with HTML. var urlPath = UrlPathString.CreateFromUnencodedString(url); url = urlPath.UrlEncodedForHttpPath; } if (setAsCurrentPageForDebugging) { _keyToCurrentPage = key; } var html5String = TempFileUtils.CreateHtml5StringFromXml(dom.RawDom); lock (_urlToSimulatedPageContent) { _urlToSimulatedPageContent[key] = html5String; } return(new SimulatedPageFile() { Key = url }); }
/// <summary> /// This code sets things up so that we can edit (or make a thumbnail of, etc.) one page of a book. /// This is tricky because we have to satisfy several constraints: /// - We need to make this page content the 'src' of an iframe in a browser. So it has to be /// locatable by url. /// - It needs to appear to the browser to be a document in the book's folder. This allows local /// hrefs (e.g., src of images) that are normally relative to the whole-book file to locate /// the images. (We previously did this by making a file elsewhere and setting the 'base' /// for interpreting urls. But this fails for internal hrefs (starting with #)). /// - We don't want to risk leaving junk page files in the real book folder if anything goes wrong. /// - There may be several of these simulated pages around at the same time (e.g., when the thumbnailer is /// working on several threads). /// - The simulated files need to hang around for an unpredictable time (until the browser is done /// with them). /// The solution we have adopted is to make this server simulate files in the book folder. /// That is, the src for the page iframe is set to a localhost: url which maps to a file in the /// book folder. This means that any local hrefs (e.g., to images) will become server requests /// for the right file in the right folder. However, the page file never exists as a real file /// system file; instead, a request for the page file itself will be intercepted, and this server /// simply returns the content it has remembered. /// To manage the lifetime of the page data, we use a SimulatedPageFile object, which the Browser /// disposes of when it is no longer looking at that URL. Its dispose method tells this class /// to discard the simulated page data. /// To handle the need for multiple simulated page files and quickly check whether a particular /// url is one of them, we have a dictionary in which the urls are keys. /// A marker is inserted into the generated urls if the input HtmlDom wants to use original images. /// </summary> /// <param name="dom"></param> /// <param name="escapeUrlAsNeededForSrcAttribute">If this is true, the url will be inserted by JavaScript into /// a src attr for an IFrame. We need to account for this because un-escaped quotation marks in the /// URL can cause errors in JavaScript strings.</param> /// <returns></returns> public static SimulatedPageFile MakeSimulatedPageFileInBookFolder(HtmlDom dom, bool escapeUrlAsNeededForSrcAttribute = false, bool setAsCurrentPageForDebugging = false) { var simulatedPageFileName = Path.ChangeExtension(Guid.NewGuid().ToString(), ".html"); var pathToSimulatedPageFile = simulatedPageFileName; // a default, if there is no special folder if (dom.BaseForRelativePaths != null) { pathToSimulatedPageFile = Path.Combine(dom.BaseForRelativePaths, simulatedPageFileName).Replace('\\', '/'); } // FromLocalHost is smart about doing nothing if it is not a localhost url. In case it is, we // want the OriginalImageMarker (if any) after the localhost stuff. pathToSimulatedPageFile = pathToSimulatedPageFile.FromLocalhost(); if (dom.UseOriginalImages) { pathToSimulatedPageFile = OriginalImageMarker + "/" + pathToSimulatedPageFile; } var url = pathToSimulatedPageFile.ToLocalhost(); var key = pathToSimulatedPageFile.Replace('\\', '/'); if (escapeUrlAsNeededForSrcAttribute) { // We need to UrlEncode the single and double quote characters so they will play nicely with JavaScript. url = EscapeUrlQuotes(url); // When JavaScript inserts our path into the html it replaces the three magic html characters with these substitutes. // We need to modify our key so that when the JavaScript comes looking for the page its modified url will // generate the right key. key = SimulateJavaScriptHandlingOfHtml(key); } if (setAsCurrentPageForDebugging) { _keyToCurrentPage = key; } var html5String = TempFileUtils.CreateHtml5StringFromXml(dom.RawDom); lock (_urlToSimulatedPageContent) { _urlToSimulatedPageContent[key] = html5String; } return(new SimulatedPageFile() { Key = url }); }
private string MakeAccordionContent() { var path = FileLocator.GetFileDistributedWithApplication("BloomBrowserUI/bookEdit/accordion", "Accordion.htm"); _accordionFolder = Path.GetDirectoryName(path); var domForAccordion = new HtmlDom(XmlHtmlConverter.GetXmlDomFromHtmlFile(path)); // embed settings on the page var tools = _currentlyDisplayedBook.BookInfo.Tools.Where(t => t.Enabled == true).ToList(); var settings = new Dictionary <string, object> { { "current", AccordionToolNameToDirectoryName(_currentlyDisplayedBook.BookInfo.CurrentTool) } }; var decodableTool = tools.FirstOrDefault(t => t.Name == "decodableReader"); if (decodableTool != null && !string.IsNullOrEmpty(decodableTool.State)) { settings.Add("decodableState", decodableTool.State); } var leveledTool = tools.FirstOrDefault(t => t.Name == "leveledReader"); if (leveledTool != null && !string.IsNullOrEmpty(leveledTool.State)) { settings.Add("leveledState", leveledTool.State); } var settingsStr = JsonConvert.SerializeObject(settings); settingsStr = String.Format("function GetAccordionSettings() {{ return {0};}}", settingsStr) + "\n$(document).ready(function() { restoreAccordionSettings(GetAccordionSettings()); });"; var scriptElement = domForAccordion.RawDom.CreateElement("script"); scriptElement.SetAttribute("type", "text/javascript"); scriptElement.SetAttribute("id", "ui-accordionSettings"); scriptElement.InnerText = settingsStr; domForAccordion.Head.InsertAfter(scriptElement, domForAccordion.Head.LastChild); // get additional tabs to load var checkedBoxes = new List <string>(); if (tools.Any(t => t.Name == "decodableReader")) { AppendAccordionPanel(domForAccordion, FileLocator.GetFileDistributedWithApplication(Path.Combine(_accordionFolder, "DecodableRT", "DecodableRT.htm"))); checkedBoxes.Add("showDRT"); } if (tools.Any(t => t.Name == "leveledReader")) { AppendAccordionPanel(domForAccordion, FileLocator.GetFileDistributedWithApplication(Path.Combine(_accordionFolder, "LeveledRT", "LeveledRT.htm"))); checkedBoxes.Add("showLRT"); } // Load settings into the accordion panel AppendAccordionPanel(domForAccordion, FileLocator.GetFileDistributedWithApplication(Path.Combine(_accordionFolder, "settings", "Settings.htm"))); // check the appropriate boxes foreach (var checkBoxId in checkedBoxes) { domForAccordion.Body.SelectSingleNode("//div[@id='" + checkBoxId + "']").InnerXml = "✔"; } XmlHtmlConverter.MakeXmlishTagsSafeForInterpretationAsHtml(domForAccordion.RawDom); return(TempFileUtils.CreateHtml5StringFromXml(domForAccordion.RawDom)); }