diff --git a/.editorconfig b/.editorconfig index 29e21d01ed..c63ef39430 100644 --- a/.editorconfig +++ b/.editorconfig @@ -33,4 +33,5 @@ dotnet_naming_style.prefix_underscore.required_prefix = _ [*.cs] csharp_style_var_for_built_in_types = true:suggestion csharp_style_var_when_type_is_apparent = true:suggestion -csharp_style_var_elsewhere = true:suggestion \ No newline at end of file +csharp_style_var_elsewhere = true:suggestion +csharp_prefer_braces = false : none diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 96014f65b7..6caeadd0e5 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -82,7 +82,6 @@ You can get in touch with [the PR team](#the-pr-team) in multiple ways, we love - If there's an existing issue on the issue tracker then that's a good place to leave questions and discuss how to start or move forward - Unsure where to start? Did something not work as expected? Try leaving a note in the ["Contributing to Umbraco"](https://our.umbraco.com/forum/contributing-to-umbraco-cms/) forum, the team monitors that one closely -- We're also [active in the Gitter chatroom](https://gitter.im/umbraco/Umbraco-CMS) ## Code of Conduct diff --git a/.gitignore b/.gitignore index 529adef976..279bdb39dd 100644 --- a/.gitignore +++ b/.gitignore @@ -107,6 +107,7 @@ src/Umbraco.Web.UI.Client/[Bb]uild/[Bb]elle/ src/Umbraco.Web.UI/[Uu]ser[Cc]ontrols/ src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css +src/Umbraco.Web.UI.Client/vwd.webinfo src/Umbraco.Web.UI/App_Plugins/* src/*.psess diff --git a/build/NuSpecs/UmbracoCms.Web.nuspec b/build/NuSpecs/UmbracoCms.Web.nuspec index adf090c69b..51d7e3b8d0 100644 --- a/build/NuSpecs/UmbracoCms.Web.nuspec +++ b/build/NuSpecs/UmbracoCms.Web.nuspec @@ -14,7 +14,7 @@ Contains the core assemblies needed to run Umbraco Cms en-US umbraco - + - + @@ -72,7 +72,7 @@ - + @@ -88,8 +88,8 @@ - + Properties\SolutionInfo.cs diff --git a/src/Umbraco.Examine/UmbracoContentIndex.cs b/src/Umbraco.Examine/UmbracoContentIndex.cs index 822117de46..e16015e192 100644 --- a/src/Umbraco.Examine/UmbracoContentIndex.cs +++ b/src/Umbraco.Examine/UmbracoContentIndex.cs @@ -52,13 +52,13 @@ namespace Umbraco.Examine /// public UmbracoContentIndex( string name, - IEnumerable fieldDefinitions, + FieldDefinitionCollection fieldDefinitions, Directory luceneDirectory, Analyzer defaultAnalyzer, IProfilingLogger profilingLogger, ILocalizationService languageService, IContentValueSetValidator validator, - IReadOnlyDictionary> indexValueTypes = null) + IReadOnlyDictionary indexValueTypes = null) : base(name, fieldDefinitions, luceneDirectory, defaultAnalyzer, profilingLogger, validator, indexValueTypes) { if (validator == null) throw new ArgumentNullException(nameof(validator)); @@ -122,7 +122,7 @@ namespace Umbraco.Examine //anywhere else in this class Current.Services.PublicAccessService, parentId, - ConfigIndexCriteria.IncludeItemTypes, ConfigIndexCriteria.ExcludeItemTypes); + ConfigIndexCriteria?.IncludeItemTypes, ConfigIndexCriteria?.ExcludeItemTypes); PublishedValuesOnly = supportUnpublished; } @@ -200,41 +200,17 @@ namespace Umbraco.Examine var descendantPath = $@"\-1\,*{nodeId}\,*"; var rawQuery = $"{IndexPathFieldName}:{descendantPath}"; var searcher = GetSearcher(); - var c = searcher.CreateCriteria(); - var filtered = c.RawQuery(rawQuery); - var results = searcher.Search(filtered); + var c = searcher.CreateQuery(); + var filtered = c.NativeQuery(rawQuery); + var results = filtered.Execute(); ProfilingLogger.Debug(GetType(), "DeleteFromIndex with query: {Query} (found {TotalItems} results)", rawQuery, results.TotalItemCount); //need to queue a delete item for each one found - foreach (var r in results) - { - QueueIndexOperation(new IndexOperation(new ValueSet(r.Id), IndexOperationType.Delete)); - } + QueueIndexOperation(results.Select(r => new IndexOperation(new ValueSet(r.Id), IndexOperationType.Delete))); base.PerformDeleteFromIndex(nodeId, onComplete); } - - /// - /// Overridden to ensure that the variant system fields have the right value types - /// - /// - /// - /// - protected override FieldValueTypeCollection CreateFieldValueTypes(IReadOnlyDictionary> indexValueTypesFactory = null) - { - //fixme: languages are dynamic so although this will work on startup it wont work when languages are edited - foreach(var lang in LanguageService.GetAllLanguages()) - { - foreach (var field in UmbracoIndexFieldDefinitions) - { - var def = new FieldDefinition($"{field.Name}_{lang.IsoCode.ToLowerInvariant()}", field.Type); - FieldDefinitionCollection.TryAdd(def.Name, def); - } - } - - return base.CreateFieldValueTypes(indexValueTypesFactory); - } } } diff --git a/src/Umbraco.Examine/UmbracoExamineExtensions.cs b/src/Umbraco.Examine/UmbracoExamineExtensions.cs index 8be5a6c1e3..f33b7587e0 100644 --- a/src/Umbraco.Examine/UmbracoExamineExtensions.cs +++ b/src/Umbraco.Examine/UmbracoExamineExtensions.cs @@ -1,10 +1,6 @@ -using System; -using System.ComponentModel; -using System.Web; -using Examine.LuceneEngine.SearchCriteria; -using Examine.SearchCriteria; +using Examine.LuceneEngine.Search; +using Examine.Search; using Umbraco.Core; -using Umbraco.Examine.Config; namespace Umbraco.Examine { diff --git a/src/Umbraco.Examine/UmbracoExamineIndex.cs b/src/Umbraco.Examine/UmbracoExamineIndex.cs index 591ca1c3c8..47712ee755 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndex.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndex.cs @@ -20,10 +20,9 @@ namespace Umbraco.Examine { /// - /// An abstract provider containing the basic functionality to be able to query against - /// Umbraco data. + /// An abstract provider containing the basic functionality to be able to query against Umbraco data. /// - public abstract class UmbracoExamineIndex : LuceneIndex, IUmbracoIndexer, IIndexDiagnostics + public abstract class UmbracoExamineIndex : LuceneIndex, IUmbracoIndex, IIndexDiagnostics { // note // wrapping all operations that end up calling base.SafelyProcessQueueItems in a safe call @@ -53,6 +52,7 @@ namespace Umbraco.Examine { ProfilingLogger = Current.ProfilingLogger; _configBased = true; + _diagnostics = new UmbracoExamineIndexDiagnostics(this, ProfilingLogger.Logger); } /// @@ -67,12 +67,12 @@ namespace Umbraco.Examine /// protected UmbracoExamineIndex( string name, - IEnumerable fieldDefinitions, + FieldDefinitionCollection fieldDefinitions, Directory luceneDirectory, Analyzer defaultAnalyzer, IProfilingLogger profilingLogger, IValueSetValidator validator = null, - IReadOnlyDictionary> indexValueTypes = null) + IReadOnlyDictionary indexValueTypes = null) : base(name, fieldDefinitions, luceneDirectory, defaultAnalyzer, validator, indexValueTypes) { ProfilingLogger = profilingLogger ?? throw new ArgumentNullException(nameof(profilingLogger)); @@ -86,60 +86,8 @@ namespace Umbraco.Examine private readonly bool _configBased = false; - /// - /// A type that defines the type of index for each Umbraco field (non user defined fields) - /// Alot of standard umbraco fields shouldn't be tokenized or even indexed, just stored into lucene - /// for retreival after searching. - /// - public static readonly FieldDefinition[] UmbracoIndexFieldDefinitions = - { - new FieldDefinition("parentID", FieldDefinitionTypes.Integer), - new FieldDefinition("level", FieldDefinitionTypes.Integer), - new FieldDefinition("writerID", FieldDefinitionTypes.Integer), - new FieldDefinition("creatorID", FieldDefinitionTypes.Integer), - new FieldDefinition("sortOrder", FieldDefinitionTypes.Integer), - new FieldDefinition("template", FieldDefinitionTypes.Integer), - - new FieldDefinition("createDate", FieldDefinitionTypes.DateTime), - new FieldDefinition("updateDate", FieldDefinitionTypes.DateTime), - - new FieldDefinition("key", FieldDefinitionTypes.InvariantCultureIgnoreCase), - new FieldDefinition("version", FieldDefinitionTypes.Raw), - new FieldDefinition("nodeType", FieldDefinitionTypes.InvariantCultureIgnoreCase), - new FieldDefinition("template", FieldDefinitionTypes.Raw), - new FieldDefinition("urlName", FieldDefinitionTypes.InvariantCultureIgnoreCase), - new FieldDefinition("path", FieldDefinitionTypes.Raw), - - new FieldDefinition("email", FieldDefinitionTypes.EmailAddress), - - new FieldDefinition(PublishedFieldName, FieldDefinitionTypes.Raw), - new FieldDefinition(NodeKeyFieldName, FieldDefinitionTypes.Raw), - new FieldDefinition(IndexPathFieldName, FieldDefinitionTypes.Raw), - new FieldDefinition(IconFieldName, FieldDefinitionTypes.Raw) - }; - protected IProfilingLogger ProfilingLogger { get; } - /// - /// Overridden to ensure that the umbraco system field definitions are in place - /// - /// - /// - protected override FieldValueTypeCollection CreateFieldValueTypes(IReadOnlyDictionary> indexValueTypesFactory = null) - { - //if config based then ensure the value types else it's assumed these were passed in via ctor - if (_configBased) - { - foreach (var field in UmbracoIndexFieldDefinitions) - { - FieldDefinitionCollection.TryAdd(field.Name, field); - } - } - - - return base.CreateFieldValueTypes(indexValueTypesFactory); - } - /// /// When set to true Umbraco will keep the index in sync with Umbraco data automatically /// @@ -147,6 +95,14 @@ namespace Umbraco.Examine public bool PublishedValuesOnly { get; protected set; } = false; + /// + public IEnumerable GetFields() + { + //we know this is a LuceneSearcher + var searcher = (LuceneSearcher) GetSearcher(); + return searcher.GetAllIndexedFields(); + } + protected ConfigIndexCriteria ConfigIndexCriteria { get; private set; } /// @@ -174,12 +130,15 @@ namespace Umbraco.Examine EnableDefaultEventHandler = enabled; } - //Need to check if the index set or IndexerData is specified... - if (config["indexSet"] == null && FieldDefinitionCollection.Count == 0) + //this is config based, so add the default definitions + foreach (var field in UmbracoFieldDefinitionCollection.UmbracoIndexFieldDefinitions) { - //if we don't have either, then we'll try to set the index set by naming conventions - var found = false; + FieldDefinitionCollection.TryAdd(field); + } + //Need to check if the index set is specified... + if (config["indexSet"] == null) + { var possibleSuffixes = new[] {"Index", "Indexer"}; foreach (var suffix in possibleSuffixes) { @@ -200,36 +159,29 @@ namespace Umbraco.Examine ConfigIndexCriteria = CreateFieldDefinitionsFromConfig(indexSet); foreach (var fieldDefinition in ConfigIndexCriteria.StandardFields.Union(ConfigIndexCriteria.UserFields)) { - FieldDefinitionCollection.TryAdd(fieldDefinition.Name, fieldDefinition); + //replace any existing or add + FieldDefinitionCollection.AddOrUpdate(fieldDefinition); } - found = true; break; } - - if (!found) - throw new ArgumentNullException("indexSet on LuceneExamineIndexer provider has not been set in configuration and/or the IndexerData property has not been explicitly set"); - } - else if (config["indexSet"] != null) + else { //if an index set is specified, ensure it exists and initialize the indexer based on the set if (IndexSets.Instance.Sets[config["indexSet"]] == null) - { throw new ArgumentException("The indexSet specified for the LuceneExamineIndexer provider does not exist"); - } - else + + IndexSetName = config["indexSet"]; + + var indexSet = IndexSets.Instance.Sets[IndexSetName]; + + //get the index criteria and ensure folder + ConfigIndexCriteria = CreateFieldDefinitionsFromConfig(indexSet); + foreach (var fieldDefinition in ConfigIndexCriteria.StandardFields.Union(ConfigIndexCriteria.UserFields)) { - IndexSetName = config["indexSet"]; - - var indexSet = IndexSets.Instance.Sets[IndexSetName]; - - //get the index criteria and ensure folder - ConfigIndexCriteria = CreateFieldDefinitionsFromConfig(indexSet); - foreach (var fieldDefinition in ConfigIndexCriteria.StandardFields.Union(ConfigIndexCriteria.UserFields)) - { - FieldDefinitionCollection.TryAdd(fieldDefinition.Name, fieldDefinition); - } + //replace any existing or add + FieldDefinitionCollection.AddOrUpdate(fieldDefinition); } } diff --git a/src/Umbraco.Examine/UmbracoExamineSearcher.cs b/src/Umbraco.Examine/UmbracoExamineSearcher.cs deleted file mode 100644 index 50c6c21e37..0000000000 --- a/src/Umbraco.Examine/UmbracoExamineSearcher.cs +++ /dev/null @@ -1,52 +0,0 @@ -//using System; -//using System.ComponentModel; -//using System.IO; -//using System.Linq; -//using Umbraco.Core; -//using Examine.LuceneEngine.Providers; -//using Lucene.Net.Analysis; -//using Lucene.Net.Index; -//using Umbraco.Core.Composing; -//using Umbraco.Examine.Config; -//using Directory = Lucene.Net.Store.Directory; -//using Examine.LuceneEngine; - -//namespace Umbraco.Examine -//{ -// /// -// /// An Examine searcher which uses Lucene.Net as the -// /// -// public class UmbracoExamineSearcher : LuceneSearcher -// { -// /// -// /// Constructor to allow for creating an indexer at runtime -// /// -// /// -// /// -// /// -// public UmbracoExamineSearcher(string name, Directory luceneDirectory, Analyzer analyzer, FieldValueTypeCollection fieldValueTypeCollection) -// : base(name, luceneDirectory, analyzer, fieldValueTypeCollection) -// { -// } - -// /// -// public UmbracoExamineSearcher(string name, IndexWriter writer, Analyzer analyzer, FieldValueTypeCollection fieldValueTypeCollection) -// : base(name, writer, analyzer, fieldValueTypeCollection) -// { -// } - -// /// -// /// Returns a list of fields to search on, this will also exclude the IndexPathFieldName and node type alias -// /// -// /// -// public override string[] GetAllIndexedFields() -// { -// var fields = base.GetAllIndexedFields(); -// return fields -// .Where(x => x != UmbracoExamineIndexer.IndexPathFieldName) -// .Where(x => x != LuceneIndex.ItemTypeFieldName) -// .ToArray(); -// } - -// } -//} diff --git a/src/Umbraco.Examine/UmbracoFieldDefinitionCollection.cs b/src/Umbraco.Examine/UmbracoFieldDefinitionCollection.cs new file mode 100644 index 0000000000..97d1f68727 --- /dev/null +++ b/src/Umbraco.Examine/UmbracoFieldDefinitionCollection.cs @@ -0,0 +1,75 @@ +using Examine; + +namespace Umbraco.Examine +{ + /// + /// Custom allowing dynamic creation of + /// + public class UmbracoFieldDefinitionCollection : FieldDefinitionCollection + { + + public UmbracoFieldDefinitionCollection() + : base(UmbracoIndexFieldDefinitions) + { + } + + /// + /// A type that defines the type of index for each Umbraco field (non user defined fields) + /// Alot of standard umbraco fields shouldn't be tokenized or even indexed, just stored into lucene + /// for retreival after searching. + /// + public static readonly FieldDefinition[] UmbracoIndexFieldDefinitions = + { + new FieldDefinition("parentID", FieldDefinitionTypes.Integer), + new FieldDefinition("level", FieldDefinitionTypes.Integer), + new FieldDefinition("writerID", FieldDefinitionTypes.Integer), + new FieldDefinition("creatorID", FieldDefinitionTypes.Integer), + new FieldDefinition("sortOrder", FieldDefinitionTypes.Integer), + new FieldDefinition("template", FieldDefinitionTypes.Integer), + + new FieldDefinition("createDate", FieldDefinitionTypes.DateTime), + new FieldDefinition("updateDate", FieldDefinitionTypes.DateTime), + + new FieldDefinition("key", FieldDefinitionTypes.InvariantCultureIgnoreCase), + new FieldDefinition("version", FieldDefinitionTypes.Raw), + new FieldDefinition("nodeType", FieldDefinitionTypes.InvariantCultureIgnoreCase), + new FieldDefinition("template", FieldDefinitionTypes.Raw), + new FieldDefinition("urlName", FieldDefinitionTypes.InvariantCultureIgnoreCase), + new FieldDefinition("path", FieldDefinitionTypes.Raw), + + new FieldDefinition("email", FieldDefinitionTypes.EmailAddress), + + new FieldDefinition(UmbracoExamineIndex.PublishedFieldName, FieldDefinitionTypes.Raw), + new FieldDefinition(UmbracoExamineIndex.NodeKeyFieldName, FieldDefinitionTypes.Raw), + new FieldDefinition(UmbracoExamineIndex.IndexPathFieldName, FieldDefinitionTypes.Raw), + new FieldDefinition(UmbracoExamineIndex.IconFieldName, FieldDefinitionTypes.Raw) + }; + + ///// + ///// Overridden to dynamically add field definitions for culture variations + ///// + ///// + ///// + ///// + //public override bool TryGetValue(string fieldName, out FieldDefinition fieldDefinition) + //{ + // var result = base.TryGetValue(fieldName, out fieldDefinition); + // if (result) return true; + + // //if the fieldName is not suffixed with _iso-Code + // var underscoreIndex = fieldName.LastIndexOf('_'); + // if (underscoreIndex == -1) return false; + + + + // var isoCode = fieldName.Substring(underscoreIndex); + // if (isoCode.Length < 6) return false; //invalid isoCode + + // var hyphenIndex = isoCode.IndexOf('-'); + // if (hyphenIndex != 3) return false; //invalid isoCode + + // //we'll assume this is a valid isoCode + + //} + } +} diff --git a/src/Umbraco.Examine/UmbracoMemberIndex.cs b/src/Umbraco.Examine/UmbracoMemberIndex.cs index 451bfbbcb7..fae818a4a5 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndex.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndex.cs @@ -42,8 +42,8 @@ namespace Umbraco.Examine /// /// public UmbracoMemberIndex( - string name, - IEnumerable fieldDefinitions, + string name, + FieldDefinitionCollection fieldDefinitions, Directory luceneDirectory, Analyzer analyzer, IProfilingLogger profilingLogger, @@ -64,10 +64,10 @@ namespace Umbraco.Examine /// /// /// - protected override FieldValueTypeCollection CreateFieldValueTypes(IReadOnlyDictionary> indexValueTypesFactory = null) + protected override FieldValueTypeCollection CreateFieldValueTypes(IReadOnlyDictionary indexValueTypesFactory = null) { var keyDef = new FieldDefinition("__key", FieldDefinitionTypes.Raw); - FieldDefinitionCollection.TryAdd(keyDef.Name, keyDef); + FieldDefinitionCollection.TryAdd(keyDef); return base.CreateFieldValueTypes(indexValueTypesFactory); } diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index c736b47e53..d88df766e3 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -171,9 +171,9 @@ namespace Umbraco.Tests.PublishedContent //ensure it still exists in the index (raw examine search) - var criteria = searcher.CreateCriteria(); + var criteria = searcher.CreateQuery(); var filter = criteria.Id(3113); - var found = searcher.Search(filter.Compile()); + var found = filter.Execute(); Assert.IsNotNull(found); Assert.AreEqual(1, found.TotalItemCount); diff --git a/src/Umbraco.Tests/Services/EntityServiceTests.cs b/src/Umbraco.Tests/Services/EntityServiceTests.cs index 1db653f4ab..2425f8b74a 100644 --- a/src/Umbraco.Tests/Services/EntityServiceTests.cs +++ b/src/Umbraco.Tests/Services/EntityServiceTests.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -37,6 +38,53 @@ namespace Umbraco.Tests.Services } } + [Test] + public void EntityService_Can_Get_Paged_Descendants_Ordering_Path() + { + + var contentType = ServiceContext.ContentTypeService.Get("umbTextpage"); + + var root = MockedContent.CreateSimpleContent(contentType); + ServiceContext.ContentService.Save(root); + var rootId = root.Id; + var ids = new List(); + for (int i = 0; i < 10; i++) + { + var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root); + ServiceContext.ContentService.Save(c1); + ids.Add(c1.Id); + root = c1; // make a hierarchy + } + + var service = ServiceContext.EntityService; + + long total; + + var entities = service.GetPagedDescendants(rootId, UmbracoObjectTypes.Document, 0, 6, out total).ToArray(); + Assert.That(entities.Length, Is.EqualTo(6)); + Assert.That(total, Is.EqualTo(10)); + Assert.AreEqual(ids[0], entities[0].Id); + + entities = service.GetPagedDescendants(rootId, UmbracoObjectTypes.Document, 1, 6, out total).ToArray(); + Assert.That(entities.Length, Is.EqualTo(4)); + Assert.That(total, Is.EqualTo(10)); + Assert.AreEqual(ids[6], entities[0].Id); + + //Test ordering direction + + entities = service.GetPagedDescendants(rootId, UmbracoObjectTypes.Document, 0, 6, out total, + ordering: Ordering.By("Path", Direction.Descending)).ToArray(); + Assert.That(entities.Length, Is.EqualTo(6)); + Assert.That(total, Is.EqualTo(10)); + Assert.AreEqual(ids[ids.Count - 1], entities[0].Id); + + entities = service.GetPagedDescendants(rootId, UmbracoObjectTypes.Document, 1, 6, out total, + ordering: Ordering.By("Path", Direction.Descending)).ToArray(); + Assert.That(entities.Length, Is.EqualTo(4)); + Assert.That(total, Is.EqualTo(10)); + Assert.AreEqual(ids[ids.Count - 1 - 6], entities[0].Id); + } + [Test] public void EntityService_Can_Get_Paged_Content_Children() { @@ -45,21 +93,41 @@ namespace Umbraco.Tests.Services var root = MockedContent.CreateSimpleContent(contentType); ServiceContext.ContentService.Save(root); + var ids = new List(); for (int i = 0; i < 10; i++) { var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root); ServiceContext.ContentService.Save(c1); + ids.Add(c1.Id); } var service = ServiceContext.EntityService; long total; + var entities = service.GetPagedChildren(root.Id, UmbracoObjectTypes.Document, 0, 6, out total).ToArray(); Assert.That(entities.Length, Is.EqualTo(6)); Assert.That(total, Is.EqualTo(10)); + Assert.AreEqual(ids[0], entities[0].Id); + entities = service.GetPagedChildren(root.Id, UmbracoObjectTypes.Document, 1, 6, out total).ToArray(); Assert.That(entities.Length, Is.EqualTo(4)); Assert.That(total, Is.EqualTo(10)); + Assert.AreEqual(ids[6], entities[0].Id); + + //Test ordering direction + + entities = service.GetPagedChildren(root.Id, UmbracoObjectTypes.Document, 0, 6, out total, + ordering: Ordering.By("SortOrder", Direction.Descending)).ToArray(); + Assert.That(entities.Length, Is.EqualTo(6)); + Assert.That(total, Is.EqualTo(10)); + Assert.AreEqual(ids[ids.Count - 1], entities[0].Id); + + entities = service.GetPagedChildren(root.Id, UmbracoObjectTypes.Document, 1, 6, out total, + ordering: Ordering.By("SortOrder", Direction.Descending)).ToArray(); + Assert.That(entities.Length, Is.EqualTo(4)); + Assert.That(total, Is.EqualTo(10)); + Assert.AreEqual(ids[ids.Count - 1 - 6], entities[0].Id); } [Test] @@ -206,10 +274,12 @@ namespace Umbraco.Tests.Services var service = ServiceContext.EntityService; long total; - var entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Document, 0, 10, out total, filter: "ssss").ToArray(); + var entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Document, 0, 10, out total, + filter: SqlContext.Query().Where(x => x.Name.Contains("ssss"))).ToArray(); Assert.That(entities.Length, Is.EqualTo(10)); Assert.That(total, Is.EqualTo(10)); - entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Document, 0, 50, out total, filter: "tttt").ToArray(); + entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Document, 0, 50, out total, + filter: SqlContext.Query().Where(x => x.Name.Contains("tttt"))).ToArray(); Assert.That(entities.Length, Is.EqualTo(50)); Assert.That(total, Is.EqualTo(50)); } @@ -389,10 +459,12 @@ namespace Umbraco.Tests.Services var service = ServiceContext.EntityService; long total; - var entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Media, 0, 10, out total, filter: "ssss").ToArray(); + var entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Media, 0, 10, out total, + filter: SqlContext.Query().Where(x => x.Name.Contains("ssss"))).ToArray(); Assert.That(entities.Length, Is.EqualTo(10)); Assert.That(total, Is.EqualTo(10)); - entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Media, 0, 50, out total, filter: "tttt").ToArray(); + entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Media, 0, 50, out total, + filter: SqlContext.Query().Where(x => x.Name.Contains("tttt"))).ToArray(); Assert.That(entities.Length, Is.EqualTo(50)); Assert.That(total, Is.EqualTo(50)); } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 99d67c31e2..ea8f1fd13c 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -77,7 +77,7 @@ - + 1.8.9 diff --git a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs index 47fa32cbaa..ed5ae07fc0 100644 --- a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs @@ -36,7 +36,7 @@ namespace Umbraco.Tests.UmbracoExamine var valueSet = node.ConvertToValueSet(IndexTypes.Content); indexer.IndexItems(new[] { valueSet }); - var found = searcher.Search(searcher.CreateCriteria().Id((string)node.Attribute("id")).Compile()); + var found = searcher.CreateQuery().Id((string)node.Attribute("id")).Execute(); Assert.AreEqual(0, found.TotalItemCount); } diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 09ad340e2b..fbbf2042ca 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -164,7 +164,7 @@ namespace Umbraco.Tests.UmbracoExamine var i = new UmbracoContentIndex( "testIndexer", - UmbracoExamineIndex.UmbracoIndexFieldDefinitions, + new UmbracoFieldDefinitionCollection(), luceneDir, analyzer, profilingLogger, diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index f45761dd54..ba6a83adff 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -103,7 +103,7 @@ namespace Umbraco.Tests.UmbracoExamine var searcher = indexer.GetSearcher(); - var results = searcher.Search(searcher.CreateCriteria().Id(555).Compile()); + var results = searcher.CreateQuery().Id(555).Execute(); Assert.AreEqual(1, results.TotalItemCount); var result = results.First(); @@ -128,8 +128,6 @@ namespace Umbraco.Tests.UmbracoExamine validator: new ContentValueSetValidator(false))) using (indexer.ProcessNonAsync()) { - contentRebuilder.RegisterIndex(indexer.Name); - mediaRebuilder.RegisterIndex(indexer.Name); var searcher = indexer.GetSearcher(); @@ -137,7 +135,7 @@ namespace Umbraco.Tests.UmbracoExamine contentRebuilder.Populate(indexer); mediaRebuilder.Populate(indexer); - var result = searcher.Search(searcher.CreateCriteria().All().Compile()); + var result = searcher.CreateQuery().All().Execute(); Assert.AreEqual(29, result.TotalItemCount); } @@ -208,7 +206,7 @@ namespace Umbraco.Tests.UmbracoExamine indexer.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); //it will not exist because it exists under 2222 - var results = searcher.Search(searcher.CreateCriteria().Id(2112).Compile()); + var results = searcher.CreateQuery().Id(2112).Execute(); Assert.AreEqual(0, results.Count()); //now mimic moving 2112 to 1116 @@ -220,7 +218,7 @@ namespace Umbraco.Tests.UmbracoExamine indexer.IndexItems(new[] { node.ConvertToValueSet(IndexTypes.Media) }); //now ensure it exists - results = searcher.Search(searcher.CreateCriteria().Id(2112).Compile()); + results = searcher.CreateQuery().Id(2112).Execute(); Assert.AreEqual(1, results.Count()); } } @@ -251,7 +249,7 @@ namespace Umbraco.Tests.UmbracoExamine indexer1.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); //it will exist because it exists under 2222 - var results = searcher.Search(searcher.CreateCriteria().Id(2112).Compile()); + var results = searcher.CreateQuery().Id(2112).Execute(); Assert.AreEqual(1, results.Count()); //now mimic moving the node underneath 1116 instead of 2222 @@ -262,7 +260,7 @@ namespace Umbraco.Tests.UmbracoExamine indexer1.IndexItems(new[] { node.ConvertToValueSet(IndexTypes.Media) }); //now ensure it's deleted - results = searcher.Search(searcher.CreateCriteria().Id(2112).Compile()); + results = searcher.CreateQuery().Id(2112).Execute(); Assert.AreEqual(0, results.Count()); } } @@ -281,14 +279,13 @@ namespace Umbraco.Tests.UmbracoExamine validator: new ContentValueSetValidator(false))) using (indexer.ProcessNonAsync()) { - rebuilder.RegisterIndex(indexer.Name); var searcher = indexer.GetSearcher(); //create the whole thing rebuilder.Populate(indexer); - var result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndex.CategoryFieldName, IndexTypes.Content).Compile()); + var result = searcher.CreateQuery().Field(LuceneIndex.CategoryFieldName, IndexTypes.Content).Execute(); Assert.AreEqual(21, result.TotalItemCount); //delete all content @@ -299,13 +296,13 @@ namespace Umbraco.Tests.UmbracoExamine //ensure it's all gone - result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndex.CategoryFieldName, IndexTypes.Content).Compile()); + result = searcher.CreateQuery().Field(LuceneIndex.CategoryFieldName, IndexTypes.Content).Execute(); Assert.AreEqual(0, result.TotalItemCount); //call our indexing methods rebuilder.Populate(indexer); - result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndex.CategoryFieldName, IndexTypes.Content).Compile()); + result = searcher.CreateQuery().Field(LuceneIndex.CategoryFieldName, IndexTypes.Content).Execute(); Assert.AreEqual(21, result.TotalItemCount); } } @@ -333,10 +330,10 @@ namespace Umbraco.Tests.UmbracoExamine indexer.DeleteFromIndex(1140.ToString()); //this node had children: 1141 & 1142, let's ensure they are also removed - var results = searcher.Search(searcher.CreateCriteria().Id(1141).Compile()); + var results = searcher.CreateQuery().Id(1141).Execute(); Assert.AreEqual(0, results.Count()); - results = searcher.Search(searcher.CreateCriteria().Id(1142).Compile()); + results = searcher.CreateQuery().Id(1142).Execute(); Assert.AreEqual(0, results.Count()); } diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index fbfb2f90c0..7aa36f16e3 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -2,16 +2,16 @@ using System.Collections.Generic; using System.Linq; using Examine; +using Examine.Search; using NUnit.Framework; -using Examine.LuceneEngine.SearchCriteria; using Moq; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Services; -using Umbraco.Examine; using Umbraco.Tests.Testing; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Composing; +using Umbraco.Examine; namespace Umbraco.Tests.UmbracoExamine { @@ -60,21 +60,20 @@ namespace Umbraco.Tests.UmbracoExamine using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir)) using (indexer.ProcessNonAsync()) { - rebuilder.RegisterIndex(indexer.Name); indexer.CreateIndex(); rebuilder.Populate(indexer); var searcher = indexer.GetSearcher(); - var numberSortedCriteria = searcher.CreateCriteria() - .ParentId(1148).And() + var numberSortedCriteria = searcher.CreateQuery() + .ParentId(1148) .OrderBy(new SortableField("sortOrder", SortType.Int)); - var numberSortedResult = searcher.Search(numberSortedCriteria.Compile()); + var numberSortedResult = numberSortedCriteria.Execute(); - var stringSortedCriteria = searcher.CreateCriteria() - .ParentId(1148).And() - .OrderBy("sortOrder"); //will default to string - var stringSortedResult = searcher.Search(stringSortedCriteria.Compile()); + var stringSortedCriteria = searcher.CreateQuery() + .ParentId(1148) + .OrderBy(new SortableField("sortOrder"));//will default to string + var stringSortedResult = stringSortedCriteria.Execute(); Assert.AreEqual(12, numberSortedResult.TotalItemCount); Assert.AreEqual(12, stringSortedResult.TotalItemCount); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js index deb9f4c1ac..f01083d725 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditorheader.directive.js @@ -262,7 +262,7 @@ Use this directive to construct a header inside the main editor window. icon: "=", hideIcon: "@", alias: "=", - hideAlias: "@", + hideAlias: "=", description: "=", hideDescription: "@", descriptionLocked: "@", diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index 23f3fb6d58..ecc92263bc 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -30,12 +30,6 @@ function navigationService($routeParams, $location, $q, $timeout, $injector, eve var nonRoutingQueryStrings = ["mculture", "cculture"]; var retainedQueryStrings = ['mculture']; - //used to track the current dialog object - var currentDialog = null; - - //tracks the user profile dialog - var userDialog = null; - function setMode(mode) { switch (mode) { case 'tree': @@ -287,7 +281,7 @@ function navigationService($routeParams, $location, $q, $timeout, $injector, eve appState.setGlobalState("showTray", false); }, - /** + /** * @ngdoc method * @name umbraco.services.navigationService#syncTree * @methodOf umbraco.services.navigationService diff --git a/src/Umbraco.Web.UI.Client/src/less/healthcheck.less b/src/Umbraco.Web.UI.Client/src/less/healthcheck.less index d40b59cf81..9e1362d50a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/healthcheck.less +++ b/src/Umbraco.Web.UI.Client/src/less/healthcheck.less @@ -242,4 +242,5 @@ .umb-healthcheck-group__details-status-action-description { margin-top: 5px; font-size: 12px; + padding-left: 165px; } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js index 54d5da29a8..75bf414099 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.controller.js @@ -1,7 +1,7 @@ - (function() { - "use strict"; +(function () { + "use strict"; - function CompositionsController($scope,$location) { + function CompositionsController($scope, $location, $filter) { var vm = this; var oldModel = null; @@ -19,18 +19,34 @@ back the changes on cancel */ oldModel = angular.copy($scope.model); - if(!$scope.model.title) { + if (!$scope.model.title) { $scope.model.title = "Compositions"; } + // group the content types by their container paths + vm.availableGroups = $filter("orderBy")( + _.map( + _.groupBy($scope.model.availableCompositeContentTypes, function (compositeContentType) { + return compositeContentType.contentType.metaData.containerPath; + }), function (group) { + return { + containerPath: group[0].contentType.metaData.containerPath, + compositeContentTypes: group + }; + } + ), function (group) { + return group.containerPath.replace(/\//g, " "); + }); } + + function isSelected(alias) { - if($scope.model.contentType.compositeContentTypes.indexOf(alias) !== -1) { + if ($scope.model.contentType.compositeContentTypes.indexOf(alias) !== -1) { return true; } } - + function openContentType(contentType, section) { var url = (section === "documentType" ? "/settings/documenttypes/edit/" : "/settings/mediaTypes/edit/") + contentType.id; $location.path(url); @@ -38,19 +54,19 @@ function submit() { if ($scope.model && $scope.model.submit) { - + // check if any compositions has been removed vm.compositionRemoved = false; - for(var i = 0; oldModel.compositeContentTypes.length > i; i++) { + for (var i = 0; oldModel.compositeContentTypes.length > i; i++) { var oldComposition = oldModel.compositeContentTypes[i]; - if(_.contains($scope.model.compositeContentTypes, oldComposition) === false) { + if (_.contains($scope.model.compositeContentTypes, oldComposition) === false) { vm.compositionRemoved = true; } } /* submit the form if there havne't been removed any composition or the confirm checkbox has been checked */ - if(!vm.compositionRemoved || vm.allowSubmit) { + if (!vm.compositionRemoved || vm.allowSubmit) { $scope.model.submit($scope.model); } } @@ -63,8 +79,8 @@ } onInit(); - } + } - angular.module("umbraco").controller("Umbraco.Editors.CompositionsController", CompositionsController); + angular.module("umbraco").controller("Umbraco.Editors.CompositionsController", CompositionsController); })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html index 5d7a5420db..84fbab7cb2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html @@ -60,29 +60,35 @@ -
    -
  • - -
    - -
    - - - -
  • -
+
+
    +
  • + + {{group.containerPath}} +
  • +
  • + +
    + +
    + + + +
  • +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html index 7d8ce3e13e..2ce49880d7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html @@ -97,14 +97,7 @@ - - @@ -268,4 +261,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js index d4be18cf05..92e02d0d14 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js @@ -34,22 +34,25 @@ function ContentDeleteController($scope, $timeout, contentResource, treeService, toggleDeleting(false); if (rootNode) { - $timeout(function () { - //ensure the recycle bin has child nodes now - var recycleBin = treeService.getDescendantNode(rootNode, -20); - if (recycleBin) { - //TODO: This seems to return a rejection and we end up with "Possibly unhanded rejection" - treeService.syncTree({ node: recycleBin, path: treeService.getPath(recycleBin), forceReload: true }); + //ensure the recycle bin has child nodes now + var recycleBin = treeService.getDescendantNode(rootNode, -20); + if (recycleBin) { + recycleBin.hasChildren = true; + //reload the recycle bin if it's already expanded so the deleted item is shown + if (recycleBin.expanded) { + treeService.loadNodeChildren({ node: recycleBin, section: "content" }); } - }, 500); + } } - + //if the current edited item is the same one as we're deleting, we need to navigate elsewhere if (editorState.current && editorState.current.id == $scope.currentNode.id) { //If the deleted item lived at the root then just redirect back to the root, otherwise redirect to the item's parent var location = "/content"; - if ($scope.currentNode.parentId.toString() !== "-1") + if ($scope.currentNode.parentId.toString() === "-20") + location = "/content/content/recyclebin"; + else if ($scope.currentNode.parentId.toString() !== "-1") location = "/content/content/edit/" + $scope.currentNode.parentId; $location.path(location); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/notify.html b/src/Umbraco.Web.UI.Client/src/views/content/notify.html index cb12d92f08..e7c4d4d785 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/notify.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/notify.html @@ -15,10 +15,11 @@
{{currentNode.name}}
+ -
+
-
Set your notification for {{ currentNode.name }}
+ Set your notification for {{ currentNode.name }}
-