Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update RemoveControlsAndKeepContent to support nested table rows #48

Merged
merged 1 commit into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 46 additions & 13 deletions OpenXMLTemplates/Documents/TemplateDocument.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using OpenXMLTemplates.Utils;
Expand Down Expand Up @@ -113,25 +114,57 @@ internal void AddControl(ContentControl control, bool isFirstOrder)

public void RemoveControlsAndKeepContent()
{
foreach (var control in allContentControls)
{
var sdtElement = control.SdtElement;

var contentElement = sdtElement.Descendants()
.FirstOrDefault(d => d is SdtContentBlock || d is SdtContentRun);
if (contentElement != null)
foreach (var contentElementChildElement in contentElement.ChildElements.ToList())
{
contentElementChildElement.Remove();
sdtElement.InsertBeforeSelf(contentElementChildElement);
}
var contentControls = WordprocessingDocument.MainDocumentPart.Document.Descendants<SdtElement>().ToList();

sdtElement.Remove();
// Process each content control from innermost to outermost
foreach (var sdt in contentControls.OrderByDescending(s => s.Ancestors<SdtElement>().Count()))
{
if (sdt is SdtBlock sdtBlock)
{
HandleSdtContent(sdtBlock, sdtBlock.SdtContentBlock);
}
else if (sdt is SdtRun sdtRun)
{
HandleSdtContent(sdtRun, sdtRun.SdtContentRun);
}
else if (sdt is SdtRow sdtRow)
{
HandleSdtRow(sdtRow);
}
}

allContentControls.Clear();
innerContentControls.Clear();
firstOrderContentControls.Clear();
}

private static void HandleSdtContent(SdtElement sdt, OpenXmlElement content)
{
if (content != null)
{
var parent = sdt.Parent;
var elementsToMove = content.ChildElements.ToArray(); // Make a copy to avoid modifying the collection during iteration
foreach (var elem in elementsToMove)
{
parent.InsertBefore(elem.CloneNode(true), sdt);
}
// Remove the content control itself
sdt.Remove();
}
}
private static void HandleSdtRow(SdtRow sdtRow)
{
if (sdtRow.SdtContentRow != null)
{
var tableRow = sdtRow.SdtContentRow.GetFirstChild<TableRow>();
if (tableRow != null)
{
var parent = sdtRow.Parent; // This should be the Table
parent.InsertBefore(tableRow.CloneNode(true), sdtRow);
sdtRow.Remove();
}
}
}

}
}
Binary file added OpenXMLTemplatesTest/ControlRemovalTest/Doc.docx
Binary file not shown.
57 changes: 57 additions & 0 deletions OpenXMLTemplatesTest/ControlRemovalTest/Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System.IO;
using System.Linq;
using NUnit.Framework;
using OpenXMLTemplates;
using OpenXMLTemplates.ControlReplacers;
using OpenXMLTemplates.Documents;
using OpenXMLTemplates.Engine;
using OpenXMLTemplates.Variables;
namespace OpenXMLTempaltesTest.ControlRemovalTest
{
public class Tests
{
private TemplateDocument GetDoc => new TemplateDocument(this.CurrentFolder() + "Doc.docx");
private string GetData => File.ReadAllText(this.CurrentFolder() + "data.json");

[Test]
public void TestControlRemoval()
{
using var doc = GetDoc;
var data = GetData;

var src = new VariableSource();
src.LoadDataFromJson(data);

var engine = new DefaultOpenXmlTemplateEngine
{
KeepContentControlAfterReplacement = false
};
engine.ReplaceAll(doc, src);

doc.SaveAs(this.CurrentFolder() + "result.docx");

// confirm new content has been included in document
string? docText = null;

Check warning on line 34 in OpenXMLTemplatesTest/ControlRemovalTest/Tests.cs

View workflow job for this annotation

GitHub Actions / Build and test

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

Check warning on line 34 in OpenXMLTemplatesTest/ControlRemovalTest/Tests.cs

View workflow job for this annotation

GitHub Actions / Build and test

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.

using (StreamReader sr = new StreamReader(doc.WordprocessingDocument.MainDocumentPart.GetStream()))
{
docText = sr.ReadToEnd();
}

Assert.IsTrue(docText != null);
var replacedText = src.GetVariable<string>("nested.[1].nestedList.[1]");
Assert.IsTrue(docText.Contains(replacedText));

// confirm controls have been removed
Assert.AreEqual(0,
doc.WordprocessingDocument.ContentControls().Count(cc =>
cc.GetContentControlTag() != null && cc.GetContentControlTag().StartsWith("repeatingitem")));

Assert.AreEqual(0,
doc.WordprocessingDocument.ContentControls().Count(cc =>
cc.GetContentControlTag() != null && cc.GetContentControlTag() == "repeating_nestedList"));

doc.WordprocessingDocument.AssertValid();
}
}
}
35 changes: 35 additions & 0 deletions OpenXMLTemplatesTest/ControlRemovalTest/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"items": [
"Item 1",
"Item 2",
"Item 3",
"Item 4"
],
"complexItems": [
{
"name": "First Name",
"address": "First Address"
},
{
"name": "Second name",
"address": "Second address"
}
],
"nested": [
{
"name": "List 1",
"nestedList": [
"item1-1",
"item1-2",
"item1-3"
]
},
{
"name": "List 2",
"nestedList": [
"item2-1",
"item2-2"
]
}
]
}
Binary file added OpenXMLTemplatesTest/ControlRemovalTest/~$Doc.docx
Binary file not shown.
6 changes: 6 additions & 0 deletions OpenXMLTemplatesTest/OpenXMLTemplatesTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
<ProjectReference Include="..\OpenXMLTemplates\OpenXMLTemplates.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="ControlRemovalTest\data.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="ControlRemovalTest\Doc.docx">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="ControlReplacersTests\PictureControlReplacerTests\data.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
Expand Down
Loading