Port 7.7 - WIP

This commit is contained in:
Stephan
2017-09-19 15:51:47 +02:00
parent d54658009c
commit 9ed6576908
126 changed files with 3447 additions and 596 deletions

View File

@@ -20,8 +20,21 @@ namespace Umbraco.Web.Models
public string OldPassword { get; set; }
/// <summary>
/// Set to true if the password is to be reset - only valid when: EnablePasswordReset = true
/// Set to true if the password is to be reset
/// </summary>
/// <remarks>
/// <para>
/// This operator is different between using ASP.NET Identity APIs and Membership APIs.
/// </para>
/// <para>
/// When using Membership APIs, this is only valid when: EnablePasswordReset = true and it will reset the password to something auto generated.
/// </para>
/// <para>
/// When using ASP.NET Identity APIs this needs to be set if an administrator user that has access to the Users section is changing another users
/// password. This flag is required to indicate that the oldPassword value is not required and that we are in fact performing a password reset and
/// then a password change if the executing user has access to do so.
/// </para>
/// </remarks>
[DataMember(Name = "reset")]
public bool? Reset { get; set; }

View File

@@ -1,15 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using System.Runtime.Serialization;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Validation;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Trees;
namespace Umbraco.Web.Models.ContentEditing
{
@@ -57,6 +49,8 @@ namespace Umbraco.Web.Models.ContentEditing
/// </remarks>
[DataMember(Name = "allowedActions")]
public IEnumerable<string> AllowedActions { get; set; }
[DataMember(Name = "isBlueprint")]
public bool IsBlueprint { get; set; }
}
}

View File

@@ -1,20 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Serialization;
namespace Umbraco.Web.Models.ContentEditing
{
/// <summary>
/// Represents a section (application) in the back office
/// </summary>
[DataContract(Name = "section", Namespace = "")]
public class Section
{
[DataMember(Name = "name")]
public string Name { get; set; }
@@ -24,5 +17,11 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "alias")]
public string Alias { get; set; }
/// <summary>
/// In some cases a custom route path can be specified so that when clicking on a section it goes to this
/// path instead of the normal dashboard path
/// </summary>
[DataMember(Name = "routePath")]
public string RoutePath { get; set; }
}
}

View File

@@ -27,10 +27,14 @@ namespace Umbraco.Web.Models.ContentEditing
public string EmailHash { get; set; }
[Obsolete("This should not be used it exists for legacy reasons only, use user groups instead, it will be removed in future versions")]
[EditorBrowsable(EditorBrowsableState.Never)]
[EditorBrowsable(EditorBrowsableState.Never)]
[ReadOnly(true)]
[DataMember(Name = "userType")]
public string UserType { get; set; }
public string UserType { get; set; }
[ReadOnly(true)]
[DataMember(Name = "userGroups")]
public string[] UserGroups { get; set; }
/// <summary>
/// Gets/sets the number of seconds for the user's auth ticket to expire

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.Serialization;
@@ -38,6 +39,40 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "resetPasswordValue")]
[ReadOnly(true)]
public string ResetPasswordValue { get; set; }
/// <summary>
/// A readonly value showing the user's current calculated start content ids
/// </summary>
[DataMember(Name = "calculatedStartContentIds")]
[ReadOnly(true)]
public IEnumerable<EntityBasic> CalculatedStartContentIds { get; set; }
/// <summary>
/// A readonly value showing the user's current calculated start media ids
/// </summary>
[DataMember(Name = "calculatedStartMediaIds")]
[ReadOnly(true)]
public IEnumerable<EntityBasic> CalculatedStartMediaIds { get; set; }
[DataMember(Name = "failedPasswordAttempts")]
[ReadOnly(true)]
public int FailedPasswordAttempts { get; set; }
[DataMember(Name = "lastLockoutDate")]
[ReadOnly(true)]
public DateTime LastLockoutDate { get; set; }
[DataMember(Name = "lastPasswordChangeDate")]
[ReadOnly(true)]
public DateTime LastPasswordChangeDate { get; set; }
[DataMember(Name = "createDate")]
[ReadOnly(true)]
public DateTime CreateDate { get; set; }
[DataMember(Name = "updateDate")]
[ReadOnly(true)]
public DateTime UpdateDate { get; set; }
}
}

View File

@@ -2,6 +2,8 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.Serialization;
using Umbraco.Core;
using Umbraco.Core.Configuration;
namespace Umbraco.Web.Models.ContentEditing
{
@@ -18,7 +20,10 @@ namespace Umbraco.Web.Models.ContentEditing
[DataMember(Name = "email", IsRequired = true)]
[Required]
[EmailAddress]
public string Email { get; set; }
public string Email { get; set; }
[DataMember(Name = "username")]
public string Username { get; set; }
[DataMember(Name = "message")]
public string Message { get; set; }
@@ -26,7 +31,10 @@ namespace Umbraco.Web.Models.ContentEditing
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (UserGroups.Any() == false)
yield return new ValidationResult("A user must be assigned to at least one group", new[] { "UserGroups" });
yield return new ValidationResult("A user must be assigned to at least one group", new[] { "UserGroups" });
if (UmbracoConfig.For.UmbracoSettings().Security.UsernameIsEmail == false && Username.IsNullOrWhiteSpace())
yield return new ValidationResult("A username cannot be empty", new[] { "Username" });
}
}
}

View File

@@ -2,6 +2,7 @@
using System.ComponentModel.DataAnnotations;
using System.Web;
using Umbraco.Core;
using Umbraco.Web.Composing;
using Umbraco.Web.Security;
namespace Umbraco.Web.Models
@@ -22,9 +23,9 @@ namespace Umbraco.Web.Models
private LoginStatusModel(bool doLookup)
{
if (doLookup && HttpContext.Current != null)
if (doLookup && Current.UmbracoContext != null)
{
var helper = new MembershipHelper(new HttpContextWrapper(HttpContext.Current));
var helper = new MembershipHelper(Current.UmbracoContext);
var model = helper.GetCurrentLoginStatus();
if (model != null)
{

View File

@@ -10,8 +10,8 @@ using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Composing;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Trees;
using Umbraco.Web.Routing;
using Umbraco.Web.Trees;
using Umbraco.Web._Legacy.Actions;
namespace Umbraco.Web.Models.Mapping
@@ -31,7 +31,8 @@ namespace Umbraco.Web.Models.Mapping
//FROM IContent TO ContentItemDisplay
CreateMap<IContent, ContentItemDisplay>()
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.Document, src.Key)))
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src =>
Udi.Create(src.IsBlueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, src.Key)))
.ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src)))
.ForMember(dest => dest.Updater, opt => opt.ResolveUsing(src => creatorResolver.Resolve(src)))
.ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon))
@@ -59,7 +60,8 @@ namespace Umbraco.Web.Models.Mapping
//FROM IContent TO ContentItemBasic<ContentPropertyBasic, IContent>
CreateMap<IContent, ContentItemBasic<ContentPropertyBasic, IContent>>()
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.Document, src.Key)))
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src =>
Udi.Create(src.IsBlueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, src.Key)))
.ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src)))
.ForMember(dest => dest.Updater, opt => opt.ResolveUsing(src => creatorResolver.Resolve(src)))
.ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon))
@@ -70,7 +72,8 @@ namespace Umbraco.Web.Models.Mapping
//FROM IContent TO ContentItemDto<IContent>
CreateMap<IContent, ContentItemDto<IContent>>()
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.Document, src.Key)))
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src =>
Udi.Create(src.IsBlueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, src.Key)))
.ForMember(dest => dest.Owner, opt => opt.ResolveUsing(src => contentOwnerResolver.Resolve(src)))
.ForMember(dest => dest.HasPublishedVersion, opt => opt.MapFrom(src => src.HasPublishedVersion))
.ForMember(dest => dest.Updater, opt => opt.Ignore())

View File

@@ -14,9 +14,8 @@ namespace Umbraco.Web.Models.Mapping
_textService = textService;
CreateMap<Core.Models.Section, Section>()
.ForMember(
dto => dto.Name,
expression => expression.MapFrom(section => _textService.Localize("sections/" + section.Alias, (IDictionary<string, string>)null)))
.ForMember(dest => dest.RoutePath, opt => opt.Ignore())
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => _textService.Localize("sections/" + src.Alias, (IDictionary<string, string>)null)))
.ReverseMap(); //backwards too!
}
}

View File

@@ -223,13 +223,33 @@ namespace Umbraco.Web.Models.Mapping
display.AssignedPermissions = allAssignedPermissions;
});
//Important! Currently we are never mapping to multiple UserDisplay objects but if we start doing that
// this will cause an N+1 and we'll need to change how this works.
CreateMap<IUser, UserDisplay>()
.ForMember(dest => dest.Avatars, opt => opt.MapFrom(user => user.GetCurrentUserAvatarUrls(userService, runtimeCache)))
.ForMember(dest => dest.Username, opt => opt.MapFrom(user => user.Username))
.ForMember(dest => dest.LastLoginDate, opt => opt.MapFrom(user => user.LastLoginDate == default(DateTime) ? null : (DateTime?)user.LastLoginDate))
.ForMember(dest => dest.UserGroups, opt => opt.MapFrom(user => user.Groups))
.ForMember(dest => dest.StartContentIds, opt => opt.UseValue(Enumerable.Empty<EntityBasic>()))
.ForMember(dest => dest.StartMediaIds, opt => opt.UseValue(Enumerable.Empty<EntityBasic>()))
.ForMember(
dest => dest.CalculatedStartContentIds,
opt => opt.MapFrom(src => GetStartNodeValues(
src.CalculateContentStartNodeIds(entityService),
textService, entityService, UmbracoObjectTypes.Document, "content/contentRoot")))
.ForMember(
dest => dest.CalculatedStartMediaIds,
opt => opt.MapFrom(src => GetStartNodeValues(
src.CalculateMediaStartNodeIds(entityService),
textService, entityService, UmbracoObjectTypes.Media, "media/mediaRoot")))
.ForMember(
dest => dest.StartContentIds,
opt => opt.MapFrom(src => GetStartNodeValues(
src.StartContentIds.ToArray(),
textService, entityService, UmbracoObjectTypes.Document, "content/contentRoot")))
.ForMember(
dest => dest.StartMediaIds,
opt => opt.MapFrom(src => GetStartNodeValues(
src.StartMediaIds.ToArray(),
textService, entityService, UmbracoObjectTypes.Media, "media/mediaRoot")))
.ForMember(dest => dest.Culture, opt => opt.MapFrom(user => user.GetUserCulture(textService)))
.ForMember(
dest => dest.AvailableCultures,
@@ -247,40 +267,7 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.ResetPasswordValue, opt => opt.Ignore())
.ForMember(dest => dest.Alias, opt => opt.Ignore())
.ForMember(dest => dest.Trashed, opt => opt.Ignore())
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore())
.AfterMap((user, display) =>
{
//Important! Currently we are never mapping to multiple UserDisplay objects but if we start doing that
// this will cause an N+1 and we'll need to change how this works.
var startContentIds = user.StartContentIds.ToArray();
if (startContentIds.Length > 0)
{
//TODO: Update GetAll to be able to pass in a parameter like on the normal Get to NOT load in the entire object!
var startNodes = new List<EntityBasic>();
if (startContentIds.Contains(-1))
{
startNodes.Add(RootNode(textService.Localize("content/contentRoot")));
}
var contentItems = entityService.GetAll(UmbracoObjectTypes.Document, startContentIds);
startNodes.AddRange(Mapper.Map<IEnumerable<IUmbracoEntity>, IEnumerable<EntityBasic>>(contentItems));
display.StartContentIds = startNodes;
}
var startMediaIds = user.StartMediaIds.ToArray();
if (startMediaIds.Length > 0)
{
var startNodes = new List<EntityBasic>();
if (startContentIds.Contains(-1))
{
startNodes.Add(RootNode(textService.Localize("media/mediaRoot")));
}
var mediaItems = entityService.GetAll(UmbracoObjectTypes.Media, startMediaIds);
startNodes.AddRange(Mapper.Map<IEnumerable<IUmbracoEntity>, IEnumerable<EntityBasic>>(mediaItems));
display.StartMediaIds = startNodes;
}
});
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore());
CreateMap<IUser, UserBasic>()
//Loading in the user avatar's requires an external request if they don't have a local file avatar, this means that initial load of paging may incur a cost
@@ -317,20 +304,23 @@ namespace Umbraco.Web.Models.Mapping
dest => dest.EmailHash,
opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().GenerateHash()))
.ForMember(dest => dest.SecondsUntilTimeout, opt => opt.Ignore())
.ForMember(dest => dest.UserGroups, opt => opt.Ignore())
.AfterMap((user, detail) =>
{
//we need to map the legacy UserType
//the best we can do here is to return the user's first user group as a IUserType object
//but we should attempt to return any group that is the built in ones first
var groups = user.Groups.ToArray();
detail.UserGroups = user.Groups.Select(x => x.Alias).ToArray();
if (groups.Length == 0)
{
//In backwards compatibility land, a user type cannot be null! so we need to return a fake one.
//In backwards compatibility land, a user type cannot be null! so we need to return a fake one.
detail.UserType = "temp";
}
else
{
var builtIns = new[] { Constants.Security.AdminGroupAlias, "writer", "editor", "translator" };
var builtIns = new[] { Constants.Security.AdminGroupAlias, "writer", "editor", Constants.Security.TranslatorGroupAlias };
var foundBuiltIn = groups.FirstOrDefault(x => builtIns.Contains(x.Alias));
if (foundBuiltIn != null)
{
@@ -361,6 +351,22 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dest => dest.SessionId, opt => opt.MapFrom(user => user.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : user.SecurityStamp));
}
private IEnumerable<EntityBasic> GetStartNodeValues(int[] startNodeIds,
ILocalizedTextService textService, IEntityService entityService, UmbracoObjectTypes objectType,
string localizedKey)
{
if (startNodeIds.Length <= 0)
return Enumerable.Empty<EntityBasic>();
var startNodes = new List<EntityBasic>();
if (startNodeIds.Contains(-1))
startNodes.Add(RootNode(textService.Localize(localizedKey)));
var mediaItems = entityService.GetAll(objectType, startNodeIds);
startNodes.AddRange(Mapper.Map<IEnumerable<IUmbracoEntity>, IEnumerable<EntityBasic>>(mediaItems));
return startNodes;
}
private void MapUserGroupBasic(ISectionService sectionService, IEntityService entityService, ILocalizedTextService textService, dynamic group, UserGroupBasic display)
{
var allSections = sectionService.GetSections();

View File

@@ -4,6 +4,7 @@ using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web;
using System.Web.Mvc;
using Umbraco.Web.Composing;
using Umbraco.Web.Security;
namespace Umbraco.Web.Models
@@ -24,9 +25,9 @@ namespace Umbraco.Web.Models
private ProfileModel(bool doLookup)
{
MemberProperties = new List<UmbracoProperty>();
if (doLookup)
if (doLookup && Current.UmbracoContext != null)
{
var helper = new MembershipHelper(new HttpContextWrapper(HttpContext.Current));
var helper = new MembershipHelper(Current.UmbracoContext);
var model = helper.GetCurrentMemberProfileModel();
MemberProperties = model.MemberProperties;
}

View File

@@ -5,6 +5,7 @@ using System.ComponentModel.DataAnnotations;
using System.Web;
using System.Web.Mvc;
using Umbraco.Core;
using Umbraco.Web.Composing;
using Umbraco.Web.Security;
namespace Umbraco.Web.Models
@@ -29,9 +30,9 @@ namespace Umbraco.Web.Models
MemberProperties = new List<UmbracoProperty>();
LoginOnSuccess = true;
CreatePersistentLoginCookie = true;
if (doLookup && HttpContext.Current != null)
if (doLookup && Current.UmbracoContext != null)
{
var helper = new MembershipHelper(new HttpContextWrapper(HttpContext.Current));
var helper = new MembershipHelper(Current.UmbracoContext);
var model = helper.CreateRegistrationModel(MemberTypeAlias);
MemberProperties = model.MemberProperties;
}