public List<INCoverNode> ParseXmlDocument(XmlDocument results, Regex partOfPathToKeep) { foreach (var node in results.SafeSelectNodes("//doc").Where(node => !XmlNodeHelper.GetBoolAttribute("excluded", node))) { documents[XmlNodeHelper.GetIntAttribute("id", node)] = XmlNodeHelper.GetStringAttribute("url", node); } return (results.SafeSelectNodes("//seqpnt").Select(node => ParseNode(node, partOfPathToKeep))).ToList(); }
public List<INCoverNode> ParseXmlDocument( XmlDocument results, Regex partOfPathToKeep) { foreach (var node in results.SafeSelectNodes("//File")) { _documents[XmlNodeHelper.GetIntAttribute("uid", node)] = XmlNodeHelper.GetStringAttribute("fullPath", node); } return (results.SafeSelectNodes("//SequencePoint") .Select(node => ParseNode(node, partOfPathToKeep))).ToList(); }
public void PrepareElementsOnPage_FromShell_MakesVernacularAndNationalEmpty() { var contents = @"<div class='bloom-page numberedPage A5Portrait bloom-monolingual' id='f4a22289-1755-4b79-afc1-5d20eaa892fe'> <div class='marginBox'> <div class='bloom-imageContainer'> <img alt='' src='test.png' height='20' width='20'/> </div> <div class='bloom-translationGroup normal-style'> <div style='' class='bloom-editable' contenteditable='true' lang='en'>The Mother said, Nurse! The Nurse answered.</div> <div style='' class='bloom-editable' contenteditable='true' lang='tpi'>Mama i tok: Sista. Sista i tok: Bai mi stori long yu.</div> </div> </div></div>"; var dom = new XmlDocument(); dom.LoadXml(contents); TranslationGroupManager.PrepareElementsInPageOrDocument((XmlElement)dom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")[0], _collectionSettings.Object); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div/div[contains(@class, 'bloom-editable') and @contenteditable='true' ]", 5); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='xyz']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='es']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='en' and contains(., 'The Mother')]", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='tpi' and contains(., 'Mama i tok')]", 1); AssertThatXmlIn.Dom(dom).HasNoMatchForXpath("//div[@lang='xyz' and contains(., 'The Mother')]"); AssertThatXmlIn.Dom(dom).HasNoMatchForXpath("//div[@lang='xyz' and contains(., 'Mama i tok')]"); AssertThatXmlIn.Dom(dom).HasNoMatchForXpath("//div[@lang='fr' and contains(., 'The Mother')]"); AssertThatXmlIn.Dom(dom).HasNoMatchForXpath("//div[@lang='fr' and contains(., 'Mama i tok')]"); }
public void DeleteNodes() { var dom = new XmlDocument(); dom.LoadXml(@"<?xml version='1.0' encoding='utf-8'?> <html> <body> <div class='A'><textarea>A1</textarea></div> <div class='B'><textarea>B</textarea></div> <div class='A'><textarea>A2</textarea></div> </body> </html>"); dom.DeleteNodes("descendant-or-self::*[contains(@class, 'A')]"); Assert.AreEqual(1, dom.SafeSelectNodes("html/body/div").Count); Assert.AreEqual("<textarea>B</textarea>", dom.SafeSelectNodes("html/body/div")[0].InnerXml); }
/// <summary> /// for testing.... todo: maybe they should test ProcessAndCopyImage() directly, instead /// </summary> public void ChangePicture(string bookFolderPath, XmlDocument dom, string imageId, PalasoImage imageInfo) { var matches = dom.SafeSelectNodes("//img[@id='" + imageId + "']"); XmlElement img = matches[0] as XmlElement; var imageFileName = ProcessAndCopyImage(imageInfo, bookFolderPath); img.SetAttribute("src", imageFileName); }
public IEnumerable<IChangeReport> DescribeInitialContents(FileInRevision fileInRevision, TempFile file) { var dom = new XmlDocument(); dom.Load(file.Path); foreach (XmlNode e in dom.SafeSelectNodes("notes/annotation")) { yield return new XmlAdditionChangeReport(fileInRevision, e); } }
public void PrepareElementsOnPage_HasLabelElementInsideTranslationGroup_LeavesUntouched() { var contents = @"<div class='bloom-page bloom-translationGroup'> <label class='bloom-bubble'>something helpful</label> </div>"; var dom = new XmlDocument(); dom.LoadXml(contents); TranslationGroupManager.PrepareElementsInPageOrDocument((XmlElement)dom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")[0], _collectionSettings.Object); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//label[@class='bloom-bubble']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//label[@class='bloom-bubble' and text()='something helpful']", 1); }
public void PrepareElementsOnPage_HasEmptyTranslationGroup_MakesVernacularAndNational() { var contents = @"<div class='bloom-page bloom-translationGroup'> </div>"; var dom = new XmlDocument(); dom.LoadXml(contents); TranslationGroupManager.PrepareElementsInPageOrDocument((XmlElement)dom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")[0], _collectionSettings.Object); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div/div[contains(@class, 'bloom-editable') and @contenteditable='true' ]", 3); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='xyz']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='es']", 1); }
public void PrepareElementsOnPage_HasTextAreaInsideTranslationGroup_MakesVernacularAndNational() { var contents = @"<div class='bloom-page bloom-translationGroup'> <textarea lang='en'>This is some English</textarea> </div>"; var dom = new XmlDocument(); dom.LoadXml(contents); TranslationGroupManager.PrepareElementsInPageOrDocument((XmlElement)dom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")[0], _collectionSettings.Object); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//textarea", 4); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@lang='en']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@lang='fr']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@lang='es']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@lang='xyz']", 1); }
public void PrepareElementsOnPage_HasNonEditableDiv_LeavesAlone() { var contents = @"<div class='bloom-page'> <table class='bloom-translationGroup'> <!-- table is used for vertical alignment of the div on some pages --> <td> <div lang='en'>This is some English</div> </td> </table> </div>"; var dom = new XmlDocument(); dom.LoadXml(contents); TranslationGroupManager.PrepareElementsInPageOrDocument((XmlElement)dom.SafeSelectNodes("//div[@class='bloom-page']")[0], _collectionSettings.Object); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//td/div", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//td/div[@lang='en']", 1); }
public static void RemoveStyleSheetIfFound(XmlDocument dom, string cssFilePath) { foreach (XmlElement linkNode in dom.SafeSelectNodes("/html/head/link")) { var href = linkNode.GetAttribute("href"); if (href == null) { continue; } //strip it down to just the name+extension, so other forms (e.g., via slightly different urls) will be removed. var path = href.ToLower().Replace("file://", ""); if(Path.GetFileName(path)==Path.GetFileName(cssFilePath.ToLower())) { linkNode.ParentNode.RemoveChild(linkNode); } } }
public void MergeConflictFiles_AncestorExistsButNoConflicts() { using ( GroupOfConflictFiles group = new GroupOfConflictFiles("<notes/>", "<notes><annotation guid='bobGuid'/></notes>", "<notes><annotation guid='sallyGuid'/></notes>") ) { MergeOrder order = new MergeOrder( group.BobFile.Path, group.AncestorFile.Path, group.SallyFile.Path, new NullMergeSituation()); new ChorusNotesFileHandler().Do3WayMerge(order); XmlDocument doc = new XmlDocument(); doc.Load(group.BobFile.Path); Assert.AreEqual(2, doc.SafeSelectNodes("notes/annotation").Count); } }
public void PrepareDataBookTranslationGroups_PlaceholdersCreatedAsNeeded() { var contents = @"<div class='bloom-page'> <div class='bloom-translationGroup'> <div class='bloom-editable' data-book='bookTitle' lang='en'>Some English</div> </div> </div>"; var dom = new XmlDocument(); dom.LoadXml(contents); var languages = new string[] {"en","es","fr"}; TranslationGroupManager.PrepareDataBookTranslationGroups((XmlElement)dom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")[0], languages); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div/div[contains(@class, 'bloom-editable')]", 3); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='es']", 1); //should touch the existing one AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='en' and text()='Some English']", 1); }
public void UpdateContentLanguageClasses_ExistingPageWith3rdLangRemoved_RemovesContentLanguageClassButLeavesOtherClasses() { var contents = @"<div class='bloom-page'> <div class='bloom-translationGroup'> <textarea lang='en'></textarea> <textarea class='bloom-content2' lang='222'></textarea> <textarea class='foo bloom-content3 bar' lang='333'></textarea> </div> </div>"; var dom = new XmlDocument(); dom.LoadXml(contents); var pageDiv = (XmlElement)dom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")[0]; TranslationGroupManager.UpdateContentLanguageClasses(pageDiv, "xyz", _collectionSettings.Object.Language2Iso639Code, _collectionSettings.Object.Language3Iso639Code, "222", null); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@lang='222' and contains(@class, 'bloom-content2')]", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@lang='333' and contains(@class, 'bloom-content3')]", 0); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@lang='333' and contains(@class, 'foo')]", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@lang='333' and contains(@class, 'bar')]", 1); }
/// <summary> /// If an element has empty contents, like <textarea></textarea>, browsers will sometimes drop the end tag, so that now, when we read it back into xml, /// anything following the <textarea> will be interpreted as part of the <textarea>! This method makes sure such tags are never totally empty. /// </summary> /// <param name="dom"></param> public static void MakeXmlishTagsSafeForInterpretationAsHtml(XmlDocument dom) { foreach (XmlElement node in dom.SafeSelectNodes("//textarea")) { if (!node.HasChildNodes) { node.AppendChild(node.OwnerDocument.CreateTextNode("")); } } foreach (XmlElement node in dom.SafeSelectNodes("//div")) { if (!node.HasChildNodes) { node.AppendChild(node.OwnerDocument.CreateTextNode("")); } } foreach (XmlElement node in dom.SafeSelectNodes("//p")) //without this, an empty paragraph suddenly takes over the subsequent elements. Browser sees <p></p> and thinks... let's just make it <p>, shall we? Stupid optional-closing language, html is.... { if (!node.HasChildNodes) { node.AppendChild(node.OwnerDocument.CreateTextNode("")); } } foreach (XmlElement node in dom.SafeSelectNodes("//span")) { if (!node.HasChildNodes) { node.AppendChild(node.OwnerDocument.CreateTextNode("")); } } foreach (XmlElement node in dom.SafeSelectNodes("//script")) { if (string.IsNullOrEmpty(node.InnerText) && node.ChildNodes.Count == 0) { node.InnerText = " "; } } foreach (XmlElement node in dom.SafeSelectNodes("//style")) { if (string.IsNullOrEmpty(node.InnerText) && node.ChildNodes.Count == 0) { node.InnerText = " "; } } }
public void UpdateContentLanguageClasses_NewPage_AddsContentLanguageClasses() { var contents = @"<div class='bloom-page'> <div class='bloom-translationGroup'> <textarea lang='en'></textarea> <textarea lang='222'></textarea> <textarea lang='333'></textarea> </div> </div>"; var dom = new XmlDocument(); dom.LoadXml(contents); var pageDiv = (XmlElement)dom.SafeSelectNodes("//div[@class='bloom-page']")[0]; TranslationGroupManager.UpdateContentLanguageClasses(pageDiv, "xyz", _collectionSettings.Object.Language2Iso639Code, _collectionSettings.Object.Language3Iso639Code, "222", "333"); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@lang='222' and contains(@class, 'bloom-content2')]", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//textarea[@lang='333' and contains(@class, 'bloom-content3')]", 1); }
private string GetMetaValue(XmlDocument Dom, string name, string defaultValue) { var nameSuggestion = Dom.SafeSelectNodes("//head/meta[@name='" + name + "']"); if (nameSuggestion.Count > 0) { return ((XmlElement)nameSuggestion[0]).GetAttribute("content"); } return defaultValue; }
public string GetMergedLift() { // string path = Path.Combine(System.Environment.GetEnvironmentVariable("temp"), // @"chorusMergeResult" + Path.GetFileName(_alphaLiftPath) + ".txt"); // // File.WriteAllText(path, "ENter GetMergedLift()"); var ancestorDom = new XmlDocument(); ancestorDom.LoadXml(_ancestorLift); //var comonIdToNodeIndex = new Dictionary<string, XmlNode>(StringComparer.OrdinalIgnoreCase); var alphaDom = new XmlDocument(); alphaDom.LoadXml(_alphaLift); var alphaIdToNodeIndex = new Dictionary<string, XmlNode>(StringComparer.OrdinalIgnoreCase); var betaDom = new XmlDocument(); betaDom.LoadXml(_betaLift); var betaIdToNodeIndex = new Dictionary<string, XmlNode>(StringComparer.OrdinalIgnoreCase); var processedIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase); // The memory stream, rather than a string builder, is needed to avoid utf-16 coming out using (var memoryStream = new MemoryStream()) { //foreach (XmlNode commonNode in _ancestorDom.SafeSelectNodes("lift/entry")) // comonIdToNodeIndex[LiftUtils.GetId(commonNode)] = commonNode; foreach (XmlNode alphaNode in alphaDom.SafeSelectNodes("lift/entry")) alphaIdToNodeIndex[LiftUtils.GetId(alphaNode)] = alphaNode; foreach (XmlNode betaNode in betaDom.SafeSelectNodes("lift/entry")) betaIdToNodeIndex[LiftUtils.GetId(betaNode)] = betaNode; var settings = CanonicalXmlSettings.CreateXmlWriterSettings(); settings.CloseOutput = false; using (var writer = XmlWriter.Create(memoryStream, settings)) { WriteStartOfLiftElement(writer, alphaDom); ProcessHeaderNodeHACK(writer, alphaDom, betaDom); // Process alpha's entries ProcessEntries(writer, EventListener, _mergingStrategy, ancestorDom, processedIds, alphaDom, "alpha", _alphaLift, betaIdToNodeIndex, "beta", _betaLift); // Process beta's entries ProcessEntries(writer, EventListener, _mergingStrategy, ancestorDom, processedIds, betaDom, "beta", _betaLift, alphaIdToNodeIndex, "alpha", _alphaLift); writer.WriteEndElement(); writer.Close(); } // Don't use GetBuffer()!!! it pads the results with nulls: return Encoding.UTF8.GetString(memoryStream.ToArray()); // This works but doubles the ram use: return Encoding.UTF8.GetString(memoryStream.ToArray()); return Encoding.UTF8.GetString(memoryStream.GetBuffer(), 0, (int)memoryStream.Position); } }
private static void DeletePages(XmlDocument bookDom, Func<XmlElement, bool> pageSelectingPredicate) { // Seems safest to make a list so we're not modifying the document while iterating through it. var pagesToDelete = new List<XmlElement>(); foreach (XmlElement node in bookDom.SafeSelectNodes("//div[contains(@class, 'bloom-page')]")) { if (pageSelectingPredicate(node)) { pagesToDelete.Add(node); } } foreach (var node in pagesToDelete) { // An earlier version of this method just set the visibility of the pages we don't want // in this printout to display:none, like this: //node.SetAttribute("style", "", "display:none"); // However, this runs up against a defect in Gecko PDF generation: apparently when // all the content after the last page in a paginated document is display:none, Gecko // puts in an extra blank page. We suspect something like code that detects that // the current page is finished and the document is not finished and starts a new page, // which turns out not to be needed. The extra blank page can mess up booklet generation // and cause an extra sheet of paper to be used (leaving a wasted four blank pages at // the end). See BL-705. node.ParentNode.RemoveChild(node); } }
private static void HideEverythingButFirstPageAndRemoveScripts(XmlDocument bookDom) { bool onFirst = true; foreach (XmlElement node in bookDom.SafeSelectNodes("//div[contains(@class, 'bloom-page')]")) { if (!onFirst) { node.SetAttribute("style", "", "display:none"); } onFirst =false; } //Without casting to array, Mono considers this manipulating the enumerable list foreach (var node in bookDom.SafeSelectNodes("//script").Cast<XmlNode>().ToArray()) { //TODO: this removes image scaling, which is ok so long as it's already scaled with width/height attributes node.ParentNode.RemoveChild(node); } }
/// <summary> /// Where, for example, somewhere on a page something has data-book='foo' lan='fr', /// we set the value of that element to French subvalue of the data item 'foo', if we have one. /// </summary> private void UpdateDomFromDataSet(DataSet data, string elementName,XmlDocument targetDom) { try { string query = String.Format("//{0}[(@data-book or @data-collection or @data-library)]", elementName); XmlNodeList nodesOfInterest = targetDom.SafeSelectNodes(query); foreach (XmlElement node in nodesOfInterest) { string key = node.GetAttribute("data-book").Trim(); if (key == String.Empty) { key = node.GetAttribute("data-collection").Trim(); if(key==string.Empty) { key = node.GetAttribute("data-library").Trim(); //"library" is the old name for what is now "collection" } } if (!String.IsNullOrEmpty(key) && data.TextVariables.ContainsKey(key)) { if (node.Name.ToLower() == "img") { string imageName = WebUtility.HtmlDecode(data.TextVariables[key].TextAlternatives.GetFirstAlternative()); string oldImageName = WebUtility.HtmlDecode(node.GetAttribute("src")); node.SetAttribute("src", imageName); if (oldImageName != imageName) { Guard.AgainstNull(_updateImgNode, "_updateImgNode"); _updateImgNode(node); } } else { string lang = node.GetOptionalStringAttribute("lang", "*"); if (lang == "N1" || lang == "N2" || lang == "V") lang = data.WritingSystemAliases[lang]; // //see comment later about the inability to clear a value. TODO: when we re-write Bloom, make sure this is possible // if(data.TextVariables[key].TextAlternatives.Forms.Length==0) // { // //no text forms == desire to remove it. THe multitextbase prohibits empty strings, so this is the best we can do: completly remove the item. // targetDom.RemoveChild(node); // } // else if (!String.IsNullOrEmpty(lang)) //if we don't even have this language specified (e.g. no national language), the give up { //Ideally, we have this string, in this desired language. string s = data.TextVariables[key].TextAlternatives.GetBestAlternativeString(new []{lang, "*"}); //But if not, maybe we should copy one in from another national language if(string.IsNullOrEmpty(s)) s = PossiblyCopyFromAnotherLanguage(node, lang, data, key); //NB: this was the focus of a multi-hour bug search, and it's not clear that I got it right. //The problem is that the title page has N1 and n2 alternatives for title, the cover may not. //the gather page was gathering no values for those alternatives (why not), and so GetBestAlternativeSTring //was giving "", which we then used to remove our nice values. //REVIEW: what affect will this have in other pages, other circumstances. Will it make it impossible to clear a value? //Hoping not, as we are differentiating between "" and just not being in the multitext at all. //don't overwrite a datadiv alternative with empty just becuase this page has no value for it. if (s == "" && !data.TextVariables[key].TextAlternatives.ContainsAlternative(lang)) continue; //hack: until I think of a more elegant way to avoid repeating the language name in N2 when it's the exact same as N1... if (data.WritingSystemAliases.Count != 0 && lang == data.WritingSystemAliases["N2"] && s == data.TextVariables[key].TextAlternatives.GetBestAlternativeString(new[] { data. WritingSystemAliases ["N1"] , "*" })) { s = ""; //don't show it in N2, since it's the same as N1 } node.InnerXml = s; //meaning, we'll take "*" if you have it but not the exact choice. * is used for languageName, at least in dec 2011 } } } } } catch (Exception error) { throw new ApplicationException( "Error in MakeAllFieldsOfElementTypeConsistent(," + elementName + "). RawDom was:\r\n" + targetDom.OuterXml, error); } }
public void UpdateContentLanguageClasses_Typical_MetadataPage_TurnsOnCorrectLanguages() { var contents = @"<div class='bloom-page' > <div class='bloom-translationGroup' data-default-languages='N1,N2'> <div class='bloom-editable' lang='xyz'></div> <div class='bloom-editable' lang='fr'></div> <div class='bloom-editable' lang='es'></div> </div> </div>"; var dom = new XmlDocument(); dom.LoadXml(contents); var pageDiv = (XmlElement)dom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")[0]; TranslationGroupManager.UpdateContentLanguageClasses(pageDiv, _collectionSettings.Object, "xyz", "222", "333"); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[contains(@class, 'bloom-visibility-code-on')]", 2); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr' and contains(@class, 'bloom-visibility-code-on')]", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='es' and contains(@class, 'bloom-visibility-code-on')]", 1); }
public void UpdateContentLanguageClasses_TranslationGroupHasPlaceHolder_PlaceholderCopiedToNewChildren() { var contents = @"<div class='bloom-page bloom-trilingual'> <div class='bloom-translationGroup' data-placeholder='copy me' > </div> </div>"; var dom = new XmlDocument(); dom.LoadXml(contents); TranslationGroupManager.PrepareElementsInPageOrDocument((XmlElement)dom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")[0], _collectionSettings.Object); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div/div[contains(@class, 'bloom-editable') and @contenteditable='true' ]", 3); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='xyz' and @data-placeholder='copy me']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr' and @data-placeholder='copy me']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='es' and @data-placeholder='copy me']", 1); }
public void UpdateContentLanguageClasses_PrototypeElementHasImageContainer_ImageContainerCopiedToNewSibling() { const string contents = @"<div class='bloom-page'> <div class='bloom-translationGroup'> <div class='bloom-editable' lang='123'> Do not copy me. <br>Do not copy me.</br> <p>Do not copy me.</p> <div class='bloom-imageContainer'> <img src='foo.png'></img> <div contentEditable='true' class='caption'>Do not copy me</div> </div> Do not copy me. <p>Do not copy me.</p> <p class='foo bloom-cloneToOtherLanguages bar'>Do copy me.</p> </div> </div> </div>"; var dom = new XmlDocument(); dom.LoadXml(contents); TranslationGroupManager.PrepareElementsInPageOrDocument((XmlElement)dom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")[0], _collectionSettings.Object); //the added french should have all the structure including a copy of the image container div, but none of the text except from the bloom-cloneToOtherLanguages paragraph AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr']/div/img[@src='foo.png']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr']/div/div[@contentEditable='true']", 1); //should clear out the caption text (using a raw contentEditable for the caption just becuase bloom-editables inside of bloom-editables is beyond my ambitions at the moment. AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr']/div/div[contains(@class,'caption') and @contentEditable='true' and not(text())]", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr']/*[contains(text(),'Do not')]", 0); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr']//br", 0); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr']//p[not(contains(@class,'bloom-cloneToOtherLanguages'))]", 0); // get rid of all paragraphs, except for... AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr']//p[contains(@class,'bloom-cloneToOtherLanguages')]", 1); // the one with "bloom-cloneToOtherLanguages" AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr']/*[contains(text(),'Do copy me')]", 1); }
public void UpdateContentLanguageClasses_PrototypeHasUnderlinedText_CopyHasNone() { var contents = @"<div class='bloom-page numberedPage A5Portrait bloom-monolingual' id='f4a22289-1755-4b79-afc1-5d20eaa892fe'> <div class='marginBox'> <div class='bloom-translationGroup normal-style'> <div style='' class='bloom-editable' contenteditable='true' lang='en'>The <i>Mother</i> said, <u>Nurse!</u> The Nurse <b>answered</b>.</div> </div></div></div>"; var dom = new XmlDocument(); dom.LoadXml(contents); TranslationGroupManager.PrepareElementsInPageOrDocument((XmlElement)dom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")[0], _collectionSettings.Object); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='xyz']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='fr']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='es']", 1); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[@lang='en' and contains(., 'The Mother')]", 1); AssertThatXmlIn.Dom(dom).HasNoMatchForXpath("//div[@lang='fr']/u"); AssertThatXmlIn.Dom(dom).HasNoMatchForXpath("//div[@lang='fr']/b"); AssertThatXmlIn.Dom(dom).HasNoMatchForXpath("//div[@lang='fr']/i"); }
public static void RemoveAllContentTypesMetas(XmlDocument dom) { foreach (XmlElement n in dom.SafeSelectNodes("//head/meta[@http-equiv='Content-Type']")) { n.ParentNode.RemoveChild(n); } }
public void UpdateContentLanguageClasses_TrilingualBook_AddsBloomTrilingualClassToTranslationGroup() { var contents = @"<div class='bloom-page bloom-bilingual'> <div class='bloom-translationGroup'> <textarea lang='en'></textarea> </div> </div>"; var dom = new XmlDocument(); dom.LoadXml(contents); var pageDiv = (XmlElement)dom.SafeSelectNodes("//div[contains(@class,'bloom-page')]")[0]; TranslationGroupManager.UpdateContentLanguageClasses(pageDiv, _collectionSettings.Object, "xyz", "222", "333"); AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[contains(@class, 'bloom-bilingual')]", 0);//should remove that one AssertThatXmlIn.Dom(dom).HasSpecifiedNumberOfMatchesForXpath("//div[contains(@class, 'bloom-trilingual')]", 1); }
/// <summary> /// Where, for example, somewhere on a page something has data-book='foo' lang='fr', /// we set the value of that element to French subvalue of the data item 'foo', if we have one. /// </summary> private void UpdateDomFromDataSet(DataSet data, string elementName,XmlDocument targetDom, HashSet<Tuple<string, string>> itemsToDelete) { try { var query = String.Format("//{0}[(@data-book or @data-collection or @data-library or @data-book-attributes)]", elementName); var nodesOfInterest = targetDom.SafeSelectNodes(query); foreach (XmlElement node in nodesOfInterest) { var key = node.GetAttribute("data-book").Trim(); if (key == string.Empty) { key = node.GetAttribute("data-book-attributes").Trim(); if (key != string.Empty) { UpdateAttributes(data, node, key); continue; } key = node.GetAttribute("data-collection").Trim(); if (key == string.Empty) { key = node.GetAttribute("data-library").Trim(); //"library" is the old name for what is now "collection" } } if (string.IsNullOrEmpty(key)) continue; if (data.TextVariables.ContainsKey(key)) { if (UpdateImageFromDataSet(data, node, key)) continue; var lang = node.GetOptionalStringAttribute("lang", "*"); if (lang == "N1" || lang == "N2" || lang == "V") lang = data.WritingSystemAliases[lang]; // //see comment later about the inability to clear a value. TODO: when we re-write Bloom, make sure this is possible // if(data.TextVariables[key].TextAlternatives.Forms.Length==0) // { // //no text forms == desire to remove it. THe multitextbase prohibits empty strings, so this is the best we can do: completly remove the item. // targetDom.RemoveChild(node); // } // else if (!string.IsNullOrEmpty(lang)) //if we don't even have this language specified (e.g. no national language), the give up { //Ideally, we have this string, in this desired language. var s = data.TextVariables[key].TextAlternatives.GetBestAlternativeString(new[] {lang, "*"}); if(KeysOfVariablesThatAreUrlEncoded.Contains(key)) { Debug.Assert(!s.Contains("&"),"In memory, all image urls should be encoded such that & is just &."); } //But if not, maybe we should copy one in from another national language if (string.IsNullOrEmpty(s)) s = PossiblyCopyFromAnotherLanguage(node, lang, data, key); //NB: this was the focus of a multi-hour bug search, and it's not clear that I got it right. //The problem is that the title page has N1 and n2 alternatives for title, the cover may not. //the gather page was gathering no values for those alternatives (why not), and so GetBestAlternativeSTring //was giving "", which we then used to remove our nice values. //REVIEW: what affect will this have in other pages, other circumstances. Will it make it impossible to clear a value? //Hoping not, as we are differentiating between "" and just not being in the multitext at all. //don't overwrite a datadiv alternative with empty just becuase this page has no value for it. if (s == "" && !data.TextVariables[key].TextAlternatives.ContainsAlternative(lang)) continue; //hack: until I think of a more elegant way to avoid repeating the language name in N2 when it's the exact same as N1... if (data.WritingSystemAliases.Count != 0 && lang == data.WritingSystemAliases["N2"] && s == data.TextVariables[key].TextAlternatives.GetBestAlternativeString(new[] { data. WritingSystemAliases ["N1"] , "*" })) { s = ""; //don't show it in N2, since it's the same as N1 } SetInnerXmlPreservingLabel(key, node, s); } } else if (!HtmlDom.IsImgOrSomethingWithBackgroundImage(node)) { // See whether we need to delete something var lang = node.GetOptionalStringAttribute("lang", "*"); if (lang == "N1" || lang == "N2" || lang == "V") lang = data.WritingSystemAliases[lang]; if (itemsToDelete.Contains(Tuple.Create(key, lang))) { SetInnerXmlPreservingLabel(key, node, "");// a later process may remove node altogether. } } } } catch (Exception error) { throw new ApplicationException( "Error in UpdateDomFromDataSet(," + elementName + "). RawDom was:\r\n" + targetDom.OuterXml, error); } }
/// <summary> /// Add to the dictionary which maps original to Localized strings an entry for any language code that doesn't already /// have one. We have localizations for a few major languages that map e.g. de->German/Deutsch/etc, so they are functioning /// not just to localize but to expand from a language code to an actual name. For any other languages where we don't /// have localization information, we'd like to at least expand the cryptic code into a name. This method does that. /// </summary> /// <param name="xmlDocument"></param> /// <param name="mapOriginalToLocalized"></param> internal static void AddLanguagesUsedInPage(XmlDocument xmlDocument, Dictionary<string, string> mapOriginalToLocalized) { var langs = xmlDocument.SafeSelectNodes("//*[@lang]").Cast<XmlElement>() .Select(e => e.Attributes["lang"].Value) .Distinct() .Where(lang => !mapOriginalToLocalized.ContainsKey(lang)) .ToList(); if (langs.Any()) { // We don't have a localization for these languages, but we can at least try to give them a name var lookup = new LanguageLookupModel(); // < 1ms foreach (var lang in langs) // may include things like empty string, z, *, but this is harmless as they are not language codes. { string match; if (lookup.GetBestLanguageName(lang, out match)) // some better name found mapOriginalToLocalized[lang] = match; } } }
public void FileAlreadyExists_AddsNewConflicts() { using (TempFile logFile = TempFile.CreateAndGetPathButDontMakeTheFile()) { using (ChorusNotesMergeEventListener log = new ChorusNotesMergeEventListener(logFile.Path)) { log.ConflictOccurred(new DummyConflict()); log.ConflictOccurred(new DummyConflict()); } using (ChorusNotesMergeEventListener log2 = new ChorusNotesMergeEventListener(logFile.Path)) { log2.ConflictOccurred(new DummyConflict()); log2.ConflictOccurred(new DummyConflict()); } XmlDocument doc = new XmlDocument(); doc.Load(logFile.Path); Assert.AreEqual(4, doc.SafeSelectNodes("notes/annotation").Count); } }