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

8012: Add Excel Export functionality to DynamicForms #8016

Merged
merged 5 commits into from
Apr 16, 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
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,11 @@ public ActionResult BulkDelete(IEnumerable<int> submissionIds) {

return Redirect(Request.UrlReferrer.ToString());
}

public ActionResult Export(string id) =>
File(
_formService.ExportSubmissions(id),
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"Export.xlsx");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="DocumentFormat.OpenXml, Version=3.0.2.0, Culture=neutral, PublicKeyToken=8fb06cb64d019a17, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\DocumentFormat.OpenXml.3.0.2\lib\net46\DocumentFormat.OpenXml.dll</HintPath>
</Reference>
<Reference Include="DocumentFormat.OpenXml.Framework, Version=3.0.2.0, Culture=neutral, PublicKeyToken=8fb06cb64d019a17, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\DocumentFormat.OpenXml.Framework.3.0.2\lib\net46\DocumentFormat.OpenXml.Framework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.4.1.0\lib\net472\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath>
</Reference>
Expand All @@ -68,6 +74,7 @@
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
Expand Down Expand Up @@ -96,6 +103,7 @@
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Xml.Linq" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Content Include="Assets\CSS\DynamicForms-Admin.css" />
Expand Down Expand Up @@ -597,4 +605,4 @@
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>
</Project>
132 changes: 111 additions & 21 deletions src/Orchard.Web/Modules/Orchard.DynamicForms/Services/FormService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using Orchard.Collections;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
Expand All @@ -21,6 +25,7 @@
using Orchard.Layouts.Services;
using Orchard.Localization.Services;
using Orchard.Services;
using Orchard.Utility.Extensions;

namespace Orchard.DynamicForms.Services {
public class FormService : IFormService {
Expand All @@ -38,16 +43,16 @@ public class FormService : IFormService {
private readonly ICultureAccessor _cultureAccessor;

public FormService(
ILayoutSerializer serializer,
IClock clock,
IRepository<Submission> submissionRepository,
IFormElementEventHandler elementHandlers,
IContentDefinitionManager contentDefinitionManager,
IBindingManager bindingManager,
IDynamicFormEventHandler formEventHandler,
ILayoutSerializer serializer,
IClock clock,
IRepository<Submission> submissionRepository,
IFormElementEventHandler elementHandlers,
IContentDefinitionManager contentDefinitionManager,
IBindingManager bindingManager,
IDynamicFormEventHandler formEventHandler,
Lazy<IEnumerable<IElementValidator>> validators,
IDateLocalizationServices dateLocalizationServices,
IOrchardServices services,
IDateLocalizationServices dateLocalizationServices,
IOrchardServices services,
ICultureAccessor cultureAccessor) {

_serializer = serializer;
Expand Down Expand Up @@ -152,6 +157,90 @@ public IPageOfItems<Submission> GetSubmissions(string formName = null, int? skip
};
}

public Stream ExportSubmissions(string formName = null) {
var stream = new MemoryStream();

string GetColumnId(int columnNumber) {
string result = "";
do {
result = ((char)((columnNumber - 1) % 26 + (int)'A')).ToString() + result;
columnNumber = (columnNumber - 1) / 26;
} while (columnNumber != 0);
return result;
}

// Create a spreadsheet document.
var spreadsheetDocument = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook);

// Add a WorkbookPart to the document.
var workbookpart = spreadsheetDocument.AddWorkbookPart();
workbookpart.Workbook = new Workbook();

// Add a WorksheetPart to the WorkbookPart.
var worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
var sheetData = new SheetData();
worksheetPart.Worksheet = new Worksheet(sheetData);

// Add Sheets to the Workbook.
var sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild(new Sheets());

// Fetch submissions
var query = _submissionRepository.Table;

if (!String.IsNullOrWhiteSpace(formName)) {
query = query.Where(x => x.FormName == formName);
}

var submissions = new Orderable<Submission>(query).Desc(x => x.CreatedUtc).Queryable.ToArray();

foreach (var formGroup in submissions.GroupBy(s => s.FormName)) {
// Append a new worksheet and associate it with the workbook.
var sheet = new Sheet() {
Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart),
SheetId = 1,
Name = formGroup.Key
};
sheets.Append(sheet);

var data = GenerateDataTable(formGroup);
uint rowIndex = 1;

var headerRow = new Row { RowIndex = rowIndex };
sheetData.Append(headerRow);

for (int i = 0; i < data.Columns.Count; i++) {
var title = data.Columns[i].ToString().CamelFriendly();
headerRow.Append(new Cell {
CellReference = GetColumnId(i + 1) + rowIndex,
InlineString = new InlineString { Text = new Text(title) },
DataType = new EnumValue<CellValues>(CellValues.InlineString),
});
}

foreach (DataRow dataRow in data.Rows) {
rowIndex++;
var row = new Row { RowIndex = rowIndex };
sheetData.Append(row);
for (int i = 0; i < data.Columns.Count; i++) {
var value = dataRow[data.Columns[i]];
row.Append(new Cell {
CellReference = GetColumnId(i + 1) + rowIndex,
InlineString = new InlineString { Text = new Text(value.ToString()) },
DataType = new EnumValue<CellValues>(CellValues.InlineString),
});
}
}
}

workbookpart.Workbook.Save();

// Close the document.
spreadsheetDocument.Dispose();
stream.Seek(0, SeekOrigin.Begin);

return stream;
}

public void DeleteSubmission(Submission submission) {
_submissionRepository.Delete(submission);
}
Expand Down Expand Up @@ -191,10 +280,10 @@ public NameValueCollection ReadElementValues(Form form, IValueProvider valueProv

// Collect any remaining form values not handled by any specific element.
var requestForm = _services.WorkContext.HttpContext.Request.Form;
var blackList = new[] {"__RequestVerificationToken", "formName", "contentId"};
foreach (var key in
from string key in requestForm
where !String.IsNullOrWhiteSpace(key) && !blackList.Contains(key) && values[key] == null
var blackList = new[] { "__RequestVerificationToken", "formName", "contentId" };
foreach (var key in
from string key in requestForm
where !String.IsNullOrWhiteSpace(key) && !blackList.Contains(key) && values[key] == null
select key) {

values.Add(key, requestForm[key]);
Expand All @@ -204,13 +293,14 @@ from string key in requestForm
}

public DataTable GenerateDataTable(IEnumerable<Submission> submissions) {
var records = submissions.Select(x => Tuple.Create(x, x.ToNameValues())).ToArray();
var records = submissions.Select(x => System.Tuple.Create(x, x.ToNameValues())).ToArray();
var columnNames = new HashSet<string>();
var dataTable = new DataTable();

foreach (var key in
from record in records
from string key in record.Item2 where !columnNames.Contains(key)
foreach (var key in
from record in records
from string key in record.Item2
where !columnNames.Contains(key)
where !String.IsNullOrWhiteSpace(key)
select key) {
columnNames.Add(key);
Expand Down Expand Up @@ -282,7 +372,7 @@ public ContentItem CreateContentItem(Form form, IValueProvider valueProvider) {
if (form.Publication == "Publish" || !contentTypeSettings.Draftable) {
_contentManager.Publish(contentItem);
}

return contentItem;
}

Expand All @@ -307,8 +397,8 @@ public void RegisterClientValidationAttributes(FormElement element, RegisterClie
}

private static void InvokePartBindings(
ContentItem contentItem,
IEnumerable<ContentPartBindingDescriptor> lookup,
ContentItem contentItem,
IEnumerable<ContentPartBindingDescriptor> lookup,
PartBindingSettings partBindingSettings,
string value) {

Expand Down Expand Up @@ -348,7 +438,7 @@ private static void InvokeFieldBindings(
if (field == null)
return;

var fieldBindingDescriptorsQuery =
var fieldBindingDescriptorsQuery =
from partBindingDescriptor in lookup
where partBindingDescriptor.Part.PartDefinition.Name == partBindingSettings.Name
from fieldBindingDescriptor in partBindingDescriptor.FieldBindings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.IO;
using System.Web.Mvc;
using Orchard.Collections;
using Orchard.ContentManagement;
Expand All @@ -20,6 +21,7 @@ public interface IFormService : IDependency {
Submission CreateSubmission(Submission submission);
Submission GetSubmission(int id);
IPageOfItems<Submission> GetSubmissions(string formName = null, int? skip = null, int? take = null);
Stream ExportSubmissions(string formName = null);
BenedekFarkas marked this conversation as resolved.
Show resolved Hide resolved
void DeleteSubmission(Submission submission);
int DeleteSubmissions(IEnumerable<int> submissionIds);
void ReadElementValues(FormElement element, ReadElementValuesContext context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
dataColumns.Add(Model.Submissions.Columns[i]);
}
}
<div class="manage">
@Html.ActionLink(T("Export").Text, "Export", "SubmissionAdmin", new { id = Model.FormName, area = "Orchard.DynamicForms" }, new { @class = "button primaryAction" })
</div>
@using (Html.BeginFormAntiForgeryPost()) {
<fieldset class="bulk-actions">
<label for="publishActions">@T("Actions:")</label>
Expand Down
4 changes: 3 additions & 1 deletion src/Orchard.Web/Modules/Orchard.DynamicForms/packages.config
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="DocumentFormat.OpenXml" version="3.0.2" targetFramework="net48" />
<package id="DocumentFormat.OpenXml.Framework" version="3.0.2" targetFramework="net48" />
<package id="Microsoft.AspNet.Mvc" version="5.2.7" targetFramework="net48" />
<package id="Microsoft.AspNet.Razor" version="3.2.7" targetFramework="net48" />
<package id="Microsoft.AspNet.WebPages" version="3.2.7" targetFramework="net48" />
<package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="4.1.0" targetFramework="net48" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net48" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net48" />
</packages>
</packages>
Loading