From 05e7e12614f134604b1a8e9769e3d002059f2b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gatis=20Berg=C5=A1pics?= Date: Mon, 3 Aug 2020 13:57:24 +0300 Subject: [PATCH 1/6] Adding query string capability to filter data by column and exact value --- .gitignore | 11 +++ CHANGELOG.md | 5 +- README.md | 2 +- src/Dds/Interfaces/ICrudService.cs | 2 +- src/Dds/Services/CrudService.cs | 38 +++++--- src/Dds/Services/StoreService.cs | 16 +++- .../Geta.DdsAdmin/Admin/Constants.cs | Bin 357 -> 926 bytes .../Geta.DdsAdmin/Admin/Data.ashx.cs | 10 ++- .../Geta.DdsAdmin/Admin/DdsAdmin.aspx | 12 +-- .../Geta.DdsAdmin/Admin/DdsAdmin.aspx.cs | 84 ++++++++++++++---- .../Admin/DdsAdmin.aspx.designer.cs | 18 ++-- 11 files changed, 149 insertions(+), 49 deletions(-) diff --git a/.gitignore b/.gitignore index 53321ba..704b4ec 100644 --- a/.gitignore +++ b/.gitignore @@ -199,3 +199,14 @@ FakesAssemblies/ License.config /Blomsterringen.Web/imagecache maildrop +.idea/.idea.Geta.DdsAdmin/riderModule.iml +.idea/.idea.Geta.DdsAdmin/.idea/vcs.xml +.idea/.idea.Geta.DdsAdmin/.idea/misc.xml +.idea/.idea.Geta.DdsAdmin/.idea/indexLayout.xml +.idea/.idea.Geta.DdsAdmin/.idea/.name +.idea/.idea.Geta.DdsAdmin/.idea/.idea.Geta.DdsAdmin.iml +.idea/.idea.Geta.DdsAdmin/.idea/contentModel.xml +.idea/.idea.Geta.DdsAdmin/.idea/modules.xml +.idea/.idea.Geta.DdsAdmin/.idea/projectSettingsUpdater.xml +.idea/.idea.Geta.DdsAdmin/.idea/workspace.xml +.idea/.idea.Geta.DdsAdmin/.idea/encodings.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fe2dea..d443551 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,4 +4,7 @@ All notable changes to this project will be documented in this file. ## [11.0.0] - Upgraded to CMS 11 - - Upgraded to framework version 4.6.1 \ No newline at end of file + - Upgraded to framework version 4.6.1 + + ## [11.0.4.6] + - Adding query string capability to filter data by column and exact value \ No newline at end of file diff --git a/README.md b/README.md index 716ae7c..a34b13b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Where can I get it? 1. Add [EPiServer Nuget feed](http://nuget.episerver.com/en/Feed/) in Visual Studio Package Manager options (Tools ->Options -> Package Manager -> Package Sources). 1. Install DDSAdmin via NuGet in Visual Studio. Ensure that you also install the required dependencies. 1. Rebuild your solution. -1. Start editing your data in EPiServer DDS storages. It will be available in CMS menu: Geta->DDS Admin. +1. Start editing your data in EPiServer DDS storage's. It will be available in CMS menu: Geta->DDS Admin. Where can I get more info? ------------------------------ diff --git a/src/Dds/Interfaces/ICrudService.cs b/src/Dds/Interfaces/ICrudService.cs index efd5fc5..9d0697e 100644 --- a/src/Dds/Interfaces/ICrudService.cs +++ b/src/Dds/Interfaces/ICrudService.cs @@ -7,7 +7,7 @@ public interface ICrudService { StringResponse Create(string storeName, Dictionary values); StringResponse Delete(string storeName, string identityId); - ReadResponse Read(string storeName, int start, int pageSize, string search, int sortByColumn, string sortDirection); + ReadResponse Read(string storeName, int start, int pageSize, string search, int sortByColumn, string sortDirection, string filterColumnName, string filter); StringResponse Update(string storeName, int columnId, string value, string id, string columnName); } } diff --git a/src/Dds/Services/CrudService.cs b/src/Dds/Services/CrudService.cs index 5a36243..26b7377 100644 --- a/src/Dds/Services/CrudService.cs +++ b/src/Dds/Services/CrudService.cs @@ -74,7 +74,7 @@ public StringResponse Delete(string storeName, string identityId) return response; } - public ReadResponse Read(string storeName, int start, int pageSize, string search, int sortByColumn, string sortDirection) + public ReadResponse Read(string storeName, int start, int pageSize, string search, int sortByColumn, string sortDirection, string filterColumnName, string filter) { var response = new ReadResponse { Success = false }; logger.Debug("Read started"); @@ -83,22 +83,38 @@ public ReadResponse Read(string storeName, int start, int pageSize, string searc var store = DynamicDataStoreFactory.Instance.GetStore(storeName); var storeMetadata = store.Metadata(); - // TODO: we cannot order here due to fact that this is PropertyBag, if we could it would be great performance boost - // var orderBy = sortByColumn == 0 ? "Id" : StoreMetadata.Columns.ToList()[sortByColumn - 1].PropertyName; - var query = store.ItemsAsPropertyBag(); // .OrderBy(orderBy); + List data; + int count; - var data = sortByColumn == 0 && string.IsNullOrEmpty(search) - ? (sortDirection == "asc" - ? query.OrderBy(r => r.Id).Skip(start).Take(pageSize).ToList() - : query.OrderByDescending(r => r.Id).Skip(start).Take(pageSize).ToList()) - : query.ToList(); + var preSorted = false; + + if (string.IsNullOrWhiteSpace(filterColumnName) || string.IsNullOrWhiteSpace(filter)) + { + // TODO: we cannot order here due to fact that this is PropertyBag, if we could it would be great performance boost + // var orderBy = sortByColumn == 0 ? "Id" : StoreMetadata.Columns.ToList()[sortByColumn - 1].PropertyName; + var query = store.ItemsAsPropertyBag(); // .OrderBy(orderBy); + + preSorted = sortByColumn == 0 && string.IsNullOrEmpty(search); + + data = preSorted + ? (sortDirection == "asc" + ? query.OrderBy(r => r.Id).Skip(start).Take(pageSize).ToList() + : query.OrderByDescending(r => r.Id).Skip(start).Take(pageSize).ToList()) + : query.ToList(); + count = query.Count(); + } + else + { + data = store.FindAsPropertyBag(filterColumnName, filter).ToList(); + count = data.Count; + } List> stringData; - if (sortByColumn == 0 && string.IsNullOrEmpty(search)) + if (preSorted) { // no sorting and no filtering, use fast code then stringData = FormatData(storeMetadata, data); - totalCount = query.Count(); + totalCount = count; } else { diff --git a/src/Dds/Services/StoreService.cs b/src/Dds/Services/StoreService.cs index a308dfc..6258af6 100644 --- a/src/Dds/Services/StoreService.cs +++ b/src/Dds/Services/StoreService.cs @@ -80,12 +80,24 @@ public bool Delete(string storeName, Identity id) } } - public bool Flush(string storeName) + public bool Flush(string storeName, string filterColumnName, string filter) { try { var store = DynamicDataStoreFactory.Instance.GetStore(storeName); - store.DeleteAll(); + if (!string.IsNullOrWhiteSpace(filterColumnName) && !string.IsNullOrWhiteSpace(filter)) + { + var itemsToBeDeleted = store.FindAsPropertyBag(filterColumnName, filter).ToList(); + foreach (var itemToBeDeleted in itemsToBeDeleted) + { + store.Delete(itemToBeDeleted.Id); + } + } + else + { + store.DeleteAll(); + } + return true; } catch (Exception ex) diff --git a/src/modules/_protected/Geta.DdsAdmin/Admin/Constants.cs b/src/modules/_protected/Geta.DdsAdmin/Admin/Constants.cs index c7459a5d0498ea91d81836a77529c4f459b3b42a..c1d642f67226db484af78ca58de67a4b87ebb719 100644 GIT binary patch literal 926 zcmb`GO$x#=5QX2`Q-}xf0^&jx6+~Tlg4hNXS{0)U5wEU(X)VTppj1La=BMxF%}j51 zZDo|p%4yFn;nviM^N7{fKq-+PXRZyo?cmYLb_-7&Z>bZo|IWC8X$Pa#JJZWw zmtlyK3>L4;S1zNN(s%8h_fz&iUr{kuRSh}o@{Cbrm^D9Xt4Q0L4%X||7~GU) fK@V%`BBnCWMmcrar||kez@RMdTOMh - <%# CurrentStoreName %> + <%# CurrentStoreName %> <%# CurrentFilterMessage%> " + Text = "" }); Page.Header.Controls.Add(new Literal { - Text = "" + Text = "" }); } @@ -87,9 +95,28 @@ protected string SetItem(RepeaterItem repeaterItem) return string.Empty; } + protected string GetParameters() + { + var result = $"{Constants.StoreKey}={CurrentStoreName}"; + + if (CurrentFilterMessage != null) + { + result += $"&{Constants.FilterColumnNameKey}={CurrentFilterColumnName}"; + result += $"&{Constants.FilterKey}={CurrentFilter}"; + } + + return result; + } + private void GetQueryStringParameters() { CurrentStoreName = HttpUtility.HtmlEncode(Request.QueryString[Constants.StoreKey]); + CurrentFilterColumnName = HttpUtility.HtmlEncode(Request.QueryString[Constants.FilterColumnNameKey]); + CurrentFilter = HttpUtility.HtmlEncode(Request.QueryString[Constants.FilterKey]); + CurrentFilterMessage = + !string.IsNullOrWhiteSpace(CurrentFilterColumnName) && !string.IsNullOrWhiteSpace(CurrentFilter) + ? $"filtered by {CurrentFilterColumnName} = {CurrentFilter}" + : null; CustomHeading = HttpUtility.HtmlEncode(Request.QueryString[Constants.HeadingKey]); CustomMessage = HttpUtility.HtmlEncode(Request.QueryString[Constants.MessageKey]); @@ -101,10 +128,10 @@ private void GetQueryStringParameters() } HiddenColumns = hiddenColumns.Split(new[] - { - "," - }, - StringSplitOptions.RemoveEmptyEntries).Select(item => Convert.ToInt32(item)).ToArray(); + { + "," + }, + StringSplitOptions.RemoveEmptyEntries).Select(item => Convert.ToInt32(item)).ToArray(); } private void LoadAndDisplayData() @@ -127,12 +154,23 @@ private void LoadAndDisplayData() repForm.DataSource = Store.Columns; repColumnsHeader.DataBind(); repForm.DataBind(); + + Flush.Text = string.IsNullOrWhiteSpace(CurrentFilterMessage) ? "Delete all data" : "Delete filtered data"; + Flush.OnClientClick = string.IsNullOrWhiteSpace(CurrentFilterMessage) + ? "return confirm('Do you really want to delete all data from this table?')" + : "return confirm('Do you really want to delete filtered data from this table?')"; + Export.Text = string.IsNullOrWhiteSpace(CurrentFilterMessage) + ? "Export to Excel" + : "Export filtered data to Excel"; } protected void FlushStore(object sender, EventArgs e) { var storeName = Request.Form["CurrentStoreName"]; - _storeService.Flush(storeName); + var filterColumnName = Request.Form["CurrentFilterColumnName"]; + var filter = Request.Form["CurrentFilter"]; + + _storeService.Flush(storeName, filterColumnName, filter); Response.Redirect(Request.RawUrl); } @@ -140,7 +178,10 @@ protected void FlushStore(object sender, EventArgs e) protected void ExportStore(object sender, EventArgs e) { var storeName = Request.Form["CurrentStoreName"]; - var ddsDataSet = GetDdsStoreAsDataSet(storeName); + var filterColumnName = Request.Form["CurrentFilterColumnName"]; + var filter = Request.Form["CurrentFilter"]; + + var ddsDataSet = GetDdsStoreAsDataSet(storeName, filterColumnName, filter); using (var wb = new XLWorkbook()) { @@ -150,7 +191,18 @@ protected void ExportStore(object sender, EventArgs e) Response.Buffer = true; Response.Charset = ""; Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; - Response.AddHeader("content-disposition", $"attachment;filename={storeName}.xlsx"); + if (!string.IsNullOrWhiteSpace(filterColumnName) && !string.IsNullOrWhiteSpace(filter)) + { + var fileName = $"{storeName}.{filterColumnName}.{filter}.xlsx"; + Path.GetInvalidFileNameChars() + .Aggregate(fileName, (current, c) => current.Replace(c, '_')); + Response.AddHeader("content-disposition", $"attachment;filename={fileName.Substring(0, Math.Min(fileName.Length, 200))}"); + } + else + { + Response.AddHeader("content-disposition", $"attachment;filename={storeName}.xlsx"); + } + using (var MyMemoryStream = new MemoryStream()) { wb.SaveAs(MyMemoryStream); @@ -161,7 +213,7 @@ protected void ExportStore(object sender, EventArgs e) } } - private DataSet GetDdsStoreAsDataSet(string storeName) + private DataSet GetDdsStoreAsDataSet(string storeName, string filterColumnName, string filter) { var dataTable = new DataTable("record"); @@ -169,12 +221,12 @@ private DataSet GetDdsStoreAsDataSet(string storeName) foreach (var column in columns.Columns) { - dataTable.Columns.Add(column.PropertyName, typeof (string)); + dataTable.Columns.Add(column.PropertyName, typeof(string)); } - var allRecords = _crudService.Read(storeName, 0, int.MaxValue, null, 0, null); + var allRecords = _crudService.Read(storeName, 0, int.MaxValue, null, 0, null, filterColumnName, filter); - if (allRecords == null || !allRecords.Success || allRecords.TotalCount == 0) + if (allRecords == null || !allRecords.Success) { return null; } @@ -203,4 +255,4 @@ private DataSet GetDdsStoreAsDataSet(string storeName) }; } } -} +} \ No newline at end of file diff --git a/src/modules/_protected/Geta.DdsAdmin/Admin/DdsAdmin.aspx.designer.cs b/src/modules/_protected/Geta.DdsAdmin/Admin/DdsAdmin.aspx.designer.cs index 946160b..0655fb1 100644 --- a/src/modules/_protected/Geta.DdsAdmin/Admin/DdsAdmin.aspx.designer.cs +++ b/src/modules/_protected/Geta.DdsAdmin/Admin/DdsAdmin.aspx.designer.cs @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ // // This code was generated by a tool. // @@ -17,7 +17,7 @@ public partial class DdsAdmin { /// /// /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. + /// To modify, move the field declaration from the designer file to a code-behind file. /// protected global::System.Web.UI.HtmlControls.HtmlHead head1; @@ -26,7 +26,7 @@ public partial class DdsAdmin { /// /// /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. + /// To modify, move the field declaration from the designer file to a code-behind file. /// protected global::System.Web.UI.WebControls.Panel hdivNoStoreTypeSelected; @@ -35,7 +35,7 @@ public partial class DdsAdmin { /// /// /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. + /// To modify, move the field declaration from the designer file to a code-behind file. /// protected global::System.Web.UI.WebControls.Panel hdivStoreTypeDoesntExist; @@ -44,7 +44,7 @@ public partial class DdsAdmin { /// /// /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. + /// To modify, move the field declaration from the designer file to a code-behind file. /// protected global::System.Web.UI.WebControls.Panel hdivStoreTypeSelected; @@ -53,7 +53,7 @@ public partial class DdsAdmin { /// /// /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. + /// To modify, move the field declaration from the designer file to a code-behind file. /// protected global::System.Web.UI.WebControls.Repeater repForm; @@ -62,7 +62,7 @@ public partial class DdsAdmin { /// /// /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. + /// To modify, move the field declaration from the designer file to a code-behind file. /// protected global::System.Web.UI.WebControls.Button Flush; @@ -71,7 +71,7 @@ public partial class DdsAdmin { /// /// /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. + /// To modify, move the field declaration from the designer file to a code-behind file. /// protected global::System.Web.UI.WebControls.Button Export; @@ -80,7 +80,7 @@ public partial class DdsAdmin { /// /// /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. + /// To modify, move the field declaration from the designer file to a code-behind file. /// protected global::System.Web.UI.WebControls.Repeater repColumnsHeader; } From b350e4d3e12b15eb85f3e90cf711f073e0060b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gatis=20Berg=C5=A1pics?= Date: Mon, 10 Aug 2020 14:19:45 +0300 Subject: [PATCH 2/6] Update .gitignore --- .gitignore | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 704b4ec..c1312c6 100644 --- a/.gitignore +++ b/.gitignore @@ -129,7 +129,7 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings +# TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj @@ -199,14 +199,4 @@ FakesAssemblies/ License.config /Blomsterringen.Web/imagecache maildrop -.idea/.idea.Geta.DdsAdmin/riderModule.iml -.idea/.idea.Geta.DdsAdmin/.idea/vcs.xml -.idea/.idea.Geta.DdsAdmin/.idea/misc.xml -.idea/.idea.Geta.DdsAdmin/.idea/indexLayout.xml -.idea/.idea.Geta.DdsAdmin/.idea/.name -.idea/.idea.Geta.DdsAdmin/.idea/.idea.Geta.DdsAdmin.iml -.idea/.idea.Geta.DdsAdmin/.idea/contentModel.xml -.idea/.idea.Geta.DdsAdmin/.idea/modules.xml -.idea/.idea.Geta.DdsAdmin/.idea/projectSettingsUpdater.xml -.idea/.idea.Geta.DdsAdmin/.idea/workspace.xml -.idea/.idea.Geta.DdsAdmin/.idea/encodings.xml +.idea From 3a8e957f872a94da0334e1bf21f31c2f1f9e0af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gatis=20Berg=C5=A1pics?= Date: Mon, 10 Aug 2020 16:00:01 +0300 Subject: [PATCH 3/6] code review --- src/Dds/Interfaces/ICrudService.cs | 2 +- src/Dds/Services/CrudService.cs | 6 +++--- src/Dds/Services/StoreService.cs | 2 +- .../_protected/Geta.DdsAdmin/Admin/DdsAdmin.aspx | 10 +++++----- .../Geta.DdsAdmin/Admin/DdsAdmin.aspx.cs | 14 ++++++++++---- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/Dds/Interfaces/ICrudService.cs b/src/Dds/Interfaces/ICrudService.cs index 9d0697e..c7129be 100644 --- a/src/Dds/Interfaces/ICrudService.cs +++ b/src/Dds/Interfaces/ICrudService.cs @@ -7,7 +7,7 @@ public interface ICrudService { StringResponse Create(string storeName, Dictionary values); StringResponse Delete(string storeName, string identityId); - ReadResponse Read(string storeName, int start, int pageSize, string search, int sortByColumn, string sortDirection, string filterColumnName, string filter); + ReadResponse Read(string storeName, int start, int pageSize, string search, int sortByColumn, string sortDirection, string filterByColumn, string filterValue); StringResponse Update(string storeName, int columnId, string value, string id, string columnName); } } diff --git a/src/Dds/Services/CrudService.cs b/src/Dds/Services/CrudService.cs index 26b7377..0c8a0c3 100644 --- a/src/Dds/Services/CrudService.cs +++ b/src/Dds/Services/CrudService.cs @@ -74,7 +74,7 @@ public StringResponse Delete(string storeName, string identityId) return response; } - public ReadResponse Read(string storeName, int start, int pageSize, string search, int sortByColumn, string sortDirection, string filterColumnName, string filter) + public ReadResponse Read(string storeName, int start, int pageSize, string search, int sortByColumn, string sortDirection, string filterByColumn, string filterValue) { var response = new ReadResponse { Success = false }; logger.Debug("Read started"); @@ -88,7 +88,7 @@ public ReadResponse Read(string storeName, int start, int pageSize, string searc var preSorted = false; - if (string.IsNullOrWhiteSpace(filterColumnName) || string.IsNullOrWhiteSpace(filter)) + if (string.IsNullOrWhiteSpace(filterByColumn)) { // TODO: we cannot order here due to fact that this is PropertyBag, if we could it would be great performance boost // var orderBy = sortByColumn == 0 ? "Id" : StoreMetadata.Columns.ToList()[sortByColumn - 1].PropertyName; @@ -105,7 +105,7 @@ public ReadResponse Read(string storeName, int start, int pageSize, string searc } else { - data = store.FindAsPropertyBag(filterColumnName, filter).ToList(); + data = store.FindAsPropertyBag(filterByColumn, filterValue).ToList(); count = data.Count; } diff --git a/src/Dds/Services/StoreService.cs b/src/Dds/Services/StoreService.cs index 6258af6..e4dc0d5 100644 --- a/src/Dds/Services/StoreService.cs +++ b/src/Dds/Services/StoreService.cs @@ -85,7 +85,7 @@ public bool Flush(string storeName, string filterColumnName, string filter) try { var store = DynamicDataStoreFactory.Instance.GetStore(storeName); - if (!string.IsNullOrWhiteSpace(filterColumnName) && !string.IsNullOrWhiteSpace(filter)) + if (!string.IsNullOrWhiteSpace(filterColumnName)) { var itemsToBeDeleted = store.FindAsPropertyBag(filterColumnName, filter).ToList(); foreach (var itemToBeDeleted in itemsToBeDeleted) diff --git a/src/modules/_protected/Geta.DdsAdmin/Admin/DdsAdmin.aspx b/src/modules/_protected/Geta.DdsAdmin/Admin/DdsAdmin.aspx index 4ecd995..21ce4d8 100644 --- a/src/modules/_protected/Geta.DdsAdmin/Admin/DdsAdmin.aspx +++ b/src/modules/_protected/Geta.DdsAdmin/Admin/DdsAdmin.aspx @@ -86,23 +86,23 @@