diff --git a/src/Umbraco.Core/Composing/CompositionRoots/RepositoryCompositionRoot.cs b/src/Umbraco.Core/Composing/CompositionRoots/RepositoryCompositionRoot.cs index ff4e77d6b7..a4bc34247e 100644 --- a/src/Umbraco.Core/Composing/CompositionRoots/RepositoryCompositionRoot.cs +++ b/src/Umbraco.Core/Composing/CompositionRoots/RepositoryCompositionRoot.cs @@ -3,6 +3,7 @@ using LightInject; using Umbraco.Core.Cache; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.Repositories.Interfaces; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Composing.CompositionRoots @@ -47,13 +48,14 @@ namespace Umbraco.Core.Composing.CompositionRoots container.Register(); container.Register(); container.Register(); + container.Register(); container.Register(); container.Register(); container.Register(); container.Register(); container.Register(); container.Register(); - container.Register(); + container.Register(); container.Register(); container.Register(); container.Register(); diff --git a/src/Umbraco.Core/Configuration/UmbracoConfig.cs b/src/Umbraco.Core/Configuration/UmbracoConfig.cs index 0776af1784..c549350616 100644 --- a/src/Umbraco.Core/Configuration/UmbracoConfig.cs +++ b/src/Umbraco.Core/Configuration/UmbracoConfig.cs @@ -88,7 +88,7 @@ namespace Umbraco.Core.Configuration if (_healthChecks == null) { var ex = new ConfigurationErrorsException("Could not load the " + typeof(IHealthChecks) + " from config file, ensure the web.config and healthchecks.config files are formatted correctly"); - LogHelper.Error("Config error", ex); + Current.Logger.Error("Config error", ex); throw ex; } diff --git a/src/Umbraco.Core/Constants-Applications.cs b/src/Umbraco.Core/Constants-Applications.cs index 3f5d659f39..014c4af450 100644 --- a/src/Umbraco.Core/Constants-Applications.cs +++ b/src/Umbraco.Core/Constants-Applications.cs @@ -144,8 +144,6 @@ public const string PartialViewMacros = "partialViewMacros"; - public const string Users = "users"; - //TODO: Fill in the rest! } } diff --git a/src/Umbraco.Core/CoreRuntime.cs b/src/Umbraco.Core/CoreRuntime.cs index bfee15f71b..bd7d435557 100644 --- a/src/Umbraco.Core/CoreRuntime.cs +++ b/src/Umbraco.Core/CoreRuntime.cs @@ -91,15 +91,6 @@ namespace Umbraco.Core var componentTypes = ResolveComponentTypes(); _bootLoader = new BootLoader(container); _bootLoader.Boot(componentTypes, _state.Level); - - // this was done in Complete() right before running the Started event handlers - // "special case for the user service, we need to tell it if it's an upgrade, if so we need to ensure that - // exceptions are bubbled up if a user is attempted to be persisted during an upgrade (i.e. when they auth to login)" - // - // was *always* setting the value to true which is?! so using the runtime level - // and then, it is *never* resetted to false, meaning Umbraco has been running with IsUpgrading being true? - // fixme - this is... bad - ((UserService) Current.Services.UserService).IsUpgrading = _state.Level == RuntimeLevel.Upgrade; } catch (Exception e) { diff --git a/src/Umbraco.Core/Models/Identity/IdentityProfile.cs b/src/Umbraco.Core/Models/Identity/IdentityProfile.cs index b6a39227d3..6870bff642 100644 --- a/src/Umbraco.Core/Models/Identity/IdentityProfile.cs +++ b/src/Umbraco.Core/Models/Identity/IdentityProfile.cs @@ -1,11 +1,9 @@ using System; using System.Linq; using AutoMapper; -using Umbraco.Core.Composing; using Umbraco.Core.Models.Membership; using Umbraco.Core.Security; using Umbraco.Core.Services; -using Umbraco.Core.Models.EntityBase; namespace Umbraco.Core.Models.Identity { @@ -36,7 +34,6 @@ namespace Umbraco.Core.Models.Identity .ForMember(dest => dest.AllowedSections, opt => opt.MapFrom(src => src.AllowedSections.ToArray())) .ForMember(dest => dest.LockoutEnabled, opt => opt.Ignore()) .ForMember(dest => dest.Logins, opt => opt.Ignore()) - .ForMember(dest => dest.LoginsChanged, opt => opt.Ignore()) .ForMember(dest => dest.EmailConfirmed, opt => opt.Ignore()) .ForMember(dest => dest.PhoneNumber, opt => opt.Ignore()) .ForMember(dest => dest.PhoneNumberConfirmed, opt => opt.Ignore()) @@ -53,14 +50,12 @@ namespace Umbraco.Core.Models.Identity .ConstructUsing(source => new UserData(Guid.NewGuid().ToString("N"))) //this is the 'session id' .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id)) .ForMember(dest => dest.AllowedApplications, opt => opt.MapFrom(src => src.AllowedSections)) + .ForMember(dest => dest.Roles, opt => opt.MapFrom(src => new[] { src.Roles.Select(x => x.RoleId).ToArray()})) + .ForMember(dest => dest.RealName, opt => opt.MapFrom(src => src.Name)) //When mapping to UserData which is used in the authcookie we want ALL start nodes including ones defined on the groups .ForMember(dest => dest.StartContentNodes, opt => opt.MapFrom(src => src.CalculatedContentStartNodeIds)) //When mapping to UserData which is used in the authcookie we want ALL start nodes including ones defined on the groups .ForMember(dest => dest.StartMediaNodes, opt => opt.MapFrom(src => src.CalculatedMediaStartNodeIds)) - .ForMember(dest => dest.RealName, opt => opt.MapFrom(src => src.Name)) - .ForMember(dest => dest.Roles, opt => opt.MapFrom(user => new[] { user.UserTypeAlias })) - .ForMember(dest => dest.StartContentNode, opt => opt.MapFrom(src => src.StartContentId)) - .ForMember(dest => dest.StartMediaNode, opt => opt.MapFrom(src => src.StartMediaId)) .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.UserName)) .ForMember(dest => dest.Culture, opt => opt.MapFrom(src => src.Culture)) .ForMember(dest => dest.SessionId, opt => opt.MapFrom(src => src.SecurityStamp.IsNullOrWhiteSpace() ? Guid.NewGuid().ToString("N") : src.SecurityStamp)); diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs index 0b4435585d..c25e139b33 100644 --- a/src/Umbraco.Core/Models/Member.cs +++ b/src/Umbraco.Core/Models/Member.cs @@ -67,7 +67,7 @@ namespace Umbraco.Core.Models /// /// /// - public Member(string name, string email, string username, IMemberType contentType) + public Member(string name, string email, string username, IMemberType contentType, bool isApproved = true) : base(name, -1, contentType, new PropertyCollection()) { if (string.IsNullOrWhiteSpace(email)) throw new ArgumentNullOrEmptyException(nameof(email)); @@ -78,7 +78,7 @@ namespace Umbraco.Core.Models _contentTypeAlias = contentType.Alias; _email = email; _username = username; - IsApproved = true; + IsApproved = isApproved; //this cannot be null but can be empty _rawPasswordValue = ""; @@ -120,10 +120,8 @@ namespace Umbraco.Core.Models public Member(string name, string email, string username, string rawPasswordValue, IMemberType contentType, bool isApproved) : base(name, -1, contentType, new PropertyCollection()) { - Mandate.ParameterNotNull(contentType, "contentType"); - + _contentType = contentType ?? throw new ArgumentNullException(nameof(contentType)); _contentTypeAlias = contentType.Alias; - _contentType = contentType; _email = email; _username = username; _rawPasswordValue = rawPasswordValue; diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs index 688b600041..07868f911a 100644 --- a/src/Umbraco.Core/Models/Membership/User.cs +++ b/src/Umbraco.Core/Models/Membership/User.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using System.Linq; using System.Reflection; using System.Runtime.Serialization; +using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; using Umbraco.Core.Models.EntityBase; @@ -311,7 +312,7 @@ namespace Umbraco.Core.Models.Membership if (foundBuiltIn != null) { //if the group isn't IUserGroup we'll need to look it up - realGroup = foundBuiltIn as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(foundBuiltIn.Id); + realGroup = foundBuiltIn as IUserGroup ?? Current.Services.UserService.GetUserGroupById(foundBuiltIn.Id); //return a mapped version of the group return new UserType @@ -329,7 +330,7 @@ namespace Umbraco.Core.Models.Membership //otherwise return the first //if the group isn't IUserGroup we'll need to look it up - realGroup = groups[0] as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(groups[0].Id); + realGroup = groups[0] as IUserGroup ?? Current.Services.UserService.GetUserGroupById(groups[0].Id); //return a mapped version of the group return new UserType { @@ -350,7 +351,7 @@ namespace Umbraco.Core.Models.Membership return; //the only other option we have here is to lookup the group (and we'll need to use singletons here :( ) - var found = ApplicationContext.Current.Services.UserService.GetUserGroupByAlias(value.Alias); + var found = Current.Services.UserService.GetUserGroupByAlias(value.Alias); if (found == null) throw new InvalidOperationException("No user group was found with the alias " + value.Alias + ", this API (IUser.UserType) is obsolete, use user groups instead"); @@ -375,7 +376,7 @@ namespace Umbraco.Core.Models.Membership if (customUserGroup != null) { //if the group isn't IUserGroup we'll need to look it up - var realGroup = customUserGroup as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(customUserGroup.Id); + var realGroup = customUserGroup as IUserGroup ?? Current.Services.UserService.GetUserGroupById(customUserGroup.Id); realGroup.RemoveAllowedSection(sectionAlias); //now we need to flag this for saving (hack!) GroupsToSave.Add(realGroup); @@ -405,7 +406,7 @@ namespace Umbraco.Core.Models.Membership if (admin != null) { //if the group isn't IUserGroup we'll need to look it up - var realGroup = admin as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(admin.Id); + var realGroup = admin as IUserGroup ?? Current.Services.UserService.GetUserGroupById(admin.Id); realGroup.AddAllowedSection(sectionAlias); //now we need to flag this for saving (hack!) GroupsToSave.Add(realGroup); @@ -417,7 +418,7 @@ namespace Umbraco.Core.Models.Membership if (customUserGroup != null) { //if the group isn't IUserGroup we'll need to look it up - var realGroup = customUserGroup as IUserGroup ?? ApplicationContext.Current.Services.UserService.GetUserGroupById(customUserGroup.Id); + var realGroup = customUserGroup as IUserGroup ?? Current.Services.UserService.GetUserGroupById(customUserGroup.Id); realGroup.AddAllowedSection(sectionAlias); //now we need to flag this for saving (hack!) GroupsToSave.Add(realGroup); diff --git a/src/Umbraco.Core/Models/Rdbms/User2UserGroupDto.cs b/src/Umbraco.Core/Models/Rdbms/User2UserGroupDto.cs index 65b48b628a..72fa1593a0 100644 --- a/src/Umbraco.Core/Models/Rdbms/User2UserGroupDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/User2UserGroupDto.cs @@ -16,4 +16,22 @@ namespace Umbraco.Core.Models.Rdbms [ForeignKey(typeof(UserGroupDto))] public int UserGroupId { get; set; } } + + [TableName("umbracoUser2UserGroup")] + [ExplicitColumns] + internal class User2UserGroupReadOnlyDto + { + [Column("userId")] + [PrimaryKeyColumn(AutoIncrement = false, Name = "PK_user2userGroup", OnColumns = "userId, userGroupId")] + [ForeignKey(typeof(UserDto))] + public int UserId { get; set; } + + [Column("userGroupId")] + [ForeignKey(typeof(UserGroupDto))] + public int UserGroupId { get; set; } + + [ResultColumn] + [Reference(ReferenceType.Foreign)] // fixme + public UserGroupDto UserGroupDto { get; set; } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Rdbms/UserDto.cs b/src/Umbraco.Core/Models/Rdbms/UserDto.cs index 669cb91908..7e39d2c3a3 100644 --- a/src/Umbraco.Core/Models/Rdbms/UserDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/UserDto.cs @@ -108,7 +108,7 @@ namespace Umbraco.Core.Models.Rdbms [Reference(ReferenceType.Many, ReferenceMemberName = "UserId")] public List UserGroupDtos { get; set; } - [ResultColumn] + [ResultColumn] [Reference(ReferenceType.Many, ReferenceMemberName = "UserId")] public HashSet UserStartNodeDtos { get; set; } } diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs index 740fab9345..19b870feec 100644 --- a/src/Umbraco.Core/Models/UserExtensions.cs +++ b/src/Umbraco.Core/Models/UserExtensions.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net; using Umbraco.Core.Exceptions; using Umbraco.Core.Cache; +using Umbraco.Core.Composing; using Umbraco.Core.IO; using Umbraco.Core.Models.Identity; using Umbraco.Core.Models.Membership; @@ -83,7 +84,7 @@ namespace Umbraco.Core.Models } //use the custom avatar - var avatarUrl = FileSystemProviderManager.Current.MediaFileSystem.GetUrl(user.Avatar); + var avatarUrl = Current.FileSystems.MediaFileSystem.GetUrl(user.Avatar); return new[] { avatarUrl + "?width=30&height=30&mode=crop", diff --git a/src/Umbraco.Core/Persistence/Mappers/ExternalLoginMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ExternalLoginMapper.cs index c4226797e2..c1b07a8117 100644 --- a/src/Umbraco.Core/Persistence/Mappers/ExternalLoginMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/ExternalLoginMapper.cs @@ -16,12 +16,9 @@ namespace Umbraco.Core.Persistence.Mappers #region Overrides of BaseMapper - internal override ConcurrentDictionary PropertyInfoCache - { - get { return PropertyInfoCacheInstance; } - } + internal override ConcurrentDictionary PropertyInfoCache => PropertyInfoCacheInstance; - internal override void BuildMap() + protected override void BuildMap() { CacheMap(src => src.Id, dto => dto.Id); CacheMap(src => src.CreateDate, dto => dto.CreateDate); diff --git a/src/Umbraco.Core/Persistence/Mappers/UserGroupMapper.cs b/src/Umbraco.Core/Persistence/Mappers/UserGroupMapper.cs index cd376e79a9..31237fd977 100644 --- a/src/Umbraco.Core/Persistence/Mappers/UserGroupMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/UserGroupMapper.cs @@ -23,12 +23,9 @@ namespace Umbraco.Core.Persistence.Mappers #region Overrides of BaseMapper - internal override ConcurrentDictionary PropertyInfoCache - { - get { return PropertyInfoCacheInstance; } - } + internal override ConcurrentDictionary PropertyInfoCache => PropertyInfoCacheInstance; - internal override void BuildMap() + protected override void BuildMap() { CacheMap(src => src.Id, dto => dto.Id); CacheMap(src => src.Alias, dto => dto.Alias); diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index accd8379ca..b81674974e 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -205,7 +205,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial _database.Insert(new UserGroup2AppDto { UserGroupId = 3, AppAlias = Constants.Applications.Content }); _database.Insert(new UserGroup2AppDto { UserGroupId = 3, AppAlias = Constants.Applications.Media }); _database.Insert(new UserGroup2AppDto { UserGroupId = 4, AppAlias = Constants.Applications.Translation }); - _database.Insert("umbracoUser2app", "user", false, new User2AppDto { UserId = 0, AppAlias = Constants.Applications.Translation }); } private void CreateCmsPropertyTypeGroupData() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs index 8a8bbc068c..1d02c96fd5 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs @@ -1,4 +1,5 @@ using System; +using NPoco; using Umbraco.Core.Configuration; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.DatabaseAnnotations; diff --git a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs index d001358a97..a44838dbda 100644 --- a/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs +++ b/src/Umbraco.Core/Persistence/NPocoSqlExtensions.cs @@ -49,6 +49,23 @@ namespace Umbraco.Core.Persistence return sql; } + public static Sql WhereIn(this Sql sql, Expression> fieldSelector, Sql inSql) + { + return sql.WhereIn(fieldSelector, inSql, false); + } + + public static Sql WhereNotIn(this Sql sql, Expression> fieldSelector, Sql inSql) + { + return sql.WhereIn(fieldSelector, inSql, true); + } + + private static Sql WhereIn(this Sql sql, Expression> fieldSelector, Sql inSql, bool not) + { + var fieldName = GetFieldName(fieldSelector, sql.SqlContext.SqlSyntax); + sql.Where(fieldName + (not ? " NOT" : "") +" IN (" + inSql.SQL + ")"); // fixme what about args? + return sql; + } + #endregion #region From @@ -175,7 +192,7 @@ namespace Umbraco.Core.Persistence x.Value.ColumnName, string.IsNullOrEmpty(x.Value.ColumnAlias) ? x.Value.MemberInfoKey : x.Value.ColumnAlias)); - sql.Select(string.Join(", ", columns)); + sql.Select(columns); if (refexpr == null) return sql; refexpr(new RefSql(sql, null)); @@ -216,6 +233,13 @@ namespace Umbraco.Core.Persistence return refSql; } + public static Sql Select(this Sql sql, params Expression>[] columns) + { + var fieldNames = columns.Select(x => GetFieldName(x, sql.SqlContext.SqlSyntax)).ToArray(); + sql.Select(fieldNames); + return sql; + } + public class RefSql { public RefSql(Sql sql, string prefix) diff --git a/src/Umbraco.Core/Persistence/Querying/Query.cs b/src/Umbraco.Core/Persistence/Querying/Query.cs index aca69bccae..d9282525d0 100644 --- a/src/Umbraco.Core/Persistence/Querying/Query.cs +++ b/src/Umbraco.Core/Persistence/Querying/Query.cs @@ -5,6 +5,7 @@ using System.Linq.Expressions; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.SqlSyntax; using System.Text; +using NPoco; namespace Umbraco.Core.Persistence.Querying { @@ -14,20 +15,12 @@ namespace Umbraco.Core.Persistence.Querying /// public class Query : IQuery { - private readonly ISqlSyntaxProvider _sqlSyntax; - private readonly IMapperCollection _mappers; + private readonly SqlContext _sqlContext; private readonly List> _wheres = new List>(); - public Query(ISqlSyntaxProvider sqlSyntax, IMapperCollection mappers) - { - _sqlSyntax = sqlSyntax; - _mappers = mappers; - } - public Query(SqlContext sqlContext) { - _sqlSyntax = sqlContext.SqlSyntax; - _mappers = sqlContext.Mappers; + _sqlContext = sqlContext; } /// @@ -39,7 +32,7 @@ namespace Umbraco.Core.Persistence.Querying { if (predicate == null) return this; - var expressionHelper = new ModelToSqlExpressionVisitor(_sqlSyntax, _mappers); + var expressionHelper = new ModelToSqlExpressionVisitor(_sqlContext.SqlSyntax, _sqlContext.Mappers); var whereExpression = expressionHelper.Visit(predicate); _wheres.Add(new Tuple(whereExpression, expressionHelper.GetSqlParameters())); return this; @@ -49,7 +42,7 @@ namespace Umbraco.Core.Persistence.Querying { if (fieldSelector == null) return this; - var expressionHelper = new ModelToSqlExpressionVisitor(_sqlSyntax, _mappers); + var expressionHelper = new ModelToSqlExpressionVisitor(_sqlContext.SqlSyntax, _sqlContext.Mappers); var whereExpression = expressionHelper.Visit(fieldSelector); _wheres.Add(new Tuple(whereExpression + " IN (@values)", new object[] { new { values } })); return this; @@ -66,18 +59,18 @@ namespace Umbraco.Core.Persistence.Querying StringBuilder sb = null; List parameters = null; - Sql sql = null; + Sql sql = null; foreach (var predicate in predicates) { // see notes in Where() - var expressionHelper = new ModelToSqlExpressionVisitor(); + var expressionHelper = new ModelToSqlExpressionVisitor(_sqlContext.SqlSyntax, _sqlContext.Mappers); var whereExpression = expressionHelper.Visit(predicate); if (sb == null) { sb = new StringBuilder("("); parameters = new List(); - sql = new Sql(); + sql = Sql.BuilderFor(_sqlContext); } else { diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentBlueprintRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentBlueprintRepository.cs index 547d59a528..b2e48da30c 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentBlueprintRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentBlueprintRepository.cs @@ -2,6 +2,7 @@ using System; using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; +using Umbraco.Core.Persistence.Repositories.Interfaces; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories @@ -15,7 +16,7 @@ namespace Umbraco.Core.Persistence.Repositories /// /// TODO: Create a helper method to contain most of the underlying logic for the ContentRepository /// - internal class ContentBlueprintRepository : ContentRepository + internal class ContentBlueprintRepository : ContentRepository, IContentBlueprintRepository { public ContentBlueprintRepository(IScopeUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, IContentSection settings) : base(work, cacheHelper, logger, contentTypeRepository, templateRepository, tagRepository, settings) diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs index 35fbcc4a11..3fbf534077 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs @@ -307,8 +307,8 @@ namespace Umbraco.Core.Persistence.Repositories private IEnumerable PerformGetAllPaths(Guid objectTypeId, Action filter = null) { var sql = new Sql("SELECT id, path FROM umbracoNode WHERE umbracoNode.nodeObjectType=@type", new { type = objectTypeId }); - if (filter != null) filter(sql); - return _work.Database.Fetch(sql); + filter?.Invoke(sql); + return UnitOfWork.Database.Fetch(sql); } public virtual IEnumerable GetByQuery(IQuery query) diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentBlueprintRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentBlueprintRepository.cs new file mode 100644 index 0000000000..4fdef0d50e --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentBlueprintRepository.cs @@ -0,0 +1,5 @@ +namespace Umbraco.Core.Persistence.Repositories.Interfaces +{ + interface IContentBlueprintRepository : IContentRepository + { } +} diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserGroupRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserGroupRepository.cs index dd6188e31c..c75e2bdb41 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserGroupRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserGroupRepository.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Persistence.Repositories { - public interface IUserGroupRepository : IRepositoryQueryable + public interface IUserGroupRepository : IUnitOfWorkRepository, IQueryRepository { /// /// Gets a group by it's alias @@ -55,6 +56,5 @@ namespace Umbraco.Core.Persistence.Repositories /// Permissions as enumerable list of /// Specify the nodes to replace permissions for void AssignGroupPermission(int groupId, char permission, params int[] entityIds); - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs index 053235de7a..12d23bb0b8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IUserRepository.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq.Expressions; using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.DatabaseModelDefinitions; @@ -37,10 +36,6 @@ namespace Umbraco.Core.Persistence.Repositories /// Id of group IEnumerable GetAllNotInGroup(int groupId); - [Obsolete("Use the overload with long operators instead")] - [EditorBrowsable(EditorBrowsableState.Never)] - IEnumerable GetPagedResultsByQuery(IQuery query, int pageIndex, int pageSize, out int totalRecords, Expression> orderBy); - /// /// Gets paged user results /// @@ -54,7 +49,9 @@ namespace Umbraco.Core.Persistence.Repositories /// Optional parameter to filter by specified user groups /// Optional parameter to filter by specfied user state /// - IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, Expression> orderBy, Direction orderDirection, string[] userGroups = null, UserState[] userState = null, IQuery filter = null); + IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, + Expression> orderBy, Direction orderDirection = Direction.Ascending, + string[] userGroups = null, UserState[] userState = null, IQuery filter = null); /// /// Returns a user by username @@ -82,7 +79,6 @@ namespace Umbraco.Core.Persistence.Repositories IProfile GetProfile(string username); IProfile GetProfile(int id); - IDictionary GetUserStates(); - + IDictionary GetUserStates(); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs index ba2d0ad83a..fb6da888ef 100644 --- a/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/PermissionRepository.cs @@ -25,7 +25,6 @@ namespace Umbraco.Core.Persistence.Repositories internal class PermissionRepository : NPocoRepositoryBase where TEntity : class, IAggregateRoot { - public PermissionRepository(IScopeUnitOfWork work, CacheHelper cache, ILogger logger) : base(work, cache, logger) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs index 743361d7dc..b5f4970c36 100644 --- a/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/UserRepository.cs @@ -7,9 +7,7 @@ using System.Text; using System.Web.Security; using Newtonsoft.Json; using NPoco; -using Umbraco.Core; using Umbraco.Core.Cache; -using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; @@ -19,11 +17,9 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Security; -#error the entire clads needs to be refactored for proper NPoco usage namespace Umbraco.Core.Persistence.Repositories { /// @@ -33,8 +29,7 @@ namespace Umbraco.Core.Persistence.Repositories { private readonly IMapperCollection _mapperCollection; private readonly IDictionary _passwordConfig; - private PermissionRepository _permissionRepository; - + /// /// Constructor /// @@ -44,45 +39,39 @@ namespace Umbraco.Core.Persistence.Repositories /// /// A dictionary specifying the configuration for user passwords. If this is null then no password configuration will be persisted or read. /// - #error password config, change: null will ??? - public UserRepository(IScopeUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IMapperCollection mapperCollection, IDictionary passwordConfig = null) + public UserRepository(IScopeUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IMapperCollection mapperCollection) : base(work, cacheHelper, logger) { _mapperCollection = mapperCollection; - if (passwordConfig == null) // fixme this is bad bc we may want it to remain null?! - { - var userMembershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider(); - passwordConfig = userMembershipProvider == null || userMembershipProvider.PasswordFormat != MembershipPasswordFormat.Hashed - ? null - : new Dictionary { { "hashAlgorithm", Membership.HashAlgorithmType } }; - } + var userMembershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider(); + _passwordConfig = userMembershipProvider == null || userMembershipProvider.PasswordFormat != MembershipPasswordFormat.Hashed + ? null + : new Dictionary { { "hashAlgorithm", Membership.HashAlgorithmType } }; + } + + // for tests + internal UserRepository(IScopeUnitOfWork work, CacheHelper cacheHelper, ILogger logger, IMapperCollection mapperCollection, IDictionary passwordConfig) + : base(work, cacheHelper, logger) + { + _mapperCollection = mapperCollection; _passwordConfig = passwordConfig; } - // note: is ok to 'new' the repo here as it's a sub-repo really - private PermissionRepository PermissionRepository => _permissionRepository - ?? (_permissionRepository = new PermissionRepository(UnitOfWork, _cacheHelper)); - #region Overrides of RepositoryBase protected override IUser PerformGet(int id) { - var sql = GetQueryWithGroups(); - sql.Where(GetBaseWhereClause(), new { Id = id }); - sql //must be included for relator to work - .OrderBy(d => d.Id, SqlSyntax) - .OrderBy(d => d.Id, SqlSyntax) - .OrderBy(d => d.Id, SqlSyntax); + var sql = Sql() + .Select() + .From() + .Where(x => x.Id == id); - var dto = Database.Fetch(new UserGroupRelator().Map, sql) - .FirstOrDefault(); + var dtos = Database.Fetch(sql); + if (dtos.Count == 0) return null; - if (dto == null) - return null; - - var user = UserFactory.BuildEntity(dto); - return user; + PerformGetReferencedDtos(dtos); + return UserFactory.BuildEntity(dtos[0]); } /// @@ -98,32 +87,7 @@ namespace Umbraco.Core.Persistence.Repositories /// public IUser GetByUsername(string username, bool includeSecurityData) { - UserDto dto; - if (includeSecurityData) - { - var sql = GetQueryWithGroups(); - sql.Where(userDto => userDto.Login == username, SqlSyntax); - sql //must be included for relator to work - .OrderBy(d => d.Id, SqlSyntax) - .OrderBy(d => d.Id, SqlSyntax) - .OrderBy(d => d.Id, SqlSyntax); - dto = Database - .Fetch( - new UserGroupRelator().Map, sql) - .FirstOrDefault(); - } - else - { - var sql = GetBaseQuery("umbracoUser.*"); - sql.Where(userDto => userDto.Login == username, SqlSyntax); - dto = Database.FirstOrDefault(sql); - } - - if (dto == null) - return null; - - var user = UserFactory.BuildEntity(dto); - return user; + return GetWith(sql => sql.Where(x => x.Login == username), includeSecurityData); } /// @@ -139,58 +103,19 @@ namespace Umbraco.Core.Persistence.Repositories /// public IUser Get(int id, bool includeSecurityData) { - UserDto dto; - if (includeSecurityData) - { - var sql = GetQueryWithGroups(); - sql.Where(GetBaseWhereClause(), new { Id = id }); - sql //must be included for relator to work - .OrderBy(d => d.Id, SqlSyntax) - .OrderBy(d => d.Id, SqlSyntax) - .OrderBy(d => d.Id, SqlSyntax); - dto = Database - .Fetch( - new UserGroupRelator().Map, sql) - .FirstOrDefault(); - } - else - { - var sql = GetBaseQuery("umbracoUser.*"); - sql.Where(GetBaseWhereClause(), new { Id = id }); - dto = Database.FirstOrDefault(sql); - } - - if (dto == null) - return null; - - var user = UserFactory.BuildEntity(dto); - return user; + return GetWith(sql => sql.Where(x => x.Id == id), includeSecurityData); } public IProfile GetProfile(string username) { - var sql = GetBaseQuery(false).Where(userDto => userDto.UserName == username, SqlSyntax); - - var dto = Database.Fetch(sql) - .FirstOrDefault(); - - if (dto == null) - return null; - - return new UserProfile(dto.Id, dto.UserName); + var dto = GetDtoWith(sql => sql.Where(x => x.UserName == username), false); + return dto == null ? null : new UserProfile(dto.Id, dto.UserName); } public IProfile GetProfile(int id) { - var sql = GetBaseQuery(false).Where(userDto => userDto.Id == id, SqlSyntax); - - var dto = Database.Fetch(sql) - .FirstOrDefault(); - - if (dto == null) - return null; - - return new UserProfile(dto.Id, dto.UserName); + var dto = GetDtoWith(sql => sql.Where(x => x.Id == id), false); + return dto == null ? null : new UserProfile(dto.Id, dto.UserName); } public IDictionary GetUserStates() @@ -220,43 +145,134 @@ ORDER BY colName"; protected override IEnumerable PerformGetAll(params int[] ids) { - var sql = GetQueryWithGroups(); - if (ids.Any()) - { - sql.Where("umbracoUser.id in (@ids)", new { ids = ids }); - } - #error refactor with FetchOneToMany - sql //must be included for relator to work - .OrderBy(d => d.Id, SqlSyntax) - .OrderBy(d => d.Id, SqlSyntax) - .OrderBy(d => d.Id, SqlSyntax); - - var users = ConvertFromDtos(Database.Fetch(new UserGroupRelator().Map, sql)) - .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. - + var dtos = GetDtosWith(sql => sql.WhereIn(x => x.Id, ids), true); + var users = new IUser[dtos.Count]; + var i = 0; + foreach (var dto in dtos) + users[i++] = UserFactory.BuildEntity(dto); return users; } protected override IEnumerable PerformGetByQuery(IQuery query) { - var sqlClause = GetQueryWithGroups(); - var translator = new SqlTranslator(sqlClause, query); - var sql = translator.Translate(); - #error obviously refactor with FetchOneToMany! - sql //must be included for relator to work - .OrderBy(d => d.Id, SqlSyntax) - .OrderBy(d => d.Id, SqlSyntax) - .OrderBy(d => d.Id, SqlSyntax); - - var dtos = Database.Fetch(new UserGroupRelator().Map, sql) - .DistinctBy(x => x.Id); - - var users = ConvertFromDtos(dtos) - .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. + var dtos = GetDtosWith(sql => new SqlTranslator(sql, query).Translate(), true) + .DistinctBy(x => x.Id) + .ToList(); + var users = new IUser[dtos.Count]; + var i = 0; + foreach (var dto in dtos) + users[i++] = UserFactory.BuildEntity(dto); return users; } + private IUser GetWith(Action> with, bool includeReferences) + { + var dto = GetDtoWith(with, includeReferences); + return dto == null ? null : UserFactory.BuildEntity(dto); + } + + private UserDto GetDtoWith(Action> with, bool includeReferences) + { + var dtos = GetDtosWith(with, includeReferences); + return dtos.FirstOrDefault(); + } + + private List GetDtosWith(Action> with, bool includeReferences) + { + var sql = Sql() + .Select() + .From(); + + with(sql); + + var dtos = Database.Fetch(sql); + + if (includeReferences) + PerformGetReferencedDtos(dtos); + + return dtos; + } + + // NPoco cannot fetch 2+ references at a time + // plus it creates a combinatorial explosion + // better use extra queries + // unfortunately, SqlCe and MySql don't support multiple result sets + private void PerformGetReferencedDtos(List dtos) + { + if (dtos.Count == 0) return; + + var userIds = dtos.Count == 1 ? new List(dtos[0].Id) : dtos.Select(x => x.Id).ToList(); + var xUsers = dtos.Count == 1 ? null : dtos.ToDictionary(x => x.Id, x => x); + + // get users2groups + + var sql = Sql() + .Select() + .From() + .WhereIn(x => x.UserId, userIds); + + var users2groups = Database.Fetch(sql); + var groupIds = users2groups.Select(x => x.UserGroupId).ToList(); + + // get groups + + sql = Sql() + .Select() + .From() + .WhereIn(x => x.Id, userIds); + + var groups = Database.Fetch(sql) + .ToDictionary(x => x.Id, x => x); + + // get groups2apps + + sql = Sql() + .Select() + .From() + .WhereIn(x => x.UserGroupId, groupIds); + + var groups2apps = Database.Fetch(sql) + .GroupBy(x => x.UserGroupId) + .ToDictionary(x => x.Key, x => x); + + // get start nodes + + sql = Sql() + .Select() + .From() + .WhereIn(x => x.UserId, userIds); + + var startNodes = Database.Fetch(sql); + + // map groups + + foreach (var user2group in users2groups) + { + if (groups.TryGetValue(user2group.UserGroupId, out var group)) + { + var dto = xUsers == null ? dtos[0] : xUsers[user2group.UserId]; + dto.UserGroupDtos.Add(group); // user2group is distinct + } + } + + // map start nodes + + foreach (var startNode in startNodes) + { + var dto = xUsers == null ? dtos[0] : xUsers[startNode.UserId]; + dto.UserStartNodeDtos.Add(startNode); // hashset = distinct + } + + // map apps + + foreach (var group in groups.Values) + { + if (groups2apps.TryGetValue(group.Id, out var list)) + group.UserGroup2AppDtos = list.ToList(); // groups2apps is distinct + } + } + #endregion #region Overrides of NPocoRepositoryBase @@ -264,31 +280,18 @@ ORDER BY colName"; protected override Sql GetBaseQuery(bool isCount) { if (isCount) - return Sql().SelectCount().From(); + return Sql() + .SelectCount() + .From(); return Sql() - .Select(r => r.Select(referenceName: "User2AppDtos")) - .From() - .LeftJoin() - .On(left => left.Id, right => right.UserId); - } - -#error prob needs to be refactored entirely - /// - /// A query to return a user with it's groups and with it's groups sections - /// - /// - private Sql GetQueryWithGroups() - { - //base query includes user groups - var sql = GetBaseQuery("umbracoUser.*, umbracoUserGroup.*, umbracoUserGroup2App.*, umbracoUserStartNode.*"); - AddGroupLeftJoin(sql); - return sql; + .Select() + .From(); } private static void AddGroupLeftJoin(Sql sql) { - sql + sql .LeftJoin() .On(left => left.UserId, right => right.Id) .LeftJoin() @@ -352,7 +355,7 @@ ORDER BY colName"; { AddingOrUpdateStartNodes(entity, Enumerable.Empty(), UserStartNodeDto.StartNodeTypeValue.Content, entity.StartContentIds); } - + if (entity.IsPropertyDirty("StartMediaIds")) { AddingOrUpdateStartNodes(entity, Enumerable.Empty(), UserStartNodeDto.StartNodeTypeValue.Media, entity.StartMediaIds); @@ -557,44 +560,23 @@ ORDER BY colName"; private IEnumerable GetAllInOrNotInGroup(int groupId, bool include) { - #error this needs to be rewritten - var sql = new Sql(); - sql.Select("*") + var sql = Sql() + .Select() .From(); - var innerSql = new Sql(); - innerSql.Select("umbracoUser.id") - .From() - .LeftJoin() - .On(left => left.Id, right => right.UserId) - .Where("umbracoUser2UserGroup.userGroupId = " + groupId); + var inSql = Sql() + .Select(x => x.UserId) + .From() + .Where(x => x.UserGroupId == groupId); + + if (include) + sql.WhereIn(x => x.Id, inSql); + else + sql.WhereNotIn(x => x.Id, inSql); - sql.Where(string.Format("umbracoUser.id {0} ({1})", - include ? "IN" : "NOT IN", - innerSql.SQL)); return ConvertFromDtos(Database.Fetch(sql)); } - [Obsolete("Use the overload with long operators instead")] - [EditorBrowsable(EditorBrowsableState.Never)] - public IEnumerable GetPagedResultsByQuery(IQuery query, int pageIndex, int pageSize, out int totalRecords, Expression> orderBy) - { - if (orderBy == null) throw new ArgumentNullException("orderBy"); - - // get the referenced column name and find the corresp mapped column name - var expressionMember = ExpressionHelper.GetMemberInfo(orderBy); - var mapper = MappingResolver.Current.ResolveMapperByType(typeof(IUser)); - var mappedField = mapper.Map(expressionMember.Name); - - if (mappedField.IsNullOrWhiteSpace()) - throw new ArgumentException("Could not find a mapping for the column specified in the orderBy clause"); - - long tr; - var results = GetPagedResultsByQuery(query, Convert.ToInt64(pageIndex), pageSize, out tr, mappedField, Direction.Ascending); - totalRecords = Convert.ToInt32(tr); - return results; - } - /// /// Gets paged user results /// @@ -611,7 +593,9 @@ ORDER BY colName"; /// /// The query supplied will ONLY work with data specifically on the umbracoUser table because we are using NPoco paging (SQL paging) /// - public IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, Expression> orderBy, Direction orderDirection, string[] userGroups = null, UserState[] userState = null, IQuery filter = null) + public IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, + Expression> orderBy, Direction orderDirection = Direction.Ascending, + string[] userGroups = null, UserState[] userState = null, IQuery filter = null) { if (orderBy == null) throw new ArgumentNullException(nameof(orderBy)); @@ -626,36 +610,33 @@ ORDER BY colName"; return GetPagedResultsByQuery(query, pageIndex, pageSize, out totalRecords, mappedField, orderDirection, userGroups, userState, filter); } - - - private IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, string orderBy, Direction orderDirection, - string[] userGroups = null, - UserState[] userState = null, - IQuery filter = null) + private IEnumerable GetPagedResultsByQuery(IQuery query, long pageIndex, int pageSize, out long totalRecords, + string orderBy, Direction orderDirection = Direction.Ascending, + string[] userGroups = null, UserState[] userState = null, IQuery filter = null) { - if (string.IsNullOrWhiteSpace(orderBy)) throw new ArgumentException("Value cannot be null or whitespace.", "orderBy"); + if (string.IsNullOrWhiteSpace(orderBy)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(orderBy)); - - Sql filterSql = null; - if (filter != null || (userGroups != null && userGroups.Length > 0) || (userState != null && userState.Length > 0 && userState.Contains(UserState.All) == false)) - filterSql = new Sql(); + var filterSql = filter != null + || userGroups != null && userGroups.Length > 0 + || userState != null && userState.Length > 0 && userState.Contains(UserState.All) == false + ? Sql() : null; if (filter != null) { - foreach (var filterClause in filter.GetWhereClauses()) - { - filterSql.Append(string.Format("AND ({0})", filterClause.Item1), filterClause.Item2); - } + foreach (var clause in filter.GetWhereClauses()) + filterSql.Append($"AND ({clause.Item1})", clause.Item2); } + if (userGroups != null && userGroups.Length > 0) { - var subQuery = @"AND (umbracoUser.id IN (SELECT DISTINCT umbracoUser.id + const string subQuery = @"AND (umbracoUser.id IN (SELECT DISTINCT umbracoUser.id FROM umbracoUser INNER JOIN umbracoUser2UserGroup ON umbracoUser2UserGroup.userId = umbracoUser.id INNER JOIN umbracoUserGroup ON umbracoUserGroup.id = umbracoUser2UserGroup.userGroupId WHERE umbracoUserGroup.userGroupAlias IN (@userGroups)))"; - filterSql.Append(subQuery, new { userGroups = userGroups }); + filterSql.Append(subQuery, new { userGroups }); } + if (userState != null && userState.Length > 0) { //the "ALL" state doesn't require any filtering so we ignore that, if it exists in the list we don't do any filtering @@ -686,111 +667,50 @@ ORDER BY colName"; } sb.Append(")"); - filterSql.Append("AND " + sb); } } - // Get base query for returning IDs - var sqlBaseIds = GetBaseQuery("id"); + // create base query + var sql = Sql() + .Select() + .From(); - if (query == null) query = new Query(); - var translatorIds = new SqlTranslator(sqlBaseIds, query); - var sqlQueryIds = translatorIds.Translate(); + // apply query + if (query != null) + sql = new SqlTranslator(sql, query).Translate(); - //get sorted and filtered sql - var sqlNodeIdsWithSort = GetSortedSqlForPagedResults( - GetFilteredSqlForPagedResults(sqlQueryIds, filterSql), - orderDirection, orderBy); + // get sorted and filtered sql + var sqlNodeIdsWithSort = ApplySort(ApplyFilter(sql, filterSql), orderDirection, orderBy); - // Get page of results and total count + // get a page of results and total count var pagedResult = Database.Page(pageIndex + 1, pageSize, sqlNodeIdsWithSort); totalRecords = Convert.ToInt32(pagedResult.TotalItems); - //NOTE: We need to check the actual items returned, not the 'totalRecords', that is because if you request a page number - // that doesn't actually have any data on it, the totalRecords will still indicate there are records but there are none in - // the pageResult. - if (pagedResult.Items.Any()) - { - //Create the inner paged query that was used above to get the paged result, we'll use that as the inner sub query - var args = sqlNodeIdsWithSort.Arguments; - string sqlStringCount, sqlStringPage; - Database.BuildPageQueries(pageIndex * pageSize, pageSize, sqlNodeIdsWithSort.SQL, ref args, out sqlStringCount, out sqlStringPage); - - var sqlQueryFull = GetBaseQuery("umbracoUser.*, umbracoUserGroup.*, umbracoUserGroup2App.*, umbracoUserStartNode.*"); - - var fullQueryWithPagedInnerJoin = sqlQueryFull - .Append("INNER JOIN (") - //join the paged query with the paged query arguments - .Append(sqlStringPage, args) - .Append(") temp ") - .Append("ON umbracoUser.id = temp.id"); - - AddGroupLeftJoin(fullQueryWithPagedInnerJoin); - - //get sorted and filtered sql - var fullQuery = GetSortedSqlForPagedResults( - GetFilteredSqlForPagedResults(fullQueryWithPagedInnerJoin, filterSql), - orderDirection, orderBy); - - var users = ConvertFromDtos(Database.Fetch(new UserGroupRelator().Map, fullQuery)) - .ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching. - - return users; - } - - return Enumerable.Empty(); + // map references + PerformGetReferencedDtos(pagedResult.Items); + return pagedResult.Items.Select(UserFactory.BuildEntity); } - private Sql GetFilteredSqlForPagedResults(Sql sql, Sql filterSql) + private Sql ApplyFilter(Sql sql, Sql filterSql) { - Sql filteredSql; + if (filterSql == null) return sql; - // Apply filter - if (filterSql != null) - { - var sqlFilter = " WHERE " + filterSql.SQL.TrimStart("AND "); + sql.Append(Sql(" WHERE " + filterSql.SQL.TrimStart("AND "), filterSql.Arguments)); - //NOTE: this is certainly strange - NPoco handles this much better but we need to re-create the sql - // instance a couple of times to get the parameter order correct, for some reason the first - // time the arguments don't show up correctly but the SQL argument parameter names are actually updated - // accordingly - so we re-create it again. In v8 we don't need to do this and it's already taken care of. + return sql; + } - filteredSql = new Sql(sql.SQL, sql.Arguments); - var args = filteredSql.Arguments.Concat(filterSql.Arguments).ToArray(); - filteredSql = new Sql( - string.Format("{0} {1}", filteredSql.SQL, sqlFilter), - args); - filteredSql = new Sql(filteredSql.SQL, args); - } + private static Sql ApplySort(Sql sql, Direction orderDirection, string orderBy) + { + if (string.IsNullOrEmpty(orderBy)) return sql; + + if (orderDirection == Direction.Ascending) + sql.OrderBy(orderBy); else - { - //copy to var so that the original isn't changed - filteredSql = new Sql(sql.SQL, sql.Arguments); - } - return filteredSql; - } + sql.OrderByDescending(orderBy); - private Sql GetSortedSqlForPagedResults(Sql sql, Direction orderDirection, string orderBy) - { - //copy to var so that the original isn't changed - var sortedSql = new Sql(sql.SQL, sql.Arguments); - - // Apply order according to parameters - if (string.IsNullOrEmpty(orderBy) == false) - { - //each order by param needs to be in a bracket! see: https://github.com/toptensoftware/PetaPoco/issues/177 - var orderByParams = new[] { string.Format("({0})", orderBy) }; - if (orderDirection == Direction.Ascending) - { - sortedSql.OrderBy(orderByParams); - } - else - { - sortedSql.OrderByDescending(orderByParams); - } - } - return sortedSql; + return sql; } internal IEnumerable GetNextUsers(int id, int count) diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs index 21a1767b24..0728d06636 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs @@ -128,7 +128,7 @@ namespace Umbraco.Core.Persistence public IQuery Query() { EnsureConfigured(); - return new Query(_sqlSyntax, _mappers); + return new Query(_sqlContext); } /// diff --git a/src/Umbraco.Core/Security/BackOfficeUserStore.cs b/src/Umbraco.Core/Security/BackOfficeUserStore.cs index 75a656ee49..89f093bb19 100644 --- a/src/Umbraco.Core/Security/BackOfficeUserStore.cs +++ b/src/Umbraco.Core/Security/BackOfficeUserStore.cs @@ -8,6 +8,7 @@ using System.Web.Security; using AutoMapper; using Microsoft.AspNet.Identity; using Microsoft.Owin; +using Umbraco.Core.Exceptions; using Umbraco.Core.Models; using Umbraco.Core.Models.EntityBase; using Umbraco.Core.Models.Identity; @@ -74,7 +75,7 @@ namespace Umbraco.Core.Security public Task CreateAsync(BackOfficeIdentityUser user) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); //the password must be 'something' it could be empty if authenticating // with an external provider so we'll just generate one and prefix it, the @@ -115,7 +116,7 @@ namespace Umbraco.Core.Security public async Task UpdateAsync(BackOfficeIdentityUser user) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); var asInt = user.Id.TryConvertTo(); if (asInt == false) @@ -147,7 +148,7 @@ namespace Umbraco.Core.Security public Task DeleteAsync(BackOfficeIdentityUser user) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); var asInt = user.Id.TryConvertTo(); if (asInt == false) @@ -208,7 +209,7 @@ namespace Umbraco.Core.Security public Task SetPasswordHashAsync(BackOfficeIdentityUser user, string passwordHash) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); if (string.IsNullOrEmpty(passwordHash)) throw new ArgumentNullOrEmptyException(nameof(passwordHash)); user.PasswordHash = passwordHash; @@ -224,7 +225,7 @@ namespace Umbraco.Core.Security public Task GetPasswordHashAsync(BackOfficeIdentityUser user) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); return Task.FromResult(user.PasswordHash); } @@ -237,7 +238,7 @@ namespace Umbraco.Core.Security public Task HasPasswordAsync(BackOfficeIdentityUser user) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); return Task.FromResult(string.IsNullOrEmpty(user.PasswordHash) == false); } @@ -250,7 +251,7 @@ namespace Umbraco.Core.Security public Task SetEmailAsync(BackOfficeIdentityUser user, string email) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); if (email.IsNullOrWhiteSpace()) throw new ArgumentNullException("email"); user.Email = email; @@ -266,7 +267,7 @@ namespace Umbraco.Core.Security public Task GetEmailAsync(BackOfficeIdentityUser user) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); return Task.FromResult(user.Email); } @@ -320,7 +321,7 @@ namespace Umbraco.Core.Security public Task AddLoginAsync(BackOfficeIdentityUser user, UserLoginInfo login) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); if (login == null) throw new ArgumentNullException("login"); var logins = user.Logins; @@ -339,7 +340,7 @@ namespace Umbraco.Core.Security public Task RemoveLoginAsync(BackOfficeIdentityUser user, UserLoginInfo login) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); if (login == null) throw new ArgumentNullException("login"); var provider = login.LoginProvider; @@ -359,7 +360,7 @@ namespace Umbraco.Core.Security public Task> GetLoginsAsync(BackOfficeIdentityUser user) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); return Task.FromResult((IList) user.Logins.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey)).ToList()); } @@ -404,7 +405,7 @@ namespace Umbraco.Core.Security public Task AddToRoleAsync(BackOfficeIdentityUser user, string roleName) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Value cannot be null or whitespace.", "roleName"); var userRole = user.Roles.SingleOrDefault(r => r.RoleId == roleName); @@ -425,7 +426,7 @@ namespace Umbraco.Core.Security public Task RemoveFromRoleAsync(BackOfficeIdentityUser user, string roleName) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Value cannot be null or whitespace.", "roleName"); var userRole = user.Roles.SingleOrDefault(r => r.RoleId == roleName); @@ -446,7 +447,7 @@ namespace Umbraco.Core.Security public Task> GetRolesAsync(BackOfficeIdentityUser user) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); return Task.FromResult((IList)user.Roles.Select(x => x.RoleId).ToList()); } @@ -458,7 +459,7 @@ namespace Umbraco.Core.Security public Task IsInRoleAsync(BackOfficeIdentityUser user, string roleName) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); return Task.FromResult(user.Roles.Select(x => x.RoleId).InvariantContains(roleName)); } @@ -470,7 +471,7 @@ namespace Umbraco.Core.Security public Task SetSecurityStampAsync(BackOfficeIdentityUser user, string stamp) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); user.SecurityStamp = stamp; return Task.FromResult(0); @@ -484,7 +485,7 @@ namespace Umbraco.Core.Security public Task GetSecurityStampAsync(BackOfficeIdentityUser user) { ThrowIfDisposed(); - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); //the stamp cannot be null, so if it is currently null then we'll just return a hash of the password return Task.FromResult(user.SecurityStamp.IsNullOrWhiteSpace() @@ -535,7 +536,7 @@ namespace Umbraco.Core.Security /// public Task GetLockoutEndDateAsync(BackOfficeIdentityUser user) { - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); return user.LockoutEndDateUtc.HasValue ? Task.FromResult(DateTimeOffset.MaxValue) @@ -549,7 +550,7 @@ namespace Umbraco.Core.Security /// public Task SetLockoutEndDateAsync(BackOfficeIdentityUser user, DateTimeOffset lockoutEnd) { - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); user.LockoutEndDateUtc = lockoutEnd.UtcDateTime; return Task.FromResult(0); } @@ -561,7 +562,7 @@ namespace Umbraco.Core.Security /// public Task IncrementAccessFailedCountAsync(BackOfficeIdentityUser user) { - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); user.AccessFailedCount++; return Task.FromResult(user.AccessFailedCount); } @@ -573,7 +574,7 @@ namespace Umbraco.Core.Security /// public Task ResetAccessFailedCountAsync(BackOfficeIdentityUser user) { - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); user.AccessFailedCount = 0; return Task.FromResult(0); } @@ -586,7 +587,7 @@ namespace Umbraco.Core.Security /// public Task GetAccessFailedCountAsync(BackOfficeIdentityUser user) { - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); return Task.FromResult(user.AccessFailedCount); } @@ -597,7 +598,7 @@ namespace Umbraco.Core.Security /// public Task GetLockoutEnabledAsync(BackOfficeIdentityUser user) { - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); return Task.FromResult(user.LockoutEnabled); } @@ -608,7 +609,7 @@ namespace Umbraco.Core.Security /// public Task SetLockoutEnabledAsync(BackOfficeIdentityUser user, bool enabled) { - if (user == null) throw new ArgumentNullException("user"); + if (user == null) throw new ArgumentNullException(nameof(user)); user.LockoutEnabled = enabled; return Task.FromResult(0); } diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 0255f5bbb4..690d4aacf7 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.Repositories.Interfaces; using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Services.Changes; diff --git a/src/Umbraco.Core/Services/MemberService.cs b/src/Umbraco.Core/Services/MemberService.cs index 031f6dc823..5ad0623fbb 100644 --- a/src/Umbraco.Core/Services/MemberService.cs +++ b/src/Umbraco.Core/Services/MemberService.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.UnitOfWork; using System.Linq; +using Umbraco.Core.Composing; using Umbraco.Core.Exceptions; using Umbraco.Core.IO; using Umbraco.Core.Persistence.Repositories; @@ -166,12 +167,45 @@ namespace Umbraco.Core.Services /// Alias of the Type /// Is the member approved /// - IMember IMembershipMemberService.CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias, bool isApproved) + IMember IMembershipMemberService.CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias) { - var memberType = FindMemberTypeByAlias(memberTypeAlias); - return CreateMemberWithIdentity(username, email, username, passwordValue, memberType, isApproved); + return CreateMemberWithIdentity(username, email, username, passwordValue, memberTypeAlias); } + /// + /// Creates and persists a new + /// + /// An can be of type or + /// Username of the to create + /// Email of the to create + /// This value should be the encoded/encrypted/hashed value for the password that will be stored in the database + /// Alias of the Type + /// + IMember IMembershipMemberService.CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias, bool isApproved = true) + { + return CreateMemberWithIdentity(username, email, username, passwordValue, memberTypeAlias, isApproved); + } + + public IMember CreateMemberWithIdentity(string username, string email, string memberTypeAlias) + { + return CreateMemberWithIdentity(username, email, username, "", memberTypeAlias); + } + + public IMember CreateMemberWithIdentity(string username, string email, string memberTypeAlias, bool isApproved) + { + return CreateMemberWithIdentity(username, email, username, "", memberTypeAlias, isApproved); + } + + public IMember CreateMemberWithIdentity(string username, string email, string name, string memberTypeAlias) + { + return CreateMemberWithIdentity(username, email, name, "", memberTypeAlias); + } + + public IMember CreateMemberWithIdentity(string username, string email, string name, string memberTypeAlias, bool isApproved) + { + return CreateMemberWithIdentity(username, email, name, "", memberTypeAlias, isApproved); + } + /// /// Creates and persists a Member /// @@ -183,7 +217,7 @@ namespace Umbraco.Core.Services /// Alias of the MemberType the Member should be based on /// Optional IsApproved of the Member to create /// - public IMember CreateMemberWithIdentity(string username, string email, string name, string memberTypeAlias, bool isApproved = true) + public IMember CreateMemberWithIdentity(string username, string email, string name, string passwordValue, string memberTypeAlias, bool isApproved = true) { using (var uow = UowProvider.CreateUnitOfWork()) { @@ -194,64 +228,7 @@ namespace Umbraco.Core.Services if (memberType == null) throw new ArgumentException("No member type with that alias.", nameof(memberTypeAlias)); // causes rollback - var member = new Member(name, email.ToLower().Trim(), username, memberType, isApproved); - CreateMember(uow, member, 0, true); - - uow.Complete(); - return member; - } - } - - /// - /// Creates and persists a Member - /// - /// Using this method will persist the Member object before its returned - /// meaning that it will have an Id available (unlike the CreateMember method) - /// Username of the Member to create - /// Email of the Member to create - /// MemberType the Member should be based on - /// - public IMember CreateMemberWithIdentity(string username, string email, IMemberType memberType) - { - return CreateMemberWithIdentity(username, email, username, "", memberType); - } - - /// - /// Creates and persists a Member - /// - /// Using this method will persist the Member object before its returned - /// meaning that it will have an Id available (unlike the CreateMember method) - /// Username of the Member to create - /// Email of the Member to create - /// Name of the Member to create - /// MemberType the Member should be based on - /// - public IMember CreateMemberWithIdentity(string username, string email, string name, IMemberType memberType) - { - return CreateMemberWithIdentity(username, email, name, "", memberType); - } - - /// - /// Creates and persists a new - /// - /// An can be of type or - /// Username of the to create - /// Email of the to create - /// This value should be the encoded/encrypted/hashed value for the password that will be stored in the database - /// Alias of the Type - /// - IMember IMembershipMemberService.CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias) - { - using (var uow = UowProvider.CreateUnitOfWork()) - { - uow.WriteLock(Constants.Locks.MemberTree); - - // ensure it all still make sense - var memberType = GetMemberType(uow, memberTypeAlias); // + locks - if (memberType == null) - throw new ArgumentException("No member type with that alias.", nameof(memberTypeAlias)); // causes rollback - - var member = new Member(username, email.ToLower().Trim(), username, passwordValue, memberType); + var member = new Member(name, email.ToLower().Trim(), username, passwordValue, memberType, isApproved); CreateMember(uow, member, -1, true); uow.Complete(); @@ -259,6 +236,45 @@ namespace Umbraco.Core.Services } } + public IMember CreateMemberWithIdentity(string username, string email, IMemberType memberType) + { + return CreateMemberWithIdentity(username, email, username, "", memberType); + } + + /// + /// Creates and persists a Member + /// + /// Using this method will persist the Member object before its returned + /// meaning that it will have an Id available (unlike the CreateMember method) + /// Username of the Member to create + /// Email of the Member to create + /// MemberType the Member should be based on + /// + public IMember CreateMemberWithIdentity(string username, string email, IMemberType memberType, bool isApproved) + { + return CreateMemberWithIdentity(username, email, username, "", memberType, isApproved); + } + + public IMember CreateMemberWithIdentity(string username, string email, string name, IMemberType memberType) + { + return CreateMemberWithIdentity(username, email, name, "", memberType); + } + + /// + /// Creates and persists a Member + /// + /// Using this method will persist the Member object before its returned + /// meaning that it will have an Id available (unlike the CreateMember method) + /// Username of the Member to create + /// Email of the Member to create + /// Name of the Member to create + /// MemberType the Member should be based on + /// + public IMember CreateMemberWithIdentity(string username, string email, string name, IMemberType memberType, bool isApproved) + { + return CreateMemberWithIdentity(username, email, name, "", memberType, isApproved); + } + /// /// Creates and persists a Member /// @@ -270,7 +286,7 @@ namespace Umbraco.Core.Services /// This value should be the encoded/encrypted/hashed value for the password that will be stored in the database /// MemberType the Member should be based on /// - private IMember CreateMemberWithIdentity(string username, string email, string name, string passwordValue, IMemberType memberType) + private IMember CreateMemberWithIdentity(string username, string email, string name, string passwordValue, IMemberType memberType, bool isApproved = true) { if (memberType == null) throw new ArgumentNullException(nameof(memberType)); @@ -283,7 +299,7 @@ namespace Umbraco.Core.Services if (vrfy == null || vrfy.Id != memberType.Id) throw new ArgumentException($"Member type with alias {memberType.Alias} does not exist or is a different member type."); // causes rollback - var member = new Member(name, email.ToLower().Trim(), username, passwordValue, memberType); + var member = new Member(name, email.ToLower().Trim(), username, passwordValue, memberType, isApproved); CreateMember(uow, member, -1, true); uow.Complete(); @@ -1319,6 +1335,12 @@ namespace Umbraco.Core.Services return GetMemberType(uow, memberTypeAlias); } } + + // fixme - this should not be here, or??? + public string GetDefaultMemberType() + { + return Current.Services.MemberTypeService.GetDefault(); + } #endregion } diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 5451cdf1e5..13e6b9297f 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -11,9 +11,7 @@ using Umbraco.Core.Configuration; using Umbraco.Core.Events; using Umbraco.Core.Exceptions; using Umbraco.Core.Logging; -using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; -using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; @@ -27,15 +25,12 @@ namespace Umbraco.Core.Services /// public class UserService : ScopeRepositoryService, IUserService { - //TODO: We need to change the isUpgrading flag to use an app state enum as described here: http://issues.umbraco.org/issue/U4-6816 - // in the meantime, we will use a boolean which we are currently using during upgrades to ensure that a user object is not persisted during this phase, otherwise - // exceptions can occur if the db is not in it's correct state. - internal bool IsUpgrading { get; set; } + private readonly bool _isUpgrading; - public UserService(IScopeUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory) + public UserService(IScopeUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IRuntimeState runtimeState) : base(provider, logger, eventMessagesFactory) { - IsUpgrading = false; + _isUpgrading = runtimeState.Level == RuntimeLevel.Install || runtimeState.Level == RuntimeLevel.Upgrade; } #region Implementation of IMembershipUserService @@ -209,7 +204,7 @@ namespace Umbraco.Core.Services //version checks in the BackOfficeSignInManager and calling into other special overloads that we'd need //like "GetUserById(int id, bool includeSecurityData)" which may cause confusion because the result of //that method would not be cached. - if (ApplicationContext.Current.IsUpgrading) + if (_isUpgrading) { //NOTE: this will not be cached return repository.GetByUsername(username, includeSecurityData: false); @@ -335,7 +330,7 @@ namespace Umbraco.Core.Services catch (DbException ex) { // if we are upgrading and an exception occurs, log and swallow it - if (IsUpgrading == false) throw; + if (_isUpgrading == false) throw; Logger.Warn(ex, "An error occurred attempting to save a user instance during upgrade, normally this warning can be ignored"); @@ -586,8 +581,7 @@ namespace Umbraco.Core.Services IQuery filterQuery = null; if (filter.IsNullOrWhiteSpace() == false) { -#error query - filterQuery = Query.Builder.Where(x => x.Name.Contains(filter) || x.Username.Contains(filter)); + filterQuery = uow.Query().Where(x => x.Name.Contains(filter) || x.Username.Contains(filter)); } var repository = uow.CreateRepository(); @@ -673,7 +667,7 @@ namespace Umbraco.Core.Services /// public IProfile GetProfileByUserName(string username) { - using (var uow = UowProvider.GetUnitOfWork(readOnly: true)) + using (var uow = UowProvider.CreateUnitOfWork(readOnly: true)) { var repository = uow.CreateRepository(); return repository.GetProfile(username); @@ -704,7 +698,7 @@ namespace Umbraco.Core.Services //version checks in the BackOfficeSignInManager and calling into other special overloads that we'd need //like "GetUserById(int id, bool includeSecurityData)" which may cause confusion because the result of //that method would not be cached. - if (ApplicationContext.Current.IsUpgrading) + if (_isUpgrading) { //NOTE: this will not be cached return repository.Get(id, includeSecurityData: false); @@ -964,7 +958,7 @@ namespace Umbraco.Core.Services if (groups == null) throw new ArgumentNullException(nameof(groups)); using (var uow = UowProvider.CreateUnitOfWork(readOnly: true)) { - var repository = uow.CreateRepository(); + var repository = uow.CreateRepository(); return repository.GetPermissions(groups.Select(x => x.ToReadOnlyGroup()).ToArray(), fallbackToDefaultPermissions, nodeIds); } } diff --git a/src/Umbraco.Core/UdiEntityType.cs b/src/Umbraco.Core/UdiEntityType.cs index e786ac3146..1f64adb3fb 100644 --- a/src/Umbraco.Core/UdiEntityType.cs +++ b/src/Umbraco.Core/UdiEntityType.cs @@ -90,8 +90,6 @@ namespace Umbraco.Core [UdiType(UdiType.StringUdi)] public const string PartialViewMacro = "partial-view-macro"; [UdiType(UdiType.StringUdi)] - public const string UserControl = "usercontrol"; - [UdiType(UdiType.StringUdi)] public const string Xslt = "xslt"; public static string FromUmbracoObjectType(UmbracoObjectTypes umbracoObjectType) diff --git a/src/Umbraco.Core/UdiGetterExtensions.cs b/src/Umbraco.Core/UdiGetterExtensions.cs index 3e4d2ea51a..66c66955ca 100644 --- a/src/Umbraco.Core/UdiGetterExtensions.cs +++ b/src/Umbraco.Core/UdiGetterExtensions.cs @@ -190,17 +190,6 @@ namespace Umbraco.Core return new GuidUdi(Constants.UdiEntityType.Macro, entity.Key).EnsureClosed(); } - /// - /// Gets the entity identifier of the entity. - /// - /// The entity. - /// The entity identifier of the entity. - public static StringUdi GetUdi(this IUserControl entity) - { - if (entity == null) throw new ArgumentNullException("entity"); - return new StringUdi(Constants.UdiEntityType.UserControl, entity.Path.TrimStart('/')).EnsureClosed(); - } - /// /// Gets the entity identifier of the entity. /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index fb8ccee8da..e2095c737b 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -524,6 +524,7 @@ + @@ -1093,6 +1094,7 @@ + @@ -1502,8 +1504,9 @@ - - + + + \ No newline at end of file diff --git a/src/Umbraco.Web/Trees/ISearchableTree.cs b/src/Umbraco.Web/Trees/ISearchableTree.cs new file mode 100644 index 0000000000..ac733539b2 --- /dev/null +++ b/src/Umbraco.Web/Trees/ISearchableTree.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Umbraco.Web.Models.ContentEditing; + +namespace Umbraco.Web.Trees +{ + public interface ISearchableTree + { + /// + /// The alias of the tree that the belongs to + /// + string TreeAlias { get; } + + /// + /// Searches for results based on the entity type + /// + /// + /// + /// + /// + /// + /// A starting point for the search, generally a node id, but for members this is a member type alias + /// + /// + IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null); + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 43ae28f4ab..a5749cf7a8 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -297,6 +297,7 @@ + @@ -314,6 +315,7 @@ + @@ -324,12 +326,15 @@ + ASPXCodeBehind + + @@ -1560,7 +1565,6 @@ -