From 97607bc83b0f027eb65b976b6fe355ec2dd01b40 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 22 Feb 2018 15:32:33 +0100 Subject: [PATCH] Added front and back end logic to export member data --- src/Umbraco.Core/Models/PropertyType.cs | 3 +- src/Umbraco.Core/Services/IMemberService.cs | 5 +- src/Umbraco.Core/Services/MemberService.cs | 89 ++++++++++++- .../src/common/resources/member.resource.js | 118 +++++++++++------- .../src/views/member/edit.html | 12 +- .../views/member/member.edit.controller.js | 6 + src/Umbraco.Web/Editors/MemberController.cs | 10 ++ 7 files changed, 190 insertions(+), 53 deletions(-) diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs index 0aebcb6544..492051fca7 100644 --- a/src/Umbraco.Core/Models/PropertyType.cs +++ b/src/Umbraco.Core/Models/PropertyType.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics; -using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Text.RegularExpressions; @@ -436,4 +435,4 @@ namespace Umbraco.Core.Models return clone; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Services/IMemberService.cs b/src/Umbraco.Core/Services/IMemberService.cs index 9153d23da9..ea214bbe76 100644 --- a/src/Umbraco.Core/Services/IMemberService.cs +++ b/src/Umbraco.Core/Services/IMemberService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Net.Http; using System.Xml.Linq; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; @@ -199,6 +200,8 @@ namespace Umbraco.Core.Services /// Id of the MemberType void DeleteMembersOfType(int memberTypeId); + HttpResponseMessage ExportMemberData(Guid key); + [Obsolete("Use the overload with 'long' parameter types instead")] [EditorBrowsable(EditorBrowsableState.Never)] IEnumerable FindMembersByDisplayName(string displayNameToMatch, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith); @@ -249,4 +252,4 @@ namespace Umbraco.Core.Services /// IEnumerable GetMembersByPropertyValue(string propertyTypeAlias, DateTime value, ValuePropertyMatchType matchType = ValuePropertyMatchType.Exact); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index f22c63e652..1a88f0b714 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.IO; using System.Threading; using System.Web.Security; using System.Xml.Linq; @@ -14,6 +15,9 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; using Umbraco.Core.Security; namespace Umbraco.Core.Services @@ -253,6 +257,89 @@ namespace Umbraco.Core.Services } } } + /// + /// Exports Member data by unique key + /// + /// + /// + public HttpResponseMessage ExportMemberData(Guid key) + { + + //Filters + List memberPropFilter = new List + { + "RawPasswordValue","ParentId","SortOrder", "Level", "Path", "CreatorId", "Version", "ContentTypeId", "HasIdentity", + "PropertyGroups", "PropertyTypes", "ProviderUserKey", "ContentType" + }; + + List propertiesFilter = new List + { + "PropertyType", "Version", "Id", "HasIdentity", "Key" + }; + + + //Get the member + var member = GetByKey(key); + var memberProperties = member.GetType().GetProperties(); + + string fileName = member.Name + "_" + member.Email + ".txt"; + + using (MemoryStream ms = new MemoryStream()) + { + using (TextWriter tw = new StreamWriter(ms)) + { + foreach (var memberProp in memberProperties) + { + if (memberPropFilter.Contains(memberProp.Name)) continue; + + var propValue = memberProp.GetValue(member, null); + var type = propValue?.GetType(); + + if (type == typeof(PropertyCollection)) + { + tw.WriteLine(""); + tw.WriteLine("PROPERTIES"); + tw.WriteLine("**********"); + + if (propValue is PropertyCollection pc) + foreach (var prop in pc) + { + var propProperties = prop.GetType().GetProperties(); + + //Writing the proerty name + tw.WriteLine("Name : " + prop.PropertyType.Name); + + foreach (var p in propProperties) + { + if (propertiesFilter.Contains(p.Name)) continue; + var pValue = p.GetValue(prop, null); + tw.WriteLine(p.Name + " : " + pValue); + } + + tw.WriteLine("------------------------"); + } + } + else + { + tw.WriteLine(memberProp.Name + " : " + propValue); + } + } + + tw.Flush(); + } + + HttpResponseMessage httpResponseMessage = new HttpResponseMessage(); + httpResponseMessage.Content = new ByteArrayContent(ms.ToArray()); + httpResponseMessage.Content.Headers.Add("x-filename", fileName); + httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); + httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); + httpResponseMessage.Content.Headers.ContentDisposition.FileName = fileName; + httpResponseMessage.StatusCode = HttpStatusCode.OK; + + return httpResponseMessage; + } + } + [Obsolete("Use the overload with 'long' parameter types instead")] [EditorBrowsable(EditorBrowsableState.Never)] @@ -588,7 +675,7 @@ namespace Umbraco.Core.Services Mandate.ParameterCondition(pageIndex >= 0, "pageIndex"); Mandate.ParameterCondition(pageSize > 0, "pageSize"); - using (var uow = UowProvider.GetUnitOfWork(readOnly:true)) + using (var uow = UowProvider.GetUnitOfWork(readOnly: true)) { var repository = RepositoryFactory.CreateMemberRepository(uow); return repository.GetPagedXmlEntriesByPath("-1", pageIndex, pageSize, null, out totalRecords); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/member.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/member.resource.js index 2073307db9..f98d30ee69 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/member.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/member.resource.js @@ -10,8 +10,8 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) { return umbRequestHelper.postSaveContent({ restApiUrl: umbRequestHelper.getApiUrl( - "memberApiBaseUrl", - "PostSave"), + "memberApiBaseUrl", + "PostSave"), content: content, action: action, files: files, @@ -67,35 +67,35 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) { } var params = [ - { pageNumber: options.pageNumber }, - { pageSize: options.pageSize }, - { orderBy: options.orderBy }, - { orderDirection: options.orderDirection }, - { orderBySystemField: toBool(options.orderBySystemField) }, - { filter: options.filter } + { pageNumber: options.pageNumber }, + { pageSize: options.pageSize }, + { orderBy: options.orderBy }, + { orderDirection: options.orderDirection }, + { orderBySystemField: toBool(options.orderBySystemField) }, + { filter: options.filter } ]; if (memberTypeAlias != null) { params.push({ memberTypeAlias: memberTypeAlias }); } return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "memberApiBaseUrl", - "GetPagedResults", - params)), - 'Failed to retrieve member paged result'); + $http.get( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "GetPagedResults", + params)), + 'Failed to retrieve member paged result'); }, getListNode: function (listName) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "memberApiBaseUrl", - "GetListNodeDisplay", - [{ listName: listName }])), - 'Failed to retrieve data for member list ' + listName); + $http.get( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "GetListNodeDisplay", + [{ listName: listName }])), + 'Failed to retrieve data for member list ' + listName); }, /** @@ -122,12 +122,12 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) { getByKey: function (key) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "memberApiBaseUrl", - "GetByKey", - [{ key: key }])), - 'Failed to retrieve data for member id ' + key); + $http.get( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "GetByKey", + [{ key: key }])), + 'Failed to retrieve data for member id ' + key); }, /** @@ -152,12 +152,12 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) { */ deleteByKey: function (key) { return umbRequestHelper.resourcePromise( - $http.post( - umbRequestHelper.getApiUrl( - "memberApiBaseUrl", - "DeleteByKey", - [{ key: key }])), - 'Failed to delete item ' + key); + $http.post( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "DeleteByKey", + [{ key: key }])), + 'Failed to delete item ' + key); }, /** @@ -194,20 +194,20 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) { if (alias) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "memberApiBaseUrl", - "GetEmpty", - [{ contentTypeAlias: alias }])), - 'Failed to retrieve data for empty member item type ' + alias); + $http.get( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "GetEmpty", + [{ contentTypeAlias: alias }])), + 'Failed to retrieve data for empty member item type ' + alias); } else { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "memberApiBaseUrl", - "GetEmpty")), - 'Failed to retrieve data for empty member item type ' + alias); + $http.get( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "GetEmpty")), + 'Failed to retrieve data for empty member item type ' + alias); } }, @@ -242,6 +242,38 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) { */ save: function (member, isNew, files) { return saveMember(member, "save" + (isNew ? "New" : ""), files); + }, + + exportMemberData: function (key) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "memberApiBaseUrl", + "ExportMemberData", + [{ key: key }])) + .success(function (data, status, headers) { + + headers = headers(); + + var filename = headers['x-filename']; + var contentType = headers['content-type']; + + var linkElement = document.createElement('a'); + + var blob = new Blob([data], { type: contentType }); + var url = window.URL.createObjectURL(blob); + + linkElement.setAttribute('href', url); + linkElement.setAttribute("download", filename); + + var clickEvent = new MouseEvent("click", { + "view": window, + "bubbles": true, + "cancelable": false + }); + + linkElement.dispatchEvent(clickEvent); + })); } }; } diff --git a/src/Umbraco.Web.UI.Client/src/views/member/edit.html b/src/Umbraco.Web.UI.Client/src/views/member/edit.html index d72750c770..3008786d3a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/member/edit.html @@ -20,13 +20,13 @@ - + + + + + - - - - - + diff --git a/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js index 215cd7777c..842590c4e7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/member/member.edit.controller.js @@ -17,6 +17,7 @@ function MemberEditController($scope, $routeParams, $location, $q, $window, appS $scope.page.nameLocked = false; $scope.page.listViewPath = null; $scope.page.saveButtonState = "init"; + $scope.page.exportButton = "init"; $scope.busy = false; $scope.page.listViewPath = ($routeParams.page && $routeParams.listName) @@ -171,6 +172,11 @@ function MemberEditController($scope, $routeParams, $location, $q, $window, appS }; + $scope.export = function() { + var memberKey = $scope.content.key; + memberResource.exportMemberData(memberKey); + } + } angular.module("umbraco").controller("Umbraco.Editors.Member.EditController", MemberEditController); diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs index 2440ad0691..3ad4d05eb7 100644 --- a/src/Umbraco.Web/Editors/MemberController.cs +++ b/src/Umbraco.Web/Editors/MemberController.cs @@ -774,5 +774,15 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.OK); } + /// + /// Exports member data + /// + /// + /// + [HttpGet] + public HttpResponseMessage ExportMemberData(Guid key) + { + return Services.MemberService.ExportMemberData(key); + } } }