diff --git a/src/Umbraco.Core/Services/IMemberService.cs b/src/Umbraco.Core/Services/IMemberService.cs
index d57e110a16..021b0c4fbe 100644
--- a/src/Umbraco.Core/Services/IMemberService.cs
+++ b/src/Umbraco.Core/Services/IMemberService.cs
@@ -199,15 +199,7 @@ namespace Umbraco.Core.Services
///
/// Id of the MemberType
void DeleteMembersOfType(int memberTypeId);
-
- ///
- /// Exports member data based on their unique Id
- ///
- /// The unique member identifier
- /// The user requesting the export
- ///
- HttpResponseMessage ExportMemberData(Guid key, IUser currentUser);
-
+
[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);
diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs
index 71751c4d4e..33563e5285 100644
--- a/src/Umbraco.Core/Services/MemberService.cs
+++ b/src/Umbraco.Core/Services/MemberService.cs
@@ -258,108 +258,6 @@ namespace Umbraco.Core.Services
}
}
- ///
- /// Exports member data based on their unique Id
- ///
- /// The unique member identifier
- /// The user requesting the export
- ///
- public HttpResponseMessage ExportMemberData(Guid key, IUser currentUser)
- {
- var httpResponseMessage = new HttpResponseMessage();
- if (currentUser.HasAccessToSensitiveData() == false)
- {
- httpResponseMessage.StatusCode = HttpStatusCode.Forbidden;
- return httpResponseMessage;
- }
-
- var memberPropertyFilter = new List
- {
- "RawPasswordValue",
- "ParentId",
- "SortOrder",
- "Level",
- "Path",
- "CreatorId",
- "Version",
- "ContentTypeId",
- "HasIdentity",
- "PropertyGroups",
- "PropertyTypes",
- "ProviderUserKey",
- "ContentType"
- };
- var propertiesFilter = new List
- {
- "PropertyType",
- "Version",
- "Id",
- "HasIdentity",
- "Key"
- };
-
- var member = GetByKey(key);
- var memberProperties = member.GetType().GetProperties();
- var fileName = $"{member.Name}_{member.Email}.txt";
-
- using (var memoryStream = new MemoryStream())
- {
- using (var textWriter = new StreamWriter(memoryStream))
- {
- foreach (var memberProperty in memberProperties)
- {
- if (memberPropertyFilter.Contains(memberProperty.Name))
- continue;
-
- var propertyValue = memberProperty.GetValue(member, null);
- var type = propertyValue?.GetType();
-
- if (type == typeof(PropertyCollection))
- {
- textWriter.WriteLine("");
- textWriter.WriteLine("PROPERTIES");
- textWriter.WriteLine("**********");
-
- if (propertyValue is PropertyCollection propertyCollection)
- {
- foreach (var property in propertyCollection)
- {
- var propProperties = property.GetType().GetProperties();
-
- textWriter.WriteLine("Name : " + property.PropertyType.Name);
-
- foreach (var p in propProperties)
- {
- if (propertiesFilter.Contains(p.Name)) continue;
- var pValue = p.GetValue(property, null);
- textWriter.WriteLine(p.Name + " : " + pValue);
- }
-
- textWriter.WriteLine("------------------------");
- }
- }
- }
- else
- {
- textWriter.WriteLine(memberProperty.Name + " : " + propertyValue);
- }
- }
-
- textWriter.Flush();
- }
-
- httpResponseMessage.Content = new ByteArrayContent(memoryStream.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)]
public IEnumerable FindMembersByDisplayName(string displayNameToMatch, int pageIndex, int pageSize, out int totalRecords, StringPropertyMatchType matchType = StringPropertyMatchType.StartsWith)
diff --git a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs
index 0cd03df990..22d150ac3b 100644
--- a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs
+++ b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs
@@ -293,7 +293,7 @@ AnotherContentFinder
public void Resolves_Actions()
{
var actions = _manager.ResolveActions();
- Assert.AreEqual(39, actions.Count());
+ Assert.AreEqual(38, actions.Count());
}
[Test]
diff --git a/src/Umbraco.Web/Editors/MemberController.cs b/src/Umbraco.Web/Editors/MemberController.cs
index 24035db814..96233592ac 100644
--- a/src/Umbraco.Web/Editors/MemberController.cs
+++ b/src/Umbraco.Web/Editors/MemberController.cs
@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
+using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
@@ -783,8 +786,132 @@ namespace Umbraco.Web.Editors
[HttpGet]
public HttpResponseMessage ExportMemberData(Guid key)
{
- var currentUser = UmbracoContext.Current.Security.CurrentUser;
- return Services.MemberService.ExportMemberData(key, currentUser);
+ var currentUser = Security.CurrentUser;
+
+ var httpResponseMessage = Request.CreateResponse();
+ if (currentUser.HasAccessToSensitiveData() == false)
+ {
+ httpResponseMessage.StatusCode = HttpStatusCode.Forbidden;
+ return httpResponseMessage;
+ }
+
+ var member = Services.MemberService.GetByKey(key);
+ var memberProperties = member.GetType().GetProperties().ToList();
+ //since we want to write the property types last, we'll re-order this list
+ var propertyTypesProperties = memberProperties.Where(x => x.PropertyType == typeof(PropertyCollection)).ToList();
+ foreach (var propertyType in propertyTypesProperties)
+ {
+ memberProperties.Remove(propertyType);
+ }
+ //now re-add them to the end (there will only be one, but we'll do this just to be complete)
+ foreach (var propertyTypesProperty in propertyTypesProperties)
+ {
+ memberProperties.Add(propertyTypesProperty);
+ }
+
+ var fileName = $"{member.Name}_{member.Email}.txt";
+
+ using (var memoryStream = new MemoryStream())
+ {
+ using (var textWriter = new StreamWriter(memoryStream))
+ {
+ foreach (var memberProperty in memberProperties)
+ {
+ ReportWriter.WritePropertyValue(member, memberProperty, textWriter);
+ }
+
+ textWriter.Flush();
+ }
+
+ httpResponseMessage.Content = new ByteArrayContent(memoryStream.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;
+ }
+
}
+
+ private static class ReportWriter
+ {
+ private static readonly List MemberPropertyFilter = new List
+ {
+ "RawPasswordValue",
+ "RawPasswordAnswerValue",
+ "ParentId",
+ "SortOrder",
+ "Level",
+ "Path",
+ "CreatorId",
+ "Version",
+ "ContentTypeId",
+ "HasIdentity",
+ "PropertyGroups",
+ "PropertyTypes",
+ "ProviderUserKey",
+ "ContentType",
+ "DeletedDate"
+ };
+ private static readonly List PropertiesFilter = new List
+ {
+ "PropertyType",
+ "Version",
+ "Id",
+ "HasIdentity",
+ "Key",
+ "DeletedDate"
+ };
+
+ public static void WritePropertyValue(object owner, PropertyInfo prop, StreamWriter textWriter)
+ {
+ if (owner == null) throw new ArgumentNullException(nameof(owner));
+ if (prop == null) throw new ArgumentNullException(nameof(prop));
+ if (textWriter == null) throw new ArgumentNullException(nameof(textWriter));
+ if (MemberPropertyFilter.Contains(prop.Name))
+ return;
+
+ var propertyValue = prop.GetValue(owner, null) ?? string.Empty;
+ var type = prop.PropertyType;
+
+ if (propertyValue is DateTime time)
+ {
+ textWriter.WriteLine(prop.Name + " : " + time.ToIsoString());
+ }
+ else if (type == typeof(PropertyCollection))
+ {
+ textWriter.WriteLine("");
+ textWriter.WriteLine("PROPERTIES");
+ textWriter.WriteLine("**********");
+
+ if (propertyValue is PropertyCollection propertyCollection)
+ {
+ foreach (var property in propertyCollection)
+ {
+ var propProperties = property.GetType().GetProperties();
+
+ textWriter.WriteLine("Name : " + property.PropertyType.Name);
+
+ foreach (var p in propProperties)
+ {
+ if (PropertiesFilter.Contains(p.Name)) continue;
+
+ WritePropertyValue(property, p, textWriter); //recurse
+ }
+
+ textWriter.WriteLine("------------------------");
+ }
+ }
+ }
+ else
+ {
+ textWriter.WriteLine(prop.Name + " : " + propertyValue);
+ }
+ }
+ }
+
+
}
}
diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs
index 78edd5e9a1..bed5a1e995 100644
--- a/src/Umbraco.Web/Trees/MemberTreeController.cs
+++ b/src/Umbraco.Web/Trees/MemberTreeController.cs
@@ -17,10 +17,10 @@ using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi.Filters;
using umbraco;
using umbraco.BusinessLogic.Actions;
-using umbraco.cms.Actions;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Search;
using Constants = Umbraco.Core.Constants;
+using Umbraco.Core.Services;
namespace Umbraco.Web.Trees
{
@@ -182,9 +182,12 @@ namespace Umbraco.Web.Trees
//add delete option for all members
menu.Items.Add(ui.Text("actions", ActionDelete.Instance.Alias));
- if (UmbracoContext.Current.Security.CurrentUser.HasAccessToSensitiveData())
+ if (Security.CurrentUser.HasAccessToSensitiveData())
{
- menu.Items.Add(ui.Text("actions", ActionExportMember.Instance.Alias));
+ menu.Items.Add(new MenuItem("export", Services.TextService.Localize("actions/export"))
+ {
+ Icon = "download-alt"
+ });
}
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index e9d00005ba..7dda5bedab 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -40,6 +40,7 @@
..\
true
+ latest
bin\Debug\
@@ -66,6 +67,7 @@
AllRules.ruleset
false
Off
+ latest
bin\Release\
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj.DotSettings b/src/Umbraco.Web/Umbraco.Web.csproj.DotSettings
index 662f95686e..c54c126d26 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj.DotSettings
+++ b/src/Umbraco.Web/Umbraco.Web.csproj.DotSettings
@@ -1,2 +1,2 @@
- CSharp50
\ No newline at end of file
+ CSharp70
\ No newline at end of file
diff --git a/src/umbraco.cms/Actions/ActionExportMember.cs b/src/umbraco.cms/Actions/ActionExportMember.cs
deleted file mode 100644
index ac00d0ec88..0000000000
--- a/src/umbraco.cms/Actions/ActionExportMember.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using System;
-using umbraco.BasePages;
-using umbraco.interfaces;
-
-namespace umbraco.cms.Actions
-{
- public class ActionExportMember: IAction
- {
- //create singleton
-#pragma warning disable 612, 618
- private static readonly ActionExportMember m_instance = new ActionExportMember();
-#pragma warning restore 612, 618
-
- ///
- /// A public constructor exists ONLY for backwards compatibility in regards to 3rd party add-ons.
- /// All Umbraco assemblies should use the singleton instantiation (this.Instance)
- /// When this applicatio is refactored, this constuctor should be made private.
- ///
- [Obsolete("Use the singleton instantiation instead of a constructor")]
- public ActionExportMember() { }
-
- public static ActionExportMember Instance
- {
- get { return m_instance; }
- }
-
- #region IAction Members
-
- public char Letter
- {
- get
- {
- return 'E';
- }
- }
-
- public string JsFunctionName
- {
- get
- {
- return string.Format("{0}.actionExportMember()", ClientTools.Scripts.GetAppActions);
- }
- }
-
- public string JsSource
- {
- get
- {
- return null;
- }
- }
-
- public string Alias
- {
- get
- {
- return "export";
- }
- }
-
- public string Icon
- {
- get
- {
- return "download-alt";
- }
- }
-
- public bool ShowInNotifier
- {
- get
- {
- return true;
- }
- }
- public bool CanBePermissionAssigned
- {
- get
- {
- return true;
- }
- }
- #endregion
-
- }
-}
diff --git a/src/umbraco.cms/umbraco.cms.csproj b/src/umbraco.cms/umbraco.cms.csproj
index 1f63c9dcd4..42db5b90ee 100644
--- a/src/umbraco.cms/umbraco.cms.csproj
+++ b/src/umbraco.cms/umbraco.cms.csproj
@@ -183,7 +183,6 @@
-