From 63ae00b0f3e4bf9451f6f560a66d87762cb54ee1 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 15 Oct 2019 00:07:44 +1100 Subject: [PATCH] Fixes Search an element from its GUID with the backoffice #6185 (#6530) --- src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 40 +++++++++++++++++-- .../Trees/ContentTreeController.cs | 17 +++++--- .../Trees/ContentTreeControllerBase.cs | 14 +++++++ .../Trees/ContentTypeTreeController.cs | 20 +++++++--- .../Trees/DataTypeTreeController.cs | 18 ++++++--- src/Umbraco.Web/Trees/MediaTreeController.cs | 6 ++- .../Trees/MediaTypeTreeController.cs | 19 ++++++--- .../Trees/TemplatesTreeController.cs | 19 ++++++--- 8 files changed, 122 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index c3c3dc75ce..b351cca972 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -7,6 +7,8 @@ using Examine; using Umbraco.Core; using Umbraco.Core.Mapping; using Umbraco.Core.Models; +using Umbraco.Core.Models.Entities; +using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Examine; using Umbraco.Web.Models.ContentEditing; @@ -14,6 +16,7 @@ using Umbraco.Web.Trees; namespace Umbraco.Web.Search { + /// /// Used for internal Umbraco implementations of /// @@ -24,22 +27,25 @@ namespace Umbraco.Web.Search private readonly ILocalizationService _languageService; private readonly IEntityService _entityService; private readonly UmbracoMapper _mapper; + private readonly ISqlContext _sqlContext; public UmbracoTreeSearcher(IExamineManager examineManager, UmbracoContext umbracoContext, ILocalizationService languageService, IEntityService entityService, - UmbracoMapper mapper) + UmbracoMapper mapper, + ISqlContext sqlContext) { _examineManager = examineManager ?? throw new ArgumentNullException(nameof(examineManager)); _umbracoContext = umbracoContext; _languageService = languageService; _entityService = entityService; _mapper = mapper; + _sqlContext = sqlContext; } /// - /// Searches for results based on the entity type + /// Searches Examine for results based on the entity type /// /// /// @@ -61,7 +67,7 @@ namespace Umbraco.Web.Search string type; var indexName = Constants.UmbracoIndexes.InternalIndexName; - var fields = new[] { "id", "__NodeId" }; + var fields = new[] { "id", "__NodeId", "__Key" }; // TODO: WE should try to allow passing in a lucene raw query, however we will still need to do some manual string // manipulation for things like start paths, member types, etc... @@ -70,12 +76,18 @@ namespace Umbraco.Web.Search //} + //special GUID check since if a user searches on one specifically we need to escape it + if (Guid.TryParse(query, out var g)) + { + query = "\"" + g.ToString() + "\""; + } + switch (entityType) { case UmbracoEntityTypes.Member: indexName = Constants.UmbracoIndexes.MembersIndexName; type = "member"; - fields = new[] { "id", "__NodeId", "email", "loginName" }; + fields = new[] { "id", "__NodeId", "__Key", "email", "loginName" }; if (searchFrom != null && searchFrom != Constants.Conventions.MemberTypes.AllMembersListId && searchFrom.Trim() != "-1") { sb.Append("+__NodeTypeAlias:"); @@ -129,6 +141,26 @@ namespace Umbraco.Web.Search } } + /// + /// Searches with the for results based on the entity type + /// + /// + /// + /// + /// + /// + /// + /// + public IEnumerable EntitySearch(UmbracoObjectTypes objectType, string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) + { + //if it's a GUID, match it + Guid.TryParse(query, out var g); + + var results = _entityService.GetPagedDescendants(objectType, pageIndex, pageSize, out totalFound, + filter: _sqlContext.Query().Where(x => x.Name.Contains(query) || x.Key == g)); + return _mapper.MapEnumerable(results); + } + private bool BuildQuery(StringBuilder sb, string query, string searchFrom, string[] fields, string type) { //build a lucene query: diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index 970191e510..3373a6fa8b 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -15,6 +15,10 @@ using Umbraco.Web.WebApi.Filters; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Search; using Constants = Umbraco.Core.Constants; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; namespace Umbraco.Web.Trees { @@ -36,20 +40,21 @@ namespace Umbraco.Web.Trees private readonly UmbracoTreeSearcher _treeSearcher; private readonly ActionCollection _actions; - public ContentTreeController(UmbracoTreeSearcher treeSearcher, ActionCollection actions) - { - _treeSearcher = treeSearcher; - _actions = actions; - } - protected override int RecycleBinId => Constants.System.RecycleBinContent; protected override bool RecycleBinSmells => Services.ContentService.RecycleBinSmells(); private int[] _userStartNodes; + protected override int[] UserStartNodes => _userStartNodes ?? (_userStartNodes = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService)); + public ContentTreeController(UmbracoTreeSearcher treeSearcher, ActionCollection actions, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + _treeSearcher = treeSearcher; + _actions = actions; + } + /// protected override TreeNode GetSingleTreeNode(IEntitySlim entity, string parentId, FormDataCollection queryStrings) { diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 015c91cb81..c3458f93a2 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -18,11 +18,23 @@ using System.Web.Http.ModelBinding; using Umbraco.Web.Actions; using Umbraco.Web.Composing; using Umbraco.Core.Security; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Persistence; namespace Umbraco.Web.Trees { public abstract class ContentTreeControllerBase : TreeController { + + protected ContentTreeControllerBase(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + } + + protected ContentTreeControllerBase() + { + } + #region Actions /// @@ -529,6 +541,8 @@ namespace Umbraco.Web.Trees private bool? _ignoreUserStartNodes; + + /// /// If the request should allows a user to choose nodes that they normally don't have access to /// diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs index bead6aa141..a865df8f36 100644 --- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs @@ -3,11 +3,17 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; using Umbraco.Web.Actions; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Trees; +using Umbraco.Web.Search; using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Trees @@ -18,6 +24,13 @@ namespace Umbraco.Web.Trees [CoreTree] public class ContentTypeTreeController : TreeController, ISearchableTree { + private readonly UmbracoTreeSearcher _treeSearcher; + + public ContentTypeTreeController(UmbracoTreeSearcher treeSearcher, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + _treeSearcher = treeSearcher; + } + protected override TreeNode CreateRootNode(FormDataCollection queryStrings) { var root = base.CreateRootNode(queryStrings); @@ -135,10 +148,7 @@ namespace Umbraco.Web.Trees } public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) - { - var results = Services.EntityService.GetPagedDescendants(UmbracoObjectTypes.DocumentType, pageIndex, pageSize, out totalFound, - filter: SqlContext.Query().Where(x => x.Name.Contains(query))); - return Mapper.MapEnumerable(results); - } + => _treeSearcher.EntitySearch(UmbracoObjectTypes.DocumentType, query, pageSize, pageIndex, out totalFound, searchFrom); + } } diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index 2465b4d45a..4e5b1df631 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -12,6 +12,11 @@ using Umbraco.Core.Services; using Umbraco.Web.Actions; using Umbraco.Web.Models.ContentEditing; using Constants = Umbraco.Core.Constants; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Web.Search; namespace Umbraco.Web.Trees { @@ -21,6 +26,13 @@ namespace Umbraco.Web.Trees [CoreTree] public class DataTypeTreeController : TreeController, ISearchableTree { + private readonly UmbracoTreeSearcher _treeSearcher; + + public DataTypeTreeController(UmbracoTreeSearcher treeSearcher, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + _treeSearcher = treeSearcher; + } + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) { var intId = id.TryConvertTo(); @@ -148,10 +160,6 @@ namespace Umbraco.Web.Trees } public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) - { - var results = Services.EntityService.GetPagedDescendants(UmbracoObjectTypes.DataType, pageIndex, pageSize, out totalFound, - filter: SqlContext.Query().Where(x => x.Name.Contains(query))); - return Mapper.MapEnumerable(results); - } + => _treeSearcher.EntitySearch(UmbracoObjectTypes.DataType, query, pageSize, pageIndex, out totalFound, searchFrom); } } diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index f4f373f9a4..df44a809c9 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -16,6 +16,10 @@ using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Search; using Constants = Umbraco.Core.Constants; using Umbraco.Core.Services.Implement; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; namespace Umbraco.Web.Trees { @@ -35,7 +39,7 @@ namespace Umbraco.Web.Trees { private readonly UmbracoTreeSearcher _treeSearcher; - public MediaTreeController(UmbracoTreeSearcher treeSearcher) + public MediaTreeController(UmbracoTreeSearcher treeSearcher, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { _treeSearcher = treeSearcher; } diff --git a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs index 53e30a4ee5..f85aefcace 100644 --- a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs @@ -10,6 +10,11 @@ using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Services; using Umbraco.Web.Actions; using Umbraco.Web.Models.ContentEditing; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Web.Search; namespace Umbraco.Web.Trees { @@ -19,6 +24,13 @@ namespace Umbraco.Web.Trees [CoreTree] public class MediaTypeTreeController : TreeController, ISearchableTree { + private readonly UmbracoTreeSearcher _treeSearcher; + + public MediaTypeTreeController(UmbracoTreeSearcher treeSearcher, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + _treeSearcher = treeSearcher; + } + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) { var intId = id.TryConvertTo(); @@ -117,10 +129,7 @@ namespace Umbraco.Web.Trees } public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) - { - var results = Services.EntityService.GetPagedDescendants(UmbracoObjectTypes.MediaType, pageIndex, pageSize, out totalFound, - filter: SqlContext.Query().Where(x => x.Name.Contains(query))); - return Mapper.MapEnumerable(results); - } + => _treeSearcher.EntitySearch(UmbracoObjectTypes.MediaType, query, pageSize, pageIndex, out totalFound, searchFrom); + } } diff --git a/src/Umbraco.Web/Trees/TemplatesTreeController.cs b/src/Umbraco.Web/Trees/TemplatesTreeController.cs index 8c37718c9b..d0ca8d1b2e 100644 --- a/src/Umbraco.Web/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web/Trees/TemplatesTreeController.cs @@ -3,12 +3,18 @@ using System.Globalization; using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; using Umbraco.Web.Actions; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; +using Umbraco.Web.Search; using Umbraco.Web.WebApi.Filters; using Constants = Umbraco.Core.Constants; @@ -20,6 +26,13 @@ namespace Umbraco.Web.Trees [CoreTree] public class TemplatesTreeController : TreeController, ISearchableTree { + private readonly UmbracoTreeSearcher _treeSearcher; + + public TemplatesTreeController(UmbracoTreeSearcher treeSearcher, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) + { + _treeSearcher = treeSearcher; + } + protected override TreeNode CreateRootNode(FormDataCollection queryStrings) { var root = base.CreateRootNode(queryStrings); @@ -119,10 +132,6 @@ namespace Umbraco.Web.Trees } public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) - { - var results = Services.EntityService.GetPagedDescendants(UmbracoObjectTypes.Template, pageIndex, pageSize, out totalFound, - filter: SqlContext.Query().Where(x => x.Name.Contains(query))); - return Mapper.MapEnumerable(results); - } + => _treeSearcher.EntitySearch(UmbracoObjectTypes.Template, query, pageSize, pageIndex, out totalFound, searchFrom); } }