diff --git a/src/Umbraco.Core/Constants-ObjectTypes.cs b/src/Umbraco.Core/Constants-ObjectTypes.cs index d364c1379c..229bd1b7d7 100644 --- a/src/Umbraco.Core/Constants-ObjectTypes.cs +++ b/src/Umbraco.Core/Constants-ObjectTypes.cs @@ -109,6 +109,11 @@ namespace Umbraco.Core /// public const string Member = "39EB0F98-B348-42A1-8662-E7EB18487560"; + /// + /// Guid for a Media Type object. + /// + public static readonly Guid MemberGuid = new Guid(Member); + /// /// Guid for a Member Group object. /// diff --git a/src/Umbraco.Core/EnumerableExtensions.cs b/src/Umbraco.Core/EnumerableExtensions.cs index 58d4d453b7..d708dfbf82 100644 --- a/src/Umbraco.Core/EnumerableExtensions.cs +++ b/src/Umbraco.Core/EnumerableExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; @@ -67,22 +68,15 @@ namespace Umbraco.Core } } - /// The for each. - /// The items. - /// The func. - /// item type - /// Result type - /// the Results + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Use a normal foreach loop instead, this adds more allocations than necessary")] public static TResult[] ForEach(this IEnumerable items, Func func) { return items.Select(func).ToArray(); } - /// The for each. - /// The items. - /// The action. - /// Item type - /// list of TItem + [EditorBrowsable(EditorBrowsableState.Never)] + [Obsolete("Use a normal foreach loop instead, this adds more allocations than necessary")] public static IEnumerable ForEach(this IEnumerable items, Action action) { if (items != null) @@ -101,6 +95,7 @@ namespace Umbraco.Core /// The select child. /// Item type /// list of TItem + [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("Do not use, use SelectRecursive instead which has far less potential of re-iterating an iterator which may cause significantly more SQL queries")] public static IEnumerable FlattenList(this IEnumerable e, Func> f) { diff --git a/src/Umbraco.Core/Models/ContentTypeExtensions.cs b/src/Umbraco.Core/Models/ContentTypeExtensions.cs index f0d8b10898..90488219fc 100644 --- a/src/Umbraco.Core/Models/ContentTypeExtensions.cs +++ b/src/Umbraco.Core/Models/ContentTypeExtensions.cs @@ -30,7 +30,8 @@ namespace Umbraco.Core.Models return descendants; } - throw new NotSupportedException("The content type must be " + typeof(IContentType) + " or " + typeof(IMediaType)); + //No other content types have children (i.e. member types) + return Enumerable.Empty(); } /// diff --git a/src/Umbraco.Core/Services/EntityService.cs b/src/Umbraco.Core/Services/EntityService.cs index e3bfb6dde9..08fd990036 100644 --- a/src/Umbraco.Core/Services/EntityService.cs +++ b/src/Umbraco.Core/Services/EntityService.cs @@ -375,6 +375,18 @@ namespace Umbraco.Core.Services } } + /// + /// Returns a apged collection of children + /// + /// The parent id to return children for + /// + /// + /// + /// + /// + /// + /// + /// public IEnumerable GetPagedChildren(int parentId, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords, string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = "") { diff --git a/src/Umbraco.Core/Services/IEntityService.cs b/src/Umbraco.Core/Services/IEntityService.cs index 339b0b7146..2239cb058b 100644 --- a/src/Umbraco.Core/Services/IEntityService.cs +++ b/src/Umbraco.Core/Services/IEntityService.cs @@ -138,6 +138,18 @@ namespace Umbraco.Core.Services /// An enumerable list of objects IEnumerable GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType); + /// + /// Returns a apged collection of children + /// + /// The parent id to return children for + /// + /// + /// + /// + /// + /// + /// + /// IEnumerable GetPagedChildren(int parentId, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords, string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = ""); diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 989e3dbabe..5e23e1f0d2 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -409,18 +409,45 @@ namespace Umbraco.Web.Editors string filter = "") { int intId; - if (id == Constants.Conventions.MemberTypes.AllMembersListId) - { - intId = 0; - return GetPagedChildren(intId, type, pageNumber, pageSize, orderBy, orderDirection, filter); - } - - if(int.TryParse(id, out intId)) + + if (int.TryParse(id, out intId)) { return GetPagedChildren(intId, type, pageNumber, pageSize, orderBy, orderDirection, filter); } - throw new HttpResponseException(HttpStatusCode.NotFound); + Guid guidId; + if (Guid.TryParse(id, out guidId)) + { + //Not supported currently + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + Udi udiId; + if (Udi.TryParse(id, out udiId)) + { + //Not supported currently + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + //so we don't have an INT, GUID or UDI, it's just a string, so now need to check if it's a special id or a member type + if (id == Constants.Conventions.MemberTypes.AllMembersListId) + { + //the EntityService can search paged members from the root + + intId = -1; + return GetPagedChildren(intId, type, pageNumber, pageSize, orderBy, orderDirection, filter); + } + + //the EntityService cannot search members of a certain type, this is currently not supported and would require + //quite a bit of plumbing to do in the Services/Repository, we'll revert to a paged search + + int total; + var searchResult = ExamineSearch(filter ?? "", type, pageSize, pageNumber - 1, out total, id); + + return new PagedResult(total, pageNumber, pageSize) + { + Items = searchResult + }; } /// @@ -496,12 +523,30 @@ namespace Umbraco.Web.Editors /// /// /// - /// - /// A starting point for the search, generally a node id, but for members this is a member type alias - /// + /// /// private IEnumerable ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null) { + int total; + return ExamineSearch(query, entityType, 200, 0, out total, searchFrom); + } + + /// + /// 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 + /// + /// + /// + /// + private IEnumerable ExamineSearch(string query, UmbracoEntityTypes entityType, int pageSize, int pageIndex, out int totalFound, string searchFrom = null) + { + //TODO: We need to update this to support paging + var sb = new StringBuilder(); string type; @@ -577,91 +622,113 @@ namespace Umbraco.Web.Editors query = Lucene.Net.QueryParsers.QueryParser.Escape(query); - if (query.IsNullOrWhiteSpace()) + //nothing to search + if (searchFrom.IsNullOrWhiteSpace() && query.IsNullOrWhiteSpace()) { + totalFound = 0; return new List(); } - //add back the surrounding quotes - query = string.Format("{0}{1}{0}", "\"", query); - - //node name exactly boost x 10 - sb.Append("+(__nodeName: ("); - sb.Append(query.ToLower()); - sb.Append(")^10.0 "); - - foreach (var f in fields) + //update the query with the query term + if (query.IsNullOrWhiteSpace() == false) { - //additional fields normally - sb.Append(f); - sb.Append(": ("); - sb.Append(query); + //add back the surrounding quotes + query = string.Format("{0}{1}{0}", "\"", query); + + //node name exactly boost x 10 + sb.Append("+(__nodeName: ("); + sb.Append(query.ToLower()); + sb.Append(")^10.0 "); + + foreach (var f in fields) + { + //additional fields normally + sb.Append(f); + sb.Append(": ("); + sb.Append(query); + sb.Append(") "); + } + sb.Append(") "); } } else { - if (query.Trim(new[] { '\"', '\'' }).IsNullOrWhiteSpace()) + var trimmed = query.Trim(new[] {'\"', '\''}); + + //nothing to search + if (searchFrom.IsNullOrWhiteSpace() && trimmed.IsNullOrWhiteSpace()) { + totalFound = 0; return new List(); } - - query = Lucene.Net.QueryParsers.QueryParser.Escape(query); - var querywords = query.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - - //node name exactly boost x 10 - sb.Append("+(__nodeName:"); - sb.Append("\""); - sb.Append(query.ToLower()); - sb.Append("\""); - sb.Append("^10.0 "); - - //node name normally with wildcards - sb.Append(" __nodeName:"); - sb.Append("("); - foreach (var w in querywords) + //update the query with the query term + if (trimmed.IsNullOrWhiteSpace() == false) { - sb.Append(w.ToLower()); - sb.Append("* "); - } - sb.Append(") "); + query = Lucene.Net.QueryParsers.QueryParser.Escape(query); + var querywords = query.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - foreach (var f in fields) - { - //additional fields normally - sb.Append(f); - sb.Append(":"); + //node name exactly boost x 10 + sb.Append("+(__nodeName:"); + sb.Append("\""); + sb.Append(query.ToLower()); + sb.Append("\""); + sb.Append("^10.0 "); + + //node name normally with wildcards + sb.Append(" __nodeName:"); sb.Append("("); foreach (var w in querywords) { sb.Append(w.ToLower()); sb.Append("* "); } - sb.Append(")"); - sb.Append(" "); + sb.Append(") "); + + + foreach (var f in fields) + { + //additional fields normally + sb.Append(f); + sb.Append(":"); + sb.Append("("); + foreach (var w in querywords) + { + sb.Append(w.ToLower()); + sb.Append("* "); + } + sb.Append(")"); + sb.Append(" "); + } + + sb.Append(") "); } } //must match index type - sb.Append(") +__IndexType:"); + sb.Append("+__IndexType:"); sb.Append(type); - var raw = internalSearcher.CreateSearchCriteria().RawQuery(sb.ToString()); - //limit results to 200 to avoid huge over processing (CPU) - var result = internalSearcher.Search(raw, 200); + var result = internalSearcher + //only return the number of items specified to read up to the amount of records to fill from 0 -> the number of items on the page requested + .Search(raw, pageSize * (pageIndex + 1)); + totalFound = result.TotalItemCount; + + var pagedResult = result.Skip(pageIndex); + switch (entityType) { case UmbracoEntityTypes.Member: - return MemberFromSearchResults(result); + return MemberFromSearchResults(pagedResult.ToArray()); case UmbracoEntityTypes.Media: - return MediaFromSearchResults(result); + return MediaFromSearchResults(pagedResult); case UmbracoEntityTypes.Document: - return ContentFromSearchResults(result); + return ContentFromSearchResults(pagedResult); default: throw new NotSupportedException("The " + typeof(EntityController) + " currently does not support searching against object type " + entityType); } @@ -672,7 +739,7 @@ namespace Umbraco.Web.Editors /// /// /// - private IEnumerable MemberFromSearchResults(ISearchResults results) + private IEnumerable MemberFromSearchResults(SearchResult[] results) { var mapped = Mapper.Map>(results).ToArray(); //add additional data @@ -706,7 +773,7 @@ namespace Umbraco.Web.Editors /// /// /// - private IEnumerable MediaFromSearchResults(ISearchResults results) + private IEnumerable MediaFromSearchResults(IEnumerable results) { var mapped = Mapper.Map>(results).ToArray(); //add additional data @@ -726,9 +793,9 @@ namespace Umbraco.Web.Editors /// /// /// - private IEnumerable ContentFromSearchResults(ISearchResults results) + private IEnumerable ContentFromSearchResults(IEnumerable results) { - var mapped = Mapper.Map>(results).ToArray(); + var mapped = Mapper.Map>(results).ToArray(); //add additional data foreach (var m in mapped) { diff --git a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs index cd6b9b9f83..5be9d550e5 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityModelMapper.cs @@ -21,7 +21,14 @@ namespace Umbraco.Web.Models.Mapping .ForMember(x => x.Udi, expression => expression.MapFrom(x => Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(x.NodeObjectTypeId), x.Key))) .ForMember(basic => basic.Icon, expression => expression.MapFrom(entity => entity.ContentTypeIcon)) .ForMember(dto => dto.Trashed, expression => expression.Ignore()) - .ForMember(x => x.Alias, expression => expression.Ignore()); + .ForMember(x => x.Alias, expression => expression.Ignore()) + .AfterMap((entity, basic) => + { + if (entity.NodeObjectTypeId == Constants.ObjectTypes.MemberGuid && basic.Icon.IsNullOrWhiteSpace()) + { + basic.Icon = "icon-user"; + } + }); config.CreateMap() .ForMember(x => x.Udi, expression => expression.Ignore()) @@ -151,6 +158,9 @@ namespace Umbraco.Web.Models.Mapping config.CreateMap>() .ConvertUsing(results => results.Select(Mapper.Map).ToList()); + + config.CreateMap, IEnumerable>() + .ConvertUsing(results => results.Select(Mapper.Map).ToList()); } } } \ No newline at end of file