From 62e99aaed5fdd83a652315fa312d8a810e8093d9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 21 Nov 2018 17:04:20 +1100 Subject: [PATCH 01/44] removes usages of singleton in ExamineComponent --- src/Umbraco.Web/Search/ExamineComponent.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 7a036ef712..6c152acae2 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -74,7 +74,7 @@ namespace Umbraco.Web.Search { using (profilingLogger.TraceDuration("Examine shutting down")) { - ExamineManager.Instance.Dispose(); + _examineManager.Dispose(); } }); @@ -163,7 +163,7 @@ namespace Umbraco.Web.Search else { //do all of them - ExamineManager.Instance.RebuildIndexes(); + examineManager.RebuildIndexes(); } } @@ -701,7 +701,7 @@ namespace Umbraco.Web.Search { var valueSet = UmbracoContentIndexer.GetValueSets(examineComponent._urlSegmentProviders, examineComponent._services.UserService, content); - ExamineManager.Instance.IndexItems( + examineComponent._examineManager.IndexItems( valueSet.ToArray(), examineComponent._examineManager.IndexProviders.Values.OfType() // only for the specified indexers @@ -732,7 +732,7 @@ namespace Umbraco.Web.Search { var valueSet = UmbracoContentIndexer.GetValueSets(examineComponent._urlSegmentProviders, examineComponent._services.UserService, media); - ExamineManager.Instance.IndexItems( + examineComponent._examineManager.IndexItems( valueSet.ToArray(), examineComponent._examineManager.IndexProviders.Values.OfType() // index this item for all indexers if the media is not trashed, otherwise if the item is trashed @@ -762,7 +762,7 @@ namespace Umbraco.Web.Search { var valueSet = UmbracoMemberIndexer.GetValueSets(member); - ExamineManager.Instance.IndexItems( + examineComponent._examineManager.IndexItems( valueSet.ToArray(), examineComponent._examineManager.IndexProviders.Values.OfType() //ensure that only the providers are flagged to listen execute @@ -790,7 +790,7 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, int id, bool keepIfUnpublished) { - ExamineManager.Instance.DeleteFromIndexes( + examineComponent._examineManager.DeleteFromIndexes( id.ToString(CultureInfo.InvariantCulture), examineComponent._examineManager.IndexProviders.Values.OfType() // if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content, From 1721f37c43a46088e7fa6f0d11b219a191f2c77b Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 21 Nov 2018 20:36:15 +1100 Subject: [PATCH 02/44] Gets variant data being indexed and fixes how the __Raw fields are processed --- src/Umbraco.Examine/UmbracoContentIndexer.cs | 106 ++++++++++++++---- src/Umbraco.Examine/UmbracoExamineIndexer.cs | 32 +++--- .../UmbracoExamine/IndexInitializer.cs | 8 ++ .../PropertyEditors/GridPropertyEditor.cs | 2 +- 4 files changed, 112 insertions(+), 36 deletions(-) diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index fab9f226a4..72d54c52bb 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -21,7 +21,7 @@ using Umbraco.Core.Scoping; using Umbraco.Examine.Config; using IContentService = Umbraco.Core.Services.IContentService; using IMediaService = Umbraco.Core.Services.IMediaService; - +using Examine.LuceneEngine; namespace Umbraco.Examine { @@ -33,6 +33,7 @@ namespace Umbraco.Examine protected IContentService ContentService { get; } protected IMediaService MediaService { get; } protected IUserService UserService { get; } + protected ILocalizationService LanguageService { get; } private readonly IEnumerable _urlSegmentProviders; private int? _parentId; @@ -48,6 +49,7 @@ namespace Umbraco.Examine ContentService = Current.Services.ContentService; MediaService = Current.Services.MediaService; UserService = Current.Services.UserService; + LanguageService = Current.Services.LocalizationService; _urlSegmentProviders = Current.UrlSegmentProviders; @@ -79,6 +81,7 @@ namespace Umbraco.Examine IContentService contentService, IMediaService mediaService, IUserService userService, + ILocalizationService languageService, ISqlContext sqlContext, IEnumerable urlSegmentProviders, IValueSetValidator validator, @@ -96,6 +99,7 @@ namespace Umbraco.Examine ContentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); MediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); UserService = userService ?? throw new ArgumentNullException(nameof(userService)); + LanguageService = languageService ?? throw new ArgumentNullException(nameof(languageService)); _urlSegmentProviders = urlSegmentProviders ?? throw new ArgumentNullException(nameof(urlSegmentProviders)); InitializeQueries(sqlContext); @@ -226,6 +230,27 @@ namespace Umbraco.Examine #region Protected + /// + /// Overridden to ensure that the variant system fields have the right value types + /// + /// + /// + /// + protected override FieldValueTypeCollection CreateFieldValueTypes(Directory x, 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 UmbracoIndexFields) + { + var def = new FieldDefinition($"{field.Name}_{lang.IsoCode.ToLowerInvariant()}", field.Type); + FieldDefinitionCollection.TryAdd(def.Name, def); + } + } + + return base.CreateFieldValueTypes(x, indexValueTypesFactory); + } + /// /// This is a static query, it's parameters don't change so store statically /// @@ -317,25 +342,39 @@ namespace Umbraco.Examine } } + /// + /// Creates a collection of for a collection + /// + /// + /// + /// + /// Yield returns public static IEnumerable GetValueSets(IEnumerable urlSegmentProviders, IUserService userService, params IContent[] content) { + //TODO: There is a lot of boxing going on here and ultimately all values will be boxed by Lucene anyways + // but I wonder if there's a way to reduce the boxing that we have to do or if it will matter in the end since + // Lucene will do it no matter what? One idea was to create a `FieldValue` struct which would contain `object`, `object[]`, `ValueType` and `ValueType[]` + // references and then each array is an array of `FieldValue[]` and values are assigned accordingly. Not sure if it will make a difference or not. + foreach (var c in content) { - var urlValue = c.GetUrlSegment(urlSegmentProviders); // for now, index with invariant culture + var isVariant = c.ContentType.VariesByCulture(); + + var urlValue = c.GetUrlSegment(urlSegmentProviders); //fixme: variants var values = new Dictionary { - {"icon", new object[] {c.ContentType.Icon}}, - {PublishedFieldName, new object[] {c.Published ? 1 : 0}}, + {"icon", new [] {c.ContentType.Icon}}, + {PublishedFieldName, new object[] {c.Published ? 1 : 0}}, //fixme: variants {"id", new object[] {c.Id}}, {"key", new object[] {c.Key}}, {"parentID", new object[] {c.Level > 1 ? c.ParentId : -1}}, {"level", new object[] {c.Level}}, {"creatorID", new object[] {c.CreatorId}}, {"sortOrder", new object[] {c.SortOrder}}, - {"createDate", new object[] {c.CreateDate}}, - {"updateDate", new object[] {c.UpdateDate}}, - {"nodeName", new object[] {c.Name}}, - {"urlName", new object[] {urlValue}}, + {"createDate", new object[] {c.CreateDate}}, //Always add invariant createDate + {"updateDate", new object[] {c.UpdateDate}}, //Always add invariant updateDate + {"nodeName", new object[] {c.Name}}, //Always add invariant nodeName + {"urlName", new object[] {urlValue}}, //Always add invariant urlName {"path", new object[] {c.Path}}, {"nodeType", new object[] {c.ContentType.Id}}, {"creatorName", new object[] {c.GetCreatorProfile(userService)?.Name ?? "??"}}, @@ -344,21 +383,29 @@ namespace Umbraco.Examine {"template", new object[] {c.Template?.Id ?? 0}} }; + if (isVariant) + { + foreach(var culture in c.AvailableCultures) + { + var variantUrl = c.GetUrlSegment(urlSegmentProviders, culture); + var lowerCulture = culture.ToLowerInvariant(); + values[$"urlName_{lowerCulture}"] = new object[] { variantUrl }; + values[$"nodeName_{lowerCulture}"] = new object[] { c.GetCultureName(culture) }; + values[$"{PublishedFieldName}_{lowerCulture}"] = new object[] { c.IsCulturePublished(culture) ? 1 : 0 }; + values[$"updateDate_{lowerCulture}"] = new object[] { c.GetUpdateDate(culture) }; + } + } + foreach (var property in c.Properties) { - //only add the value if its not null or empty (we'll check for string explicitly here too) - //fixme support variants with language id - var val = property.GetValue("", ""); // for now, index the invariant values - switch (val) + if (!property.PropertyType.VariesByCulture()) { - case null: - continue; - case string strVal when strVal.IsNullOrWhiteSpace() == false: - values.Add(property.Alias, new[] { val }); - break; - default: - values.Add(property.Alias, new[] { val }); - break; + AddPropertyValue(null, c, property, values); + } + else + { + foreach (var culture in c.AvailableCultures) + AddPropertyValue(culture.ToLowerInvariant(), c, property, values); } } @@ -368,6 +415,25 @@ namespace Umbraco.Examine } } + private static void AddPropertyValue(string culture, IContent c, Property property, IDictionary values) + { + var val = property.GetValue(culture); + var cultureSuffix = culture == null ? string.Empty : "_" + culture; + switch (val) + { + //only add the value if its not null or empty (we'll check for string explicitly here too) + case null: + return; + case string strVal: + if (strVal.IsNullOrWhiteSpace()) return; + values.Add($"{property.Alias}{cultureSuffix}", new[] { val }); + break; + default: + values.Add($"{property.Alias}{cultureSuffix}", new[] { val }); + break; + } + } + public static IEnumerable GetValueSets(IEnumerable urlSegmentProviders, IUserService userService, params IMedia[] media) { foreach (var m in media) diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index a4c1fb4336..0c3b141152 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -34,14 +34,15 @@ namespace Umbraco.Examine /// /// Used to store the path of a content object /// - public const string IndexPathFieldName = "__Path"; - public const string NodeKeyFieldName = "__Key"; - public const string IconFieldName = "__Icon"; - public const string PublishedFieldName = "__Published"; + public const string IndexPathFieldName = SpecialFieldPrefix + "Path"; + public const string NodeKeyFieldName = SpecialFieldPrefix + "Key"; + public const string IconFieldName = SpecialFieldPrefix + "Icon"; + public const string PublishedFieldName = SpecialFieldPrefix + "Published"; + /// /// The prefix added to a field when it is duplicated in order to store the original raw value. /// - public const string RawFieldPrefix = "__Raw_"; + public const string RawFieldPrefix = SpecialFieldPrefix + "Raw_"; /// /// Constructor for config provider based indexes @@ -110,6 +111,8 @@ namespace Umbraco.Examine new FieldDefinition("urlName", FieldDefinitionTypes.InvariantCultureIgnoreCase), new FieldDefinition("path", FieldDefinitionTypes.Raw), + new FieldDefinition(PublishedFieldName, FieldDefinitionTypes.Raw), + new FieldDefinition(NodeKeyFieldName, FieldDefinitionTypes.Raw), new FieldDefinition(IndexPathFieldName, FieldDefinitionTypes.Raw), new FieldDefinition(IconFieldName, FieldDefinitionTypes.Raw) }; @@ -339,17 +342,20 @@ namespace Umbraco.Examine } /// - /// This ensures that the special __Raw_ fields are indexed + /// This ensures that the special __Raw_ fields are indexed correctly /// /// protected override void OnDocumentWriting(DocumentWritingEventArgs docArgs) { var d = docArgs.Document; - foreach (var f in docArgs.ValueSet.Values.Where(x => x.Key.StartsWith(RawFieldPrefix))) + foreach (var f in docArgs.ValueSet.Values.Where(x => x.Key.StartsWith(RawFieldPrefix)).ToList()) { if (f.Value.Count > 0) { + //remove the original value so we can store it the correct way + d.RemoveField(f.Key); + d.Add(new Field( f.Key, f.Value[0].ToString(), @@ -359,13 +365,6 @@ namespace Umbraco.Examine } } - ProfilingLogger.Logger.Debug(GetType(), - "Write lucene doc id:{DocumentId}, category:{DocumentCategory}, type:{DocumentItemType}", - docArgs.ValueSet.Id, - docArgs.ValueSet.Category, - docArgs.ValueSet.ItemType); - - base.OnDocumentWriting(docArgs); } @@ -375,9 +374,11 @@ namespace Umbraco.Examine protected override void AddDocument(Document doc, IndexItem item, IndexWriter writer) { ProfilingLogger.Logger.Debug(GetType(), - "AddDocument {DocumentId} with type {DocumentItemType}", + "Write lucene doc id:{DocumentId}, category:{DocumentCategory}, type:{DocumentItemType}", item.ValueSet.Id, + item.ValueSet.Category, item.ValueSet.ItemType); + base.AddDocument(doc, item, writer); } @@ -407,6 +408,7 @@ namespace Umbraco.Examine { //First save the raw value to a raw field, we will change the policy of this field by detecting the prefix later e.IndexItem.ValueSet.Values[string.Concat(RawFieldPrefix, value.Key)] = new List { str }; + //now replace the original value with the stripped html //TODO: This should be done with an analzer?! e.IndexItem.ValueSet.Values[value.Key] = new List { str.StripHtml() }; diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 8b57e10849..487e2d4ed6 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -37,10 +37,17 @@ namespace Umbraco.Tests.UmbracoExamine IMediaService mediaService = null, IMemberService memberService = null, IUserService userService = null, + ILocalizationService languageService = null, IContentTypeService contentTypeService = null, IMediaTypeService mediaTypeService = null, UmbracoContentIndexerOptions options = null) { + if (languageService == null) + { + languageService = Mock.Of( + x => x.GetAllLanguages() == Array.Empty()); + } + if (contentService == null) { long longTotalRecs; @@ -179,6 +186,7 @@ namespace Umbraco.Tests.UmbracoExamine contentService, mediaService, userService, + languageService, sqlContext, new[] {new DefaultUrlSegmentProvider()}, new UmbracoContentValueSetValidator(options, Mock.Of()), diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index 0f3211b743..774c2da35f 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -73,7 +73,7 @@ namespace Umbraco.Web.PropertyEditors e.Document.Add( new Field( $"{UmbracoExamineIndexer.RawFieldPrefix}{value.Key}", - firstVal, Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO)); + firstVal, Field.Store.YES, Field.Index.NO, Field.TermVector.NO)); //now replace the original value with the combined/cleaned value e.Document.RemoveField(value.Key); From a8db9d19cb37dc4541c1483a0e75bdc59031be4c Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 21 Nov 2018 21:48:39 +1100 Subject: [PATCH 03/44] adds a __VariesByCulture field --- src/Umbraco.Examine/UmbracoContentIndexer.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 72d54c52bb..c131713856 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -360,11 +360,11 @@ namespace Umbraco.Examine { var isVariant = c.ContentType.VariesByCulture(); - var urlValue = c.GetUrlSegment(urlSegmentProviders); //fixme: variants + var urlValue = c.GetUrlSegment(urlSegmentProviders); //Always add invariant urlName var values = new Dictionary { {"icon", new [] {c.ContentType.Icon}}, - {PublishedFieldName, new object[] {c.Published ? 1 : 0}}, //fixme: variants + {PublishedFieldName, new object[] {c.Published ? 1 : 0}}, //Always add invariant published value {"id", new object[] {c.Id}}, {"key", new object[] {c.Key}}, {"parentID", new object[] {c.Level > 1 ? c.ParentId : -1}}, @@ -380,11 +380,14 @@ namespace Umbraco.Examine {"creatorName", new object[] {c.GetCreatorProfile(userService)?.Name ?? "??"}}, {"writerName", new object[] {c.GetWriterProfile(userService)?.Name ?? "??"}}, {"writerID", new object[] {c.WriterId}}, - {"template", new object[] {c.Template?.Id ?? 0}} + {"template", new object[] {c.Template?.Id ?? 0}}, + {$"{SpecialFieldPrefix}VariesByCulture", new object[] {0}}, }; if (isVariant) { + values[$"{SpecialFieldPrefix}VariesByCulture"] = new object[] { 1 }; + foreach(var culture in c.AvailableCultures) { var variantUrl = c.GetUrlSegment(urlSegmentProviders, culture); From 80af1641ae1eeadbed007d6516bedb390b7e5c24 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 21 Nov 2018 23:14:12 +1100 Subject: [PATCH 04/44] rebuilds empty indexes on startup on managed background thread --- src/Umbraco.Web/Search/ExamineComponent.cs | 96 +++++++++++++++------- 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 6c152acae2..f8afc29798 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -27,6 +27,8 @@ using Umbraco.Web.Composing; using Umbraco.Web.PropertyEditors; using Umbraco.Examine; using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Web.Scheduling; +using System.Threading.Tasks; namespace Umbraco.Web.Search { @@ -43,6 +45,7 @@ namespace Umbraco.Web.Search private IScopeProvider _scopeProvider; private UrlSegmentProviderCollection _urlSegmentProviders; private ServiceContext _services; + private BackgroundTaskRunner _rebuildOnStartupRunner; // the default enlist priority is 100 // enlist with a lower priority to ensure that anything "default" runs after us @@ -55,6 +58,11 @@ namespace Umbraco.Web.Search _urlSegmentProviders = urlSegmentProviderCollection; _scopeProvider = scopeProvider; _examineManager = examineManager; + _rebuildOnStartupRunner = new BackgroundTaskRunner( + "RebuildIndexesOnStartup", + profilingLogger.Logger, + //hook into MainDom so that no index building occurs unless on MainDom + BackgroundTaskRunner.MainDomHook.Create(null, null)); //We want to manage Examine's appdomain shutdown sequence ourselves so first we'll disable Examine's default behavior //and then we'll use MainDom to control Examine's shutdown @@ -68,7 +76,7 @@ namespace Umbraco.Web.Search var simpleFsLockFactory = new NoPrefixSimpleFsLockFactory(d); return simpleFsLockFactory; }; - + //let's deal with shutting down Examine with MainDom var examineShutdownRegistered = mainDom.Register(() => { @@ -121,22 +129,9 @@ namespace Umbraco.Web.Search //TODO: need a way to disable rebuilding on startup logger.Info("Starting initialize async background thread."); - - // make it async in order not to slow down the boot - // fixme - should be a proper background task else we cannot stop it! - var bg = new Thread(() => - { - try - { - // rebuilds any empty indexes - RebuildIndexes(true, _examineManager, logger); - } - catch (Exception ex) - { - logger.Error(ex, "Failed to rebuild empty indexes."); - } - }); - bg.Start(); + //do the rebuild on a managed background thread + var task = new RebuildOnStartupTask(_examineManager, logger); + _rebuildOnStartupRunner.TryAdd(task); } /// @@ -223,6 +218,7 @@ namespace Umbraco.Web.Search } } + #region Cache refresher updated event handlers private void MemberCacheRefresherUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs args) { if (Suspendable.ExamineEvents.CanIndex == false) @@ -276,7 +272,7 @@ namespace Umbraco.Web.Search var mediaService = _services.MediaService; - foreach (var payload in (MediaCacheRefresher.JsonPayload[]) args.MessageObject) + foreach (var payload in (MediaCacheRefresher.JsonPayload[])args.MessageObject) { if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) { @@ -333,9 +329,9 @@ namespace Umbraco.Web.Search if (args.MessageType != MessageType.RefreshByPayload) throw new NotSupportedException(); - + var changedIds = new Dictionary removedIds, List refreshedIds, List otherIds)>(); - + foreach (var payload in (ContentTypeCacheRefresher.JsonPayload[])args.MessageObject) { if (!changedIds.TryGetValue(payload.ItemType, out var idLists)) @@ -354,11 +350,11 @@ namespace Umbraco.Web.Search const int pageSize = 500; - foreach(var ci in changedIds) + foreach (var ci in changedIds) { if (ci.Value.refreshedIds.Count > 0 || ci.Value.otherIds.Count > 0) { - switch(ci.Key) + switch (ci.Key) { case var itemType when itemType == typeof(IContentType).Name: RefreshContentOfContentTypes(ci.Value.refreshedIds.Concat(ci.Value.otherIds).Distinct().ToArray()); @@ -404,7 +400,7 @@ namespace Umbraco.Web.Search const int pageSize = 500; var memberTypes = _services.MemberTypeService.GetAll(memberTypeIds); - foreach(var memberType in memberTypes) + foreach (var memberType in memberTypes) { var page = 0; var total = long.MaxValue; @@ -500,7 +496,7 @@ namespace Umbraco.Web.Search var contentService = _services.ContentService; - foreach (var payload in (ContentCacheRefresher.JsonPayload[]) args.MessageObject) + foreach (var payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject) { if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) { @@ -543,9 +539,9 @@ namespace Umbraco.Web.Search const int pageSize = 500; var page = 0; var total = long.MaxValue; - while(page * pageSize < total) + while (page * pageSize < total) { - var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total, + var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total, //order by shallowest to deepest, this allows us to check it's published state without checking every item ordering: Ordering.By("Path", Direction.Ascending)); @@ -578,7 +574,9 @@ namespace Umbraco.Web.Search // BUT ... pretty sure it is! see test "Index_Delete_Index_Item_Ensure_Heirarchy_Removed" } } + #endregion + #region ReIndex/Delete for entity private void ReIndexForContent(IContent content, IContent published) { if (published != null && content.VersionId == published.VersionId) @@ -642,9 +640,11 @@ namespace Umbraco.Web.Search if (actions != null) actions.Add(new DeferedDeleteIndex(this, entityId, keepIfUnpublished)); else - DeferedDeleteIndex.Execute(this, entityId, keepIfUnpublished); + DeferedDeleteIndex.Execute(this, entityId, keepIfUnpublished); } + #endregion + #region Defered Actions private class DeferedActions { private readonly List _actions = new List(); @@ -686,7 +686,7 @@ namespace Umbraco.Web.Search private readonly bool? _supportUnpublished; public DeferedReIndexForContent(ExamineComponent examineComponent, IContent content, bool? supportUnpublished) - { + { _examineComponent = examineComponent; _content = content; _supportUnpublished = supportUnpublished; @@ -799,6 +799,46 @@ namespace Umbraco.Web.Search .Where(x => x.EnableDefaultEventHandler)); } } + #endregion + /// + /// Background task used to rebuild empty indexes on startup + /// + private class RebuildOnStartupTask : IBackgroundTask + { + private readonly IExamineManager _examineManager; + private readonly ILogger _logger; + + public RebuildOnStartupTask(IExamineManager examineManager, ILogger logger) + { + _examineManager = examineManager; + _logger = logger; + } + + public bool IsAsync => false; + + public void Dispose() + { + throw new NotImplementedException(); + } + + public void Run() + { + try + { + // rebuilds any empty indexes + RebuildIndexes(true, _examineManager, _logger); + } + catch (Exception ex) + { + _logger.Error(ex, "Failed to rebuild empty indexes."); + } + } + + public Task RunAsync(CancellationToken token) + { + throw new NotImplementedException(); + } + } } } From 296552302945ee65e36c2b0e4af87050939d626e Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 22 Nov 2018 14:05:36 +1100 Subject: [PATCH 05/44] Configures Umbraco indexes via code, updates to latest examine, simplifies some logic --- src/Umbraco.Examine/Umbraco.Examine.csproj | 2 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 6 +- .../UmbracoContentValueSetValidator.cs | 2 + src/Umbraco.Examine/UmbracoExamineIndexer.cs | 25 ++- src/Umbraco.Examine/UmbracoExamineSearcher.cs | 21 +-- src/Umbraco.Examine/UmbracoMemberIndexer.cs | 13 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../UmbracoExamine/IndexInitializer.cs | 15 +- src/Umbraco.Web.UI/Global.asax | 1 - src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- .../config/ExamineIndex.Release.config | 20 +-- src/Umbraco.Web.UI/config/ExamineIndex.config | 22 +-- .../config/ExamineSettings.Release.config | 22 +-- .../config/ExamineSettings.config | 21 --- ...aseServerRegistrarAndMessengerComponent.cs | 2 +- src/Umbraco.Web/Search/ExamineComponent.cs | 123 ++++++++------ .../Search/IUmbracoIndexesBuilder.cs | 10 ++ .../Search/UmbracoIndexesBuilder.cs | 150 ++++++++++++++++++ src/Umbraco.Web/Suspendable.cs | 5 +- src/Umbraco.Web/Umbraco.Web.csproj | 4 +- 20 files changed, 292 insertions(+), 176 deletions(-) create mode 100644 src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs create mode 100644 src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 678ae124d2..9cee6d8c3f 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index c131713856..b4eeeac3d9 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -236,19 +236,19 @@ namespace Umbraco.Examine /// /// /// - protected override FieldValueTypeCollection CreateFieldValueTypes(Directory x, IReadOnlyDictionary> indexValueTypesFactory = null) + 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 UmbracoIndexFields) + foreach (var field in UmbracoIndexFieldDefinitions) { var def = new FieldDefinition($"{field.Name}_{lang.IsoCode.ToLowerInvariant()}", field.Type); FieldDefinitionCollection.TryAdd(def.Name, def); } } - return base.CreateFieldValueTypes(x, indexValueTypesFactory); + return base.CreateFieldValueTypes(indexValueTypesFactory); } /// diff --git a/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs b/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs index 01056be6b9..fb5c26d3c1 100644 --- a/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs +++ b/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs @@ -27,6 +27,8 @@ namespace Umbraco.Examine if (valueSet.Category == IndexTypes.Content && valueSet.Values.ContainsKey(UmbracoExamineIndexer.PublishedFieldName)) { + //fixme - variants? + var published = valueSet.Values[UmbracoExamineIndexer.PublishedFieldName] != null && valueSet.Values[UmbracoExamineIndexer.PublishedFieldName][0].Equals(1); //we don't support unpublished and the item is not published return false if (_options.SupportUnpublishedContent == false && published == false) diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index 0c3b141152..9449b7aba5 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -72,6 +72,16 @@ namespace Umbraco.Examine }); } + /// + /// Create a new + /// + /// + /// + /// + /// + /// + /// + /// protected UmbracoExamineIndexer( string name, IEnumerable fieldDefinitions, @@ -92,7 +102,7 @@ namespace Umbraco.Examine /// Alot of standard umbraco fields shouldn't be tokenized or even indexed, just stored into lucene /// for retreival after searching. /// - internal static readonly FieldDefinition[] UmbracoIndexFields = + public static readonly FieldDefinition[] UmbracoIndexFieldDefinitions = { new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("level", FieldDefinitionTypes.Integer), @@ -125,14 +135,19 @@ namespace Umbraco.Examine /// /// /// - protected override FieldValueTypeCollection CreateFieldValueTypes(Directory x, IReadOnlyDictionary> indexValueTypesFactory = null) + protected override FieldValueTypeCollection CreateFieldValueTypes(IReadOnlyDictionary> indexValueTypesFactory = null) { - foreach (var field in UmbracoIndexFields) + //if config based then ensure the value types else it's assumed these were passed in via ctor + if (_configBased) { - FieldDefinitionCollection.TryAdd(field.Name, field); + foreach (var field in UmbracoIndexFieldDefinitions) + { + FieldDefinitionCollection.TryAdd(field.Name, field); + } } + - return base.CreateFieldValueTypes(x, indexValueTypesFactory); + return base.CreateFieldValueTypes(indexValueTypesFactory); } /// diff --git a/src/Umbraco.Examine/UmbracoExamineSearcher.cs b/src/Umbraco.Examine/UmbracoExamineSearcher.cs index 4b5ac50af7..b23d2bcb29 100644 --- a/src/Umbraco.Examine/UmbracoExamineSearcher.cs +++ b/src/Umbraco.Examine/UmbracoExamineSearcher.cs @@ -9,7 +9,7 @@ using Lucene.Net.Index; using Umbraco.Core.Composing; using Umbraco.Examine.Config; using Directory = Lucene.Net.Store.Directory; - +using Examine.LuceneEngine; namespace Umbraco.Examine { @@ -29,32 +29,21 @@ namespace Umbraco.Examine _configBased = true; } - /// - /// Constructor to allow for creating an indexer at runtime - /// - /// - /// - /// - public UmbracoExamineSearcher(string name, DirectoryInfo indexPath, Analyzer analyzer) - : base(name, indexPath, analyzer) - { - _configBased = false; - } - /// /// Constructor to allow for creating an indexer at runtime /// /// /// /// - public UmbracoExamineSearcher(string name, Directory luceneDirectory, Analyzer analyzer) - : base(name, luceneDirectory, analyzer) + public UmbracoExamineSearcher(string name, Directory luceneDirectory, Analyzer analyzer, FieldValueTypeCollection fieldValueTypeCollection) + : base(name, luceneDirectory, analyzer, fieldValueTypeCollection) { _configBased = false; } /// - public UmbracoExamineSearcher(string name, IndexWriter writer, Analyzer analyzer) : base(name, writer, analyzer) + public UmbracoExamineSearcher(string name, IndexWriter writer, Analyzer analyzer, FieldValueTypeCollection fieldValueTypeCollection) + : base(name, writer, analyzer, fieldValueTypeCollection) { _configBased = false; } diff --git a/src/Umbraco.Examine/UmbracoMemberIndexer.cs b/src/Umbraco.Examine/UmbracoMemberIndexer.cs index 82bf3b9cf6..f77ebe3946 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndexer.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndexer.cs @@ -50,8 +50,8 @@ namespace Umbraco.Examine Directory luceneDirectory, Analyzer analyzer, ProfilingLogger profilingLogger, - IValueSetValidator validator, - IMemberService memberService) : + IMemberService memberService, + IValueSetValidator validator = null) : base(name, fieldDefinitions, luceneDirectory, analyzer, profilingLogger, validator) { _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); @@ -64,12 +64,12 @@ namespace Umbraco.Examine /// /// /// - protected override FieldValueTypeCollection CreateFieldValueTypes(Directory x, IReadOnlyDictionary> indexValueTypesFactory = null) + protected override FieldValueTypeCollection CreateFieldValueTypes(IReadOnlyDictionary> indexValueTypesFactory = null) { var keyDef = new FieldDefinition("__key", FieldDefinitionTypes.Raw); FieldDefinitionCollection.TryAdd(keyDef.Name, keyDef); - return base.CreateFieldValueTypes(x, indexValueTypesFactory); + return base.CreateFieldValueTypes(indexValueTypesFactory); } /// @@ -149,7 +149,8 @@ namespace Umbraco.Examine { case null: continue; - case string strVal when strVal.IsNullOrWhiteSpace() == false: + case string strVal: + if (strVal.IsNullOrWhiteSpace()) continue; values.Add(property.Alias, new[] { val }); break; default: @@ -182,6 +183,8 @@ namespace Umbraco.Examine { if (email.Count > 0) { + //TODO: This shouldn't be here, create an "Email" value type with an appropriate analyzer + //will be indexed as full text (the default anaylyzer) e.IndexItem.ValueSet.Values["_searchEmail"] = new List { email[0]?.ToString().Replace(".", " ").Replace("@", " ") }; } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 322be82ca7..bd133d4b17 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/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 487e2d4ed6..0f69563dd6 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -197,16 +197,11 @@ namespace Umbraco.Tests.UmbracoExamine return i; } - public static LuceneSearcher GetLuceneSearcher(Directory luceneDir) - { - return new LuceneSearcher("testSearcher", luceneDir, new StandardAnalyzer(Version.LUCENE_29)); - } - - public static MultiIndexSearcher GetMultiSearcher(Directory pdfDir, Directory simpleDir, Directory conventionDir, Directory cwsDir) - { - var i = new MultiIndexSearcher("testSearcher", new[] { pdfDir, simpleDir, conventionDir, cwsDir }, new StandardAnalyzer(Version.LUCENE_29)); - return i; - } + //public static MultiIndexSearcher GetMultiSearcher(Directory pdfDir, Directory simpleDir, Directory conventionDir, Directory cwsDir) + //{ + // var i = new MultiIndexSearcher("testSearcher", new[] { pdfDir, simpleDir, conventionDir, cwsDir }, new StandardAnalyzer(Version.LUCENE_29)); + // return i; + //} internal static void IndexingError(object sender, IndexingErrorEventArgs e) diff --git a/src/Umbraco.Web.UI/Global.asax b/src/Umbraco.Web.UI/Global.asax index 53d2d54050..08312748b9 100644 --- a/src/Umbraco.Web.UI/Global.asax +++ b/src/Umbraco.Web.UI/Global.asax @@ -1,2 +1 @@ <%@ Application Inherits="Umbraco.Web.UmbracoApplication" Language="C#" %> - \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index c108cffcf4..26e5436c89 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,7 +88,7 @@ - + diff --git a/src/Umbraco.Web.UI/config/ExamineIndex.Release.config b/src/Umbraco.Web.UI/config/ExamineIndex.Release.config index 300f75ba33..ec0d18aa2d 100644 --- a/src/Umbraco.Web.UI/config/ExamineIndex.Release.config +++ b/src/Umbraco.Web.UI/config/ExamineIndex.Release.config @@ -7,22 +7,4 @@ Index/Search providers can be defined in the UmbracoSettings.config More information and documentation can be found on GitHub: https://github.com/Shazwazza/Examine/ --> - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/config/ExamineIndex.config b/src/Umbraco.Web.UI/config/ExamineIndex.config index 8d8dcaaf6d..ec0d18aa2d 100644 --- a/src/Umbraco.Web.UI/config/ExamineIndex.config +++ b/src/Umbraco.Web.UI/config/ExamineIndex.config @@ -7,24 +7,4 @@ Index/Search providers can be defined in the UmbracoSettings.config More information and documentation can be found on GitHub: https://github.com/Shazwazza/Examine/ --> - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.Release.config b/src/Umbraco.Web.UI/config/ExamineSettings.Release.config index 31e1193ee9..6b3aaa0372 100644 --- a/src/Umbraco.Web.UI/config/ExamineSettings.Release.config +++ b/src/Umbraco.Web.UI/config/ExamineSettings.Release.config @@ -7,34 +7,14 @@ Index sets can be defined in the ExamineIndex.config if you're using the standar More information and documentation can be found on GitHub: https://github.com/Shazwazza/Examine/ --> + - - - - - - - - - - - - - diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.config b/src/Umbraco.Web.UI/config/ExamineSettings.config index 18c5658731..265e5ff788 100644 --- a/src/Umbraco.Web.UI/config/ExamineSettings.config +++ b/src/Umbraco.Web.UI/config/ExamineSettings.config @@ -9,32 +9,11 @@ More information and documentation can be found on GitHub: https://github.com/Sh - - - - - - - - - - - - - diff --git a/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs index 02dede1da9..9a2ca3513c 100644 --- a/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs @@ -87,7 +87,7 @@ namespace Umbraco.Web.Components //rebuild indexes if the server is not synced // NOTE: This will rebuild ALL indexes including the members, if developers want to target specific // indexes then they can adjust this logic themselves. - () => ExamineComponent.RebuildIndexes(false, _examineManager, _logger) + () => ExamineComponent.RebuildIndexes(_examineManager, _logger, false, 5000) } }); }); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index f8afc29798..867132b23f 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -4,11 +4,9 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; -using System.Xml.Linq; using Examine; using Examine.LuceneEngine; using Examine.LuceneEngine.Providers; -using Lucene.Net.Documents; using Lucene.Net.Index; using Umbraco.Core; using Umbraco.Core.Cache; @@ -19,19 +17,20 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; -using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; using Umbraco.Core.Sync; using Umbraco.Web.Cache; -using Umbraco.Web.Composing; using Umbraco.Web.PropertyEditors; using Umbraco.Examine; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Web.Scheduling; using System.Threading.Tasks; +using Umbraco.Core.Persistence; +using Umbraco.Core.Composing; namespace Umbraco.Web.Search { + /// /// Configures and installs Examine. /// @@ -43,26 +42,29 @@ namespace Umbraco.Web.Search private static volatile bool _isConfigured = false; private static readonly object IsConfiguredLocker = new object(); private IScopeProvider _scopeProvider; - private UrlSegmentProviderCollection _urlSegmentProviders; private ServiceContext _services; - private BackgroundTaskRunner _rebuildOnStartupRunner; + private static BackgroundTaskRunner _rebuildOnStartupRunner; + private static readonly object _rebuildLocker = new object(); + private IEnumerable _urlSegmentProviders; // the default enlist priority is 100 // enlist with a lower priority to ensure that anything "default" runs after us // but greater that SafeXmlReaderWriter priority which is 60 private const int EnlistPriority = 80; - internal void Initialize(IRuntimeState runtime, MainDom mainDom, PropertyEditorCollection propertyEditors, IExamineManager examineManager, ProfilingLogger profilingLogger, IScopeProvider scopeProvider, UrlSegmentProviderCollection urlSegmentProviderCollection, ServiceContext services) + public override void Compose(Composition composition) + { + base.Compose(composition); + + composition.Container.RegisterSingleton(); + } + + internal void Initialize(IRuntimeState runtime, MainDom mainDom, PropertyEditorCollection propertyEditors, IExamineManager examineManager, ProfilingLogger profilingLogger, IScopeProvider scopeProvider, IUmbracoIndexesBuilder indexBuilder, ServiceContext services, IEnumerable urlSegmentProviders) { _services = services; - _urlSegmentProviders = urlSegmentProviderCollection; _scopeProvider = scopeProvider; _examineManager = examineManager; - _rebuildOnStartupRunner = new BackgroundTaskRunner( - "RebuildIndexesOnStartup", - profilingLogger.Logger, - //hook into MainDom so that no index building occurs unless on MainDom - BackgroundTaskRunner.MainDomHook.Create(null, null)); + _urlSegmentProviders = urlSegmentProviders; //We want to manage Examine's appdomain shutdown sequence ourselves so first we'll disable Examine's default behavior //and then we'll use MainDom to control Examine's shutdown @@ -96,6 +98,12 @@ namespace Umbraco.Web.Search return; //exit, do not continue } + //create the indexes and register them with the manager + foreach(var index in indexBuilder.Create()) + { + _examineManager.AddIndexer(index.Key, index.Value); + } + profilingLogger.Logger.Debug("Examine shutdown registered with MainDom"); var registeredIndexers = examineManager.IndexProviders.Values.OfType().Count(x => x.EnableDefaultEventHandler); @@ -117,51 +125,41 @@ namespace Umbraco.Web.Search EnsureUnlocked(profilingLogger.Logger, examineManager); - RebuildIndexesOnStartup(profilingLogger.Logger); + RebuildIndexes(examineManager, profilingLogger.Logger, true, 5000); } + /// /// Called to rebuild empty indexes on startup /// /// - private void RebuildIndexesOnStartup(ILogger logger) + public static void RebuildIndexes(IExamineManager examineManager, ILogger logger, bool onlyEmptyIndexes, int waitMilliseconds = 0) { //TODO: need a way to disable rebuilding on startup - logger.Info("Starting initialize async background thread."); - //do the rebuild on a managed background thread - var task = new RebuildOnStartupTask(_examineManager, logger); - _rebuildOnStartupRunner.TryAdd(task); - } - - /// - /// Used to rebuild indexes on startup or cold boot - /// - /// - /// - /// - internal static void RebuildIndexes(bool onlyEmptyIndexes, IExamineManager examineManager, ILogger logger) - { - //do not attempt to do this if this has been disabled since we are not the main dom. - //this can be called during a cold boot - if (_disableExamineIndexing) return; - - EnsureUnlocked(logger, examineManager); - - if (onlyEmptyIndexes) + lock(_rebuildLocker) { - foreach (var indexer in examineManager.IndexProviders.Values.Where(x => x.IsIndexNew())) + if (_rebuildOnStartupRunner != null && _rebuildOnStartupRunner.IsRunning) { - indexer.RebuildIndex(); + logger.Warn("Call was made to RebuildIndexes but the task runner for rebuilding is already running"); + return; } - } - else - { - //do all of them - examineManager.RebuildIndexes(); + + logger.Info("Starting initialize async background thread."); + //do the rebuild on a managed background thread + var task = new RebuildOnStartupTask(examineManager, logger, onlyEmptyIndexes, waitMilliseconds); + + _rebuildOnStartupRunner = new BackgroundTaskRunner( + "RebuildIndexesOnStartup", + //new BackgroundTaskRunnerOptions{ LongRunning= true }, //fixme, this flag doesn't have any affect anymore + logger); + + _rebuildOnStartupRunner.TryAdd(task); } } + + /// /// Must be called to each index is unlocked before any indexing occurs /// @@ -808,11 +806,15 @@ namespace Umbraco.Web.Search { private readonly IExamineManager _examineManager; private readonly ILogger _logger; + private readonly bool _onlyEmptyIndexes; + private readonly int _waitMilliseconds; - public RebuildOnStartupTask(IExamineManager examineManager, ILogger logger) + public RebuildOnStartupTask(IExamineManager examineManager, ILogger logger, bool onlyEmptyIndexes, int waitMilliseconds = 0) { _examineManager = examineManager; _logger = logger; + _onlyEmptyIndexes = onlyEmptyIndexes; + _waitMilliseconds = waitMilliseconds; } public bool IsAsync => false; @@ -826,8 +828,8 @@ namespace Umbraco.Web.Search { try { - // rebuilds any empty indexes - RebuildIndexes(true, _examineManager, _logger); + // rebuilds indexes + RebuildIndexes(); } catch (Exception ex) { @@ -839,6 +841,35 @@ namespace Umbraco.Web.Search { throw new NotImplementedException(); } + + /// + /// Used to rebuild indexes on startup or cold boot + /// + /// + private void RebuildIndexes() + { + //do not attempt to do this if this has been disabled since we are not the main dom. + //this can be called during a cold boot + if (_disableExamineIndexing) return; + + if (_waitMilliseconds > 0) + Thread.Sleep(_waitMilliseconds); + + EnsureUnlocked(_logger, _examineManager); + + if (_onlyEmptyIndexes) + { + foreach (var indexer in _examineManager.IndexProviders.Values.Where(x => x.IsIndexNew())) + { + indexer.RebuildIndex(); + } + } + else + { + //do all of them + _examineManager.RebuildIndexes(); + } + } } } } diff --git a/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs new file mode 100644 index 0000000000..012f119238 --- /dev/null +++ b/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Examine; + +namespace Umbraco.Web.Search +{ + internal interface IUmbracoIndexesBuilder + { + IReadOnlyDictionary Create(); + } +} diff --git a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs new file mode 100644 index 0000000000..19dbc034a1 --- /dev/null +++ b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs @@ -0,0 +1,150 @@ +using System.Collections.Generic; +using Umbraco.Core.Logging; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Examine; +using Umbraco.Core.Persistence; +using Umbraco.Core.IO; +using System.IO; +using Lucene.Net.Store; +using Lucene.Net.Analysis.Standard; +using Lucene.Net.Analysis; +using Examine.LuceneEngine; +using Examine; +using Examine.LuceneEngine.Providers; +using System.Linq; +using Umbraco.Core; + +namespace Umbraco.Web.Search +{ + /// + /// Creates the indexes used by Umbraco + /// + public class UmbracoIndexesBuilder : IUmbracoIndexesBuilder + { + //TODO: we should inject the different IValueSetValidator so devs can just register them instead of overriding this class? + + public UmbracoIndexesBuilder(ProfilingLogger profilingLogger, + IContentService contentService, + IMediaService mediaService, + IUserService userService, + ILocalizationService languageService, + IPublicAccessService publicAccessService, + IMemberService memberService, + ISqlContext sqlContext, + IEnumerable urlSegmentProviders) + { + ProfilingLogger = profilingLogger ?? throw new System.ArgumentNullException(nameof(profilingLogger)); + ContentService = contentService ?? throw new System.ArgumentNullException(nameof(contentService)); + MediaService = mediaService ?? throw new System.ArgumentNullException(nameof(mediaService)); + UserService = userService ?? throw new System.ArgumentNullException(nameof(userService)); + LanguageService = languageService ?? throw new System.ArgumentNullException(nameof(languageService)); + PublicAccessService = publicAccessService ?? throw new System.ArgumentNullException(nameof(publicAccessService)); + MemberService = memberService ?? throw new System.ArgumentNullException(nameof(memberService)); + SqlContext = sqlContext ?? throw new System.ArgumentNullException(nameof(sqlContext)); + UrlSegmentProviders = urlSegmentProviders ?? throw new System.ArgumentNullException(nameof(urlSegmentProviders)); + } + + protected ProfilingLogger ProfilingLogger { get; } + protected IContentService ContentService { get; } + protected IMediaService MediaService { get; } + protected IUserService UserService { get; } + protected ILocalizationService LanguageService { get; } + protected IPublicAccessService PublicAccessService { get; } + protected IMemberService MemberService { get; } + protected ISqlContext SqlContext { get; } + protected IEnumerable UrlSegmentProviders { get; } + + public const string InternalIndexPath = "Internal"; + public const string ExternalIndexPath = "External"; + public const string MembersIndexPath = "Members"; + + /// + /// By default these are the member fields we index + /// + public static readonly string[] DefaultMemberIndexFields = new[] { "id", "nodeName", "updateDate", "writerName", "loginName", "email", "nodeTypeAlias" }; + + /// + /// Creates the Umbraco indexes + /// + /// + public IReadOnlyDictionary Create() + { + return new Dictionary + { + [InternalIndexPath] = CreateContentIndex(InternalIndexPath, new UmbracoContentIndexerOptions(true, true, null), new CultureInvariantWhitespaceAnalyzer()), + [ExternalIndexPath] = CreateContentIndex(ExternalIndexPath, new UmbracoContentIndexerOptions(false, false, null), new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30)), + [MembersIndexPath] = CreateMemberIndex() + }; + } + + private IIndexer CreateContentIndex(string name, UmbracoContentIndexerOptions options, Analyzer analyzer) + { + + var index = new UmbracoContentIndexer( + $"{name}Indexer", + //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes + UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, + GetFileSystemLuceneDirectory(name), + analyzer, + ProfilingLogger, ContentService, MediaService, UserService, LanguageService, SqlContext, UrlSegmentProviders, + GetContentValueSetValidator(options), + options); + return index; + } + + private IIndexer CreateMemberIndex() + { + var appData = Path.Combine(IOHelper.MapPath(SystemDirectories.Data), "TEMP", "ExamineIndexes", MembersIndexPath); + var index = new UmbracoMemberIndexer( + $"{MembersIndexPath}Indexer", + //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes + UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, + GetFileSystemLuceneDirectory(MembersIndexPath), + new CultureInvariantWhitespaceAnalyzer(), + ProfilingLogger, MemberService, + GetMemberValueSetValidator()); + return index; + } + + public virtual Lucene.Net.Store.Directory GetFileSystemLuceneDirectory(string name) + { + var dirInfo = new DirectoryInfo(Path.Combine(IOHelper.MapPath(SystemDirectories.Data), "TEMP", "ExamineIndexes", name)); + if (!dirInfo.Exists) + System.IO.Directory.CreateDirectory(dirInfo.FullName); + + var luceneDir = new SimpleFSDirectory(dirInfo); + //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the appdomain + //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock + //which simply checks the existence of the lock file + luceneDir.SetLockFactory(new NoPrefixSimpleFsLockFactory(dirInfo)); + return luceneDir; + } + + public virtual IValueSetValidator GetContentValueSetValidator(UmbracoContentIndexerOptions options) + { + return new UmbracoContentValueSetValidator(options, PublicAccessService); + } + + /// + /// Returns the for the member indexer + /// + /// + public virtual IValueSetValidator GetMemberValueSetValidator() + { + //This validator is used purely to filter the value set + return new ValueSetValidatorDelegate(valueSet => + { + + foreach(var key in valueSet.Values.Keys.ToList()) + { + if (!DefaultMemberIndexFields.InvariantContains(key)) + valueSet.Values.Remove(key); //remove any value with a key that doesn't match our list + } + + return true; + }); + } + + } +} diff --git a/src/Umbraco.Web/Suspendable.cs b/src/Umbraco.Web/Suspendable.cs index beb0029f4f..9bbbef9742 100644 --- a/src/Umbraco.Web/Suspendable.cs +++ b/src/Umbraco.Web/Suspendable.cs @@ -78,10 +78,9 @@ namespace Umbraco.Web if (_tried == false) return; _tried = false; - // fixme - could we fork this on a background thread? //TODO: when resuming do we always want a full rebuild of all indexes? - // fixme - can we inject IExamineManager somehow? - ExamineComponent.RebuildIndexes(false, ExamineManager.Instance, Current.Logger); + // fixme - can we inject these somehow? + ExamineComponent.RebuildIndexes(ExamineManager.Instance, Current.Logger, false); } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 32134e7a45..a5bdf8de45 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -62,7 +62,7 @@ - + 2.6.2.25 @@ -159,6 +159,8 @@ + + From c5b36773c9b71eca19e2c1f6aa9880005a1e1b49 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 22 Nov 2018 14:26:26 +1100 Subject: [PATCH 06/44] Removes the old _searchEmail and configures the 'email' field to be of type EmailAddress --- src/Umbraco.Examine/Umbraco.Examine.csproj | 2 +- src/Umbraco.Examine/UmbracoExamineIndexer.cs | 2 ++ src/Umbraco.Examine/UmbracoMemberIndexer.cs | 11 ----------- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- 6 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 9cee6d8c3f..98ad566738 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index 9449b7aba5..5a27a9c78d 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -121,6 +121,8 @@ namespace Umbraco.Examine 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), diff --git a/src/Umbraco.Examine/UmbracoMemberIndexer.cs b/src/Umbraco.Examine/UmbracoMemberIndexer.cs index f77ebe3946..62d9a7a1d0 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndexer.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndexer.cs @@ -179,17 +179,6 @@ namespace Umbraco.Examine e.IndexItem.ValueSet.Values["__key"] = key; } - if (e.IndexItem.ValueSet.Values.TryGetValue("email", out var email) && e.IndexItem.ValueSet.Values.ContainsKey("_searchEmail") == false) - { - if (email.Count > 0) - { - //TODO: This shouldn't be here, create an "Email" value type with an appropriate analyzer - - //will be indexed as full text (the default anaylyzer) - e.IndexItem.ValueSet.Values["_searchEmail"] = new List { email[0]?.ToString().Replace(".", " ").Replace("@", " ") }; - } - - } } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index bd133d4b17..84ddda4335 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.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 26e5436c89..90543b6faf 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,7 +88,7 @@ - + diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a5bdf8de45..9c98f81894 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -62,7 +62,7 @@ - + 2.6.2.25 From 845a652c1ccabc5c9e88b15385a546cd2a6b704d Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 22 Nov 2018 14:31:27 +1100 Subject: [PATCH 07/44] adds some notes --- src/Umbraco.Examine/UmbracoExamineIndexer.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index 5a27a9c78d..bd9062e143 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -19,6 +19,20 @@ using Directory = Lucene.Net.Store.Directory; namespace Umbraco.Examine { + public class HtmlValueType : IndexValueTypeBase + { + public HtmlValueType(string fieldName, bool store = true) : base(fieldName, store) + { + } + + protected override void AddSingleValue(Document doc, object value) + { + //TODO: Make this happen so we can properly analyze/tokenize html, maybe we only need an analyzer though + + throw new NotImplementedException(); + } + } + /// /// An abstract provider containing the basic functionality to be able to query against /// Umbraco data. From ff2b2893ae9f85dc6050708bc63e1d0a218c03d2 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 22 Nov 2018 15:23:50 +1100 Subject: [PATCH 08/44] adds notes --- src/Umbraco.Examine/UmbracoExamineIndexer.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index bd9062e143..d5c9adea77 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -33,6 +33,26 @@ namespace Umbraco.Examine } } + public class HtmlAnalyzer : Analyzer + { + public override TokenStream TokenStream(string fieldName, TextReader reader) + { + return new LowerCaseFilter( //case insensitive + new EmailAddressTokenizer(reader)); //email tokenizer + } + + /// + /// Used for email addresses + /// + public class HtmlTokenizer : Tokenizer + { + public override bool IncrementToken() + { + throw new NotImplementedException(); + } + } + } + /// /// An abstract provider containing the basic functionality to be able to query against /// Umbraco data. From 75116e158596bde6656d02442d63a1a4c2d04e5d Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 26 Nov 2018 11:05:53 +1100 Subject: [PATCH 09/44] Creates IUmbracoIndexer to decouple lucene from the indexers --- src/Umbraco.Examine/Umbraco.Examine.csproj | 1 + src/Umbraco.Examine/UmbracoContentIndexer.cs | 6 ------ src/Umbraco.Examine/UmbracoExamineIndexer.cs | 10 +++++++++- src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs | 1 + src/Umbraco.Web/Search/ExamineComponent.cs | 11 ++++++----- 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 98ad566738..db3570d380 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -59,6 +59,7 @@ + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index b4eeeac3d9..541ecd1b2b 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -177,12 +177,6 @@ namespace Umbraco.Examine /// public bool SupportProtectedContent { get; protected set; } - /// - /// Determines if the manager will call the indexing methods when content is saved or deleted as - /// opposed to cache being updated. - /// - public bool SupportUnpublishedContent { get; protected set; } - /// /// If set this will filter the content items allowed to be indexed /// diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index d5c9adea77..fd56ac7fc4 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -57,7 +57,7 @@ namespace Umbraco.Examine /// An abstract provider containing the basic functionality to be able to query against /// Umbraco data. /// - public abstract class UmbracoExamineIndexer : LuceneIndexer + public abstract class UmbracoExamineIndexer : LuceneIndexer, IUmbracoIndexer { // note // wrapping all operations that end up calling base.SafelyProcessQueueItems in a safe call @@ -191,6 +191,14 @@ namespace Umbraco.Examine /// public bool EnableDefaultEventHandler { get; set; } = true; + /// + /// When set to true data will not be deleted from the index if the data is being unpublished (not deleted) + /// + /// + /// Generally used only for published content + /// + public bool SupportUnpublishedContent { get; protected set; } = false; + /// /// the supported indexable types /// diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index 774c2da35f..9b5443c649 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -25,6 +25,7 @@ namespace Umbraco.Web.PropertyEditors : base(logger) { } + //TODO: Change this to use a native way of indexing data: https://github.com/umbraco/Umbraco-CMS/issues/3531 internal void DocumentWriting(object sender, Examine.LuceneEngine.DocumentWritingEventArgs e) { foreach (var value in e.ValueSet.Values) diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 867132b23f..b355f6bd91 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -106,7 +106,7 @@ namespace Umbraco.Web.Search profilingLogger.Logger.Debug("Examine shutdown registered with MainDom"); - var registeredIndexers = examineManager.IndexProviders.Values.OfType().Count(x => x.EnableDefaultEventHandler); + var registeredIndexers = examineManager.IndexProviders.Values.OfType().Count(x => x.EnableDefaultEventHandler); profilingLogger.Logger.Info("Adding examine event handlers for {RegisteredIndexers} index providers.", registeredIndexers); @@ -198,6 +198,7 @@ namespace Umbraco.Web.Search } } + //TODO: Change this to use a native way of indexing data: https://github.com/umbraco/Umbraco-CMS/issues/3531 private static void BindGridToExamine(ILogger logger, IExamineManager examineManager, IEnumerable propertyEditors) { //bind the grid property editors - this is a hack until http://issues.umbraco.org/issue/U4-8437 @@ -369,7 +370,7 @@ namespace Umbraco.Web.Search //Delete all content of this content/media/member type that is in any content indexer by looking up matched examine docs foreach (var id in ci.Value.removedIds) { - foreach (var index in _examineManager.IndexProviders.Values.OfType()) + foreach (var index in _examineManager.IndexProviders.Values.OfType()) { var searcher = index.GetSearcher(); @@ -762,7 +763,7 @@ namespace Umbraco.Web.Search examineComponent._examineManager.IndexItems( valueSet.ToArray(), - examineComponent._examineManager.IndexProviders.Values.OfType() + examineComponent._examineManager.IndexProviders.Values.OfType() //ensure that only the providers are flagged to listen execute .Where(x => x.EnableDefaultEventHandler)); } @@ -790,10 +791,10 @@ namespace Umbraco.Web.Search { examineComponent._examineManager.DeleteFromIndexes( id.ToString(CultureInfo.InvariantCulture), - examineComponent._examineManager.IndexProviders.Values.OfType() + examineComponent._examineManager.IndexProviders.Values.OfType() // if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content, // otherwise if keepIfUnpublished == false then remove from all indexes - .Where(x => keepIfUnpublished == false || (x is UmbracoContentIndexer && ((UmbracoContentIndexer)x).SupportUnpublishedContent == false)) + .Where(x => keepIfUnpublished == false || x.SupportUnpublishedContent == false) .Where(x => x.EnableDefaultEventHandler)); } } From fba549e59c2cd30440c31fefd632bc1e1968d0d3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 26 Nov 2018 11:06:11 +1100 Subject: [PATCH 10/44] Creates IUmbracoIndexer to decouple lucene from the indexers --- src/Umbraco.Examine/IUmbracoIndexer.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/Umbraco.Examine/IUmbracoIndexer.cs diff --git a/src/Umbraco.Examine/IUmbracoIndexer.cs b/src/Umbraco.Examine/IUmbracoIndexer.cs new file mode 100644 index 0000000000..c926bfb2fe --- /dev/null +++ b/src/Umbraco.Examine/IUmbracoIndexer.cs @@ -0,0 +1,23 @@ +using Examine; + +namespace Umbraco.Examine +{ + /// + /// A Marker interface for defining an Umbraco indexer + /// + public interface IUmbracoIndexer : IIndexer + { + /// + /// When set to true Umbraco will keep the index in sync with Umbraco data automatically + /// + bool EnableDefaultEventHandler { get; } + + /// + /// When set to true data will not be deleted from the index if the data is being unpublished (not deleted) + /// + /// + /// Generally used only for published content + /// + bool SupportUnpublishedContent { get; } + } +} From 4f7de35e0445686e3c9deb971dddecce7f7c1ac1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 26 Nov 2018 12:06:38 +1100 Subject: [PATCH 11/44] Adds HtmlValueType --- src/Umbraco.Core/StringExtensions.cs | 2 +- src/Umbraco.Examine/HtmlValueType.cs | 43 ++++++++++++++++++++ src/Umbraco.Examine/Umbraco.Examine.csproj | 3 +- src/Umbraco.Examine/UmbracoExamineIndexer.cs | 33 --------------- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- 7 files changed, 49 insertions(+), 38 deletions(-) create mode 100644 src/Umbraco.Examine/HtmlValueType.cs diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 9c686c4353..03a371204c 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -540,7 +540,7 @@ namespace Umbraco.Core public static string StripHtml(this string text) { const string pattern = @"<(.|\n)*?>"; - return Regex.Replace(text, pattern, String.Empty); + return Regex.Replace(text, pattern, string.Empty, RegexOptions.Compiled); } /// diff --git a/src/Umbraco.Examine/HtmlValueType.cs b/src/Umbraco.Examine/HtmlValueType.cs new file mode 100644 index 0000000000..f55023f053 --- /dev/null +++ b/src/Umbraco.Examine/HtmlValueType.cs @@ -0,0 +1,43 @@ +using Lucene.Net.Documents; +using Umbraco.Core; +using Examine.LuceneEngine.Indexing; +using Umbraco.Core.Xml; + +namespace Umbraco.Examine +{ + /// + /// Strips HTML symbols from the text + /// + public class HtmlValueType : FullTextType + { + private readonly bool _storeRawValue; + + public HtmlValueType(string fieldName, bool storeRawValue) : base(fieldName, false) + { + _storeRawValue = storeRawValue; + } + + protected override void AddSingleValue(Document doc, object value) + { + if (TryConvert(value, out var str)) + { + if (XmlHelper.CouldItBeXml(str)) + { + base.AddSingleValue(doc, str.StripHtml()); + + if (_storeRawValue) + { + doc.Add(new Field(UmbracoExamineIndexer.RawFieldPrefix + FieldName, str, + Field.Store.YES, + Field.Index.NO, + Field.TermVector.NO)); + } + } + else + base.AddSingleValue(doc, str); + } + else + base.AddSingleValue(doc, str); + } + } +} diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index db3570d380..8065cc799c 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + @@ -59,6 +59,7 @@ + diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index fd56ac7fc4..1fb3b0c3a3 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -19,39 +19,6 @@ using Directory = Lucene.Net.Store.Directory; namespace Umbraco.Examine { - public class HtmlValueType : IndexValueTypeBase - { - public HtmlValueType(string fieldName, bool store = true) : base(fieldName, store) - { - } - - protected override void AddSingleValue(Document doc, object value) - { - //TODO: Make this happen so we can properly analyze/tokenize html, maybe we only need an analyzer though - - throw new NotImplementedException(); - } - } - - public class HtmlAnalyzer : Analyzer - { - public override TokenStream TokenStream(string fieldName, TextReader reader) - { - return new LowerCaseFilter( //case insensitive - new EmailAddressTokenizer(reader)); //email tokenizer - } - - /// - /// Used for email addresses - /// - public class HtmlTokenizer : Tokenizer - { - public override bool IncrementToken() - { - throw new NotImplementedException(); - } - } - } /// /// An abstract provider containing the basic functionality to be able to query against diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 84ddda4335..c68be3a26d 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.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 90543b6faf..18d66a4111 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,7 +88,7 @@ - + diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 9c98f81894..2f8613df73 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -62,7 +62,7 @@ - + 2.6.2.25 From 9a9fcab0e9c10c31fbcfe17ac86f590bd09631d2 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 26 Nov 2018 13:33:15 +1100 Subject: [PATCH 12/44] New IValueIndexer and implements it for grid, new UmbracoValueSetBuilder to create the value sets, no more event handling for grid index data --- .../PropertyEditors/DataEditor.cs | 5 + .../PropertyEditors/DefaultValueIndexer.cs | 16 ++ .../PropertyEditors/IDataEditor.cs | 5 +- .../PropertyEditors/IValueIndexer.cs | 14 ++ src/Umbraco.Core/Umbraco.Core.csproj | 2 + src/Umbraco.Examine/Umbraco.Examine.csproj | 1 + src/Umbraco.Examine/UmbracoContentIndexer.cs | 161 +------------- .../UmbracoContentValueSetValidator.cs | 1 + src/Umbraco.Examine/UmbracoExamineIndexer.cs | 24 --- src/Umbraco.Examine/UmbracoMemberIndexer.cs | 54 +---- src/Umbraco.Examine/UmbracoValueSetBuilder.cs | 202 ++++++++++++++++++ .../UmbracoExamine/IndexInitializer.cs | 4 +- .../PropertyEditors/GridPropertyEditor.cs | 88 +------- .../PropertyEditors/GridValueIndexer.cs | 91 ++++++++ .../NestedContentPropertyEditor.cs | 2 + src/Umbraco.Web/Search/ExamineComponent.cs | 33 +-- .../Search/UmbracoIndexesBuilder.cs | 16 +- src/Umbraco.Web/Umbraco.Web.csproj | 1 + 18 files changed, 372 insertions(+), 348 deletions(-) create mode 100644 src/Umbraco.Core/PropertyEditors/DefaultValueIndexer.cs create mode 100644 src/Umbraco.Core/PropertyEditors/IValueIndexer.cs create mode 100644 src/Umbraco.Examine/UmbracoValueSetBuilder.cs create mode 100644 src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs diff --git a/src/Umbraco.Core/PropertyEditors/DataEditor.cs b/src/Umbraco.Core/PropertyEditors/DataEditor.cs index 2d0b34a849..f3a9c7f4c6 100644 --- a/src/Umbraco.Core/PropertyEditors/DataEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataEditor.cs @@ -153,6 +153,11 @@ namespace Umbraco.Core.PropertyEditors set => _defaultConfiguration = value; } + /// + /// Returns the value indexer for this editor + /// + public virtual IValueIndexer ValueIndexer => new DefaultValueIndexer(); + /// /// Creates a value editor instance. /// diff --git a/src/Umbraco.Core/PropertyEditors/DefaultValueIndexer.cs b/src/Umbraco.Core/PropertyEditors/DefaultValueIndexer.cs new file mode 100644 index 0000000000..d46f15771c --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/DefaultValueIndexer.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Returns a single field to index containing the property value + /// + public class DefaultValueIndexer : IValueIndexer + { + public IEnumerable> GetIndexValues(Property property, string culture) + { + yield return new KeyValuePair(property.Alias, new[] { property.GetValue(culture) }); + } + } +} diff --git a/src/Umbraco.Core/PropertyEditors/IDataEditor.cs b/src/Umbraco.Core/PropertyEditors/IDataEditor.cs index 8137101826..f967f6f269 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataEditor.cs @@ -3,6 +3,7 @@ using Umbraco.Core.Composing; namespace Umbraco.Core.PropertyEditors { + /// /// Represents a data editor. /// @@ -65,5 +66,7 @@ namespace Umbraco.Core.PropertyEditors /// Is expected to throw if the editor does not support being configured, e.g. for most parameter editors. /// IConfigurationEditor GetConfigurationEditor(); + + IValueIndexer ValueIndexer { get; } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/PropertyEditors/IValueIndexer.cs b/src/Umbraco.Core/PropertyEditors/IValueIndexer.cs new file mode 100644 index 0000000000..fdf3a9234f --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/IValueIndexer.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Returns indexable data for the property + /// + public interface IValueIndexer + { + //fixme: What about segments and whether we want the published value? + IEnumerable> GetIndexValues(Property property, string culture); + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 23b90aaf3c..c54f14423b 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -439,6 +439,7 @@ + @@ -447,6 +448,7 @@ + diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 8065cc799c..50561f7d8f 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -74,6 +74,7 @@ Properties\SolutionInfo.cs + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 541ecd1b2b..ae0c93a6ae 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -22,6 +22,7 @@ using Umbraco.Examine.Config; using IContentService = Umbraco.Core.Services.IContentService; using IMediaService = Umbraco.Core.Services.IMediaService; using Examine.LuceneEngine; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Examine { @@ -30,12 +31,11 @@ namespace Umbraco.Examine /// public class UmbracoContentIndexer : UmbracoExamineIndexer { + protected UmbracoValueSetBuilder ValueSetBuilder { get; } protected IContentService ContentService { get; } protected IMediaService MediaService { get; } - protected IUserService UserService { get; } protected ILocalizationService LanguageService { get; } - private readonly IEnumerable _urlSegmentProviders; private int? _parentId; #region Constructors @@ -48,10 +48,9 @@ namespace Umbraco.Examine { ContentService = Current.Services.ContentService; MediaService = Current.Services.MediaService; - UserService = Current.Services.UserService; LanguageService = Current.Services.LocalizationService; - _urlSegmentProviders = Current.UrlSegmentProviders; + ValueSetBuilder = new UmbracoValueSetBuilder(Current.PropertyEditors, Current.UrlSegmentProviders, Current.Services.UserService); InitializeQueries(Current.SqlContext); } @@ -66,9 +65,7 @@ namespace Umbraco.Examine /// /// /// - /// /// - /// /// /// /// @@ -78,12 +75,11 @@ namespace Umbraco.Examine Directory luceneDirectory, Analyzer defaultAnalyzer, ProfilingLogger profilingLogger, + UmbracoValueSetBuilder valueSetBuilder, IContentService contentService, IMediaService mediaService, - IUserService userService, ILocalizationService languageService, ISqlContext sqlContext, - IEnumerable urlSegmentProviders, IValueSetValidator validator, UmbracoContentIndexerOptions options, IReadOnlyDictionary> indexValueTypes = null) @@ -95,12 +91,10 @@ namespace Umbraco.Examine SupportProtectedContent = options.SupportProtectedContent; SupportUnpublishedContent = options.SupportUnpublishedContent; ParentId = options.ParentId; - + ValueSetBuilder = valueSetBuilder ?? throw new ArgumentNullException(nameof(valueSetBuilder)); ContentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); MediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); - UserService = userService ?? throw new ArgumentNullException(nameof(userService)); LanguageService = languageService ?? throw new ArgumentNullException(nameof(languageService)); - _urlSegmentProviders = urlSegmentProviders ?? throw new ArgumentNullException(nameof(urlSegmentProviders)); InitializeQueries(sqlContext); } @@ -292,7 +286,7 @@ namespace Umbraco.Examine content = descendants.ToArray(); } - IndexItems(GetValueSets(_urlSegmentProviders, UserService, content)); + IndexItems(ValueSetBuilder.GetValueSets(content)); pageIndex++; } while (content.Length == pageSize); @@ -327,7 +321,7 @@ namespace Umbraco.Examine media = descendants.ToArray(); } - IndexItems(GetValueSets(_urlSegmentProviders, UserService, media)); + IndexItems(ValueSetBuilder.GetValueSets(media)); pageIndex++; } while (media.Length == pageSize); @@ -336,146 +330,7 @@ namespace Umbraco.Examine } } - /// - /// Creates a collection of for a collection - /// - /// - /// - /// - /// Yield returns - public static IEnumerable GetValueSets(IEnumerable urlSegmentProviders, IUserService userService, params IContent[] content) - { - //TODO: There is a lot of boxing going on here and ultimately all values will be boxed by Lucene anyways - // but I wonder if there's a way to reduce the boxing that we have to do or if it will matter in the end since - // Lucene will do it no matter what? One idea was to create a `FieldValue` struct which would contain `object`, `object[]`, `ValueType` and `ValueType[]` - // references and then each array is an array of `FieldValue[]` and values are assigned accordingly. Not sure if it will make a difference or not. - - foreach (var c in content) - { - var isVariant = c.ContentType.VariesByCulture(); - - var urlValue = c.GetUrlSegment(urlSegmentProviders); //Always add invariant urlName - var values = new Dictionary - { - {"icon", new [] {c.ContentType.Icon}}, - {PublishedFieldName, new object[] {c.Published ? 1 : 0}}, //Always add invariant published value - {"id", new object[] {c.Id}}, - {"key", new object[] {c.Key}}, - {"parentID", new object[] {c.Level > 1 ? c.ParentId : -1}}, - {"level", new object[] {c.Level}}, - {"creatorID", new object[] {c.CreatorId}}, - {"sortOrder", new object[] {c.SortOrder}}, - {"createDate", new object[] {c.CreateDate}}, //Always add invariant createDate - {"updateDate", new object[] {c.UpdateDate}}, //Always add invariant updateDate - {"nodeName", new object[] {c.Name}}, //Always add invariant nodeName - {"urlName", new object[] {urlValue}}, //Always add invariant urlName - {"path", new object[] {c.Path}}, - {"nodeType", new object[] {c.ContentType.Id}}, - {"creatorName", new object[] {c.GetCreatorProfile(userService)?.Name ?? "??"}}, - {"writerName", new object[] {c.GetWriterProfile(userService)?.Name ?? "??"}}, - {"writerID", new object[] {c.WriterId}}, - {"template", new object[] {c.Template?.Id ?? 0}}, - {$"{SpecialFieldPrefix}VariesByCulture", new object[] {0}}, - }; - - if (isVariant) - { - values[$"{SpecialFieldPrefix}VariesByCulture"] = new object[] { 1 }; - - foreach(var culture in c.AvailableCultures) - { - var variantUrl = c.GetUrlSegment(urlSegmentProviders, culture); - var lowerCulture = culture.ToLowerInvariant(); - values[$"urlName_{lowerCulture}"] = new object[] { variantUrl }; - values[$"nodeName_{lowerCulture}"] = new object[] { c.GetCultureName(culture) }; - values[$"{PublishedFieldName}_{lowerCulture}"] = new object[] { c.IsCulturePublished(culture) ? 1 : 0 }; - values[$"updateDate_{lowerCulture}"] = new object[] { c.GetUpdateDate(culture) }; - } - } - - foreach (var property in c.Properties) - { - if (!property.PropertyType.VariesByCulture()) - { - AddPropertyValue(null, c, property, values); - } - else - { - foreach (var culture in c.AvailableCultures) - AddPropertyValue(culture.ToLowerInvariant(), c, property, values); - } - } - - var vs = new ValueSet(c.Id.ToInvariantString(), IndexTypes.Content, c.ContentType.Alias, values); - - yield return vs; - } - } - - private static void AddPropertyValue(string culture, IContent c, Property property, IDictionary values) - { - var val = property.GetValue(culture); - var cultureSuffix = culture == null ? string.Empty : "_" + culture; - switch (val) - { - //only add the value if its not null or empty (we'll check for string explicitly here too) - case null: - return; - case string strVal: - if (strVal.IsNullOrWhiteSpace()) return; - values.Add($"{property.Alias}{cultureSuffix}", new[] { val }); - break; - default: - values.Add($"{property.Alias}{cultureSuffix}", new[] { val }); - break; - } - } - - public static IEnumerable GetValueSets(IEnumerable urlSegmentProviders, IUserService userService, params IMedia[] media) - { - foreach (var m in media) - { - var urlValue = m.GetUrlSegment(urlSegmentProviders); - var values = new Dictionary - { - {"icon", new object[] {m.ContentType.Icon}}, - {"id", new object[] {m.Id}}, - {"key", new object[] {m.Key}}, - {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, - {"level", new object[] {m.Level}}, - {"creatorID", new object[] {m.CreatorId}}, - {"sortOrder", new object[] {m.SortOrder}}, - {"createDate", new object[] {m.CreateDate}}, - {"updateDate", new object[] {m.UpdateDate}}, - {"nodeName", new object[] {m.Name}}, - {"urlName", new object[] {urlValue}}, - {"path", new object[] {m.Path}}, - {"nodeType", new object[] {m.ContentType.Id}}, - {"creatorName", new object[] {m.GetCreatorProfile(userService).Name}} - }; - - foreach (var property in m.Properties) - { - //only add the value if its not null or empty (we'll check for string explicitly here too) - var val = property.GetValue(); - switch (val) - { - case null: - continue; - case string strVal when strVal.IsNullOrWhiteSpace() == false: - values.Add(property.Alias, new[] { val }); - break; - default: - values.Add(property.Alias, new[] { val }); - break; - } - } - - var vs = new ValueSet(m.Id.ToInvariantString(), IndexTypes.Media, m.ContentType.Alias, values); - - yield return vs; - } - } + #endregion diff --git a/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs b/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs index fb5c26d3c1..1881511ee3 100644 --- a/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs +++ b/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs @@ -7,6 +7,7 @@ using Umbraco.Core.Services; namespace Umbraco.Examine { + /// /// Used to validate a ValueSet for content - based on permissions, parent id, etc.... /// diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index 1fb3b0c3a3..3ede45c60a 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -419,30 +419,6 @@ namespace Umbraco.Examine e.IndexItem.ValueSet.Set(IndexPathFieldName, path); } - //strip html of all users fields if we detect it has HTML in it. - //if that is the case, we'll create a duplicate 'raw' copy of it so that we can return - //the value of the field 'as-is'. - foreach (var value in e.IndexItem.ValueSet.Values.ToList()) //ToList here to make a diff collection else we'll get collection modified errors - { - if (value.Value == null) continue; - - if (value.Value.Count > 0) - { - if (value.Value.First() is string str) - { - if (XmlHelper.CouldItBeXml(str)) - { - //First save the raw value to a raw field, we will change the policy of this field by detecting the prefix later - e.IndexItem.ValueSet.Values[string.Concat(RawFieldPrefix, value.Key)] = new List { str }; - - //now replace the original value with the stripped html - //TODO: This should be done with an analzer?! - e.IndexItem.ValueSet.Values[value.Key] = new List { str.StripHtml() }; - } - } - } - } - //icon if (e.IndexItem.ValueSet.Values.TryGetValue("icon", out var icon) && e.IndexItem.ValueSet.Values.ContainsKey(IconFieldName) == false) { diff --git a/src/Umbraco.Examine/UmbracoMemberIndexer.cs b/src/Umbraco.Examine/UmbracoMemberIndexer.cs index 62d9a7a1d0..f78348a255 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndexer.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndexer.cs @@ -23,6 +23,7 @@ namespace Umbraco.Examine /// public class UmbracoMemberIndexer : UmbracoExamineIndexer { + private readonly UmbracoValueSetBuilder _valueSetBuilder; private readonly IMemberService _memberService; /// @@ -32,6 +33,7 @@ namespace Umbraco.Examine public UmbracoMemberIndexer() { _memberService = Current.Services.MemberService; + _valueSetBuilder = new UmbracoValueSetBuilder(Current.PropertyEditors, null, null); } /// @@ -50,10 +52,12 @@ namespace Umbraco.Examine Directory luceneDirectory, Analyzer analyzer, ProfilingLogger profilingLogger, + UmbracoValueSetBuilder valueSetBuilder, IMemberService memberService, IValueSetValidator validator = null) : base(name, fieldDefinitions, luceneDirectory, analyzer, profilingLogger, validator) { + _valueSetBuilder = valueSetBuilder ?? throw new ArgumentNullException(nameof(valueSetBuilder)); _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); } @@ -99,7 +103,7 @@ namespace Umbraco.Examine { members = _memberService.GetAll(pageIndex, pageSize, out _, "LoginName", Direction.Ascending, true, null, nodeType).ToArray(); - IndexItems(GetValueSets(members)); + IndexItems(_valueSetBuilder.GetValueSets(members)); pageIndex++; } while (members.Length == pageSize); @@ -112,58 +116,14 @@ namespace Umbraco.Examine { members = _memberService.GetAll(pageIndex, pageSize, out _).ToArray(); - IndexItems(GetValueSets(members)); + IndexItems(_valueSetBuilder.GetValueSets(members)); pageIndex++; } while (members.Length == pageSize); } } - public static IEnumerable GetValueSets(params IMember[] members) - { - foreach (var m in members) - { - var values = new Dictionary - { - {"icon", new object[] {m.ContentType.Icon}}, - {"id", new object[] {m.Id}}, - {"key", new object[] {m.Key}}, - {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, - {"level", new object[] {m.Level}}, - {"creatorID", new object[] {m.CreatorId}}, - {"sortOrder", new object[] {m.SortOrder}}, - {"createDate", new object[] {m.CreateDate}}, - {"updateDate", new object[] {m.UpdateDate}}, - {"nodeName", new object[] {m.Name}}, - {"path", new object[] {m.Path}}, - {"nodeType", new object[] {m.ContentType.Id}}, - {"loginName", new object[] {m.Username}}, - {"email", new object[] {m.Email}}, - }; - - foreach (var property in m.Properties) - { - //only add the value if its not null or empty (we'll check for string explicitly here too) - var val = property.GetValue(); - switch (val) - { - case null: - continue; - case string strVal: - if (strVal.IsNullOrWhiteSpace()) continue; - values.Add(property.Alias, new[] { val }); - break; - default: - values.Add(property.Alias, new[] { val }); - break; - } - } - - var vs = new ValueSet(m.Id.ToInvariantString(), IndexTypes.Content, m.ContentType.Alias, values); - - yield return vs; - } - } + /// /// Ensure some custom values are added to the index diff --git a/src/Umbraco.Examine/UmbracoValueSetBuilder.cs b/src/Umbraco.Examine/UmbracoValueSetBuilder.cs new file mode 100644 index 0000000000..7bee1ecf3d --- /dev/null +++ b/src/Umbraco.Examine/UmbracoValueSetBuilder.cs @@ -0,0 +1,202 @@ +using Examine; +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; + +namespace Umbraco.Examine +{ + public class UmbracoValueSetBuilder + { + private readonly PropertyEditorCollection _propertyEditors; + private readonly IEnumerable _urlSegmentProviders; + private readonly IUserService _userService; + + public UmbracoValueSetBuilder(PropertyEditorCollection propertyEditors, + IEnumerable urlSegmentProviders, + IUserService userService) + { + _propertyEditors = propertyEditors; + _urlSegmentProviders = urlSegmentProviders; + _userService = userService; + } + + /// + /// Creates a collection of for a collection + /// + /// + /// + /// + /// Yield returns + public IEnumerable GetValueSets(params IContent[] content) + { + //TODO: There is a lot of boxing going on here and ultimately all values will be boxed by Lucene anyways + // but I wonder if there's a way to reduce the boxing that we have to do or if it will matter in the end since + // Lucene will do it no matter what? One idea was to create a `FieldValue` struct which would contain `object`, `object[]`, `ValueType` and `ValueType[]` + // references and then each array is an array of `FieldValue[]` and values are assigned accordingly. Not sure if it will make a difference or not. + + foreach (var c in content) + { + var isVariant = c.ContentType.VariesByCulture(); + + var urlValue = c.GetUrlSegment(_urlSegmentProviders); //Always add invariant urlName + var values = new Dictionary + { + {"icon", new [] {c.ContentType.Icon}}, + {UmbracoExamineIndexer.PublishedFieldName, new object[] {c.Published ? 1 : 0}}, //Always add invariant published value + {"id", new object[] {c.Id}}, + {"key", new object[] {c.Key}}, + {"parentID", new object[] {c.Level > 1 ? c.ParentId : -1}}, + {"level", new object[] {c.Level}}, + {"creatorID", new object[] {c.CreatorId}}, + {"sortOrder", new object[] {c.SortOrder}}, + {"createDate", new object[] {c.CreateDate}}, //Always add invariant createDate + {"updateDate", new object[] {c.UpdateDate}}, //Always add invariant updateDate + {"nodeName", new object[] {c.Name}}, //Always add invariant nodeName + {"urlName", new object[] {urlValue}}, //Always add invariant urlName + {"path", new object[] {c.Path}}, + {"nodeType", new object[] {c.ContentType.Id}}, + {"creatorName", new object[] {c.GetCreatorProfile(_userService)?.Name ?? "??"}}, + {"writerName", new object[] {c.GetWriterProfile(_userService)?.Name ?? "??"}}, + {"writerID", new object[] {c.WriterId}}, + {"template", new object[] {c.Template?.Id ?? 0}}, + {$"{UmbracoExamineIndexer.SpecialFieldPrefix}VariesByCulture", new object[] {0}}, + }; + + if (isVariant) + { + values[$"{UmbracoExamineIndexer.SpecialFieldPrefix}VariesByCulture"] = new object[] { 1 }; + + foreach (var culture in c.AvailableCultures) + { + var variantUrl = c.GetUrlSegment(_urlSegmentProviders, culture); + var lowerCulture = culture.ToLowerInvariant(); + values[$"urlName_{lowerCulture}"] = new object[] { variantUrl }; + values[$"nodeName_{lowerCulture}"] = new object[] { c.GetCultureName(culture) }; + values[$"{UmbracoExamineIndexer.PublishedFieldName}_{lowerCulture}"] = new object[] { c.IsCulturePublished(culture) ? 1 : 0 }; + values[$"updateDate_{lowerCulture}"] = new object[] { c.GetUpdateDate(culture) }; + } + } + + foreach (var property in c.Properties) + { + if (!property.PropertyType.VariesByCulture()) + { + AddPropertyValue(null, property, values); + } + else + { + foreach (var culture in c.AvailableCultures) + AddPropertyValue(culture.ToLowerInvariant(), property, values); + } + } + + var vs = new ValueSet(c.Id.ToInvariantString(), IndexTypes.Content, c.ContentType.Alias, values); + + yield return vs; + } + } + + public IEnumerable GetValueSets(params IMedia[] media) + { + foreach (var m in media) + { + var urlValue = m.GetUrlSegment(_urlSegmentProviders); + var values = new Dictionary + { + {"icon", new object[] {m.ContentType.Icon}}, + {"id", new object[] {m.Id}}, + {"key", new object[] {m.Key}}, + {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, + {"level", new object[] {m.Level}}, + {"creatorID", new object[] {m.CreatorId}}, + {"sortOrder", new object[] {m.SortOrder}}, + {"createDate", new object[] {m.CreateDate}}, + {"updateDate", new object[] {m.UpdateDate}}, + {"nodeName", new object[] {m.Name}}, + {"urlName", new object[] {urlValue}}, + {"path", new object[] {m.Path}}, + {"nodeType", new object[] {m.ContentType.Id}}, + {"creatorName", new object[] {m.GetCreatorProfile(_userService).Name}} + }; + + foreach (var property in m.Properties) + { + AddPropertyValue(null, property, values); + } + + var vs = new ValueSet(m.Id.ToInvariantString(), IndexTypes.Media, m.ContentType.Alias, values); + + yield return vs; + } + } + + public IEnumerable GetValueSets(params IMember[] members) + { + foreach (var m in members) + { + var values = new Dictionary + { + {"icon", new object[] {m.ContentType.Icon}}, + {"id", new object[] {m.Id}}, + {"key", new object[] {m.Key}}, + {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, + {"level", new object[] {m.Level}}, + {"creatorID", new object[] {m.CreatorId}}, + {"sortOrder", new object[] {m.SortOrder}}, + {"createDate", new object[] {m.CreateDate}}, + {"updateDate", new object[] {m.UpdateDate}}, + {"nodeName", new object[] {m.Name}}, + {"path", new object[] {m.Path}}, + {"nodeType", new object[] {m.ContentType.Id}}, + {"loginName", new object[] {m.Username}}, + {"email", new object[] {m.Email}}, + }; + + foreach (var property in m.Properties) + { + AddPropertyValue(null, property, values); + } + + var vs = new ValueSet(m.Id.ToInvariantString(), IndexTypes.Content, m.ContentType.Alias, values); + + yield return vs; + } + } + + private void AddPropertyValue(string culture, Property property, IDictionary values) + { + var editor = _propertyEditors[property.Alias]; + if (editor == null) return; + + var indexVals = editor.ValueIndexer.GetIndexValues(property, culture); + foreach(var keyVal in indexVals) + { + if (keyVal.Key.IsNullOrWhiteSpace()) continue; + + var cultureSuffix = culture == null ? string.Empty : "_" + culture; + + foreach(var val in keyVal.Value) + { + switch (val) + { + //only add the value if its not null or empty (we'll check for string explicitly here too) + case null: + continue; + case string strVal: + if (strVal.IsNullOrWhiteSpace()) return; + values.Add($"{keyVal.Key}{cultureSuffix}", new[] { val }); + break; + default: + values.Add($"{keyVal.Key}{cultureSuffix}", new[] { val }); + break; + } + } + } + + + } + } +} diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 0f69563dd6..2e24b1ee41 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -183,12 +183,12 @@ namespace Umbraco.Tests.UmbracoExamine luceneDir, analyzer, profilingLogger, + //fixme: need a property editor collection here + new UmbracoValueSetBuilder(null, new[] { new DefaultUrlSegmentProvider() }, userService), contentService, mediaService, - userService, languageService, sqlContext, - new[] {new DefaultUrlSegmentProvider()}, new UmbracoContentValueSetValidator(options, Mock.Of()), options); diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index 9b5443c649..1002261ca4 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -1,15 +1,9 @@ -using System; -using System.Linq; -using System.Text; +using System.Linq; using Umbraco.Core.Logging; using Examine; using Lucene.Net.Documents; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Umbraco.Core; using Umbraco.Core.PropertyEditors; -using Umbraco.Core.Xml; -using Umbraco.Examine; namespace Umbraco.Web.PropertyEditors { @@ -25,85 +19,7 @@ namespace Umbraco.Web.PropertyEditors : base(logger) { } - //TODO: Change this to use a native way of indexing data: https://github.com/umbraco/Umbraco-CMS/issues/3531 - internal void DocumentWriting(object sender, Examine.LuceneEngine.DocumentWritingEventArgs e) - { - foreach (var value in e.ValueSet.Values) - { - //if there is a value, it's a string and it's detected as json - if (value.Value.Count > 0 && value.Value[0] != null && (value.Value[0] is string firstVal) && firstVal.DetectIsJson()) - { - try - { - //TODO: We should deserialize this to Umbraco.Core.Models.GridValue instead of doing the below - var json = JsonConvert.DeserializeObject(firstVal); - - //check if this is formatted for grid json - if (json.HasValues && json.TryGetValue("name", out _) && json.TryGetValue("sections", out _)) - { - //get all values and put them into a single field (using JsonPath) - var sb = new StringBuilder(); - foreach (var row in json.SelectTokens("$.sections[*].rows[*]")) - { - var rowName = row["name"].Value(); - var areaVals = row.SelectTokens("$.areas[*].controls[*].value"); - - foreach (var areaVal in areaVals) - { - //TODO: If it's not a string, then it's a json formatted value - - // we cannot really index this in a smart way since it could be 'anything' - if (areaVal.Type == JTokenType.String) - { - var str = areaVal.Value(); - str = XmlHelper.CouldItBeXml(str) ? str.StripHtml() : str; - sb.Append(str); - sb.Append(" "); - - //add the row name as an individual field - e.Document.Add( - new Field( - $"{value.Key}.{rowName}", str, Field.Store.YES, Field.Index.ANALYZED)); - } - - } - } - - if (sb.Length > 0) - { - //First save the raw value to a raw field - e.Document.Add( - new Field( - $"{UmbracoExamineIndexer.RawFieldPrefix}{value.Key}", - firstVal, Field.Store.YES, Field.Index.NO, Field.TermVector.NO)); - - //now replace the original value with the combined/cleaned value - e.Document.RemoveField(value.Key); - e.Document.Add( - new Field( - value.Key, - sb.ToString(), Field.Store.YES, Field.Index.ANALYZED)); - } - } - } - catch (InvalidCastException) - { - //swallow...on purpose, there's a chance that this isn't the json format we are looking for - // and we don't want that to affect the website. - } - catch (JsonException) - { - //swallow...on purpose, there's a chance that this isn't json and we don't want that to affect - // the website. - } - catch (ArgumentException) - { - //swallow on purpose to prevent this error: - // Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject. - } - } - - } - } + public override IValueIndexer ValueIndexer => new GridValueIndexer(); /// /// Overridden to ensure that the value is validated diff --git a/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs b/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs new file mode 100644 index 0000000000..5160b09743 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs @@ -0,0 +1,91 @@ +using System; +using System.Text; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Umbraco.Core; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Xml; +using Umbraco.Examine; + +namespace Umbraco.Web.PropertyEditors +{ + using System.Collections.Generic; + using Umbraco.Core.Models; + + /// + /// Parses the grid value into indexable values + /// + public class GridValueIndexer : IValueIndexer + { + public IEnumerable> GetIndexValues(Property property, string culture) + { + var result = new Dictionary(); + + var val = property.GetValue(culture); + + //if there is a value, it's a string and it's detected as json + if (val is string rawVal && rawVal.DetectIsJson()) + { + try + { + //TODO: We should deserialize this to Umbraco.Core.Models.GridValue instead of doing the below + var json = JsonConvert.DeserializeObject(rawVal); + + //check if this is formatted for grid json + if (json.HasValues && json.TryGetValue("name", out _) && json.TryGetValue("sections", out _)) + { + //get all values and put them into a single field (using JsonPath) + var sb = new StringBuilder(); + foreach (var row in json.SelectTokens("$.sections[*].rows[*]")) + { + var rowName = row["name"].Value(); + var areaVals = row.SelectTokens("$.areas[*].controls[*].value"); + + foreach (var areaVal in areaVals) + { + //TODO: If it's not a string, then it's a json formatted value - + // we cannot really index this in a smart way since it could be 'anything' + if (areaVal.Type == JTokenType.String) + { + var str = areaVal.Value(); + str = XmlHelper.CouldItBeXml(str) ? str.StripHtml() : str; + sb.Append(str); + sb.Append(" "); + + //add the row name as an individual field + result.Add($"{property.Alias}.{rowName}", new[] { str }); + } + } + } + + if (sb.Length > 0) + { + //First save the raw value to a raw field + result.Add($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new[] { rawVal }); + + //index the property with the combined/cleaned value + result.Add(property.Alias, new[] { sb.ToString() }); + } + } + } + catch (InvalidCastException) + { + //swallow...on purpose, there's a chance that this isn't the json format we are looking for + // and we don't want that to affect the website. + } + catch (JsonException) + { + //swallow...on purpose, there's a chance that this isn't json and we don't want that to affect + // the website. + } + catch (ArgumentException) + { + //swallow on purpose to prevent this error: + // Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject. + } + } + + return result; + } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs index f7d886f637..c4978e62bc 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs @@ -42,6 +42,8 @@ namespace Umbraco.Web.PropertyEditors : Current.Services.ContentTypeService.Get(contentTypeAlias); } + //fixme: Need to add a custom IValueIndexer for this editor + #region Pre Value Editor protected override IConfigurationEditor CreateConfigurationEditor() => new NestedContentConfigurationEditor(); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index b355f6bd91..a4f67d40af 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -38,6 +38,7 @@ namespace Umbraco.Web.Search public sealed class ExamineComponent : UmbracoComponentBase, IUmbracoCoreComponent { private IExamineManager _examineManager; + private UmbracoValueSetBuilder _valueSetBuilder; private static bool _disableExamineIndexing = false; private static volatile bool _isConfigured = false; private static readonly object IsConfiguredLocker = new object(); @@ -59,12 +60,13 @@ namespace Umbraco.Web.Search composition.Container.RegisterSingleton(); } - internal void Initialize(IRuntimeState runtime, MainDom mainDom, PropertyEditorCollection propertyEditors, IExamineManager examineManager, ProfilingLogger profilingLogger, IScopeProvider scopeProvider, IUmbracoIndexesBuilder indexBuilder, ServiceContext services, IEnumerable urlSegmentProviders) + internal void Initialize(IRuntimeState runtime, MainDom mainDom, PropertyEditorCollection propertyEditors, IExamineManager examineManager, ProfilingLogger profilingLogger, IScopeProvider scopeProvider, IUmbracoIndexesBuilder indexBuilder, ServiceContext services, IEnumerable urlSegmentProviders, UmbracoValueSetBuilder valueSetBuilder) { _services = services; _scopeProvider = scopeProvider; _examineManager = examineManager; _urlSegmentProviders = urlSegmentProviders; + _valueSetBuilder = valueSetBuilder; //We want to manage Examine's appdomain shutdown sequence ourselves so first we'll disable Examine's default behavior //and then we'll use MainDom to control Examine's shutdown @@ -114,8 +116,6 @@ namespace Umbraco.Web.Search if (registeredIndexers == 0) return; - BindGridToExamine(profilingLogger.Logger, examineManager, propertyEditors); - // bind to distributed cache events - this ensures that this logic occurs on ALL servers // that are taking part in a load balanced environment. ContentCacheRefresher.CacheUpdated += ContentCacheRefresherUpdated; @@ -197,26 +197,7 @@ namespace Umbraco.Web.Search } } } - - //TODO: Change this to use a native way of indexing data: https://github.com/umbraco/Umbraco-CMS/issues/3531 - private static void BindGridToExamine(ILogger logger, IExamineManager examineManager, IEnumerable propertyEditors) - { - //bind the grid property editors - this is a hack until http://issues.umbraco.org/issue/U4-8437 - try - { - var grid = propertyEditors.OfType().FirstOrDefault(); - if (grid != null) - { - foreach (var i in examineManager.IndexProviders.Values.OfType()) - i.DocumentWriting += grid.DocumentWriting; - } - } - catch (Exception ex) - { - logger.Error(ex, "Failed to bind grid property editor."); - } - } - + #region Cache refresher updated event handlers private void MemberCacheRefresherUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs args) { @@ -698,7 +679,7 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, IContent content, bool? supportUnpublished) { - var valueSet = UmbracoContentIndexer.GetValueSets(examineComponent._urlSegmentProviders, examineComponent._services.UserService, content); + var valueSet = examineComponent._valueSetBuilder.GetValueSets(content); examineComponent._examineManager.IndexItems( valueSet.ToArray(), @@ -729,7 +710,7 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, IMedia media, bool isPublished) { - var valueSet = UmbracoContentIndexer.GetValueSets(examineComponent._urlSegmentProviders, examineComponent._services.UserService, media); + var valueSet = examineComponent._valueSetBuilder.GetValueSets(media); examineComponent._examineManager.IndexItems( valueSet.ToArray(), @@ -759,7 +740,7 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, IMember member) { - var valueSet = UmbracoMemberIndexer.GetValueSets(member); + var valueSet = examineComponent._valueSetBuilder.GetValueSets(member); examineComponent._examineManager.IndexItems( valueSet.ToArray(), diff --git a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs index 19dbc034a1..c3e940857b 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs @@ -25,35 +25,32 @@ namespace Umbraco.Web.Search //TODO: we should inject the different IValueSetValidator so devs can just register them instead of overriding this class? public UmbracoIndexesBuilder(ProfilingLogger profilingLogger, + UmbracoValueSetBuilder valueSetBuilder, IContentService contentService, IMediaService mediaService, - IUserService userService, ILocalizationService languageService, IPublicAccessService publicAccessService, IMemberService memberService, - ISqlContext sqlContext, - IEnumerable urlSegmentProviders) + ISqlContext sqlContext) { ProfilingLogger = profilingLogger ?? throw new System.ArgumentNullException(nameof(profilingLogger)); + ValueSetBuilder = valueSetBuilder ?? throw new System.ArgumentNullException(nameof(valueSetBuilder)); ContentService = contentService ?? throw new System.ArgumentNullException(nameof(contentService)); MediaService = mediaService ?? throw new System.ArgumentNullException(nameof(mediaService)); - UserService = userService ?? throw new System.ArgumentNullException(nameof(userService)); LanguageService = languageService ?? throw new System.ArgumentNullException(nameof(languageService)); PublicAccessService = publicAccessService ?? throw new System.ArgumentNullException(nameof(publicAccessService)); MemberService = memberService ?? throw new System.ArgumentNullException(nameof(memberService)); SqlContext = sqlContext ?? throw new System.ArgumentNullException(nameof(sqlContext)); - UrlSegmentProviders = urlSegmentProviders ?? throw new System.ArgumentNullException(nameof(urlSegmentProviders)); } protected ProfilingLogger ProfilingLogger { get; } + protected UmbracoValueSetBuilder ValueSetBuilder { get; } protected IContentService ContentService { get; } protected IMediaService MediaService { get; } - protected IUserService UserService { get; } protected ILocalizationService LanguageService { get; } protected IPublicAccessService PublicAccessService { get; } protected IMemberService MemberService { get; } protected ISqlContext SqlContext { get; } - protected IEnumerable UrlSegmentProviders { get; } public const string InternalIndexPath = "Internal"; public const string ExternalIndexPath = "External"; @@ -87,7 +84,8 @@ namespace Umbraco.Web.Search UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, GetFileSystemLuceneDirectory(name), analyzer, - ProfilingLogger, ContentService, MediaService, UserService, LanguageService, SqlContext, UrlSegmentProviders, + ProfilingLogger, ValueSetBuilder, + ContentService, MediaService, LanguageService, SqlContext, GetContentValueSetValidator(options), options); return index; @@ -102,7 +100,7 @@ namespace Umbraco.Web.Search UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, GetFileSystemLuceneDirectory(MembersIndexPath), new CultureInvariantWhitespaceAnalyzer(), - ProfilingLogger, MemberService, + ProfilingLogger, ValueSetBuilder, MemberService, GetMemberValueSetValidator()); return index; } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 2f8613df73..2bc6969969 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -159,6 +159,7 @@ + From 977d18e200f86b5281d1ef70bdadc0974a11d8d0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 26 Nov 2018 13:41:32 +1100 Subject: [PATCH 13/44] Adds IValueIndexer for the RTE --- src/Umbraco.Examine/HtmlValueType.cs | 43 ------------------- src/Umbraco.Examine/Umbraco.Examine.csproj | 1 - .../PropertyEditors/RichTextPropertyEditor.cs | 17 ++++++++ 3 files changed, 17 insertions(+), 44 deletions(-) delete mode 100644 src/Umbraco.Examine/HtmlValueType.cs diff --git a/src/Umbraco.Examine/HtmlValueType.cs b/src/Umbraco.Examine/HtmlValueType.cs deleted file mode 100644 index f55023f053..0000000000 --- a/src/Umbraco.Examine/HtmlValueType.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Lucene.Net.Documents; -using Umbraco.Core; -using Examine.LuceneEngine.Indexing; -using Umbraco.Core.Xml; - -namespace Umbraco.Examine -{ - /// - /// Strips HTML symbols from the text - /// - public class HtmlValueType : FullTextType - { - private readonly bool _storeRawValue; - - public HtmlValueType(string fieldName, bool storeRawValue) : base(fieldName, false) - { - _storeRawValue = storeRawValue; - } - - protected override void AddSingleValue(Document doc, object value) - { - if (TryConvert(value, out var str)) - { - if (XmlHelper.CouldItBeXml(str)) - { - base.AddSingleValue(doc, str.StripHtml()); - - if (_storeRawValue) - { - doc.Add(new Field(UmbracoExamineIndexer.RawFieldPrefix + FieldName, str, - Field.Store.YES, - Field.Index.NO, - Field.TermVector.NO)); - } - } - else - base.AddSingleValue(doc, str); - } - else - base.AddSingleValue(doc, str); - } - } -} diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 50561f7d8f..c7dad0cb15 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -59,7 +59,6 @@ - diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 0b091e0d47..2bd3b3e9e0 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -6,6 +6,7 @@ using Umbraco.Core.Macros; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Examine; using Umbraco.Web.Macros; namespace Umbraco.Web.PropertyEditors @@ -31,6 +32,7 @@ namespace Umbraco.Web.PropertyEditors protected override IConfigurationEditor CreateConfigurationEditor() => new RichTextConfigurationEditor(); + public override IValueIndexer ValueIndexer => new RichTextValueIndexer(); /// /// A custom value editor to ensure that macro syntax is parsed when being persisted and formatted correctly for display in the editor @@ -90,6 +92,21 @@ namespace Umbraco.Web.PropertyEditors return parsed; } } + + internal class RichTextValueIndexer : IValueIndexer + { + public IEnumerable> GetIndexValues(Property property, string culture) + { + var val = property.GetValue(culture); + + if (!(val is string strVal)) yield break; + + //index the stripped html values + yield return new KeyValuePair(property.Alias, new[] { strVal.StripHtml() }); + //store the raw value + yield return new KeyValuePair($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new[] { strVal }); + } + } } From 985572690f7c93c463210d85086273eae1da3131 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 26 Nov 2018 14:27:44 +1100 Subject: [PATCH 14/44] IValueSetBuilder implementations --- .../PropertyEditors/DefaultValueIndexer.cs | 4 +- .../PropertyEditors/IValueIndexer.cs | 2 +- src/Umbraco.Examine/BaseValueSetBuilder.cs | 51 ++++++++ ...etBuilder.cs => ContentValueSetBuilder.cs} | 110 +----------------- src/Umbraco.Examine/IValueSetBuilder.cs | 13 +++ src/Umbraco.Examine/MediaValueSetBuilder.cs | 60 ++++++++++ src/Umbraco.Examine/MemberValueSetBuilder.cs | 51 ++++++++ src/Umbraco.Examine/Umbraco.Examine.csproj | 6 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 16 ++- src/Umbraco.Examine/UmbracoMemberIndexer.cs | 6 +- .../UmbracoExamine/IndexInitializer.cs | 4 +- .../PropertyEditors/GridValueIndexer.cs | 4 +- .../PropertyEditors/RichTextPropertyEditor.cs | 4 +- src/Umbraco.Web/Search/ExamineComponent.cs | 28 ++++- .../Search/UmbracoIndexesBuilder.cs | 17 ++- 15 files changed, 243 insertions(+), 133 deletions(-) create mode 100644 src/Umbraco.Examine/BaseValueSetBuilder.cs rename src/Umbraco.Examine/{UmbracoValueSetBuilder.cs => ContentValueSetBuilder.cs} (52%) create mode 100644 src/Umbraco.Examine/IValueSetBuilder.cs create mode 100644 src/Umbraco.Examine/MediaValueSetBuilder.cs create mode 100644 src/Umbraco.Examine/MemberValueSetBuilder.cs diff --git a/src/Umbraco.Core/PropertyEditors/DefaultValueIndexer.cs b/src/Umbraco.Core/PropertyEditors/DefaultValueIndexer.cs index d46f15771c..072847896f 100644 --- a/src/Umbraco.Core/PropertyEditors/DefaultValueIndexer.cs +++ b/src/Umbraco.Core/PropertyEditors/DefaultValueIndexer.cs @@ -8,9 +8,9 @@ namespace Umbraco.Core.PropertyEditors /// public class DefaultValueIndexer : IValueIndexer { - public IEnumerable> GetIndexValues(Property property, string culture) + public IEnumerable> GetIndexValues(Property property, string culture, string segment) { - yield return new KeyValuePair(property.Alias, new[] { property.GetValue(culture) }); + yield return new KeyValuePair(property.Alias, new[] { property.GetValue(culture, segment) }); } } } diff --git a/src/Umbraco.Core/PropertyEditors/IValueIndexer.cs b/src/Umbraco.Core/PropertyEditors/IValueIndexer.cs index fdf3a9234f..eb5538b928 100644 --- a/src/Umbraco.Core/PropertyEditors/IValueIndexer.cs +++ b/src/Umbraco.Core/PropertyEditors/IValueIndexer.cs @@ -9,6 +9,6 @@ namespace Umbraco.Core.PropertyEditors public interface IValueIndexer { //fixme: What about segments and whether we want the published value? - IEnumerable> GetIndexValues(Property property, string culture); + IEnumerable> GetIndexValues(Property property, string culture, string segment); } } diff --git a/src/Umbraco.Examine/BaseValueSetBuilder.cs b/src/Umbraco.Examine/BaseValueSetBuilder.cs new file mode 100644 index 0000000000..c1780a9b7d --- /dev/null +++ b/src/Umbraco.Examine/BaseValueSetBuilder.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Examine +{ + public abstract class BaseValueSetBuilder + { + private readonly PropertyEditorCollection _propertyEditors; + + public BaseValueSetBuilder(PropertyEditorCollection propertyEditors) + { + _propertyEditors = propertyEditors ?? throw new System.ArgumentNullException(nameof(propertyEditors)); + } + + protected void AddPropertyValue(Property property, string culture, string segment, IDictionary values) + { + var editor = _propertyEditors[property.Alias]; + if (editor == null) return; + + var indexVals = editor.ValueIndexer.GetIndexValues(property, culture, segment); + foreach (var keyVal in indexVals) + { + if (keyVal.Key.IsNullOrWhiteSpace()) continue; + + var cultureSuffix = culture == null ? string.Empty : "_" + culture; + + foreach (var val in keyVal.Value) + { + switch (val) + { + //only add the value if its not null or empty (we'll check for string explicitly here too) + case null: + continue; + case string strVal: + if (strVal.IsNullOrWhiteSpace()) return; + values.Add($"{keyVal.Key}{cultureSuffix}", new[] { val }); + break; + default: + values.Add($"{keyVal.Key}{cultureSuffix}", new[] { val }); + break; + } + } + } + + + } + } + +} diff --git a/src/Umbraco.Examine/UmbracoValueSetBuilder.cs b/src/Umbraco.Examine/ContentValueSetBuilder.cs similarity index 52% rename from src/Umbraco.Examine/UmbracoValueSetBuilder.cs rename to src/Umbraco.Examine/ContentValueSetBuilder.cs index 7bee1ecf3d..5dd856ee7a 100644 --- a/src/Umbraco.Examine/UmbracoValueSetBuilder.cs +++ b/src/Umbraco.Examine/ContentValueSetBuilder.cs @@ -8,15 +8,16 @@ using Umbraco.Core.Strings; namespace Umbraco.Examine { - public class UmbracoValueSetBuilder + public class ContentValueSetBuilder : BaseValueSetBuilder, IValueSetBuilder { private readonly PropertyEditorCollection _propertyEditors; private readonly IEnumerable _urlSegmentProviders; private readonly IUserService _userService; - public UmbracoValueSetBuilder(PropertyEditorCollection propertyEditors, + public ContentValueSetBuilder(PropertyEditorCollection propertyEditors, IEnumerable urlSegmentProviders, IUserService userService) + : base(propertyEditors) { _propertyEditors = propertyEditors; _urlSegmentProviders = urlSegmentProviders; @@ -84,12 +85,12 @@ namespace Umbraco.Examine { if (!property.PropertyType.VariesByCulture()) { - AddPropertyValue(null, property, values); + AddPropertyValue(property, null, null, values); } else { foreach (var culture in c.AvailableCultures) - AddPropertyValue(culture.ToLowerInvariant(), property, values); + AddPropertyValue(property, culture.ToLowerInvariant(), null, values); } } @@ -98,105 +99,6 @@ namespace Umbraco.Examine yield return vs; } } - - public IEnumerable GetValueSets(params IMedia[] media) - { - foreach (var m in media) - { - var urlValue = m.GetUrlSegment(_urlSegmentProviders); - var values = new Dictionary - { - {"icon", new object[] {m.ContentType.Icon}}, - {"id", new object[] {m.Id}}, - {"key", new object[] {m.Key}}, - {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, - {"level", new object[] {m.Level}}, - {"creatorID", new object[] {m.CreatorId}}, - {"sortOrder", new object[] {m.SortOrder}}, - {"createDate", new object[] {m.CreateDate}}, - {"updateDate", new object[] {m.UpdateDate}}, - {"nodeName", new object[] {m.Name}}, - {"urlName", new object[] {urlValue}}, - {"path", new object[] {m.Path}}, - {"nodeType", new object[] {m.ContentType.Id}}, - {"creatorName", new object[] {m.GetCreatorProfile(_userService).Name}} - }; - - foreach (var property in m.Properties) - { - AddPropertyValue(null, property, values); - } - - var vs = new ValueSet(m.Id.ToInvariantString(), IndexTypes.Media, m.ContentType.Alias, values); - - yield return vs; - } - } - - public IEnumerable GetValueSets(params IMember[] members) - { - foreach (var m in members) - { - var values = new Dictionary - { - {"icon", new object[] {m.ContentType.Icon}}, - {"id", new object[] {m.Id}}, - {"key", new object[] {m.Key}}, - {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, - {"level", new object[] {m.Level}}, - {"creatorID", new object[] {m.CreatorId}}, - {"sortOrder", new object[] {m.SortOrder}}, - {"createDate", new object[] {m.CreateDate}}, - {"updateDate", new object[] {m.UpdateDate}}, - {"nodeName", new object[] {m.Name}}, - {"path", new object[] {m.Path}}, - {"nodeType", new object[] {m.ContentType.Id}}, - {"loginName", new object[] {m.Username}}, - {"email", new object[] {m.Email}}, - }; - - foreach (var property in m.Properties) - { - AddPropertyValue(null, property, values); - } - - var vs = new ValueSet(m.Id.ToInvariantString(), IndexTypes.Content, m.ContentType.Alias, values); - - yield return vs; - } - } - - private void AddPropertyValue(string culture, Property property, IDictionary values) - { - var editor = _propertyEditors[property.Alias]; - if (editor == null) return; - - var indexVals = editor.ValueIndexer.GetIndexValues(property, culture); - foreach(var keyVal in indexVals) - { - if (keyVal.Key.IsNullOrWhiteSpace()) continue; - - var cultureSuffix = culture == null ? string.Empty : "_" + culture; - - foreach(var val in keyVal.Value) - { - switch (val) - { - //only add the value if its not null or empty (we'll check for string explicitly here too) - case null: - continue; - case string strVal: - if (strVal.IsNullOrWhiteSpace()) return; - values.Add($"{keyVal.Key}{cultureSuffix}", new[] { val }); - break; - default: - values.Add($"{keyVal.Key}{cultureSuffix}", new[] { val }); - break; - } - } - } - - - } } + } diff --git a/src/Umbraco.Examine/IValueSetBuilder.cs b/src/Umbraco.Examine/IValueSetBuilder.cs new file mode 100644 index 0000000000..6b1a372e09 --- /dev/null +++ b/src/Umbraco.Examine/IValueSetBuilder.cs @@ -0,0 +1,13 @@ +using Examine; +using System.Collections.Generic; +using Umbraco.Core.Models; + +namespace Umbraco.Examine +{ + public interface IValueSetBuilder + where TContent : IContentBase + { + IEnumerable GetValueSets(params TContent[] content); + } + +} diff --git a/src/Umbraco.Examine/MediaValueSetBuilder.cs b/src/Umbraco.Examine/MediaValueSetBuilder.cs new file mode 100644 index 0000000000..6758d29f53 --- /dev/null +++ b/src/Umbraco.Examine/MediaValueSetBuilder.cs @@ -0,0 +1,60 @@ +using Examine; +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; + +namespace Umbraco.Examine +{ + public class MediaValueSetBuilder : BaseValueSetBuilder, IValueSetBuilder + { + private readonly IEnumerable _urlSegmentProviders; + private readonly IUserService _userService; + + public MediaValueSetBuilder(PropertyEditorCollection propertyEditors, + IEnumerable urlSegmentProviders, + IUserService userService) + : base(propertyEditors) + { + _urlSegmentProviders = urlSegmentProviders; + _userService = userService; + } + + public IEnumerable GetValueSets(params IMedia[] media) + { + foreach (var m in media) + { + var urlValue = m.GetUrlSegment(_urlSegmentProviders); + var values = new Dictionary + { + {"icon", new object[] {m.ContentType.Icon}}, + {"id", new object[] {m.Id}}, + {"key", new object[] {m.Key}}, + {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, + {"level", new object[] {m.Level}}, + {"creatorID", new object[] {m.CreatorId}}, + {"sortOrder", new object[] {m.SortOrder}}, + {"createDate", new object[] {m.CreateDate}}, + {"updateDate", new object[] {m.UpdateDate}}, + {"nodeName", new object[] {m.Name}}, + {"urlName", new object[] {urlValue}}, + {"path", new object[] {m.Path}}, + {"nodeType", new object[] {m.ContentType.Id}}, + {"creatorName", new object[] {m.GetCreatorProfile(_userService).Name}} + }; + + foreach (var property in m.Properties) + { + AddPropertyValue(property, null, null, values); + } + + var vs = new ValueSet(m.Id.ToInvariantString(), IndexTypes.Media, m.ContentType.Alias, values); + + yield return vs; + } + } + } + +} diff --git a/src/Umbraco.Examine/MemberValueSetBuilder.cs b/src/Umbraco.Examine/MemberValueSetBuilder.cs new file mode 100644 index 0000000000..50f4ee6992 --- /dev/null +++ b/src/Umbraco.Examine/MemberValueSetBuilder.cs @@ -0,0 +1,51 @@ +using Examine; +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Examine +{ + + public class MemberValueSetBuilder : BaseValueSetBuilder, IValueSetBuilder + { + public MemberValueSetBuilder(PropertyEditorCollection propertyEditors) + : base(propertyEditors) + { + } + + public IEnumerable GetValueSets(params IMember[] members) + { + foreach (var m in members) + { + var values = new Dictionary + { + {"icon", new object[] {m.ContentType.Icon}}, + {"id", new object[] {m.Id}}, + {"key", new object[] {m.Key}}, + {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, + {"level", new object[] {m.Level}}, + {"creatorID", new object[] {m.CreatorId}}, + {"sortOrder", new object[] {m.SortOrder}}, + {"createDate", new object[] {m.CreateDate}}, + {"updateDate", new object[] {m.UpdateDate}}, + {"nodeName", new object[] {m.Name}}, + {"path", new object[] {m.Path}}, + {"nodeType", new object[] {m.ContentType.Id}}, + {"loginName", new object[] {m.Username}}, + {"email", new object[] {m.Email}}, + }; + + foreach (var property in m.Properties) + { + AddPropertyValue(property, null, null, values); + } + + var vs = new ValueSet(m.Id.ToInvariantString(), IndexTypes.Content, m.ContentType.Alias, values); + + yield return vs; + } + } + } + +} diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index c7dad0cb15..8a0b251b80 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -53,13 +53,18 @@ + + + + + @@ -73,7 +78,6 @@ Properties\SolutionInfo.cs - diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index ae0c93a6ae..06e174fdf1 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -31,7 +31,8 @@ namespace Umbraco.Examine /// public class UmbracoContentIndexer : UmbracoExamineIndexer { - protected UmbracoValueSetBuilder ValueSetBuilder { get; } + protected IValueSetBuilder MediaValueSetBuilder { get; } + protected IValueSetBuilder ContentValueSetBuilder { get; } protected IContentService ContentService { get; } protected IMediaService MediaService { get; } protected ILocalizationService LanguageService { get; } @@ -50,7 +51,8 @@ namespace Umbraco.Examine MediaService = Current.Services.MediaService; LanguageService = Current.Services.LocalizationService; - ValueSetBuilder = new UmbracoValueSetBuilder(Current.PropertyEditors, Current.UrlSegmentProviders, Current.Services.UserService); + ContentValueSetBuilder = new ContentValueSetBuilder(Current.PropertyEditors, Current.UrlSegmentProviders, Current.Services.UserService); + MediaValueSetBuilder = new MediaValueSetBuilder(Current.PropertyEditors, Current.UrlSegmentProviders, Current.Services.UserService); InitializeQueries(Current.SqlContext); } @@ -75,7 +77,8 @@ namespace Umbraco.Examine Directory luceneDirectory, Analyzer defaultAnalyzer, ProfilingLogger profilingLogger, - UmbracoValueSetBuilder valueSetBuilder, + IValueSetBuilder contentValueSetBuilder, + IValueSetBuilder mediaValueSetBuilder, IContentService contentService, IMediaService mediaService, ILocalizationService languageService, @@ -91,7 +94,8 @@ namespace Umbraco.Examine SupportProtectedContent = options.SupportProtectedContent; SupportUnpublishedContent = options.SupportUnpublishedContent; ParentId = options.ParentId; - ValueSetBuilder = valueSetBuilder ?? throw new ArgumentNullException(nameof(valueSetBuilder)); + ContentValueSetBuilder = contentValueSetBuilder ?? throw new ArgumentNullException(nameof(contentValueSetBuilder)); + MediaValueSetBuilder = mediaValueSetBuilder ?? throw new ArgumentNullException(nameof(mediaValueSetBuilder)); ContentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); MediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); LanguageService = languageService ?? throw new ArgumentNullException(nameof(languageService)); @@ -286,7 +290,7 @@ namespace Umbraco.Examine content = descendants.ToArray(); } - IndexItems(ValueSetBuilder.GetValueSets(content)); + IndexItems(ContentValueSetBuilder.GetValueSets(content)); pageIndex++; } while (content.Length == pageSize); @@ -321,7 +325,7 @@ namespace Umbraco.Examine media = descendants.ToArray(); } - IndexItems(ValueSetBuilder.GetValueSets(media)); + IndexItems(MediaValueSetBuilder.GetValueSets(media)); pageIndex++; } while (media.Length == pageSize); diff --git a/src/Umbraco.Examine/UmbracoMemberIndexer.cs b/src/Umbraco.Examine/UmbracoMemberIndexer.cs index f78348a255..4fa924f995 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndexer.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndexer.cs @@ -23,7 +23,7 @@ namespace Umbraco.Examine /// public class UmbracoMemberIndexer : UmbracoExamineIndexer { - private readonly UmbracoValueSetBuilder _valueSetBuilder; + private readonly IValueSetBuilder _valueSetBuilder; private readonly IMemberService _memberService; /// @@ -33,7 +33,7 @@ namespace Umbraco.Examine public UmbracoMemberIndexer() { _memberService = Current.Services.MemberService; - _valueSetBuilder = new UmbracoValueSetBuilder(Current.PropertyEditors, null, null); + _valueSetBuilder = new MemberValueSetBuilder(Current.PropertyEditors); } /// @@ -52,7 +52,7 @@ namespace Umbraco.Examine Directory luceneDirectory, Analyzer analyzer, ProfilingLogger profilingLogger, - UmbracoValueSetBuilder valueSetBuilder, + IValueSetBuilder valueSetBuilder, IMemberService memberService, IValueSetValidator validator = null) : base(name, fieldDefinitions, luceneDirectory, analyzer, profilingLogger, validator) diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 2e24b1ee41..cda499b10b 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -184,7 +184,9 @@ namespace Umbraco.Tests.UmbracoExamine analyzer, profilingLogger, //fixme: need a property editor collection here - new UmbracoValueSetBuilder(null, new[] { new DefaultUrlSegmentProvider() }, userService), + new ContentValueSetBuilder(null, new[] { new DefaultUrlSegmentProvider() }, userService), + //fixme: need a property editor collection here + new MediaValueSetBuilder(null, new[] { new DefaultUrlSegmentProvider() }, userService), contentService, mediaService, languageService, diff --git a/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs b/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs index 5160b09743..64246064f3 100644 --- a/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs +++ b/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs @@ -17,11 +17,11 @@ namespace Umbraco.Web.PropertyEditors /// public class GridValueIndexer : IValueIndexer { - public IEnumerable> GetIndexValues(Property property, string culture) + public IEnumerable> GetIndexValues(Property property, string culture, string segment) { var result = new Dictionary(); - var val = property.GetValue(culture); + var val = property.GetValue(culture, segment); //if there is a value, it's a string and it's detected as json if (val is string rawVal && rawVal.DetectIsJson()) diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 2bd3b3e9e0..eac2a2066c 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -95,9 +95,9 @@ namespace Umbraco.Web.PropertyEditors internal class RichTextValueIndexer : IValueIndexer { - public IEnumerable> GetIndexValues(Property property, string culture) + public IEnumerable> GetIndexValues(Property property, string culture, string segment) { - var val = property.GetValue(culture); + var val = property.GetValue(culture, segment); if (!(val is string strVal)) yield break; diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index a4f67d40af..a8bc1649e5 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -27,6 +27,7 @@ using Umbraco.Web.Scheduling; using System.Threading.Tasks; using Umbraco.Core.Persistence; using Umbraco.Core.Composing; +using LightInject; namespace Umbraco.Web.Search { @@ -38,7 +39,9 @@ namespace Umbraco.Web.Search public sealed class ExamineComponent : UmbracoComponentBase, IUmbracoCoreComponent { private IExamineManager _examineManager; - private UmbracoValueSetBuilder _valueSetBuilder; + private IValueSetBuilder _contentValueSetBuilder; + private IValueSetBuilder _mediaValueSetBuilder; + private IValueSetBuilder _memberValueSetBuilder; private static bool _disableExamineIndexing = false; private static volatile bool _isConfigured = false; private static readonly object IsConfiguredLocker = new object(); @@ -58,15 +61,28 @@ namespace Umbraco.Web.Search base.Compose(composition); composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton, ContentValueSetBuilder>(); + composition.Container.RegisterSingleton, MediaValueSetBuilder>(); + composition.Container.RegisterSingleton, MemberValueSetBuilder>(); } - internal void Initialize(IRuntimeState runtime, MainDom mainDom, PropertyEditorCollection propertyEditors, IExamineManager examineManager, ProfilingLogger profilingLogger, IScopeProvider scopeProvider, IUmbracoIndexesBuilder indexBuilder, ServiceContext services, IEnumerable urlSegmentProviders, UmbracoValueSetBuilder valueSetBuilder) + internal void Initialize(IRuntimeState runtime, MainDom mainDom, PropertyEditorCollection propertyEditors, + IExamineManager examineManager, ProfilingLogger profilingLogger, + IScopeProvider scopeProvider, IUmbracoIndexesBuilder indexBuilder, ServiceContext services, + IEnumerable urlSegmentProviders, + IValueSetBuilder contentValueSetBuilder, + IValueSetBuilder mediaValueSetBuilder, + IValueSetBuilder memberValueSetBuilder) { _services = services; _scopeProvider = scopeProvider; _examineManager = examineManager; _urlSegmentProviders = urlSegmentProviders; - _valueSetBuilder = valueSetBuilder; + + _contentValueSetBuilder = contentValueSetBuilder; + _mediaValueSetBuilder = mediaValueSetBuilder; + _memberValueSetBuilder = memberValueSetBuilder; //We want to manage Examine's appdomain shutdown sequence ourselves so first we'll disable Examine's default behavior //and then we'll use MainDom to control Examine's shutdown @@ -679,7 +695,7 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, IContent content, bool? supportUnpublished) { - var valueSet = examineComponent._valueSetBuilder.GetValueSets(content); + var valueSet = examineComponent._contentValueSetBuilder.GetValueSets(content); examineComponent._examineManager.IndexItems( valueSet.ToArray(), @@ -710,7 +726,7 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, IMedia media, bool isPublished) { - var valueSet = examineComponent._valueSetBuilder.GetValueSets(media); + var valueSet = examineComponent._mediaValueSetBuilder.GetValueSets(media); examineComponent._examineManager.IndexItems( valueSet.ToArray(), @@ -740,7 +756,7 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, IMember member) { - var valueSet = examineComponent._valueSetBuilder.GetValueSets(member); + var valueSet = examineComponent._memberValueSetBuilder.GetValueSets(member); examineComponent._examineManager.IndexItems( valueSet.ToArray(), diff --git a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs index c3e940857b..25ba64828f 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs @@ -14,6 +14,7 @@ using Examine; using Examine.LuceneEngine.Providers; using System.Linq; using Umbraco.Core; +using Umbraco.Core.Models; namespace Umbraco.Web.Search { @@ -25,7 +26,9 @@ namespace Umbraco.Web.Search //TODO: we should inject the different IValueSetValidator so devs can just register them instead of overriding this class? public UmbracoIndexesBuilder(ProfilingLogger profilingLogger, - UmbracoValueSetBuilder valueSetBuilder, + IValueSetBuilder contentValueSetBuilder, + IValueSetBuilder mediaValueSetBuilder, + IValueSetBuilder memberValueSetBuilder, IContentService contentService, IMediaService mediaService, ILocalizationService languageService, @@ -34,7 +37,9 @@ namespace Umbraco.Web.Search ISqlContext sqlContext) { ProfilingLogger = profilingLogger ?? throw new System.ArgumentNullException(nameof(profilingLogger)); - ValueSetBuilder = valueSetBuilder ?? throw new System.ArgumentNullException(nameof(valueSetBuilder)); + ContentValueSetBuilder = contentValueSetBuilder ?? throw new System.ArgumentNullException(nameof(contentValueSetBuilder)); + MediaValueSetBuilder = mediaValueSetBuilder ?? throw new System.ArgumentNullException(nameof(mediaValueSetBuilder)); + MemberValueSetBuilder = memberValueSetBuilder ?? throw new System.ArgumentNullException(nameof(memberValueSetBuilder)); ContentService = contentService ?? throw new System.ArgumentNullException(nameof(contentService)); MediaService = mediaService ?? throw new System.ArgumentNullException(nameof(mediaService)); LanguageService = languageService ?? throw new System.ArgumentNullException(nameof(languageService)); @@ -44,7 +49,9 @@ namespace Umbraco.Web.Search } protected ProfilingLogger ProfilingLogger { get; } - protected UmbracoValueSetBuilder ValueSetBuilder { get; } + protected IValueSetBuilder ContentValueSetBuilder { get; } + protected IValueSetBuilder MediaValueSetBuilder { get; } + protected IValueSetBuilder MemberValueSetBuilder { get; } protected IContentService ContentService { get; } protected IMediaService MediaService { get; } protected ILocalizationService LanguageService { get; } @@ -84,7 +91,7 @@ namespace Umbraco.Web.Search UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, GetFileSystemLuceneDirectory(name), analyzer, - ProfilingLogger, ValueSetBuilder, + ProfilingLogger, ContentValueSetBuilder, MediaValueSetBuilder, ContentService, MediaService, LanguageService, SqlContext, GetContentValueSetValidator(options), options); @@ -100,7 +107,7 @@ namespace Umbraco.Web.Search UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, GetFileSystemLuceneDirectory(MembersIndexPath), new CultureInvariantWhitespaceAnalyzer(), - ProfilingLogger, ValueSetBuilder, MemberService, + ProfilingLogger, MemberValueSetBuilder, MemberService, GetMemberValueSetValidator()); return index; } From 9368c71cedac4e6d9ccc5748b0b93d51152c6425 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 26 Nov 2018 14:43:08 +1100 Subject: [PATCH 15/44] fixes tests --- .../PublishedContent/PublishedMediaTests.cs | 21 ++++++++++++------- .../UmbracoExamine/EventsTest.cs | 4 +++- .../UmbracoExamine/IndexInitializer.cs | 10 ++++----- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 16 ++++++++------ .../UmbracoExamine/SearchTests.cs | 8 ++++--- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index b9d106ca1b..2a3b60fcdf 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -23,6 +23,7 @@ using Umbraco.Tests.Testing; using LightInject; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Tests.PublishedContent { @@ -113,7 +114,8 @@ namespace Umbraco.Tests.PublishedContent public void Ensure_Children_Sorted_With_Examine() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + options: new UmbracoContentIndexerOptions(true, false, null))) { indexer.RebuildIndex(); @@ -138,7 +140,7 @@ namespace Umbraco.Tests.PublishedContent public void Do_Not_Find_In_Recycle_Bin() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), //include unpublished content since this uses the 'internal' indexer, it's up to the media cache to filter options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) @@ -184,7 +186,8 @@ namespace Umbraco.Tests.PublishedContent public void Children_With_Examine() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { indexer.RebuildIndex(); @@ -209,7 +212,8 @@ namespace Umbraco.Tests.PublishedContent public void Descendants_With_Examine() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { indexer.RebuildIndex(); @@ -234,7 +238,8 @@ namespace Umbraco.Tests.PublishedContent public void DescendantsOrSelf_With_Examine() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { indexer.RebuildIndex(); @@ -259,7 +264,8 @@ namespace Umbraco.Tests.PublishedContent public void Ancestors_With_Examine() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { indexer.RebuildIndex(); @@ -281,7 +287,8 @@ namespace Umbraco.Tests.PublishedContent public void AncestorsOrSelf_With_Examine() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { indexer.RebuildIndex(); diff --git a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs index e2279ee833..fa5a5afaf9 100644 --- a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs @@ -1,10 +1,12 @@ using System; using System.Linq; using Examine; +using LightInject; using Lucene.Net.Store; using NUnit.Framework; using Umbraco.Tests.Testing; using Umbraco.Examine; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Tests.UmbracoExamine { @@ -16,7 +18,7 @@ namespace Umbraco.Tests.UmbracoExamine public void Events_Ignoring_Node() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), //make parent id 999 so all are ignored options: new UmbracoContentIndexerOptions(false, false, 999))) using (indexer.ProcessNonAsync()) diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index cda499b10b..6357447d54 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -13,6 +13,7 @@ using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Strings; @@ -32,6 +33,7 @@ namespace Umbraco.Tests.UmbracoExamine ProfilingLogger profilingLogger, Directory luceneDir, ISqlContext sqlContext, + PropertyEditorCollection propertyEditors, Analyzer analyzer = null, IContentService contentService = null, IMediaService mediaService = null, @@ -179,14 +181,12 @@ namespace Umbraco.Tests.UmbracoExamine var i = new UmbracoContentIndexer( "testIndexer", - Enumerable.Empty(), + UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, luceneDir, analyzer, profilingLogger, - //fixme: need a property editor collection here - new ContentValueSetBuilder(null, new[] { new DefaultUrlSegmentProvider() }, userService), - //fixme: need a property editor collection here - new MediaValueSetBuilder(null, new[] { new DefaultUrlSegmentProvider() }, userService), + new ContentValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, userService), + new MediaValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, userService), contentService, mediaService, languageService, diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index db6f174f59..0079d1a4ae 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -8,6 +8,8 @@ using Lucene.Net.Store; using NUnit.Framework; using Umbraco.Tests.Testing; using Umbraco.Examine; +using Umbraco.Core.PropertyEditors; +using LightInject; namespace Umbraco.Tests.UmbracoExamine { @@ -24,7 +26,8 @@ namespace Umbraco.Tests.UmbracoExamine public void Rebuild_Index() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { var searcher = indexer.GetSearcher(); @@ -46,7 +49,7 @@ namespace Umbraco.Tests.UmbracoExamine public void Index_Protected_Content_Not_Indexed() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance())) using (indexer.ProcessNonAsync()) using (var searcher = ((LuceneSearcher)indexer.GetSearcher()).GetLuceneSearcher()) { @@ -77,7 +80,7 @@ namespace Umbraco.Tests.UmbracoExamine public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), //make parent id 1116 options: new UmbracoContentIndexerOptions(false, false, 1116))) using (indexer.ProcessNonAsync()) @@ -119,7 +122,7 @@ namespace Umbraco.Tests.UmbracoExamine public void Index_Move_Media_To_Non_Indexable_ParentID() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer1 = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, + using (var indexer1 = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), //make parent id 2222 options: new UmbracoContentIndexerOptions(false, false, 2222))) using (indexer1.ProcessNonAsync()) @@ -169,7 +172,8 @@ namespace Umbraco.Tests.UmbracoExamine public void Index_Reindex_Content() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { var searcher = indexer.GetSearcher(); @@ -209,7 +213,7 @@ namespace Umbraco.Tests.UmbracoExamine public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance())) using (indexer.ProcessNonAsync()) { var searcher = indexer.GetSearcher(); diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index 768d1c735c..a5c1dbf587 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -1,17 +1,17 @@ using System; using System.Collections.Generic; using System.Linq; +using LightInject; using Examine; -using Lucene.Net.Store; using NUnit.Framework; using Examine.LuceneEngine.SearchCriteria; using Moq; using Umbraco.Core.Models; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Services; using Umbraco.Examine; using Umbraco.Tests.Testing; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Tests.UmbracoExamine { @@ -54,7 +54,9 @@ namespace Umbraco.Tests.UmbracoExamine allRecs); using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, contentService: contentService)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, + Container.GetInstance(), + contentService: contentService)) using (indexer.ProcessNonAsync()) { indexer.RebuildIndex(); From b6790261c4c06b0109d24e23bfd1bb3baf71271d Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 26 Nov 2018 17:20:15 +1100 Subject: [PATCH 16/44] Adds test --- src/Umbraco.Core/Models/GridValue.cs | 6 +- src/Umbraco.Examine/BaseValueSetBuilder.cs | 22 +++- src/Umbraco.Examine/Umbraco.Examine.csproj | 2 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 4 +- .../UmbracoContentValueSetValidator.cs | 7 +- .../Collections/PropertyCollectionTests.cs | 2 +- .../Models/ContentExtensionsTests.cs | 14 +-- src/Umbraco.Tests/Models/ContentTests.cs | 58 +++++----- src/Umbraco.Tests/Models/ContentTypeTests.cs | 8 +- src/Umbraco.Tests/Models/ContentXmlTest.cs | 2 +- .../Mapping/ContentTypeModelMappingTests.cs | 2 +- .../Repositories/ContentTypeRepositoryTest.cs | 2 +- .../Services/ContentServicePerformanceTest.cs | 8 +- .../Services/ContentServiceTests.cs | 10 +- .../Services/ContentTypeServiceTests.cs | 8 +- .../Entities/MockedContentTypes.cs | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 101 +++++++++++++++++- .../UmbracoExamine/SearchTests.cs | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- .../Models/Mapping/EntityMapperProfile.cs | 24 ++--- .../PropertyEditors/GridValueIndexer.cs | 55 +++++----- .../XmlPublishedCache/PublishedMediaCache.cs | 2 +- src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 8 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- 25 files changed, 234 insertions(+), 121 deletions(-) diff --git a/src/Umbraco.Core/Models/GridValue.cs b/src/Umbraco.Core/Models/GridValue.cs index 717fcd2f88..237385f3f4 100644 --- a/src/Umbraco.Core/Models/GridValue.cs +++ b/src/Umbraco.Core/Models/GridValue.cs @@ -5,6 +5,8 @@ using Newtonsoft.Json.Linq; namespace Umbraco.Core.Models { + //TODO: Make a property value converter for this! + /// /// A model representing the value saved for the grid /// @@ -19,7 +21,7 @@ namespace Umbraco.Core.Models public class GridSection { [JsonProperty("grid")] - public string Grid { get; set; } + public string Grid { get; set; } //fixme: what is this? [JsonProperty("rows")] public IEnumerable Rows { get; set; } @@ -46,7 +48,7 @@ namespace Umbraco.Core.Models public class GridArea { [JsonProperty("grid")] - public string Grid { get; set; } + public string Grid { get; set; } //fixme: what is this? [JsonProperty("controls")] public IEnumerable Controls { get; set; } diff --git a/src/Umbraco.Examine/BaseValueSetBuilder.cs b/src/Umbraco.Examine/BaseValueSetBuilder.cs index c1780a9b7d..043e017754 100644 --- a/src/Umbraco.Examine/BaseValueSetBuilder.cs +++ b/src/Umbraco.Examine/BaseValueSetBuilder.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; @@ -16,7 +17,7 @@ namespace Umbraco.Examine protected void AddPropertyValue(Property property, string culture, string segment, IDictionary values) { - var editor = _propertyEditors[property.Alias]; + var editor = _propertyEditors[property.PropertyType.PropertyEditorAlias]; if (editor == null) return; var indexVals = editor.ValueIndexer.GetIndexValues(property, culture, segment); @@ -34,11 +35,24 @@ namespace Umbraco.Examine case null: continue; case string strVal: - if (strVal.IsNullOrWhiteSpace()) return; - values.Add($"{keyVal.Key}{cultureSuffix}", new[] { val }); + { + if (strVal.IsNullOrWhiteSpace()) return; + var key = $"{keyVal.Key}{cultureSuffix}"; + if (values.TryGetValue(key, out var v)) + values[key] = new List(v) { val }.ToArray(); + else + values.Add($"{keyVal.Key}{cultureSuffix}", new[] { val }); + } break; default: - values.Add($"{keyVal.Key}{cultureSuffix}", new[] { val }); + { + var key = $"{keyVal.Key}{cultureSuffix}"; + if (values.TryGetValue(key, out var v)) + values[key] = new List(v) { val }.ToArray(); + else + values.Add($"{keyVal.Key}{cultureSuffix}", new[] { val }); + } + break; } } diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 8a0b251b80..83a9ef0d7d 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 06e174fdf1..e5f05caf43 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -31,8 +31,8 @@ namespace Umbraco.Examine /// public class UmbracoContentIndexer : UmbracoExamineIndexer { - protected IValueSetBuilder MediaValueSetBuilder { get; } - protected IValueSetBuilder ContentValueSetBuilder { get; } + public IValueSetBuilder MediaValueSetBuilder { get; } + public IValueSetBuilder ContentValueSetBuilder { get; } protected IContentService ContentService { get; } protected IMediaService MediaService { get; } protected ILocalizationService LanguageService { get; } diff --git a/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs b/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs index 1881511ee3..fcb058d268 100644 --- a/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs +++ b/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs @@ -39,8 +39,11 @@ namespace Umbraco.Examine } //must have a 'path' - if (valueSet.Values.ContainsKey(PathKey) == false) return false; - var path = valueSet.Values[PathKey]?[0].ToString() ?? string.Empty; + if (!valueSet.Values.TryGetValue(PathKey, out var pathValues)) return false; + if (pathValues.Count == 0) return false; + if (pathValues[0] == null) return false; + if (pathValues[0].ToString().IsNullOrWhiteSpace()) return false; + var path = pathValues[0].ToString(); // Test for access if we're only indexing published content // return nothing if we're not supporting protected content and it is protected, and we're not supporting unpublished content diff --git a/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs b/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs index 82aae0ada5..404dc5ba74 100644 --- a/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs +++ b/src/Umbraco.Tests/Models/Collections/PropertyCollectionTests.cs @@ -78,7 +78,7 @@ namespace Umbraco.Tests.Models.Collections [Test] public void PropertyGroups_Collection_FirstOrDefault_Returns_Null() { - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); Assert.That(contentType.PropertyGroups, Is.Not.Null); Assert.That(contentType.PropertyGroups.FirstOrDefault(x => x.Name.InvariantEquals("Content")) == null, Is.False); diff --git a/src/Umbraco.Tests/Models/ContentExtensionsTests.cs b/src/Umbraco.Tests/Models/ContentExtensionsTests.cs index 38a93c0c35..5c95f1ead5 100644 --- a/src/Umbraco.Tests/Models/ContentExtensionsTests.cs +++ b/src/Umbraco.Tests/Models/ContentExtensionsTests.cs @@ -16,7 +16,7 @@ namespace Umbraco.Tests.Models [Test] public void DirtyProperty_Reset_Clears_SavedPublishedState() { - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); content.PublishedState = PublishedState.Publishing; @@ -29,7 +29,7 @@ namespace Umbraco.Tests.Models [Test] public void DirtyProperty_OnlyIfActuallyChanged_Content() { - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); // if you assign a content property with its value it is not dirty @@ -51,7 +51,7 @@ namespace Umbraco.Tests.Models [Test] public void DirtyProperty_OnlyIfActuallyChanged_User() { - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); var prop = content.Properties.First(); @@ -75,7 +75,7 @@ namespace Umbraco.Tests.Models [Test] public void DirtyProperty_UpdateDate() { - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); var prop = content.Properties.First(); @@ -98,7 +98,7 @@ namespace Umbraco.Tests.Models [Test] public void DirtyProperty_WasDirty_ContentProperty() { - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); content.ResetDirtyProperties(false); Assert.IsFalse(content.IsDirty()); @@ -125,7 +125,7 @@ namespace Umbraco.Tests.Models [Test] public void DirtyProperty_WasDirty_ContentSortOrder() { - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); content.ResetDirtyProperties(false); Assert.IsFalse(content.IsDirty()); @@ -152,7 +152,7 @@ namespace Umbraco.Tests.Models [Test] public void DirtyProperty_WasDirty_UserProperty() { - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); var prop = content.Properties.First(); content.ResetDirtyProperties(false); diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs index 3f9c6d1cd5..d31fcbf744 100644 --- a/src/Umbraco.Tests/Models/ContentTests.cs +++ b/src/Umbraco.Tests/Models/ContentTests.cs @@ -128,7 +128,7 @@ namespace Umbraco.Tests.Models [Test] public void All_Dirty_Properties_Get_Reset() { - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); content.ResetDirtyProperties(false); @@ -144,7 +144,7 @@ namespace Umbraco.Tests.Models public void Can_Verify_Mocked_Content() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); // Act @@ -157,7 +157,7 @@ namespace Umbraco.Tests.Models public void Can_Change_Property_Value() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); // Act @@ -173,7 +173,7 @@ namespace Umbraco.Tests.Models public void Can_Set_Property_Value_As_String() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); // Act @@ -189,7 +189,7 @@ namespace Umbraco.Tests.Models public void Can_Clone_Content_With_Reset_Identity() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); content.Id = 10; content.Key = new Guid("29181B97-CB8F-403F-86DE-5FEB497F4800"); @@ -218,7 +218,7 @@ namespace Umbraco.Tests.Models public void Can_Deep_Clone_Perf_Test() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.Id = 99; var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); var i = 200; @@ -269,7 +269,7 @@ namespace Umbraco.Tests.Models public void Can_Deep_Clone() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.Id = 99; contentType.Variations = ContentVariation.Culture; var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); @@ -394,7 +394,7 @@ namespace Umbraco.Tests.Models public void Remember_Dirty_Properties() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.Id = 99; contentType.Variations = ContentVariation.Culture; var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); @@ -475,7 +475,7 @@ namespace Umbraco.Tests.Models var ss = new SerializationService(new JsonNetSerializer()); // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.Id = 99; var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); var i = 200; @@ -530,7 +530,7 @@ namespace Umbraco.Tests.Models public void Can_Change_Property_Value_Through_Anonymous_Object() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); // Act @@ -548,7 +548,7 @@ namespace Umbraco.Tests.Models public void Can_Verify_Dirty_Property_On_Content() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); // Act @@ -564,7 +564,7 @@ namespace Umbraco.Tests.Models public void Can_Add_PropertyGroup_On_ContentType() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); // Act @@ -579,7 +579,7 @@ namespace Umbraco.Tests.Models public void Can_Remove_PropertyGroup_From_ContentType() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.ResetDirtyProperties(); // Act @@ -594,7 +594,7 @@ namespace Umbraco.Tests.Models public void Can_Add_PropertyType_To_Group_On_ContentType() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); // Act @@ -616,7 +616,7 @@ namespace Umbraco.Tests.Models public void Can_Add_New_Property_To_New_PropertyType() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); // Act @@ -638,7 +638,7 @@ namespace Umbraco.Tests.Models public void Can_Add_New_Property_To_New_PropertyType_In_New_PropertyGroup() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); // Act @@ -669,7 +669,7 @@ namespace Umbraco.Tests.Models public void Can_Update_PropertyType_Through_Content_Properties() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); // Act - note that the PropertyType's properties like SortOrder is not updated through the Content object @@ -689,7 +689,7 @@ namespace Umbraco.Tests.Models public void Can_Change_ContentType_On_Content() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var simpleContentType = MockedContentTypes.CreateSimpleContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); @@ -708,7 +708,7 @@ namespace Umbraco.Tests.Models public void Can_Change_ContentType_On_Content_And_Set_Property_Value() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var simpleContentType = MockedContentTypes.CreateSimpleContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); @@ -725,7 +725,7 @@ namespace Umbraco.Tests.Models public void Can_Change_ContentType_On_Content_And_Still_Get_Old_Properties() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var simpleContentType = MockedContentTypes.CreateSimpleContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); @@ -744,7 +744,7 @@ namespace Umbraco.Tests.Models public void Can_Change_ContentType_On_Content_And_Clear_Old_PropertyTypes() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var simpleContentType = MockedContentTypes.CreateSimpleContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); @@ -760,7 +760,7 @@ namespace Umbraco.Tests.Models [Test] public void Can_Verify_Content_Is_Published() { - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); content.ResetDirtyProperties(); @@ -794,7 +794,7 @@ namespace Umbraco.Tests.Models public void Adding_PropertyGroup_To_ContentType_Results_In_Dirty_Entity() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.ResetDirtyProperties(); // Act @@ -811,7 +811,7 @@ namespace Umbraco.Tests.Models public void After_Committing_Changes_Was_Dirty_Is_True() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.ResetDirtyProperties(); //reset // Act @@ -828,7 +828,7 @@ namespace Umbraco.Tests.Models public void After_Committing_Changes_Was_Dirty_Is_True_On_Changed_Property() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.ResetDirtyProperties(); //reset var content = MockedContent.CreateTextpageContent(contentType, "test", -1); content.ResetDirtyProperties(); @@ -859,7 +859,7 @@ namespace Umbraco.Tests.Models public void If_Not_Committed_Was_Dirty_Is_False() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); // Act contentType.Alias = "newAlias"; @@ -873,7 +873,7 @@ namespace Umbraco.Tests.Models public void Detect_That_A_Property_Is_Removed() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); Assert.That(contentType.WasPropertyDirty("HasPropertyTypeBeenRemoved"), Is.False); // Act @@ -887,7 +887,7 @@ namespace Umbraco.Tests.Models public void Adding_PropertyType_To_PropertyGroup_On_ContentType_Results_In_Dirty_Entity() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.ResetDirtyProperties(); // Act @@ -974,7 +974,7 @@ namespace Umbraco.Tests.Models [Test] public void Can_Avoid_Circular_Dependencies_In_Composition() { - var textPage = MockedContentTypes.CreateTextpageContentType(); + var textPage = MockedContentTypes.CreateTextPageContentType(); var parent = MockedContentTypes.CreateSimpleContentType("parent", "Parent", null, true); var meta = MockedContentTypes.CreateMetaContentType(); var mixin1 = MockedContentTypes.CreateSimpleContentType("mixin1", "Mixin1", new PropertyTypeCollection(true, diff --git a/src/Umbraco.Tests/Models/ContentTypeTests.cs b/src/Umbraco.Tests/Models/ContentTypeTests.cs index a0e9a370da..6ef28d8290 100644 --- a/src/Umbraco.Tests/Models/ContentTypeTests.cs +++ b/src/Umbraco.Tests/Models/ContentTypeTests.cs @@ -33,7 +33,7 @@ namespace Umbraco.Tests.Models [Test] public void Can_Deep_Clone_Content_Type_With_Reset_Identities() { - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.Id = 99; var i = 200; @@ -105,7 +105,7 @@ namespace Umbraco.Tests.Models public void Can_Deep_Clone_Content_Type_Perf_Test() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.Id = 99; var i = 200; @@ -155,7 +155,7 @@ namespace Umbraco.Tests.Models public void Can_Deep_Clone_Content_Type() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.Id = 99; var i = 200; @@ -256,7 +256,7 @@ namespace Umbraco.Tests.Models var ss = new SerializationService(new JsonNetSerializer()); // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); contentType.Id = 99; var i = 200; diff --git a/src/Umbraco.Tests/Models/ContentXmlTest.cs b/src/Umbraco.Tests/Models/ContentXmlTest.cs index 9cc41ffa6f..ab318ec1cb 100644 --- a/src/Umbraco.Tests/Models/ContentXmlTest.cs +++ b/src/Umbraco.Tests/Models/ContentXmlTest.cs @@ -18,7 +18,7 @@ namespace Umbraco.Tests.Models public void Can_Generate_Xml_Representation_Of_Content() { // Arrange - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! ServiceContext.ContentTypeService.Save(contentType); diff --git a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs index 0d5c9548fd..76e618ea26 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentTypeModelMappingTests.cs @@ -427,7 +427,7 @@ namespace Umbraco.Tests.Models.Mapping // setup the mocks to return the data we want to test against... - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); MockedContentTypes.EnsureAllIds(contentType, 8888); //Act diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index 6087279285..6a282169fb 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -964,7 +964,7 @@ namespace Umbraco.Tests.Persistence.Repositories ServiceContext.ContentTypeService.Save(simpleContentType); //Create and Save ContentType "textPage" -> (NodeDto.NodeIdSeed + 1) - ContentType textpageContentType = MockedContentTypes.CreateTextpageContentType(); + ContentType textpageContentType = MockedContentTypes.CreateTextPageContentType(); ServiceContext.ContentTypeService.Save(textpageContentType); } } diff --git a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs index 4d4825323c..322b79429e 100644 --- a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs +++ b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs @@ -65,9 +65,9 @@ namespace Umbraco.Tests.Services // ... NOPE, made even more nice changes, it is now... // 4452ms !!!!!!! - var contentType1 = MockedContentTypes.CreateTextpageContentType("test1", "test1"); - var contentType2 = MockedContentTypes.CreateTextpageContentType("test2", "test2"); - var contentType3 = MockedContentTypes.CreateTextpageContentType("test3", "test3"); + var contentType1 = MockedContentTypes.CreateTextPageContentType("test1", "test1"); + var contentType2 = MockedContentTypes.CreateTextPageContentType("test2", "test2"); + var contentType3 = MockedContentTypes.CreateTextPageContentType("test3", "test3"); ServiceContext.ContentTypeService.Save(new[] { contentType1, contentType2, contentType3 }); contentType1.AllowedContentTypes = new[] { @@ -291,7 +291,7 @@ namespace Umbraco.Tests.Services public void CreateTestData() { //Create and Save ContentType "textpage" -> NodeDto.NodeIdSeed - ContentType contentType = MockedContentTypes.CreateTextpageContentType(); + ContentType contentType = MockedContentTypes.CreateTextPageContentType(); ServiceContext.ContentTypeService.Save(contentType); } } diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 2c90ce90c5..1b12c24d82 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -92,7 +92,7 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; var contentTypeService = ServiceContext.ContentTypeService; - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); contentTypeService.Save(contentType); @@ -118,7 +118,7 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; var contentTypeService = ServiceContext.ContentTypeService; - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); contentTypeService.Save(contentType); @@ -142,7 +142,7 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; var contentTypeService = ServiceContext.ContentTypeService; - var contentType = MockedContentTypes.CreateTextpageContentType(); + var contentType = MockedContentTypes.CreateTextPageContentType(); ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); contentTypeService.Save(contentType); @@ -170,10 +170,10 @@ namespace Umbraco.Tests.Services var contentService = ServiceContext.ContentService; var contentTypeService = ServiceContext.ContentTypeService; - var ct1 = MockedContentTypes.CreateTextpageContentType("ct1"); + var ct1 = MockedContentTypes.CreateTextPageContentType("ct1"); ServiceContext.FileService.SaveTemplate(ct1.DefaultTemplate); contentTypeService.Save(ct1); - var ct2 = MockedContentTypes.CreateTextpageContentType("ct2"); + var ct2 = MockedContentTypes.CreateTextPageContentType("ct2"); ServiceContext.FileService.SaveTemplate(ct2.DefaultTemplate); contentTypeService.Save(ct2); diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index f186ae8e83..bc0854bdb7 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -611,7 +611,7 @@ namespace Umbraco.Tests.Services [Test] public void Deleting_PropertyType_Removes_The_Property_From_Content() { - IContentType contentType1 = MockedContentTypes.CreateTextpageContentType("test1", "Test1"); + IContentType contentType1 = MockedContentTypes.CreateTextPageContentType("test1", "Test1"); ServiceContext.FileService.SaveTemplate(contentType1.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType1); IContent contentItem = MockedContent.CreateTextpageContent(contentType1, "Testing", -1); @@ -633,11 +633,11 @@ namespace Umbraco.Tests.Services [Test] public void Rebuild_Content_Xml_On_Alias_Change() { - var contentType1 = MockedContentTypes.CreateTextpageContentType("test1", "Test1"); + var contentType1 = MockedContentTypes.CreateTextPageContentType("test1", "Test1"); ServiceContext.FileService.SaveTemplate(contentType1.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType1); - var contentType2 = MockedContentTypes.CreateTextpageContentType("test2", "Test2"); + var contentType2 = MockedContentTypes.CreateTextPageContentType("test2", "Test2"); ServiceContext.FileService.SaveTemplate(contentType2.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType2); @@ -701,7 +701,7 @@ namespace Umbraco.Tests.Services [Test] public void Rebuild_Content_Xml_On_Property_Removal() { - var contentType1 = MockedContentTypes.CreateTextpageContentType("test1", "Test1"); + var contentType1 = MockedContentTypes.CreateTextPageContentType("test1", "Test1"); ServiceContext.FileService.SaveTemplate(contentType1.DefaultTemplate); ServiceContext.ContentTypeService.Save(contentType1); var contentItems1 = MockedContent.CreateTextpageContent(contentType1, -1, 10).ToArray(); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index 42beea7df3..14b967b1c9 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -26,7 +26,7 @@ namespace Umbraco.Tests.TestHelpers.Entities return contentType; } - public static ContentType CreateTextpageContentType(string alias = "textPage", string name = "Text Page") + public static ContentType CreateTextPageContentType(string alias = "textPage", string name = "Text Page") { var contentType = new ContentType(-1) { diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index c68be3a26d..8262fb3cd3 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/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 0079d1a4ae..e454fe4d39 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -10,6 +10,11 @@ using Umbraco.Tests.Testing; using Umbraco.Examine; using Umbraco.Core.PropertyEditors; using LightInject; +using Umbraco.Tests.TestHelpers.Entities; +using Umbraco.Core.Models; +using Newtonsoft.Json; +using System.Collections.Generic; +using System; namespace Umbraco.Tests.UmbracoExamine { @@ -22,6 +27,98 @@ namespace Umbraco.Tests.UmbracoExamine public class IndexTest : ExamineBaseTest { + [Test] + public void Index_Property_Data_With_Value_Indexer() + { + using (var luceneDir = new RandomIdRamDirectory()) + using (var indexer = IndexInitializer.GetUmbracoIndexer( + ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + options: new UmbracoContentIndexerOptions(true, false, null))) + using (indexer.ProcessNonAsync()) + { + indexer.EnsureIndex(true); + + var contentType = MockedContentTypes.CreateBasicContentType(); + contentType.AddPropertyType(new PropertyType("test", ValueStorageType.Ntext) + { + Alias = "grid", + Name = "Grid", + PropertyEditorAlias = Core.Constants.PropertyEditors.Aliases.Grid + }); + var content = MockedContent.CreateBasicContent(contentType); + content.Id = 555; + content.Path = "-1,555"; + var gridVal = new GridValue + { + Name = "n1", + Sections = new List + { + new GridValue.GridSection + { + Grid = "g1", + Rows = new List + { + new GridValue.GridRow + { + Id = Guid.NewGuid(), + Name = "row1", + Areas = new List + { + new GridValue.GridArea + { + Grid = "g2", + Controls = new List + { + new GridValue.GridControl + { + Editor = new GridValue.GridEditor + { + Alias = "editor1", + View = "view1" + }, + Value = "value1" + }, + new GridValue.GridControl + { + Editor = new GridValue.GridEditor + { + Alias = "editor1", + View = "view1" + }, + Value = "value2" + } + } + } + } + } + } + } + } + }; + + var json = JsonConvert.SerializeObject(gridVal); + content.Properties["grid"].SetValue(json); + + + var valueSet = indexer.ContentValueSetBuilder.GetValueSets(content); + indexer.IndexItems(valueSet); + + var searcher = indexer.GetSearcher(); + + var results = searcher.Search(searcher.CreateCriteria().Id(555).Compile()); + Assert.AreEqual(1, results.TotalItemCount); + + var result = results.First(); + Assert.IsTrue(result.Values.ContainsKey("grid.row1")); + Assert.AreEqual("value1", result.AllValues["grid.row1"][0]); + Assert.AreEqual("value2", result.AllValues["grid.row1"][1]); + Assert.IsTrue(result.Values.ContainsKey("grid")); + Assert.AreEqual("value1 value2 ", result["grid"]); + Assert.IsTrue(result.Values.ContainsKey($"{UmbracoExamineIndexer.RawFieldPrefix}grid")); + Assert.AreEqual(json, result[$"{UmbracoExamineIndexer.RawFieldPrefix}grid"]); + } + } + [Test] public void Rebuild_Index() { @@ -98,7 +195,7 @@ namespace Umbraco.Tests.UmbracoExamine Assert.AreEqual("-1,1111,2222,2112", currPath); //ensure it's indexed - indexer.IndexItems(new []{ node.ConvertToValueSet(IndexTypes.Media) }); + indexer.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); //it will not exist because it exists under 2222 var results = searcher.Search(searcher.CreateCriteria().Id(2112).Compile()); @@ -140,7 +237,7 @@ namespace Umbraco.Tests.UmbracoExamine Assert.AreEqual("-1,1111,2222,2112", currPath); //ensure it's indexed - indexer1.IndexItems(new[] { node.ConvertToValueSet(IndexTypes.Media) }); + indexer1.IndexItem(node.ConvertToValueSet(IndexTypes.Media)); diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index a5c1dbf587..58df17c052 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -87,7 +87,7 @@ namespace Umbraco.Tests.UmbracoExamine var currentSort = 0; foreach (var searchResult in results) { - var sort = int.Parse(searchResult.Fields["sortOrder"]); + var sort = int.Parse(searchResult.Values["sortOrder"]); if (currentSort >= sort) { return false; diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 18d66a4111..832760bde2 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,7 +88,7 @@ - + diff --git a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs index bfabfd799a..5d41c8cb0c 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs @@ -123,22 +123,22 @@ namespace Umbraco.Web.Models.Mapping .AfterMap((src, dest) => { //get the icon if there is one - dest.Icon = src.Fields.ContainsKey(UmbracoExamineIndexer.IconFieldName) - ? src.Fields[UmbracoExamineIndexer.IconFieldName] + dest.Icon = src.Values.ContainsKey(UmbracoExamineIndexer.IconFieldName) + ? src.Values[UmbracoExamineIndexer.IconFieldName] : "icon-document"; - dest.Name = src.Fields.ContainsKey("nodeName") ? src.Fields["nodeName"] : "[no name]"; - if (src.Fields.ContainsKey(UmbracoExamineIndexer.NodeKeyFieldName)) + dest.Name = src.Values.ContainsKey("nodeName") ? src.Values["nodeName"] : "[no name]"; + if (src.Values.ContainsKey(UmbracoExamineIndexer.NodeKeyFieldName)) { Guid key; - if (Guid.TryParse(src.Fields[UmbracoExamineIndexer.NodeKeyFieldName], out key)) + if (Guid.TryParse(src.Values[UmbracoExamineIndexer.NodeKeyFieldName], out key)) { dest.Key = key; //need to set the UDI - if (src.Fields.ContainsKey(LuceneIndexer.CategoryFieldName)) + if (src.Values.ContainsKey(LuceneIndexer.CategoryFieldName)) { - switch (src.Fields[LuceneIndexer.CategoryFieldName]) + switch (src.Values[LuceneIndexer.CategoryFieldName]) { case IndexTypes.Member: dest.Udi = new GuidUdi(Constants.UdiEntityType.Member, dest.Key); @@ -154,10 +154,10 @@ namespace Umbraco.Web.Models.Mapping } } - if (src.Fields.ContainsKey("parentID")) + if (src.Values.ContainsKey("parentID")) { int parentId; - if (int.TryParse(src.Fields["parentID"], out parentId)) + if (int.TryParse(src.Values["parentID"], out parentId)) { dest.ParentId = parentId; } @@ -166,11 +166,11 @@ namespace Umbraco.Web.Models.Mapping dest.ParentId = -1; } } - dest.Path = src.Fields.ContainsKey(UmbracoExamineIndexer.IndexPathFieldName) ? src.Fields[UmbracoExamineIndexer.IndexPathFieldName] : ""; + dest.Path = src.Values.ContainsKey(UmbracoExamineIndexer.IndexPathFieldName) ? src.Values[UmbracoExamineIndexer.IndexPathFieldName] : ""; - if (src.Fields.ContainsKey(LuceneIndexer.ItemTypeFieldName)) + if (src.Values.ContainsKey(LuceneIndexer.ItemTypeFieldName)) { - dest.AdditionalData.Add("contentType", src.Fields[LuceneIndexer.ItemTypeFieldName]); + dest.AdditionalData.Add("contentType", src.Values[LuceneIndexer.ItemTypeFieldName]); } }); diff --git a/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs b/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs index 64246064f3..d2e01d0c73 100644 --- a/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs +++ b/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs @@ -10,6 +10,7 @@ using Umbraco.Examine; namespace Umbraco.Web.PropertyEditors { using System.Collections.Generic; + using System.Linq; using Umbraco.Core.Models; /// @@ -19,7 +20,7 @@ namespace Umbraco.Web.PropertyEditors { public IEnumerable> GetIndexValues(Property property, string culture, string segment) { - var result = new Dictionary(); + var result = new List>(); var val = property.GetValue(culture, segment); @@ -28,44 +29,40 @@ namespace Umbraco.Web.PropertyEditors { try { - //TODO: We should deserialize this to Umbraco.Core.Models.GridValue instead of doing the below - var json = JsonConvert.DeserializeObject(rawVal); + var gridVal = JsonConvert.DeserializeObject(rawVal); - //check if this is formatted for grid json - if (json.HasValues && json.TryGetValue("name", out _) && json.TryGetValue("sections", out _)) + //get all values and put them into a single field (using JsonPath) + var sb = new StringBuilder(); + foreach (var row in gridVal.Sections.SelectMany(x => x.Rows)) { - //get all values and put them into a single field (using JsonPath) - var sb = new StringBuilder(); - foreach (var row in json.SelectTokens("$.sections[*].rows[*]")) + var rowName = row.Name; + + foreach (var control in row.Areas.SelectMany(x => x.Controls)) { - var rowName = row["name"].Value(); - var areaVals = row.SelectTokens("$.areas[*].controls[*].value"); + var controlVal = control.Value; - foreach (var areaVal in areaVals) + //TODO: If it's not a string, then it's a json formatted value - + // we cannot really index this in a smart way since it could be 'anything' + if (controlVal.Type == JTokenType.String) { - //TODO: If it's not a string, then it's a json formatted value - - // we cannot really index this in a smart way since it could be 'anything' - if (areaVal.Type == JTokenType.String) - { - var str = areaVal.Value(); - str = XmlHelper.CouldItBeXml(str) ? str.StripHtml() : str; - sb.Append(str); - sb.Append(" "); + var str = controlVal.Value(); + str = XmlHelper.CouldItBeXml(str) ? str.StripHtml() : str; + sb.Append(str); + sb.Append(" "); - //add the row name as an individual field - result.Add($"{property.Alias}.{rowName}", new[] { str }); - } + //add the row name as an individual field + result.Add(new KeyValuePair($"{property.Alias}.{rowName}", new[] { str })); } } + } - if (sb.Length > 0) - { - //First save the raw value to a raw field - result.Add($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new[] { rawVal }); + if (sb.Length > 0) + { + //First save the raw value to a raw field + result.Add(new KeyValuePair($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new[] { rawVal })); - //index the property with the combined/cleaned value - result.Add(property.Alias, new[] { sb.ToString() }); - } + //index the property with the combined/cleaned value + result.Add(new KeyValuePair(property.Alias, new[] { sb.ToString() })); } } catch (InvalidCastException) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 79ff0c33a8..d334c6ab48 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -359,7 +359,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return new CacheValues { - Values = searchResult.Fields, + Values = searchResult.Values, FromExamine = true }; } diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index 262c2c80e3..6290307926 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -278,14 +278,14 @@ namespace Umbraco.Web.Search } var searchResult = results.First(x => x.Id == m.Id.ToString()); - if (searchResult.Fields.ContainsKey("email") && searchResult.Fields["email"] != null) + if (searchResult.Values.ContainsKey("email") && searchResult.Values["email"] != null) { - m.AdditionalData["Email"] = results.First(x => x.Id == m.Id.ToString()).Fields["email"]; + m.AdditionalData["Email"] = results.First(x => x.Id == m.Id.ToString()).Values["email"]; } - if (searchResult.Fields.ContainsKey("__key") && searchResult.Fields["__key"] != null) + if (searchResult.Values.ContainsKey("__key") && searchResult.Values["__key"] != null) { Guid key; - if (Guid.TryParse(searchResult.Fields["__key"], out key)) + if (Guid.TryParse(searchResult.Values["__key"], out key)) { m.Key = key; } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 2bc6969969..0e18f07a2b 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -62,7 +62,7 @@ - + 2.6.2.25 From ff3af8b7a70f134f54f142e638824f39dcd0d3c8 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 27 Nov 2018 17:43:56 +1100 Subject: [PATCH 17/44] Updates the content value set validator + tests --- src/Umbraco.Core/Models/PublicAccessEntry.cs | 4 + src/Umbraco.Examine/ContentValueSetBuilder.cs | 4 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 76 +++++- .../UmbracoContentIndexerOptions.cs | 33 ++- .../UmbracoContentValueSetValidator.cs | 53 ++-- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../UmbracoExamine/EventsTest.cs | 7 +- .../UmbracoExamine/TestDataService.cs | 32 --- .../UmbracoContentValueSetValidatorTests.cs | 236 ++++++++++++++++++ 9 files changed, 387 insertions(+), 60 deletions(-) delete mode 100644 src/Umbraco.Tests/UmbracoExamine/TestDataService.cs create mode 100644 src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs diff --git a/src/Umbraco.Core/Models/PublicAccessEntry.cs b/src/Umbraco.Core/Models/PublicAccessEntry.cs index e93dc56e35..df2d9f9ddc 100644 --- a/src/Umbraco.Core/Models/PublicAccessEntry.cs +++ b/src/Umbraco.Core/Models/PublicAccessEntry.cs @@ -21,6 +21,10 @@ namespace Umbraco.Core.Models public PublicAccessEntry(IContent protectedNode, IContent loginNode, IContent noAccessNode, IEnumerable ruleCollection) { + if (protectedNode == null) throw new ArgumentNullException(nameof(protectedNode)); + if (loginNode == null) throw new ArgumentNullException(nameof(loginNode)); + if (noAccessNode == null) throw new ArgumentNullException(nameof(noAccessNode)); + LoginNodeId = loginNode.Id; NoAccessNodeId = noAccessNode.Id; _protectedNodeId = protectedNode.Id; diff --git a/src/Umbraco.Examine/ContentValueSetBuilder.cs b/src/Umbraco.Examine/ContentValueSetBuilder.cs index 5dd856ee7a..ec52bbbd4f 100644 --- a/src/Umbraco.Examine/ContentValueSetBuilder.cs +++ b/src/Umbraco.Examine/ContentValueSetBuilder.cs @@ -63,12 +63,12 @@ namespace Umbraco.Examine {"writerName", new object[] {c.GetWriterProfile(_userService)?.Name ?? "??"}}, {"writerID", new object[] {c.WriterId}}, {"template", new object[] {c.Template?.Id ?? 0}}, - {$"{UmbracoExamineIndexer.SpecialFieldPrefix}VariesByCulture", new object[] {0}}, + {UmbracoContentIndexer.VariesByCultureFieldName, new object[] {0}}, }; if (isVariant) { - values[$"{UmbracoExamineIndexer.SpecialFieldPrefix}VariesByCulture"] = new object[] { 1 }; + values[UmbracoContentIndexer.VariesByCultureFieldName] = new object[] { 1 }; foreach (var culture in c.AvailableCultures) { diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index e5f05caf43..984f70edcd 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -31,6 +31,8 @@ namespace Umbraco.Examine /// public class UmbracoContentIndexer : UmbracoExamineIndexer { + public const string VariesByCultureFieldName = UmbracoExamineIndexer.SpecialFieldPrefix + "VariesByCulture"; + public IValueSetBuilder MediaValueSetBuilder { get; } public IValueSetBuilder ContentValueSetBuilder { get; } protected IContentService ContentService { get; } @@ -304,10 +306,6 @@ namespace Umbraco.Examine mediaParentId = ParentId.Value; } - // merge note: 7.5 changes this to use mediaService.GetPagedXmlEntries but we cannot merge the - // change here as mediaService does not provide access to Xml in v8 - and actually Examine should - // not assume that Umbraco provides Xml at all. - IMedia[] media; do @@ -334,9 +332,77 @@ namespace Umbraco.Examine } } - + //TODO: We want to make a public method that iterates a data set, potentially with callbacks so that we can iterate + // a single data set once but populate multiple indexes with it. This is for Startup performance when no indexes exist. + // This could be used for any indexer of type UmbracoContentIndexer - BUT that means we need to make another interface + // for content indexers since UmbracoContentIndexer is strongly tied to lucene, so maybe we have a more generic interface + // or add to the current IUmbracoIndexer interface #endregion } + + public class ContentIndexDataSource + { + public ContentIndexDataSource(bool supportUnpublishedContent, int? parentId, + IContentService contentService, ISqlContext sqlContext, + IValueSetBuilder contentValueSetBuilder) + { + SupportUnpublishedContent = supportUnpublishedContent; + ParentId = parentId; + ContentService = contentService; + _contentValueSetBuilder = contentValueSetBuilder; + if (sqlContext == null) throw new ArgumentNullException(nameof(sqlContext)); + _publishedQuery = sqlContext.Query().Where(x => x.Published); + + } + + public bool SupportUnpublishedContent { get; } + public int? ParentId { get; } + public IContentService ContentService { get; } + + /// + /// This is a static query, it's parameters don't change so store statically + /// + private static IQuery _publishedQuery; + private readonly IValueSetBuilder _contentValueSetBuilder; + + public void Index(params IIndexer[] indexes) + { + const int pageSize = 10000; + var pageIndex = 0; + + var contentParentId = -1; + if (ParentId.HasValue && ParentId.Value > 0) + { + contentParentId = ParentId.Value; + } + IContent[] content; + + do + { + long total; + + IEnumerable descendants; + if (SupportUnpublishedContent) + { + descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total); + } + else + { + //add the published filter + //note: We will filter for published variants in the validator + descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, + _publishedQuery, Ordering.By("Path", Direction.Ascending)); + } + + content = descendants.ToArray(); + + foreach(var index in indexes) + index.IndexItems(_contentValueSetBuilder.GetValueSets(content)); + + pageIndex++; + } while (content.Length == pageSize); + } + } } diff --git a/src/Umbraco.Examine/UmbracoContentIndexerOptions.cs b/src/Umbraco.Examine/UmbracoContentIndexerOptions.cs index 47b8a76c0f..55fd35e012 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexerOptions.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexerOptions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace Umbraco.Examine { @@ -10,14 +11,42 @@ namespace Umbraco.Examine { public bool SupportUnpublishedContent { get; private set; } public bool SupportProtectedContent { get; private set; } - //TODO: We should make this a GUID! But to do that we sort of need to store the 'Path' as a comma list of GUIDs instead of int public int? ParentId { get; private set; } - public UmbracoContentIndexerOptions(bool supportUnpublishedContent, bool supportProtectedContent, int? parentId) + /// + /// Optional inclusion list of content types to index + /// + /// + /// All other types will be ignored if they do not match this list + /// + public IEnumerable IncludeContentTypes { get; private set; } + + /// + /// Optional exclusion list of content types to ignore + /// + /// + /// Any content type alias matched in this will not be included in the index + /// + public IEnumerable ExcludeContentTypes { get; private set; } + + /// + /// Creates a new + /// + /// If the index supports unpublished content + /// If the index supports protected content + /// Optional value indicating to only index content below this ID + /// Optional content type alias inclusion list + /// Optional content type alias exclusion list + public UmbracoContentIndexerOptions(bool supportUnpublishedContent, bool supportProtectedContent, + int? parentId = null, IEnumerable includeContentTypes = null, IEnumerable excludeContentTypes = null) { SupportUnpublishedContent = supportUnpublishedContent; SupportProtectedContent = supportProtectedContent; ParentId = parentId; + IncludeContentTypes = includeContentTypes; + ExcludeContentTypes = excludeContentTypes; } + + } } diff --git a/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs b/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs index fcb058d268..220f082e48 100644 --- a/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs +++ b/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs @@ -1,8 +1,10 @@ using System; +using System.Collections.Generic; using System.Linq; using Examine; using Examine.LuceneEngine.Providers; using Umbraco.Core; +using Umbraco.Core.Models; using Umbraco.Core.Services; namespace Umbraco.Examine @@ -15,7 +17,9 @@ namespace Umbraco.Examine { private readonly UmbracoContentIndexerOptions _options; private readonly IPublicAccessService _publicAccessService; + private const string PathKey = "path"; + public UmbracoContentValueSetValidator(UmbracoContentIndexerOptions options, IPublicAccessService publicAccessService) { _options = options; @@ -25,16 +29,31 @@ namespace Umbraco.Examine public bool Validate(ValueSet valueSet) { //check for published content - if (valueSet.Category == IndexTypes.Content - && valueSet.Values.ContainsKey(UmbracoExamineIndexer.PublishedFieldName)) + if (valueSet.Category == IndexTypes.Content && !_options.SupportUnpublishedContent) { - //fixme - variants? - - var published = valueSet.Values[UmbracoExamineIndexer.PublishedFieldName] != null && valueSet.Values[UmbracoExamineIndexer.PublishedFieldName][0].Equals(1); - //we don't support unpublished and the item is not published return false - if (_options.SupportUnpublishedContent == false && published == false) - { + if (!valueSet.Values.TryGetValue(UmbracoExamineIndexer.PublishedFieldName, out var published)) return false; + + if (!published[0].Equals(1)) + return false; + + //deal with variants, if there are unpublished variants than we need to remove them from the value set + if (valueSet.Values.TryGetValue(UmbracoContentIndexer.VariesByCultureFieldName, out var variesByCulture) + && variesByCulture.Count > 0 && variesByCulture[0].Equals(1)) + { + //so this valueset is for a content that varies by culture, now check for non-published cultures and remove those values + foreach(var publishField in valueSet.Values.Where(x => x.Key.StartsWith($"{UmbracoExamineIndexer.PublishedFieldName}_")).ToList()) + { + if (publishField.Value.Count <= 0 || !publishField.Value[0].Equals(1)) + { + //this culture is not published, so remove all of these culture values + var cultureSuffix = publishField.Key.Substring(publishField.Key.LastIndexOf('_')); + foreach (var cultureField in valueSet.Values.Where(x => x.Key.InvariantEndsWith(cultureSuffix)).ToList()) + { + valueSet.Values.Remove(cultureField.Key); + } + } + } } } @@ -45,11 +64,9 @@ namespace Umbraco.Examine if (pathValues[0].ToString().IsNullOrWhiteSpace()) return false; var path = pathValues[0].ToString(); - // Test for access if we're only indexing published content // return nothing if we're not supporting protected content and it is protected, and we're not supporting unpublished content if (valueSet.Category == IndexTypes.Content - && _options.SupportUnpublishedContent == false - && _options.SupportProtectedContent == false + && !_options.SupportProtectedContent && _publicAccessService.IsProtected(path)) { return false; @@ -58,20 +75,26 @@ namespace Umbraco.Examine //check if this document is a descendent of the parent if (_options.ParentId.HasValue && _options.ParentId.Value > 0) { - if (path.IsNullOrWhiteSpace()) return false; - if (path.Contains(string.Concat(",", _options.ParentId.Value, ",")) == false) + if (!path.Contains(string.Concat(",", _options.ParentId.Value, ","))) return false; } //check for recycle bin - if (_options.SupportUnpublishedContent == false) + if (!_options.SupportUnpublishedContent) { - if (path.IsNullOrWhiteSpace()) return false; var recycleBinId = valueSet.Category == IndexTypes.Content ? Constants.System.RecycleBinContent : Constants.System.RecycleBinMedia; if (path.Contains(string.Concat(",", recycleBinId, ","))) return false; } + //check if this document is of a correct type of node type alias + if (_options.IncludeContentTypes != null && !_options.IncludeContentTypes.Contains(valueSet.ItemType)) + return false; + + //if this node type is part of our exclusion list + if (_options.ExcludeContentTypes != null && _options.ExcludeContentTypes.Contains(valueSet.ItemType)) + return false; + return true; } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 8262fb3cd3..10ba02b314 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -206,6 +206,7 @@ + @@ -463,7 +464,6 @@ - True True diff --git a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs index fa5a5afaf9..217ce307f0 100644 --- a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs @@ -10,6 +10,7 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Tests.UmbracoExamine { + [TestFixture] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] public class EventsTest : ExamineBaseTest @@ -24,7 +25,7 @@ namespace Umbraco.Tests.UmbracoExamine using (indexer.ProcessNonAsync()) { var searcher = indexer.GetSearcher(); - + var contentService = new ExamineDemoDataContentService(); //get a node from the data repo var node = contentService.GetPublishedContentByXPath("//*[string-length(@id)>0 and number(@id)>0]") @@ -33,9 +34,9 @@ namespace Umbraco.Tests.UmbracoExamine .First(); var valueSet = node.ConvertToValueSet(IndexTypes.Content); - indexer.IndexItems(new[] {valueSet}); + indexer.IndexItems(new[] { valueSet }); - var found = searcher.Search(searcher.CreateCriteria().Id((string) node.Attribute("id")).Compile()); + var found = searcher.Search(searcher.CreateCriteria().Id((string)node.Attribute("id")).Compile()); Assert.AreEqual(0, found.TotalItemCount); } diff --git a/src/Umbraco.Tests/UmbracoExamine/TestDataService.cs b/src/Umbraco.Tests/UmbracoExamine/TestDataService.cs deleted file mode 100644 index 856c3e99aa..0000000000 --- a/src/Umbraco.Tests/UmbracoExamine/TestDataService.cs +++ /dev/null @@ -1,32 +0,0 @@ -//using System.IO; -//using Umbraco.Tests.TestHelpers; -//using UmbracoExamine.DataServices; - -//namespace Umbraco.Tests.UmbracoExamine -//{ -// public class TestDataService : IDataService -// { - -// public TestDataService() -// { -// ContentService = new TestContentService(); -// LogService = new TestLogService(); -// MediaService = new TestMediaService(); -// } - -// #region IDataService Members - -// public IContentService ContentService { get; internal set; } - -// public ILogService LogService { get; internal set; } - -// public IMediaService MediaService { get; internal set; } - -// public string MapPath(string virtualPath) -// { -// return new DirectoryInfo(TestHelper.CurrentAssemblyDirectory) + "\\" + virtualPath.Replace("/", "\\"); -// } - -// #endregion -// } -//} diff --git a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs new file mode 100644 index 0000000000..d06b2b4fff --- /dev/null +++ b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs @@ -0,0 +1,236 @@ +using Examine; +using NUnit.Framework; +using Umbraco.Examine; +using Moq; +using Umbraco.Core.Services; +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.Models; +using System; +using System.Linq; + +namespace Umbraco.Tests.UmbracoExamine +{ + [TestFixture] + public class UmbracoContentValueSetValidatorTests + { + [Test] + public void Must_Have_Path() + { + var validator = new UmbracoContentValueSetValidator( + new UmbracoContentIndexerOptions(true, true, null), + Mock.Of()); + + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.IsTrue(result); + } + + [Test] + public void Parent_Id() + { + var validator = new UmbracoContentValueSetValidator( + new UmbracoContentIndexerOptions(true, true, 555), + Mock.Of()); + + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,444" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555,777" })); + Assert.IsTrue(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555,777,999" })); + Assert.IsTrue(result); + } + + [Test] + public void Inclusion_List() + { + var validator = new UmbracoContentValueSetValidator( + new UmbracoContentIndexerOptions(true, true, includeContentTypes: new List { "include-content" }), + Mock.Of()); + + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, "include-content", new { hello = "world", path = "-1,555" })); + Assert.IsTrue(result); + } + + [Test] + public void Exclusion_List() + { + var validator = new UmbracoContentValueSetValidator( + new UmbracoContentIndexerOptions(true, true, excludeContentTypes: new List { "exclude-content" }), + Mock.Of()); + + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); + Assert.IsTrue(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.IsTrue(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, "exclude-content", new { hello = "world", path = "-1,555" })); + Assert.IsFalse(result); + } + + [Test] + public void Inclusion_Exclusion_List() + { + var validator = new UmbracoContentValueSetValidator( + new UmbracoContentIndexerOptions(true, true, + includeContentTypes: new List { "include-content", "exclude-content" }, + excludeContentTypes: new List { "exclude-content" }), + Mock.Of()); + + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, "exclude-content", new { hello = "world", path = "-1,555" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, "include-content", new { hello = "world", path = "-1,555" })); + Assert.IsTrue(result); + } + + [Test] + public void Recycle_Bin() + { + var validator = new UmbracoContentValueSetValidator( + new UmbracoContentIndexerOptions(false, true, null), + Mock.Of()); + + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555,777" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, + new Dictionary + { + ["hello"] = "world", + ["path"] = "-1,555", + [UmbracoExamineIndexer.PublishedFieldName] = 1 + })); + Assert.IsTrue(result); + } + + [Test] + public void Published_Only() + { + var validator = new UmbracoContentValueSetValidator( + new UmbracoContentIndexerOptions(false, true, null), + Mock.Of()); + + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, + new Dictionary + { + ["hello"] = "world", + ["path"] = "-1,555", + [UmbracoExamineIndexer.PublishedFieldName] = 0 + })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, + new Dictionary + { + ["hello"] = "world", + ["path"] = "-1,555", + [UmbracoExamineIndexer.PublishedFieldName] = 1 + })); + Assert.IsTrue(result); + } + + [Test] + public void Published_Only_With_Variants() + { + var validator = new UmbracoContentValueSetValidator( + new UmbracoContentIndexerOptions(false, true, null), + Mock.Of()); + + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, + new Dictionary + { + ["hello"] = "world", + ["path"] = "-1,555", + [UmbracoContentIndexer.VariesByCultureFieldName] = 1, + [UmbracoExamineIndexer.PublishedFieldName] = 0 + })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Content, + new Dictionary + { + ["hello"] = "world", + ["path"] = "-1,555", + [UmbracoContentIndexer.VariesByCultureFieldName] = 1, + [UmbracoExamineIndexer.PublishedFieldName] = 1 + })); + Assert.IsTrue(result); + + var valueSet = new ValueSet("555", IndexTypes.Content, + new Dictionary + { + ["hello"] = "world", + ["path"] = "-1,555", + [UmbracoContentIndexer.VariesByCultureFieldName] = 1, + [$"{UmbracoExamineIndexer.PublishedFieldName}_en-us"] = 1, + ["hello_en-us"] = "world", + ["title_en-us"] = "my title", + [$"{UmbracoExamineIndexer.PublishedFieldName}_es-es"] = 0, + ["hello_es-ES"] = "world", + ["title_es-ES"] = "my title", + [UmbracoExamineIndexer.PublishedFieldName] = 1 + }); + Assert.AreEqual(10, valueSet.Values.Count()); + Assert.IsTrue(valueSet.Values.ContainsKey($"{UmbracoExamineIndexer.PublishedFieldName}_es-es")); + Assert.IsTrue(valueSet.Values.ContainsKey("hello_es-ES")); + Assert.IsTrue(valueSet.Values.ContainsKey("title_es-ES")); + + result = validator.Validate(valueSet); + Assert.IsTrue(result); + + Assert.AreEqual(7, valueSet.Values.Count()); //filtered to 7 values (removes es-es values) + Assert.IsFalse(valueSet.Values.ContainsKey($"{UmbracoExamineIndexer.PublishedFieldName}_es-es")); + Assert.IsFalse(valueSet.Values.ContainsKey("hello_es-ES")); + Assert.IsFalse(valueSet.Values.ContainsKey("title_es-ES")); + } + + [Test] + public void Non_Protected() + { + var publicAccessService = new Mock(); + publicAccessService.Setup(x => x.IsProtected("-1,555")) + .Returns(Attempt.Succeed(new PublicAccessEntry(Guid.NewGuid(), 555, 444, 333, Enumerable.Empty()))); + publicAccessService.Setup(x => x.IsProtected("-1,777")) + .Returns(Attempt.Fail()); + var validator = new UmbracoContentValueSetValidator( + new UmbracoContentIndexerOptions(true, false, null), + publicAccessService.Object); + + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("777", IndexTypes.Content, new { hello = "world", path = "-1,777" })); + Assert.IsTrue(result); + } + } +} From 3fd0aef18ed13e2d107436c1451dfef9714947d1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 28 Nov 2018 01:23:02 +1100 Subject: [PATCH 18/44] decouples data lookup from indexers and rebuilding logic --- .../Config/IndexFieldCollection.cs | 47 ++++ .../Config/IndexSetCollection.cs | 44 --- src/Umbraco.Examine/ContentIndexPopulator.cs | 97 +++++++ src/Umbraco.Examine/IIndexPopulator.cs | 16 ++ src/Umbraco.Examine/IUmbracoContentIndexer.cs | 12 + src/Umbraco.Examine/IUmbracoIndexer.cs | 1 + src/Umbraco.Examine/IUmbracoMediaIndexer.cs | 12 + src/Umbraco.Examine/IndexRebuilder.cs | 48 ++++ src/Umbraco.Examine/MediaIndexPopulator.cs | 68 +++++ src/Umbraco.Examine/MemberIndexPopulator.cs | 42 +++ .../PublishedContentIndexPopulator.cs | 22 ++ src/Umbraco.Examine/Umbraco.Examine.csproj | 11 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 215 +-------------- src/Umbraco.Examine/UmbracoExamineIndexer.cs | 63 ----- src/Umbraco.Examine/UmbracoMemberIndexer.cs | 59 ---- .../PublishedContent/PublishedMediaTests.cs | 49 ++-- .../TestHelpers/Stubs/TestExamineManager.cs | 42 +-- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../UmbracoExamine/EventsTest.cs | 2 +- .../UmbracoExamine/IndexInitializer.cs | 258 ++++++++---------- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 50 ++-- .../UmbracoExamine/SearchTests.cs | 13 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- ...aseServerRegistrarAndMessengerComponent.cs | 9 +- .../Editors/ExamineManagementController.cs | 4 +- src/Umbraco.Web/Search/ExamineComponent.cs | 117 ++++---- .../Search/IUmbracoIndexesBuilder.cs | 2 +- .../Search/UmbracoIndexesBuilder.cs | 46 ++-- src/Umbraco.Web/Suspendable.cs | 12 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- 30 files changed, 670 insertions(+), 697 deletions(-) create mode 100644 src/Umbraco.Examine/Config/IndexFieldCollection.cs create mode 100644 src/Umbraco.Examine/ContentIndexPopulator.cs create mode 100644 src/Umbraco.Examine/IIndexPopulator.cs create mode 100644 src/Umbraco.Examine/IUmbracoContentIndexer.cs create mode 100644 src/Umbraco.Examine/IUmbracoMediaIndexer.cs create mode 100644 src/Umbraco.Examine/IndexRebuilder.cs create mode 100644 src/Umbraco.Examine/MediaIndexPopulator.cs create mode 100644 src/Umbraco.Examine/MemberIndexPopulator.cs create mode 100644 src/Umbraco.Examine/PublishedContentIndexPopulator.cs diff --git a/src/Umbraco.Examine/Config/IndexFieldCollection.cs b/src/Umbraco.Examine/Config/IndexFieldCollection.cs new file mode 100644 index 0000000000..063c157dbe --- /dev/null +++ b/src/Umbraco.Examine/Config/IndexFieldCollection.cs @@ -0,0 +1,47 @@ +using System.Configuration; + +namespace Umbraco.Examine.Config +{ + public sealed class IndexFieldCollection : ConfigurationElementCollection + { + #region Overridden methods to define collection + protected override ConfigurationElement CreateNewElement() + { + return new ConfigIndexField(); + } + protected override object GetElementKey(ConfigurationElement element) + { + ConfigIndexField field = (ConfigIndexField)element; + return field.Name; + } + + public override bool IsReadOnly() + { + return false; + } + #endregion + + /// + /// Adds an index field to the collection + /// + /// + public void Add(ConfigIndexField field) + { + BaseAdd(field, true); + } + + /// + /// Default property for accessing an IndexField definition + /// + /// Field Name + /// + public new ConfigIndexField this[string name] + { + get + { + return (ConfigIndexField)this.BaseGet(name); + } + } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Examine/Config/IndexSetCollection.cs b/src/Umbraco.Examine/Config/IndexSetCollection.cs index 5ba2382563..bfce8e4cce 100644 --- a/src/Umbraco.Examine/Config/IndexSetCollection.cs +++ b/src/Umbraco.Examine/Config/IndexSetCollection.cs @@ -3,50 +3,6 @@ namespace Umbraco.Examine.Config { - public sealed class IndexFieldCollection : ConfigurationElementCollection - { - #region Overridden methods to define collection - protected override ConfigurationElement CreateNewElement() - { - return new ConfigIndexField(); - } - protected override object GetElementKey(ConfigurationElement element) - { - ConfigIndexField field = (ConfigIndexField)element; - return field.Name; - } - - public override bool IsReadOnly() - { - return false; - } - #endregion - - /// - /// Adds an index field to the collection - /// - /// - public void Add(ConfigIndexField field) - { - BaseAdd(field, true); - } - - /// - /// Default property for accessing an IndexField definition - /// - /// Field Name - /// - public new ConfigIndexField this[string name] - { - get - { - return (ConfigIndexField)this.BaseGet(name); - } - } - - } - - public sealed class IndexSetCollection : ConfigurationElementCollection { #region Overridden methods to define collection diff --git a/src/Umbraco.Examine/ContentIndexPopulator.cs b/src/Umbraco.Examine/ContentIndexPopulator.cs new file mode 100644 index 0000000000..211e2d51de --- /dev/null +++ b/src/Umbraco.Examine/ContentIndexPopulator.cs @@ -0,0 +1,97 @@ +using System; +using System.Linq; +using Examine; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.DatabaseModelDefinitions; +using Umbraco.Core.Persistence.Querying; + +namespace Umbraco.Examine +{ + + /// + /// Performs the data lookups required to rebuild a content index + /// + public class ContentIndexPopulator : IIndexPopulator + { + private readonly IContentService _contentService; + private readonly IValueSetBuilder _contentValueSetBuilder; + + /// + /// This is a static query, it's parameters don't change so store statically + /// + private static IQuery _publishedQuery; + + private readonly bool _supportUnpublishedContent; + private readonly int? _parentId; + + /// + /// Default constructor to lookup all content data + /// + /// + /// + /// + public ContentIndexPopulator(IContentService contentService, ISqlContext sqlContext, IValueSetBuilder contentValueSetBuilder) + : this(true, null, contentService, sqlContext, contentValueSetBuilder) + { + } + + /// + /// Optional constructor allowing specifying custom query parameters + /// + /// + /// + /// + /// + /// + public ContentIndexPopulator(bool supportUnpublishedContent, int? parentId, IContentService contentService, ISqlContext sqlContext, IValueSetBuilder contentValueSetBuilder) + { + if (sqlContext == null) throw new ArgumentNullException(nameof(sqlContext)); + _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); + _contentValueSetBuilder = contentValueSetBuilder ?? throw new ArgumentNullException(nameof(contentValueSetBuilder)); + if (_publishedQuery != null) + _publishedQuery = sqlContext.Query().Where(x => x.Published); + _supportUnpublishedContent = supportUnpublishedContent; + _parentId = parentId; + } + + public void Populate(params IIndexer[] indexes) + { + const int pageSize = 10000; + var pageIndex = 0; + + var contentParentId = -1; + if (_parentId.HasValue && _parentId.Value > 0) + { + contentParentId = _parentId.Value; + } + IContent[] content; + + do + { + long total; + + if (_supportUnpublishedContent) + { + content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total).ToArray(); + } + else + { + //add the published filter + //note: We will filter for published variants in the validator + content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, + _publishedQuery, Ordering.By("Path", Direction.Ascending)).ToArray(); + } + + if (content.Length > 0) + { + foreach (var index in indexes) + index.IndexItems(_contentValueSetBuilder.GetValueSets(content)); + } + + pageIndex++; + } while (content.Length == pageSize); + } + } +} diff --git a/src/Umbraco.Examine/IIndexPopulator.cs b/src/Umbraco.Examine/IIndexPopulator.cs new file mode 100644 index 0000000000..fd71b3bb53 --- /dev/null +++ b/src/Umbraco.Examine/IIndexPopulator.cs @@ -0,0 +1,16 @@ +using Examine; + +namespace Umbraco.Examine +{ + /// + /// Populates indexes with data + /// + public interface IIndexPopulator + { + /// + /// Populates indexes with data + /// + /// + void Populate(params IIndexer[] indexes); + } +} diff --git a/src/Umbraco.Examine/IUmbracoContentIndexer.cs b/src/Umbraco.Examine/IUmbracoContentIndexer.cs new file mode 100644 index 0000000000..a7cc9f9c2f --- /dev/null +++ b/src/Umbraco.Examine/IUmbracoContentIndexer.cs @@ -0,0 +1,12 @@ +using Examine; + +namespace Umbraco.Examine +{ + //TODO: Rethink this, need a better way of rebuilding + /// + /// A Marker interface for defining an Umbraco content indexer + /// + public interface IUmbracoContentIndexer : IIndexer + { + } +} diff --git a/src/Umbraco.Examine/IUmbracoIndexer.cs b/src/Umbraco.Examine/IUmbracoIndexer.cs index c926bfb2fe..936c78c71d 100644 --- a/src/Umbraco.Examine/IUmbracoIndexer.cs +++ b/src/Umbraco.Examine/IUmbracoIndexer.cs @@ -2,6 +2,7 @@ namespace Umbraco.Examine { + /// /// A Marker interface for defining an Umbraco indexer /// diff --git a/src/Umbraco.Examine/IUmbracoMediaIndexer.cs b/src/Umbraco.Examine/IUmbracoMediaIndexer.cs new file mode 100644 index 0000000000..05ab46bec3 --- /dev/null +++ b/src/Umbraco.Examine/IUmbracoMediaIndexer.cs @@ -0,0 +1,12 @@ +using Examine; + +namespace Umbraco.Examine +{ + //TODO: Rethink this, need a better way of rebuilding + /// + /// A Marker interface for defining an Umbraco media indexer + /// + public interface IUmbracoMediaIndexer : IIndexer + { + } +} diff --git a/src/Umbraco.Examine/IndexRebuilder.cs b/src/Umbraco.Examine/IndexRebuilder.cs new file mode 100644 index 0000000000..5488fa2583 --- /dev/null +++ b/src/Umbraco.Examine/IndexRebuilder.cs @@ -0,0 +1,48 @@ +using System.Linq; +using Examine; + +namespace Umbraco.Examine +{ + /// + /// Utility to rebuild all indexes ensuring minimal data queries + /// + public class IndexRebuilder + { + public IExamineManager ExamineManager { get; } + private readonly ContentIndexPopulator _contentIndexPopulator; + private readonly MediaIndexPopulator _mediaIndexPopulator; + + public IndexRebuilder(IExamineManager examineManager, ContentIndexPopulator contentIndexPopulator, MediaIndexPopulator mediaIndexPopulator) + { + ExamineManager = examineManager; + _contentIndexPopulator = contentIndexPopulator; + _mediaIndexPopulator = mediaIndexPopulator; + } + + public void RebuildIndexes(bool onlyEmptyIndexes) + { + var indexes = (onlyEmptyIndexes + ? ExamineManager.IndexProviders.Values.Where(x => x.IndexExists()) + : ExamineManager.IndexProviders.Values).ToList(); + + var contentIndexes = indexes.Where(x => x is IUmbracoContentIndexer).ToArray(); + var mediaIndexes = indexes.Where(x => x is IUmbracoContentIndexer).ToArray(); + var nonUmbracoIndexes = indexes.Except(contentIndexes).Except(mediaIndexes).ToArray(); + + foreach(var index in indexes) + index.CreateIndex(); // clear the index + + //reindex all content/media indexes with the same data source/lookup + _contentIndexPopulator.Populate(contentIndexes); + _mediaIndexPopulator.Populate(mediaIndexes); + + //then do the rest + foreach (var index in nonUmbracoIndexes) + { + index.CreateIndex(); + //TODO: How to rebuild? + } + + } + } +} diff --git a/src/Umbraco.Examine/MediaIndexPopulator.cs b/src/Umbraco.Examine/MediaIndexPopulator.cs new file mode 100644 index 0000000000..0dfd9b1bc5 --- /dev/null +++ b/src/Umbraco.Examine/MediaIndexPopulator.cs @@ -0,0 +1,68 @@ +using System.Linq; +using Examine; +using Umbraco.Core.Models; +using Umbraco.Core.Services; + +namespace Umbraco.Examine +{ + /// + /// Performs the data lookups required to rebuild a media index + /// + public class MediaIndexPopulator : IIndexPopulator + { + private readonly int? _parentId; + private readonly IMediaService _mediaService; + private readonly IValueSetBuilder _mediaValueSetBuilder; + + /// + /// Default constructor to lookup all content data + /// + /// + /// + public MediaIndexPopulator(IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) + : this(null, mediaService, mediaValueSetBuilder) + { + } + + /// + /// Optional constructor allowing specifying custom query parameters + /// + /// + /// + /// + public MediaIndexPopulator(int? parentId, IMediaService mediaService, IValueSetBuilder mediaValueSetBuilder) + { + _parentId = parentId; + _mediaService = mediaService; + _mediaValueSetBuilder = mediaValueSetBuilder; + } + + public void Populate(params IIndexer[] indexes) + { + const int pageSize = 10000; + var pageIndex = 0; + + var mediaParentId = -1; + + if (_parentId.HasValue && _parentId.Value > 0) + { + mediaParentId = _parentId.Value; + } + + IMedia[] media; + + do + { + media = _mediaService.GetPagedDescendants(mediaParentId, pageIndex, pageSize, out var total).ToArray(); + + if (media.Length > 0) + { + foreach (var index in indexes) + index.IndexItems(_mediaValueSetBuilder.GetValueSets(media)); + } + + pageIndex++; + } while (media.Length == pageSize); + } + } +} diff --git a/src/Umbraco.Examine/MemberIndexPopulator.cs b/src/Umbraco.Examine/MemberIndexPopulator.cs new file mode 100644 index 0000000000..cbe763e2c1 --- /dev/null +++ b/src/Umbraco.Examine/MemberIndexPopulator.cs @@ -0,0 +1,42 @@ +using System.Linq; +using Examine; +using Umbraco.Core.Models; +using Umbraco.Core.Services; + +namespace Umbraco.Examine +{ + public class MemberIndexPopulator: IIndexPopulator + { + private readonly IMemberService _memberService; + private readonly IValueSetBuilder _valueSetBuilder; + + public MemberIndexPopulator(IMemberService memberService, IValueSetBuilder valueSetBuilder) + { + _memberService = memberService; + _valueSetBuilder = valueSetBuilder; + } + public void Populate(params IIndexer[] indexes) + { + const int pageSize = 1000; + var pageIndex = 0; + + IMember[] members; + + //TODO: Add validators for member indexers for ConfigIndexCriteria.IncludeItemTypes + + //no node types specified, do all members + do + { + members = _memberService.GetAll(pageIndex, pageSize, out _).ToArray(); + + if (members.Length > 0) + { + foreach (var index in indexes) + index.IndexItems(_valueSetBuilder.GetValueSets(members)); + } + + pageIndex++; + } while (members.Length == pageSize); + } + } +} diff --git a/src/Umbraco.Examine/PublishedContentIndexPopulator.cs b/src/Umbraco.Examine/PublishedContentIndexPopulator.cs new file mode 100644 index 0000000000..a29031ca3c --- /dev/null +++ b/src/Umbraco.Examine/PublishedContentIndexPopulator.cs @@ -0,0 +1,22 @@ +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Core.Persistence; + +namespace Umbraco.Examine +{ + /// + /// Performs the data lookups required to rebuild a content index containing only published content + /// + /// + /// The published (external) index will still rebuild just fine using the default which is what + /// is used when rebuilding all indexes, but this will be used when the single index is rebuilt and will go a little bit faster + /// since the data query is more specific. + /// + public class PublishedContentIndexPopulator : ContentIndexPopulator + { + public PublishedContentIndexPopulator(IContentService contentService, ISqlContext sqlContext, IValueSetBuilder contentValueSetBuilder) : + base(false, null, contentService, sqlContext, contentValueSetBuilder) + { + } + } +} diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 83a9ef0d7d..f5c85fada3 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + @@ -56,15 +56,24 @@ + + + + + + + + + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 984f70edcd..0433a75d41 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -2,10 +2,8 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; -using System.Linq; using Examine; using Umbraco.Core; -using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Examine.LuceneEngine.Indexing; @@ -14,29 +12,17 @@ using Lucene.Net.Analysis; using Lucene.Net.Store; using Umbraco.Core.Composing; using Umbraco.Core.Logging; -using Umbraco.Core.Persistence; -using Umbraco.Core.Persistence.DatabaseModelDefinitions; -using Umbraco.Core.Persistence.Querying; -using Umbraco.Core.Scoping; using Umbraco.Examine.Config; -using IContentService = Umbraco.Core.Services.IContentService; -using IMediaService = Umbraco.Core.Services.IMediaService; using Examine.LuceneEngine; -using Umbraco.Core.PropertyEditors; namespace Umbraco.Examine { /// /// An indexer for Umbraco content and media /// - public class UmbracoContentIndexer : UmbracoExamineIndexer + public class UmbracoContentIndexer : UmbracoExamineIndexer, IUmbracoContentIndexer, IUmbracoMediaIndexer { public const string VariesByCultureFieldName = UmbracoExamineIndexer.SpecialFieldPrefix + "VariesByCulture"; - - public IValueSetBuilder MediaValueSetBuilder { get; } - public IValueSetBuilder ContentValueSetBuilder { get; } - protected IContentService ContentService { get; } - protected IMediaService MediaService { get; } protected ILocalizationService LanguageService { get; } private int? _parentId; @@ -49,14 +35,7 @@ namespace Umbraco.Examine [EditorBrowsable(EditorBrowsableState.Never)] public UmbracoContentIndexer() { - ContentService = Current.Services.ContentService; - MediaService = Current.Services.MediaService; LanguageService = Current.Services.LocalizationService; - - ContentValueSetBuilder = new ContentValueSetBuilder(Current.PropertyEditors, Current.UrlSegmentProviders, Current.Services.UserService); - MediaValueSetBuilder = new MediaValueSetBuilder(Current.PropertyEditors, Current.UrlSegmentProviders, Current.Services.UserService); - - InitializeQueries(Current.SqlContext); } /// @@ -67,9 +46,6 @@ namespace Umbraco.Examine /// /// /// - /// - /// - /// /// /// /// @@ -79,12 +55,7 @@ namespace Umbraco.Examine Directory luceneDirectory, Analyzer defaultAnalyzer, ProfilingLogger profilingLogger, - IValueSetBuilder contentValueSetBuilder, - IValueSetBuilder mediaValueSetBuilder, - IContentService contentService, - IMediaService mediaService, ILocalizationService languageService, - ISqlContext sqlContext, IValueSetValidator validator, UmbracoContentIndexerOptions options, IReadOnlyDictionary> indexValueTypes = null) @@ -96,21 +67,9 @@ namespace Umbraco.Examine SupportProtectedContent = options.SupportProtectedContent; SupportUnpublishedContent = options.SupportUnpublishedContent; ParentId = options.ParentId; - ContentValueSetBuilder = contentValueSetBuilder ?? throw new ArgumentNullException(nameof(contentValueSetBuilder)); - MediaValueSetBuilder = mediaValueSetBuilder ?? throw new ArgumentNullException(nameof(mediaValueSetBuilder)); - ContentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); - MediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); LanguageService = languageService ?? throw new ArgumentNullException(nameof(languageService)); - - InitializeQueries(sqlContext); } - private void InitializeQueries(ISqlContext sqlContext) - { - if (sqlContext == null) throw new ArgumentNullException(nameof(sqlContext)); - if (_publishedQuery == null) - _publishedQuery = sqlContext.Query().Where(x => x.Published); - } #endregion @@ -133,9 +92,11 @@ namespace Umbraco.Examine /// /// An attempt is made to call on a provider after the provider has already been initialized. /// - + [EditorBrowsable(EditorBrowsableState.Never)] public override void Initialize(string name, NameValueCollection config) { + base.Initialize(name, config); + //check if there's a flag specifying to support unpublished content, //if not, set to false; if (config["supportUnpublished"] != null && bool.TryParse(config["supportUnpublished"], out var supportUnpublished)) @@ -143,7 +104,6 @@ namespace Umbraco.Examine else SupportUnpublishedContent = false; - //check if there's a flag specifying to support protected content, //if not, set to false; if (config["supportProtected"] != null && bool.TryParse(config["supportProtected"], out var supportProtected)) @@ -151,8 +111,6 @@ namespace Umbraco.Examine else SupportProtectedContent = false; - base.Initialize(name, config); - //now we need to build up the indexer options so we can create our validator int? parentId = null; if (IndexSetName.IsNullOrWhiteSpace() == false) @@ -160,11 +118,15 @@ namespace Umbraco.Examine var indexSet = IndexSets.Instance.Sets[IndexSetName]; parentId = indexSet.IndexParentId; } + ValueSetValidator = new UmbracoContentValueSetValidator( - new UmbracoContentIndexerOptions(SupportUnpublishedContent, SupportProtectedContent, parentId), + new UmbracoContentIndexerOptions( + SupportUnpublishedContent, SupportProtectedContent, parentId, + ConfigIndexCriteria.IncludeItemTypes, ConfigIndexCriteria.ExcludeItemTypes), //Using a singleton here, we can't inject this when using config based providers and we don't use this //anywhere else in this class Current.Services.PublicAccessService); + } #endregion @@ -186,8 +148,6 @@ namespace Umbraco.Examine protected set => _parentId = value; } - protected override IEnumerable SupportedTypes => new[] {IndexTypes.Content, IndexTypes.Media}; - #endregion #region Public methods @@ -245,164 +205,7 @@ namespace Umbraco.Examine return base.CreateFieldValueTypes(indexValueTypesFactory); } - /// - /// This is a static query, it's parameters don't change so store statically - /// - private static IQuery _publishedQuery; - - protected override void PerformIndexAll(string type) - { - const int pageSize = 10000; - var pageIndex = 0; - - switch (type) - { - case IndexTypes.Content: - var contentParentId = -1; - if (ParentId.HasValue && ParentId.Value > 0) - { - contentParentId = ParentId.Value; - } - IContent[] content; - - do - { - long total; - - IEnumerable descendants; - if (SupportUnpublishedContent) - { - descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total); - } - else - { - //add the published filter - descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, - _publishedQuery, Ordering.By("Path", Direction.Ascending)); - } - - //if specific types are declared we need to post filter them - //TODO: Update the service layer to join the cmsContentType table so we can query by content type too - if (ConfigIndexCriteria != null && ConfigIndexCriteria.IncludeItemTypes.Any()) - { - content = descendants.Where(x => ConfigIndexCriteria.IncludeItemTypes.Contains(x.ContentType.Alias)).ToArray(); - } - else - { - content = descendants.ToArray(); - } - - IndexItems(ContentValueSetBuilder.GetValueSets(content)); - - pageIndex++; - } while (content.Length == pageSize); - - break; - case IndexTypes.Media: - var mediaParentId = -1; - - if (ParentId.HasValue && ParentId.Value > 0) - { - mediaParentId = ParentId.Value; - } - - IMedia[] media; - - do - { - var descendants = MediaService.GetPagedDescendants(mediaParentId, pageIndex, pageSize, out _); - - //if specific types are declared we need to post filter them - //TODO: Update the service layer to join the cmsContentType table so we can query by content type too - if (ConfigIndexCriteria != null && ConfigIndexCriteria.IncludeItemTypes.Any()) - { - media = descendants.Where(x => ConfigIndexCriteria.IncludeItemTypes.Contains(x.ContentType.Alias)).ToArray(); - } - else - { - media = descendants.ToArray(); - } - - IndexItems(MediaValueSetBuilder.GetValueSets(media)); - - pageIndex++; - } while (media.Length == pageSize); - - break; - } - } - - //TODO: We want to make a public method that iterates a data set, potentially with callbacks so that we can iterate - // a single data set once but populate multiple indexes with it. This is for Startup performance when no indexes exist. - // This could be used for any indexer of type UmbracoContentIndexer - BUT that means we need to make another interface - // for content indexers since UmbracoContentIndexer is strongly tied to lucene, so maybe we have a more generic interface - // or add to the current IUmbracoIndexer interface - #endregion } - - public class ContentIndexDataSource - { - public ContentIndexDataSource(bool supportUnpublishedContent, int? parentId, - IContentService contentService, ISqlContext sqlContext, - IValueSetBuilder contentValueSetBuilder) - { - SupportUnpublishedContent = supportUnpublishedContent; - ParentId = parentId; - ContentService = contentService; - _contentValueSetBuilder = contentValueSetBuilder; - if (sqlContext == null) throw new ArgumentNullException(nameof(sqlContext)); - _publishedQuery = sqlContext.Query().Where(x => x.Published); - - } - - public bool SupportUnpublishedContent { get; } - public int? ParentId { get; } - public IContentService ContentService { get; } - - /// - /// This is a static query, it's parameters don't change so store statically - /// - private static IQuery _publishedQuery; - private readonly IValueSetBuilder _contentValueSetBuilder; - - public void Index(params IIndexer[] indexes) - { - const int pageSize = 10000; - var pageIndex = 0; - - var contentParentId = -1; - if (ParentId.HasValue && ParentId.Value > 0) - { - contentParentId = ParentId.Value; - } - IContent[] content; - - do - { - long total; - - IEnumerable descendants; - if (SupportUnpublishedContent) - { - descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total); - } - else - { - //add the published filter - //note: We will filter for published variants in the validator - descendants = ContentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, - _publishedQuery, Ordering.By("Path", Direction.Ascending)); - } - - content = descendants.ToArray(); - - foreach(var index in indexes) - index.IndexItems(_contentValueSetBuilder.GetValueSets(content)); - - pageIndex++; - } while (content.Length == pageSize); - } - } } diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index 3ede45c60a..19929d15fe 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -166,11 +166,6 @@ namespace Umbraco.Examine /// public bool SupportUnpublishedContent { get; protected set; } = false; - /// - /// the supported indexable types - /// - protected abstract IEnumerable SupportedTypes { get; } - protected ConfigIndexCriteria ConfigIndexCriteria { get; private set; } /// @@ -271,53 +266,6 @@ namespace Umbraco.Examine #endregion - - /// - /// override to check if we can actually initialize. - /// - /// - /// This check is required since the base examine lib will try to rebuild on startup - /// - public override void RebuildIndex() - { - if (CanInitialize()) - { - ProfilingLogger.Logger.Debug(GetType(), "Rebuilding index"); - using (new SafeCallContext()) - { - base.RebuildIndex(); - } - } - } - - /// - /// override to check if we can actually initialize. - /// - /// - /// This check is required since the base examine lib will try to rebuild on startup - /// - public override void IndexAll(string type) - { - if (CanInitialize()) - { - using (new SafeCallContext()) - { - base.IndexAll(type); - } - } - } - - public override void IndexItems(IEnumerable nodes) - { - if (CanInitialize()) - { - using (new SafeCallContext()) - { - base.IndexItems(nodes); - } - } - } - /// /// override to check if we can actually initialize. /// @@ -346,17 +294,6 @@ namespace Umbraco.Examine return _configBased == false || Current.RuntimeState.Level == RuntimeLevel.Run; } - /// - /// Reindexes all supported types - /// - protected override void PerformIndexRebuild() - { - foreach (var t in SupportedTypes) - { - IndexAll(t); - } - } - /// /// overridden for logging /// diff --git a/src/Umbraco.Examine/UmbracoMemberIndexer.cs b/src/Umbraco.Examine/UmbracoMemberIndexer.cs index 4fa924f995..4c59b4d4ca 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndexer.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndexer.cs @@ -23,17 +23,12 @@ namespace Umbraco.Examine /// public class UmbracoMemberIndexer : UmbracoExamineIndexer { - private readonly IValueSetBuilder _valueSetBuilder; - private readonly IMemberService _memberService; - /// /// Constructor for config/provider based indexes /// [EditorBrowsable(EditorBrowsableState.Never)] public UmbracoMemberIndexer() { - _memberService = Current.Services.MemberService; - _valueSetBuilder = new MemberValueSetBuilder(Current.PropertyEditors); } /// @@ -44,7 +39,6 @@ namespace Umbraco.Examine /// /// /// - /// /// public UmbracoMemberIndexer( string name, @@ -52,13 +46,9 @@ namespace Umbraco.Examine Directory luceneDirectory, Analyzer analyzer, ProfilingLogger profilingLogger, - IValueSetBuilder valueSetBuilder, - IMemberService memberService, IValueSetValidator validator = null) : base(name, fieldDefinitions, luceneDirectory, analyzer, profilingLogger, validator) { - _valueSetBuilder = valueSetBuilder ?? throw new ArgumentNullException(nameof(valueSetBuilder)); - _memberService = memberService ?? throw new ArgumentNullException(nameof(memberService)); } @@ -76,55 +66,6 @@ namespace Umbraco.Examine return base.CreateFieldValueTypes(indexValueTypesFactory); } - /// - protected override IEnumerable SupportedTypes => new[] {IndexTypes.Member}; - - /// - /// Reindex all members - /// - /// - protected override void PerformIndexAll(string type) - { - //This only supports members - if (SupportedTypes.Contains(type) == false) - return; - - const int pageSize = 1000; - var pageIndex = 0; - - IMember[] members; - - if (ConfigIndexCriteria != null && ConfigIndexCriteria.IncludeItemTypes.Any()) - { - //if there are specific node types then just index those - foreach (var nodeType in ConfigIndexCriteria.IncludeItemTypes) - { - do - { - members = _memberService.GetAll(pageIndex, pageSize, out _, "LoginName", Direction.Ascending, true, null, nodeType).ToArray(); - - IndexItems(_valueSetBuilder.GetValueSets(members)); - - pageIndex++; - } while (members.Length == pageSize); - } - } - else - { - //no node types specified, do all members - do - { - members = _memberService.GetAll(pageIndex, pageSize, out _).ToArray(); - - IndexItems(_valueSetBuilder.GetValueSets(members)); - - pageIndex++; - } while (members.Length == pageSize); - } - } - - - /// /// Ensure some custom values are added to the index /// diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 2a3b60fcdf..4b49fad3e4 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -113,12 +113,13 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Ensure_Children_Sorted_With_Examine() { + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, options: new UmbracoContentIndexerOptions(true, false, null))) { - - indexer.RebuildIndex(); + rebuilder.Populate(indexer); var searcher = indexer.GetSearcher(); var ctx = GetUmbracoContext("/test"); @@ -139,14 +140,15 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Do_Not_Find_In_Recycle_Bin() { + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, //include unpublished content since this uses the 'internal' indexer, it's up to the media cache to filter options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { - indexer.RebuildIndex(); - + rebuilder.Populate(indexer); var searcher = indexer.GetSearcher(); var ctx = GetUmbracoContext("/test"); @@ -185,13 +187,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Children_With_Examine() { + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { - indexer.RebuildIndex(); - + rebuilder.Populate(indexer); var searcher = indexer.GetSearcher(); var ctx = GetUmbracoContext("/test"); @@ -211,13 +214,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Descendants_With_Examine() { + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { - indexer.RebuildIndex(); - + rebuilder.Populate(indexer); var searcher = indexer.GetSearcher(); var ctx = GetUmbracoContext("/test"); @@ -237,13 +241,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void DescendantsOrSelf_With_Examine() { + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { - indexer.RebuildIndex(); - + rebuilder.Populate(indexer); var searcher = indexer.GetSearcher(); var ctx = GetUmbracoContext("/test"); @@ -263,13 +268,15 @@ namespace Umbraco.Tests.PublishedContent [Test] public void Ancestors_With_Examine() { + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { - indexer.RebuildIndex(); - + rebuilder.Populate(indexer); var ctx = GetUmbracoContext("/test"); var searcher = indexer.GetSearcher(); @@ -286,12 +293,14 @@ namespace Umbraco.Tests.PublishedContent [Test] public void AncestorsOrSelf_With_Examine() { + var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { - indexer.RebuildIndex(); + rebuilder.Populate(indexer); var ctx = GetUmbracoContext("/test"); diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs index 14c6a08c8b..f0abe053ac 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs @@ -9,26 +9,16 @@ namespace Umbraco.Tests.TestHelpers.Stubs private readonly ConcurrentDictionary _indexers = new ConcurrentDictionary(); private readonly ConcurrentDictionary _searchers = new ConcurrentDictionary(); - public void AddIndexer(string name, IIndexer indexer) + public void AddIndexer(IIndexer indexer) { - _indexers.TryAdd(name, indexer); + _indexers.TryAdd(indexer.Name, indexer); } - public void AddSearcher(string name, ISearcher searcher) + public void AddSearcher(ISearcher searcher) { - _searchers.TryAdd(name, searcher); + _searchers.TryAdd(searcher.Name, searcher); } - - public void DeleteFromIndexes(string nodeId) - { - //noop - } - - public void DeleteFromIndexes(string nodeId, IEnumerable providers) - { - //noop - } - + public void Dispose() { //noop @@ -39,31 +29,11 @@ namespace Umbraco.Tests.TestHelpers.Stubs return _indexers.TryGetValue(indexerName, out var indexer) ? indexer : null; } - public ISearcher GetRegisteredSearcher(string searcherName) + public ISearcher GetSearcher(string searcherName) { return _searchers.TryGetValue(searcherName, out var indexer) ? indexer : null; } - public void IndexAll(string indexCategory) - { - //noop - } - - public void IndexItems(ValueSet[] nodes) - { - //noop - } - - public void IndexItems(ValueSet[] nodes, IEnumerable providers) - { - //noop - } - - public void RebuildIndexes() - { - //noop - } - public IReadOnlyDictionary IndexProviders => _indexers; } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 10ba02b314..5fa0cf3469 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 217ce307f0..7f613753a5 100644 --- a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.UmbracoExamine public void Events_Ignoring_Node() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, //make parent id 999 so all are ignored options: new UmbracoContentIndexerOptions(false, false, 999))) using (indexer.ProcessNonAsync()) diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 6357447d54..c8a5cbceb4 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -29,168 +29,146 @@ namespace Umbraco.Tests.UmbracoExamine /// internal static class IndexInitializer { + public static ContentValueSetBuilder GetContentValueSetBuilder(PropertyEditorCollection propertyEditors) + { + var contentValueSetBuilder = new ContentValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, IndexInitializer.GetMockUserService()); + return contentValueSetBuilder; + } + + public static ContentIndexPopulator GetContentIndexRebuilder(PropertyEditorCollection propertyEditors, IContentService contentService, ISqlContext sqlContext) + { + var contentValueSetBuilder = GetContentValueSetBuilder(propertyEditors); + var contentIndexDataSource = new ContentIndexPopulator(true, null, contentService, sqlContext, contentValueSetBuilder); + return contentIndexDataSource; + } + + public static MediaIndexPopulator GetMediaIndexRebuilder(PropertyEditorCollection propertyEditors, IMediaService mediaService) + { + var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, IndexInitializer.GetMockUserService()); + var mediaIndexDataSource = new MediaIndexPopulator(null, mediaService, mediaValueSetBuilder); + return mediaIndexDataSource; + } + + public static IContentService GetMockContentService() + { + long longTotalRecs; + var demoData = new ExamineDemoDataContentService(); + + var allRecs = demoData.GetLatestContentByXPath("//*[@isDoc]") + .Root + .Elements() + .Select(x => Mock.Of( + m => + m.Id == (int)x.Attribute("id") && + m.ParentId == (int)x.Attribute("parentID") && + m.Level == (int)x.Attribute("level") && + m.CreatorId == 0 && + m.SortOrder == (int)x.Attribute("sortOrder") && + m.CreateDate == (DateTime)x.Attribute("createDate") && + m.UpdateDate == (DateTime)x.Attribute("updateDate") && + m.Name == (string)x.Attribute("nodeName") && + m.GetCultureName(It.IsAny()) == (string)x.Attribute("nodeName") && + m.Path == (string)x.Attribute("path") && + m.Properties == new PropertyCollection() && + m.ContentType == Mock.Of(mt => + mt.Icon == "test" && + mt.Alias == x.Name.LocalName && + mt.Id == (int)x.Attribute("nodeType")))) + .ToArray(); + + + return Mock.Of( + x => x.GetPagedDescendants( + It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny>(), It.IsAny()) + == allRecs); + } + + public static IUserService GetMockUserService() + { + return Mock.Of(x => x.GetProfileById(It.IsAny()) == Mock.Of(p => p.Id == 0 && p.Name == "admin")); + } + + public static IMediaService GetMockMediaService() + { + long totalRecs; + + var demoData = new ExamineDemoDataMediaService(); + + var allRecs = demoData.GetLatestMediaByXpath("//node") + .Root + .Elements() + .Select(x => Mock.Of( + m => + m.Id == (int)x.Attribute("id") && + m.ParentId == (int)x.Attribute("parentID") && + m.Level == (int)x.Attribute("level") && + m.CreatorId == 0 && + m.SortOrder == (int)x.Attribute("sortOrder") && + m.CreateDate == (DateTime)x.Attribute("createDate") && + m.UpdateDate == (DateTime)x.Attribute("updateDate") && + m.Name == (string)x.Attribute("nodeName") && + m.GetCultureName(It.IsAny()) == (string)x.Attribute("nodeName") && + m.Path == (string)x.Attribute("path") && + m.Properties == new PropertyCollection() && + m.ContentType == Mock.Of(mt => + mt.Alias == (string)x.Attribute("nodeTypeAlias") && + mt.Id == (int)x.Attribute("nodeType")))) + .ToArray(); + + // MOCK! + var mediaServiceMock = new Mock(); + + mediaServiceMock + .Setup(x => x.GetPagedDescendants( + It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny>(), It.IsAny()) + ).Returns(() => allRecs); + + //mediaServiceMock.Setup(service => service.GetPagedXmlEntries(It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs)) + // .Returns(() => allRecs.Select(x => x.ToXml())); + + return mediaServiceMock.Object; + } + + public static ILocalizationService GetMockLocalizationService() + { + return Mock.Of(x => x.GetAllLanguages() == Array.Empty()); + } + + public static IMediaTypeService GetMockMediaTypeService() + { + var mediaTypeServiceMock = new Mock(); + mediaTypeServiceMock.Setup(x => x.GetAll()) + .Returns(new List + { + new MediaType(-1) {Alias = "Folder", Name = "Folder", Id = 1031, Icon = "icon-folder"}, + new MediaType(-1) {Alias = "Image", Name = "Image", Id = 1032, Icon = "icon-picture"} + }); + return mediaTypeServiceMock.Object; + } + public static UmbracoContentIndexer GetUmbracoIndexer( ProfilingLogger profilingLogger, Directory luceneDir, - ISqlContext sqlContext, - PropertyEditorCollection propertyEditors, Analyzer analyzer = null, - IContentService contentService = null, - IMediaService mediaService = null, - IMemberService memberService = null, - IUserService userService = null, ILocalizationService languageService = null, - IContentTypeService contentTypeService = null, - IMediaTypeService mediaTypeService = null, UmbracoContentIndexerOptions options = null) { if (languageService == null) - { - languageService = Mock.Of( - x => x.GetAllLanguages() == Array.Empty()); - } - - if (contentService == null) - { - long longTotalRecs; - var demoData = new ExamineDemoDataContentService(); - - var allRecs = demoData.GetLatestContentByXPath("//*[@isDoc]") - .Root - .Elements() - .Select(x => Mock.Of( - m => - m.Id == (int)x.Attribute("id") && - m.ParentId == (int)x.Attribute("parentID") && - m.Level == (int)x.Attribute("level") && - m.CreatorId == 0 && - m.SortOrder == (int)x.Attribute("sortOrder") && - m.CreateDate == (DateTime)x.Attribute("createDate") && - m.UpdateDate == (DateTime)x.Attribute("updateDate") && - m.Name == (string)x.Attribute("nodeName") && - m.GetCultureName(It.IsAny()) == (string)x.Attribute("nodeName") && - m.Path == (string)x.Attribute("path") && - m.Properties == new PropertyCollection() && - m.ContentType == Mock.Of(mt => - mt.Icon == "test" && - mt.Alias == x.Name.LocalName && - mt.Id == (int)x.Attribute("nodeType")))) - .ToArray(); - - - contentService = Mock.Of( - x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs, It.IsAny>(), It.IsAny()) - == - allRecs); - } - if (userService == null) - { - userService = Mock.Of(x => x.GetProfileById(It.IsAny()) == Mock.Of(p => p.Id == 0 && p.Name == "admin")); - } - - if (mediaService == null) - { - long totalRecs; - - var demoData = new ExamineDemoDataMediaService(); - - var allRecs = demoData.GetLatestMediaByXpath("//node") - .Root - .Elements() - .Select(x => Mock.Of( - m => - m.Id == (int) x.Attribute("id") && - m.ParentId == (int) x.Attribute("parentID") && - m.Level == (int) x.Attribute("level") && - m.CreatorId == 0 && - m.SortOrder == (int) x.Attribute("sortOrder") && - m.CreateDate == (DateTime) x.Attribute("createDate") && - m.UpdateDate == (DateTime) x.Attribute("updateDate") && - m.Name == (string) x.Attribute("nodeName") && - m.GetCultureName(It.IsAny()) == (string)x.Attribute("nodeName") && - m.Path == (string) x.Attribute("path") && - m.Properties == new PropertyCollection() && - m.ContentType == Mock.Of(mt => - mt.Alias == (string) x.Attribute("nodeTypeAlias") && - mt.Id == (int) x.Attribute("nodeType")))) - .ToArray(); - - // MOCK! - var mediaServiceMock = new Mock(); - - mediaServiceMock - .Setup(x => x.GetPagedDescendants( - It.IsAny(), It.IsAny(), It.IsAny(), out totalRecs, It.IsAny>(), It.IsAny()) - ).Returns(() => allRecs); - - //mediaServiceMock.Setup(service => service.GetPagedXmlEntries(It.IsAny(), It.IsAny(), It.IsAny(), out longTotalRecs)) - // .Returns(() => allRecs.Select(x => x.ToXml())); - - mediaService = mediaServiceMock.Object; - - } + languageService = GetMockLocalizationService(); if (analyzer == null) - { analyzer = new StandardAnalyzer(Version.LUCENE_30); - } - - //var indexSet = new IndexSet(); - // var indexCriteria = indexSet.ToIndexCriteria(dataService, UmbracoContentIndexer.IndexFieldPolicies); - - //var i = new UmbracoContentIndexer(indexCriteria, - // luceneDir, //custom lucene directory - // dataService, - // contentService, - // mediaService, - // dataTypeService, - // userService, - // new[] { new DefaultUrlSegmentProvider() }, - // analyzer, - // false); - - //i.IndexSecondsInterval = 1; if (options == null) - { options = new UmbracoContentIndexerOptions(false, false, null); - } - - if (mediaTypeService == null) - { - var mediaTypeServiceMock = new Mock(); - mediaTypeServiceMock.Setup(x => x.GetAll()) - .Returns(new List - { - new MediaType(-1) {Alias = "Folder", Name = "Folder", Id = 1031, Icon = "icon-folder"}, - new MediaType(-1) {Alias = "Image", Name = "Image", Id = 1032, Icon = "icon-picture"} - }); - mediaTypeService = mediaTypeServiceMock.Object; - } - - // fixme oops?! - //var query = new Mock>(); - //query - // .Setup(x => x.GetWhereClauses()) - // .Returns(new List> { new Tuple($"{Constants.DatabaseSchema.Tables.Document}.published", new object[] { 1 }) }); - //scopeProvider - // .Setup(x => x.Query()) - // .Returns(query.Object); - var i = new UmbracoContentIndexer( "testIndexer", UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, luceneDir, analyzer, profilingLogger, - new ContentValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, userService), - new MediaValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, userService), - contentService, - mediaService, languageService, - sqlContext, new UmbracoContentValueSetValidator(options, Mock.Of()), options); diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index e454fe4d39..5ad542bf19 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -30,13 +30,14 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Property_Data_With_Value_Indexer() { + var contentValueSetBuilder = IndexInitializer.GetContentValueSetBuilder(Container.GetInstance()); + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer( - ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { - indexer.EnsureIndex(true); + indexer.CreateIndex(); var contentType = MockedContentTypes.CreateBasicContentType(); contentType.AddPropertyType(new PropertyType("test", ValueStorageType.Ntext) @@ -99,8 +100,7 @@ namespace Umbraco.Tests.UmbracoExamine var json = JsonConvert.SerializeObject(gridVal); content.Properties["grid"].SetValue(json); - - var valueSet = indexer.ContentValueSetBuilder.GetValueSets(content); + var valueSet = contentValueSetBuilder.GetValueSets(content); indexer.IndexItems(valueSet); var searcher = indexer.GetSearcher(); @@ -122,15 +122,19 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Rebuild_Index() { + var contentRebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext); + var mediaRebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { var searcher = indexer.GetSearcher(); //create the whole thing - indexer.RebuildIndex(); + contentRebuilder.Populate(indexer); + mediaRebuilder.Populate(indexer); var result = searcher.Search(searcher.CreateCriteria().All().Compile()); @@ -145,13 +149,17 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Protected_Content_Not_Indexed() { + var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext); + + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance())) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir)) using (indexer.ProcessNonAsync()) using (var searcher = ((LuceneSearcher)indexer.GetSearcher()).GetLuceneSearcher()) { //create the whole thing - indexer.RebuildIndex(); + rebuilder.Populate(indexer); + var protectedQuery = new BooleanQuery(); protectedQuery.Add( @@ -176,8 +184,9 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Move_Media_From_Non_Indexable_To_Indexable_ParentID() { + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, //make parent id 1116 options: new UmbracoContentIndexerOptions(false, false, 1116))) using (indexer.ProcessNonAsync()) @@ -219,7 +228,7 @@ namespace Umbraco.Tests.UmbracoExamine public void Index_Move_Media_To_Non_Indexable_ParentID() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer1 = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer1 = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, //make parent id 2222 options: new UmbracoContentIndexerOptions(false, false, 2222))) using (indexer1.ProcessNonAsync()) @@ -268,16 +277,17 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Reindex_Content() { + var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext); + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance(), + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { var searcher = indexer.GetSearcher(); //create the whole thing - indexer.RebuildIndex(); - + rebuilder.Populate(indexer); var result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndexer.CategoryFieldName, IndexTypes.Content).Compile()); Assert.AreEqual(21, result.TotalItemCount); @@ -294,9 +304,7 @@ namespace Umbraco.Tests.UmbracoExamine Assert.AreEqual(0, result.TotalItemCount); //call our indexing methods - indexer.IndexAll(IndexTypes.Content); - - + rebuilder.Populate(indexer); result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndexer.CategoryFieldName, IndexTypes.Content).Compile()); Assert.AreEqual(21, result.TotalItemCount); @@ -309,15 +317,17 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed() { + + var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext); + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, Container.GetInstance())) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir)) using (indexer.ProcessNonAsync()) { var searcher = indexer.GetSearcher(); //create the whole thing - indexer.RebuildIndex(); - + rebuilder.Populate(indexer); //now delete a node that has children diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index 58df17c052..fc95592d3d 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -7,11 +7,13 @@ using NUnit.Framework; using Examine.LuceneEngine.SearchCriteria; using Moq; using Umbraco.Core.Models; +using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Services; using Umbraco.Examine; using Umbraco.Tests.Testing; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Strings; namespace Umbraco.Tests.UmbracoExamine { @@ -53,15 +55,16 @@ namespace Umbraco.Tests.UmbracoExamine == allRecs); + var propertyEditors = Container.GetInstance(); + var rebuilder = IndexInitializer.GetContentIndexRebuilder(propertyEditors, contentService, ScopeProvider.SqlContext); + using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, ScopeProvider.SqlContext, - Container.GetInstance(), - contentService: contentService)) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir)) using (indexer.ProcessNonAsync()) { - indexer.RebuildIndex(); + indexer.CreateIndex(); + rebuilder.Populate(indexer); - var searcher = indexer.GetSearcher(); var numberSortedCriteria = searcher.CreateCriteria() diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 832760bde2..f33df18a54 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,7 +88,7 @@ - + diff --git a/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs index 9a2ca3513c..6d35580afa 100644 --- a/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; using Umbraco.Core.Sync; +using Umbraco.Examine; using Umbraco.Web.Cache; using Umbraco.Web.Composing; using Umbraco.Web.Routing; @@ -48,7 +49,7 @@ namespace Umbraco.Web.Components private BackgroundTaskRunner _processTaskRunner; private bool _started; private IBackgroundTask[] _tasks; - private IExamineManager _examineManager; + private IndexRebuilder _indexRebuilder; public override void Compose(Composition composition) { @@ -87,13 +88,13 @@ namespace Umbraco.Web.Components //rebuild indexes if the server is not synced // NOTE: This will rebuild ALL indexes including the members, if developers want to target specific // indexes then they can adjust this logic themselves. - () => ExamineComponent.RebuildIndexes(_examineManager, _logger, false, 5000) + () => ExamineComponent.RebuildIndexes(_indexRebuilder, _logger, false, 5000) } }); }); } - public void Initialize(IRuntimeState runtime, IServerRegistrar serverRegistrar, IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, IExamineManager examineManager) + public void Initialize(IRuntimeState runtime, IServerRegistrar serverRegistrar, IServerMessenger serverMessenger, IServerRegistrationService registrationService, ILogger logger, IndexRebuilder indexRebuilder) { _registrar = serverRegistrar as DatabaseServerRegistrar; if (_registrar == null) throw new Exception("panic: registar."); @@ -106,7 +107,7 @@ namespace Umbraco.Web.Components _runtime = runtime; _logger = logger; _registrationService = registrationService; - _examineManager = examineManager; + _indexRebuilder = indexRebuilder; _touchTaskRunner = new BackgroundTaskRunner("ServerRegistration", new BackgroundTaskRunnerOptions { AutoStart = true }, logger); diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs index 829c4ccf74..2c29f63613 100644 --- a/src/Umbraco.Web/Editors/ExamineManagementController.cs +++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs @@ -165,7 +165,9 @@ namespace Umbraco.Web.Editors try { - indexer.RebuildIndex(); + //TODO: Rebuilding isn't build directly into an index, we need a new IRebuildIndex or similar interface that can be registered + throw new NotImplementedException("Implement rebuilding!"); + //indexer.RebuildIndex(); } catch (Exception ex) { diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index a8bc1649e5..64a69a43fc 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -42,6 +42,8 @@ namespace Umbraco.Web.Search private IValueSetBuilder _contentValueSetBuilder; private IValueSetBuilder _mediaValueSetBuilder; private IValueSetBuilder _memberValueSetBuilder; + private ContentIndexPopulator _contentIndexPopulator; + private MediaIndexPopulator _mediaIndexPopulator; private static bool _disableExamineIndexing = false; private static volatile bool _isConfigured = false; private static readonly object IsConfiguredLocker = new object(); @@ -49,7 +51,6 @@ namespace Umbraco.Web.Search private ServiceContext _services; private static BackgroundTaskRunner _rebuildOnStartupRunner; private static readonly object _rebuildLocker = new object(); - private IEnumerable _urlSegmentProviders; // the default enlist priority is 100 // enlist with a lower priority to ensure that anything "default" runs after us @@ -60,8 +61,11 @@ namespace Umbraco.Web.Search { base.Compose(composition); + composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton(); composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(); composition.Container.RegisterSingleton, ContentValueSetBuilder>(); composition.Container.RegisterSingleton, MediaValueSetBuilder>(); composition.Container.RegisterSingleton, MemberValueSetBuilder>(); @@ -69,17 +73,19 @@ namespace Umbraco.Web.Search internal void Initialize(IRuntimeState runtime, MainDom mainDom, PropertyEditorCollection propertyEditors, IExamineManager examineManager, ProfilingLogger profilingLogger, - IScopeProvider scopeProvider, IUmbracoIndexesBuilder indexBuilder, ServiceContext services, - IEnumerable urlSegmentProviders, + IScopeProvider scopeProvider, IUmbracoIndexesBuilder indexBuilder, + IndexRebuilder indexRebuilder, ServiceContext services, IValueSetBuilder contentValueSetBuilder, IValueSetBuilder mediaValueSetBuilder, - IValueSetBuilder memberValueSetBuilder) + IValueSetBuilder memberValueSetBuilder, + ContentIndexPopulator contentIndexPopulator, PublishedContentIndexPopulator publishedContentIndexPopulator, + MediaIndexPopulator mediaIndexPopulator) { _services = services; _scopeProvider = scopeProvider; _examineManager = examineManager; - _urlSegmentProviders = urlSegmentProviders; - + _contentIndexPopulator = contentIndexPopulator; + _mediaIndexPopulator = mediaIndexPopulator; _contentValueSetBuilder = contentValueSetBuilder; _mediaValueSetBuilder = mediaValueSetBuilder; _memberValueSetBuilder = memberValueSetBuilder; @@ -111,7 +117,7 @@ namespace Umbraco.Web.Search profilingLogger.Logger.Debug("Examine shutdown not registered, this appdomain is not the MainDom, Examine will be disabled"); //if we could not register the shutdown examine ourselves, it means we are not maindom! in this case all of examine should be disabled! - Suspendable.ExamineEvents.SuspendIndexers(); + Suspendable.ExamineEvents.SuspendIndexers(profilingLogger.Logger); _disableExamineIndexing = true; return; //exit, do not continue } @@ -119,7 +125,7 @@ namespace Umbraco.Web.Search //create the indexes and register them with the manager foreach(var index in indexBuilder.Create()) { - _examineManager.AddIndexer(index.Key, index.Value); + _examineManager.AddIndexer(index); } profilingLogger.Logger.Debug("Examine shutdown registered with MainDom"); @@ -141,7 +147,7 @@ namespace Umbraco.Web.Search EnsureUnlocked(profilingLogger.Logger, examineManager); - RebuildIndexes(examineManager, profilingLogger.Logger, true, 5000); + RebuildIndexes(indexRebuilder, profilingLogger.Logger, true, 5000); } @@ -149,7 +155,7 @@ namespace Umbraco.Web.Search /// Called to rebuild empty indexes on startup /// /// - public static void RebuildIndexes(IExamineManager examineManager, ILogger logger, bool onlyEmptyIndexes, int waitMilliseconds = 0) + public static void RebuildIndexes(IndexRebuilder indexRebuilder, ILogger logger, bool onlyEmptyIndexes, int waitMilliseconds = 0) { //TODO: need a way to disable rebuilding on startup @@ -163,7 +169,7 @@ namespace Umbraco.Web.Search logger.Info("Starting initialize async background thread."); //do the rebuild on a managed background thread - var task = new RebuildOnStartupTask(examineManager, logger, onlyEmptyIndexes, waitMilliseconds); + var task = new RebuildOnStartupTask(indexRebuilder, logger, onlyEmptyIndexes, waitMilliseconds); _rebuildOnStartupRunner = new BackgroundTaskRunner( "RebuildIndexesOnStartup", @@ -695,14 +701,15 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, IContent content, bool? supportUnpublished) { - var valueSet = examineComponent._contentValueSetBuilder.GetValueSets(content); + var valueSet = examineComponent._contentValueSetBuilder.GetValueSets(content).ToList(); - examineComponent._examineManager.IndexItems( - valueSet.ToArray(), - examineComponent._examineManager.IndexProviders.Values.OfType() - // only for the specified indexers - .Where(x => supportUnpublished.HasValue == false || supportUnpublished.Value == x.SupportUnpublishedContent) - .Where(x => x.EnableDefaultEventHandler)); + foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + // only for the specified indexers + .Where(x => supportUnpublished.HasValue == false || supportUnpublished.Value == x.SupportUnpublishedContent) + .Where(x => x.EnableDefaultEventHandler)) + { + index.IndexItems(valueSet); + } } } @@ -726,15 +733,16 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, IMedia media, bool isPublished) { - var valueSet = examineComponent._mediaValueSetBuilder.GetValueSets(media); + var valueSet = examineComponent._mediaValueSetBuilder.GetValueSets(media).ToList(); - examineComponent._examineManager.IndexItems( - valueSet.ToArray(), - examineComponent._examineManager.IndexProviders.Values.OfType() - // index this item for all indexers if the media is not trashed, otherwise if the item is trashed - // then only index this for indexers supporting unpublished media - .Where(x => isPublished || (x.SupportUnpublishedContent)) - .Where(x => x.EnableDefaultEventHandler)); + foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + // index this item for all indexers if the media is not trashed, otherwise if the item is trashed + // then only index this for indexers supporting unpublished media + .Where(x => isPublished || (x.SupportUnpublishedContent)) + .Where(x => x.EnableDefaultEventHandler)) + { + index.IndexItems(valueSet); + } } } @@ -756,13 +764,13 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, IMember member) { - var valueSet = examineComponent._memberValueSetBuilder.GetValueSets(member); - - examineComponent._examineManager.IndexItems( - valueSet.ToArray(), - examineComponent._examineManager.IndexProviders.Values.OfType() - //ensure that only the providers are flagged to listen execute - .Where(x => x.EnableDefaultEventHandler)); + var valueSet = examineComponent._memberValueSetBuilder.GetValueSets(member).ToList(); + foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + //ensure that only the providers are flagged to listen execute + .Where(x => x.EnableDefaultEventHandler)) + { + index.IndexItems(valueSet); + } } } @@ -786,13 +794,15 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, int id, bool keepIfUnpublished) { - examineComponent._examineManager.DeleteFromIndexes( - id.ToString(CultureInfo.InvariantCulture), - examineComponent._examineManager.IndexProviders.Values.OfType() - // if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content, - // otherwise if keepIfUnpublished == false then remove from all indexes - .Where(x => keepIfUnpublished == false || x.SupportUnpublishedContent == false) - .Where(x => x.EnableDefaultEventHandler)); + var strId = id.ToString(CultureInfo.InvariantCulture); + foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + // if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content, + // otherwise if keepIfUnpublished == false then remove from all indexes + .Where(x => keepIfUnpublished == false || x.SupportUnpublishedContent == false) + .Where(x => x.EnableDefaultEventHandler)) + { + index.DeleteFromIndex(strId); + } } } #endregion @@ -802,15 +812,15 @@ namespace Umbraco.Web.Search /// private class RebuildOnStartupTask : IBackgroundTask { - private readonly IExamineManager _examineManager; + private readonly IndexRebuilder _indexRebuilder; private readonly ILogger _logger; private readonly bool _onlyEmptyIndexes; private readonly int _waitMilliseconds; - public RebuildOnStartupTask(IExamineManager examineManager, ILogger logger, bool onlyEmptyIndexes, int waitMilliseconds = 0) + public RebuildOnStartupTask(IndexRebuilder indexRebuilder, ILogger logger, bool onlyEmptyIndexes, int waitMilliseconds = 0) { - _examineManager = examineManager; - _logger = logger; + _indexRebuilder = indexRebuilder ?? throw new ArgumentNullException(nameof(indexRebuilder)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _onlyEmptyIndexes = onlyEmptyIndexes; _waitMilliseconds = waitMilliseconds; } @@ -843,7 +853,6 @@ namespace Umbraco.Web.Search /// /// Used to rebuild indexes on startup or cold boot /// - /// private void RebuildIndexes() { //do not attempt to do this if this has been disabled since we are not the main dom. @@ -853,20 +862,8 @@ namespace Umbraco.Web.Search if (_waitMilliseconds > 0) Thread.Sleep(_waitMilliseconds); - EnsureUnlocked(_logger, _examineManager); - - if (_onlyEmptyIndexes) - { - foreach (var indexer in _examineManager.IndexProviders.Values.Where(x => x.IsIndexNew())) - { - indexer.RebuildIndex(); - } - } - else - { - //do all of them - _examineManager.RebuildIndexes(); - } + EnsureUnlocked(_logger, _indexRebuilder.ExamineManager); + _indexRebuilder.RebuildIndexes(_onlyEmptyIndexes); } } } diff --git a/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs index 012f119238..8193117d69 100644 --- a/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs +++ b/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs @@ -5,6 +5,6 @@ namespace Umbraco.Web.Search { internal interface IUmbracoIndexesBuilder { - IReadOnlyDictionary Create(); + IEnumerable Create(); } } diff --git a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs index 25ba64828f..f8a6a16ec5 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs @@ -26,39 +26,33 @@ namespace Umbraco.Web.Search //TODO: we should inject the different IValueSetValidator so devs can just register them instead of overriding this class? public UmbracoIndexesBuilder(ProfilingLogger profilingLogger, - IValueSetBuilder contentValueSetBuilder, - IValueSetBuilder mediaValueSetBuilder, IValueSetBuilder memberValueSetBuilder, - IContentService contentService, - IMediaService mediaService, + ContentIndexPopulator contentIndexPopulator, + PublishedContentIndexPopulator publishedContentIndexPopulator, + MediaIndexPopulator mediaIndexPopulator, ILocalizationService languageService, IPublicAccessService publicAccessService, - IMemberService memberService, - ISqlContext sqlContext) + IMemberService memberService) { ProfilingLogger = profilingLogger ?? throw new System.ArgumentNullException(nameof(profilingLogger)); - ContentValueSetBuilder = contentValueSetBuilder ?? throw new System.ArgumentNullException(nameof(contentValueSetBuilder)); - MediaValueSetBuilder = mediaValueSetBuilder ?? throw new System.ArgumentNullException(nameof(mediaValueSetBuilder)); MemberValueSetBuilder = memberValueSetBuilder ?? throw new System.ArgumentNullException(nameof(memberValueSetBuilder)); - ContentService = contentService ?? throw new System.ArgumentNullException(nameof(contentService)); - MediaService = mediaService ?? throw new System.ArgumentNullException(nameof(mediaService)); + ContentIndexPopulator = contentIndexPopulator; + PublishedContentIndexPopulator = publishedContentIndexPopulator; + MediaIndexPopulator = mediaIndexPopulator; LanguageService = languageService ?? throw new System.ArgumentNullException(nameof(languageService)); PublicAccessService = publicAccessService ?? throw new System.ArgumentNullException(nameof(publicAccessService)); MemberService = memberService ?? throw new System.ArgumentNullException(nameof(memberService)); - SqlContext = sqlContext ?? throw new System.ArgumentNullException(nameof(sqlContext)); } protected ProfilingLogger ProfilingLogger { get; } - protected IValueSetBuilder ContentValueSetBuilder { get; } - protected IValueSetBuilder MediaValueSetBuilder { get; } protected IValueSetBuilder MemberValueSetBuilder { get; } - protected IContentService ContentService { get; } - protected IMediaService MediaService { get; } + protected ContentIndexPopulator ContentIndexPopulator { get; } + protected PublishedContentIndexPopulator PublishedContentIndexPopulator { get; } + protected MediaIndexPopulator MediaIndexPopulator { get; } protected ILocalizationService LanguageService { get; } protected IPublicAccessService PublicAccessService { get; } protected IMemberService MemberService { get; } - protected ISqlContext SqlContext { get; } - + public const string InternalIndexPath = "Internal"; public const string ExternalIndexPath = "External"; public const string MembersIndexPath = "Members"; @@ -72,27 +66,26 @@ namespace Umbraco.Web.Search /// Creates the Umbraco indexes /// /// - public IReadOnlyDictionary Create() + public IEnumerable Create() { - return new Dictionary + return new [] { - [InternalIndexPath] = CreateContentIndex(InternalIndexPath, new UmbracoContentIndexerOptions(true, true, null), new CultureInvariantWhitespaceAnalyzer()), - [ExternalIndexPath] = CreateContentIndex(ExternalIndexPath, new UmbracoContentIndexerOptions(false, false, null), new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30)), - [MembersIndexPath] = CreateMemberIndex() + CreateContentIndex(InternalIndexPath, new UmbracoContentIndexerOptions(true, true, null), new CultureInvariantWhitespaceAnalyzer()), + CreateContentIndex(ExternalIndexPath, new UmbracoContentIndexerOptions(false, false, null), new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30)), + CreateMemberIndex() }; } private IIndexer CreateContentIndex(string name, UmbracoContentIndexerOptions options, Analyzer analyzer) { - var index = new UmbracoContentIndexer( $"{name}Indexer", //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, GetFileSystemLuceneDirectory(name), analyzer, - ProfilingLogger, ContentValueSetBuilder, MediaValueSetBuilder, - ContentService, MediaService, LanguageService, SqlContext, + ProfilingLogger, + LanguageService, GetContentValueSetValidator(options), options); return index; @@ -100,14 +93,13 @@ namespace Umbraco.Web.Search private IIndexer CreateMemberIndex() { - var appData = Path.Combine(IOHelper.MapPath(SystemDirectories.Data), "TEMP", "ExamineIndexes", MembersIndexPath); var index = new UmbracoMemberIndexer( $"{MembersIndexPath}Indexer", //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, GetFileSystemLuceneDirectory(MembersIndexPath), new CultureInvariantWhitespaceAnalyzer(), - ProfilingLogger, MemberValueSetBuilder, MemberService, + ProfilingLogger, GetMemberValueSetValidator()); return index; } diff --git a/src/Umbraco.Web/Suspendable.cs b/src/Umbraco.Web/Suspendable.cs index 9bbbef9742..3317afe7a4 100644 --- a/src/Umbraco.Web/Suspendable.cs +++ b/src/Umbraco.Web/Suspendable.cs @@ -1,6 +1,7 @@ using System; using Examine; using Examine.Providers; +using Umbraco.Core.Logging; using Umbraco.Core.Composing; using Umbraco.Examine; using Umbraco.Web.Cache; @@ -63,24 +64,23 @@ namespace Umbraco.Web } } - public static void SuspendIndexers() + public static void SuspendIndexers(ILogger logger) { - Current.ProfilingLogger.Logger.Info(typeof (ExamineEvents), "Suspend indexers."); + logger.Info(typeof (ExamineEvents), "Suspend indexers."); _suspended = true; } - public static void ResumeIndexers() + public static void ResumeIndexers(IndexRebuilder indexRebuilder, ILogger logger) { _suspended = false; - Current.ProfilingLogger.Logger.Info(typeof (ExamineEvents), "Resume indexers (rebuild:{Tried}).", _tried); + logger.Info(typeof (ExamineEvents), "Resume indexers (rebuild:{Tried}).", _tried); if (_tried == false) return; _tried = false; //TODO: when resuming do we always want a full rebuild of all indexes? - // fixme - can we inject these somehow? - ExamineComponent.RebuildIndexes(ExamineManager.Instance, Current.Logger, false); + ExamineComponent.RebuildIndexes(indexRebuilder, logger, false); } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 0e18f07a2b..a626cd8ebd 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -62,7 +62,7 @@ - + 2.6.2.25 From e3548efed4f2e0d0bf49c0b8f98e024c684c2ab3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 28 Nov 2018 14:17:11 +1100 Subject: [PATCH 19/44] Uses IIndexPopulator to populate/rebuild indexes --- src/Umbraco.Core/Constants-Indexes.cs | 21 ++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + src/Umbraco.Examine/ContentIndexPopulator.cs | 16 ++++++---- src/Umbraco.Examine/IIndexPopulator.cs | 9 +++--- src/Umbraco.Examine/IndexPopulator.cs | 32 +++++++++++++++++++ src/Umbraco.Examine/IndexRebuilder.cs | 30 ++++++----------- src/Umbraco.Examine/MediaIndexPopulator.cs | 13 ++++++-- src/Umbraco.Examine/MemberIndexPopulator.cs | 15 ++++++--- src/Umbraco.Examine/Umbraco.Examine.csproj | 1 + src/Umbraco.Web/Search/ExamineComponent.cs | 1 - .../Search/UmbracoIndexesBuilder.cs | 30 ++++------------- 11 files changed, 105 insertions(+), 64 deletions(-) create mode 100644 src/Umbraco.Core/Constants-Indexes.cs create mode 100644 src/Umbraco.Examine/IndexPopulator.cs diff --git a/src/Umbraco.Core/Constants-Indexes.cs b/src/Umbraco.Core/Constants-Indexes.cs new file mode 100644 index 0000000000..d9d86884c4 --- /dev/null +++ b/src/Umbraco.Core/Constants-Indexes.cs @@ -0,0 +1,21 @@ +using System; +using System.ComponentModel; + +namespace Umbraco.Core +{ + public static partial class Constants + { + + public static class UmbracoIndexes + { + public const string InternalIndexName = InternalIndexPath + "Indexer"; + public const string ExternalIndexName = ExternalIndexPath + "Indexer"; + public const string MembersIndexName = MembersIndexPath + "Indexer"; + + public const string InternalIndexPath = "Internal"; + public const string ExternalIndexPath = "External"; + public const string MembersIndexPath = "Members"; + + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index c54f14423b..7e99042562 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -313,6 +313,7 @@ + diff --git a/src/Umbraco.Examine/ContentIndexPopulator.cs b/src/Umbraco.Examine/ContentIndexPopulator.cs index 211e2d51de..c84b251786 100644 --- a/src/Umbraco.Examine/ContentIndexPopulator.cs +++ b/src/Umbraco.Examine/ContentIndexPopulator.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Linq; using Examine; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Persistence; @@ -13,7 +15,7 @@ namespace Umbraco.Examine /// /// Performs the data lookups required to rebuild a content index /// - public class ContentIndexPopulator : IIndexPopulator + public class ContentIndexPopulator : IndexPopulator { private readonly IContentService _contentService; private readonly IValueSetBuilder _contentValueSetBuilder; @@ -54,9 +56,12 @@ namespace Umbraco.Examine _publishedQuery = sqlContext.Query().Where(x => x.Published); _supportUnpublishedContent = supportUnpublishedContent; _parentId = parentId; + + RegisterIndex(Constants.UmbracoIndexes.InternalIndexName); + RegisterIndex(Constants.UmbracoIndexes.ExternalIndexName); } - public void Populate(params IIndexer[] indexes) + protected override void PopulateIndexes(IEnumerable indexes) { const int pageSize = 10000; var pageIndex = 0; @@ -70,22 +75,21 @@ namespace Umbraco.Examine do { - long total; - if (_supportUnpublishedContent) { - content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total).ToArray(); + content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out _).ToArray(); } else { //add the published filter //note: We will filter for published variants in the validator - content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out total, + content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out _, _publishedQuery, Ordering.By("Path", Direction.Ascending)).ToArray(); } if (content.Length > 0) { + // ReSharper disable once PossibleMultipleEnumeration foreach (var index in indexes) index.IndexItems(_contentValueSetBuilder.GetValueSets(content)); } diff --git a/src/Umbraco.Examine/IIndexPopulator.cs b/src/Umbraco.Examine/IIndexPopulator.cs index fd71b3bb53..f4c27902eb 100644 --- a/src/Umbraco.Examine/IIndexPopulator.cs +++ b/src/Umbraco.Examine/IIndexPopulator.cs @@ -1,16 +1,15 @@ -using Examine; +using System.Collections.Generic; +using Examine; namespace Umbraco.Examine { - /// - /// Populates indexes with data - /// public interface IIndexPopulator { /// - /// Populates indexes with data + /// Populate indexers /// /// void Populate(params IIndexer[] indexes); } + } diff --git a/src/Umbraco.Examine/IndexPopulator.cs b/src/Umbraco.Examine/IndexPopulator.cs new file mode 100644 index 0000000000..af5301c230 --- /dev/null +++ b/src/Umbraco.Examine/IndexPopulator.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Linq; +using Examine; + +namespace Umbraco.Examine +{ + public abstract class IndexPopulator : IIndexPopulator + { + private readonly HashSet _registeredIndexes = new HashSet(); + + /// + /// Registers an index for this populator + /// + /// + public void RegisterIndex(string indexName) + { + _registeredIndexes.Add(indexName); + } + + /// + /// Returns a list of index names that his populate is associated with + /// + public IEnumerable RegisteredIndexes => _registeredIndexes; + + public void Populate(params IIndexer[] indexes) + { + PopulateIndexes(indexes.Where(x => RegisteredIndexes.Contains(x.Name))); + } + + protected abstract void PopulateIndexes(IEnumerable indexes); + } +} \ No newline at end of file diff --git a/src/Umbraco.Examine/IndexRebuilder.cs b/src/Umbraco.Examine/IndexRebuilder.cs index 5488fa2583..92b30537bf 100644 --- a/src/Umbraco.Examine/IndexRebuilder.cs +++ b/src/Umbraco.Examine/IndexRebuilder.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using Examine; namespace Umbraco.Examine @@ -8,41 +9,28 @@ namespace Umbraco.Examine /// public class IndexRebuilder { + private readonly IEnumerable _populators; public IExamineManager ExamineManager { get; } - private readonly ContentIndexPopulator _contentIndexPopulator; - private readonly MediaIndexPopulator _mediaIndexPopulator; - public IndexRebuilder(IExamineManager examineManager, ContentIndexPopulator contentIndexPopulator, MediaIndexPopulator mediaIndexPopulator) + public IndexRebuilder(IExamineManager examineManager, IEnumerable populators) { + _populators = populators; ExamineManager = examineManager; - _contentIndexPopulator = contentIndexPopulator; - _mediaIndexPopulator = mediaIndexPopulator; } public void RebuildIndexes(bool onlyEmptyIndexes) { var indexes = (onlyEmptyIndexes ? ExamineManager.IndexProviders.Values.Where(x => x.IndexExists()) - : ExamineManager.IndexProviders.Values).ToList(); - - var contentIndexes = indexes.Where(x => x is IUmbracoContentIndexer).ToArray(); - var mediaIndexes = indexes.Where(x => x is IUmbracoContentIndexer).ToArray(); - var nonUmbracoIndexes = indexes.Except(contentIndexes).Except(mediaIndexes).ToArray(); + : ExamineManager.IndexProviders.Values).ToArray(); foreach(var index in indexes) index.CreateIndex(); // clear the index - //reindex all content/media indexes with the same data source/lookup - _contentIndexPopulator.Populate(contentIndexes); - _mediaIndexPopulator.Populate(mediaIndexes); - - //then do the rest - foreach (var index in nonUmbracoIndexes) + foreach (var populator in _populators) { - index.CreateIndex(); - //TODO: How to rebuild? - } - + populator.Populate(indexes); + } } } } diff --git a/src/Umbraco.Examine/MediaIndexPopulator.cs b/src/Umbraco.Examine/MediaIndexPopulator.cs index 0dfd9b1bc5..99fa22acea 100644 --- a/src/Umbraco.Examine/MediaIndexPopulator.cs +++ b/src/Umbraco.Examine/MediaIndexPopulator.cs @@ -1,5 +1,7 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using Examine; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Services; @@ -8,7 +10,7 @@ namespace Umbraco.Examine /// /// Performs the data lookups required to rebuild a media index /// - public class MediaIndexPopulator : IIndexPopulator + public class MediaIndexPopulator : IndexPopulator { private readonly int? _parentId; private readonly IMediaService _mediaService; @@ -35,9 +37,12 @@ namespace Umbraco.Examine _parentId = parentId; _mediaService = mediaService; _mediaValueSetBuilder = mediaValueSetBuilder; + + RegisterIndex(Constants.UmbracoIndexes.InternalIndexName); + RegisterIndex(Constants.UmbracoIndexes.ExternalIndexName); } - public void Populate(params IIndexer[] indexes) + protected override void PopulateIndexes(IEnumerable indexes) { const int pageSize = 10000; var pageIndex = 0; @@ -57,6 +62,7 @@ namespace Umbraco.Examine if (media.Length > 0) { + // ReSharper disable once PossibleMultipleEnumeration foreach (var index in indexes) index.IndexItems(_mediaValueSetBuilder.GetValueSets(media)); } @@ -64,5 +70,6 @@ namespace Umbraco.Examine pageIndex++; } while (media.Length == pageSize); } + } } diff --git a/src/Umbraco.Examine/MemberIndexPopulator.cs b/src/Umbraco.Examine/MemberIndexPopulator.cs index cbe763e2c1..79dd31436b 100644 --- a/src/Umbraco.Examine/MemberIndexPopulator.cs +++ b/src/Umbraco.Examine/MemberIndexPopulator.cs @@ -1,11 +1,13 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using Examine; +using Lucene.Net.Util; using Umbraco.Core.Models; using Umbraco.Core.Services; namespace Umbraco.Examine { - public class MemberIndexPopulator: IIndexPopulator + public class MemberIndexPopulator : IndexPopulator { private readonly IMemberService _memberService; private readonly IValueSetBuilder _valueSetBuilder; @@ -14,8 +16,10 @@ namespace Umbraco.Examine { _memberService = memberService; _valueSetBuilder = valueSetBuilder; + + RegisterIndex(Core.Constants.UmbracoIndexes.MembersIndexName); } - public void Populate(params IIndexer[] indexes) + protected override void PopulateIndexes(IEnumerable indexes) { const int pageSize = 1000; var pageIndex = 0; @@ -31,10 +35,11 @@ namespace Umbraco.Examine if (members.Length > 0) { + // ReSharper disable once PossibleMultipleEnumeration foreach (var index in indexes) index.IndexItems(_valueSetBuilder.GetValueSets(members)); - } - + } + pageIndex++; } while (members.Length == pageSize); } diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index f5c85fada3..0bb12ae00a 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -64,6 +64,7 @@ + diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 64a69a43fc..2b2a1f1938 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -829,7 +829,6 @@ namespace Umbraco.Web.Search public void Dispose() { - throw new NotImplementedException(); } public void Run() diff --git a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs index f8a6a16ec5..339fa2f2be 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs @@ -26,36 +26,20 @@ namespace Umbraco.Web.Search //TODO: we should inject the different IValueSetValidator so devs can just register them instead of overriding this class? public UmbracoIndexesBuilder(ProfilingLogger profilingLogger, - IValueSetBuilder memberValueSetBuilder, - ContentIndexPopulator contentIndexPopulator, - PublishedContentIndexPopulator publishedContentIndexPopulator, - MediaIndexPopulator mediaIndexPopulator, ILocalizationService languageService, IPublicAccessService publicAccessService, IMemberService memberService) { ProfilingLogger = profilingLogger ?? throw new System.ArgumentNullException(nameof(profilingLogger)); - MemberValueSetBuilder = memberValueSetBuilder ?? throw new System.ArgumentNullException(nameof(memberValueSetBuilder)); - ContentIndexPopulator = contentIndexPopulator; - PublishedContentIndexPopulator = publishedContentIndexPopulator; - MediaIndexPopulator = mediaIndexPopulator; LanguageService = languageService ?? throw new System.ArgumentNullException(nameof(languageService)); PublicAccessService = publicAccessService ?? throw new System.ArgumentNullException(nameof(publicAccessService)); MemberService = memberService ?? throw new System.ArgumentNullException(nameof(memberService)); } protected ProfilingLogger ProfilingLogger { get; } - protected IValueSetBuilder MemberValueSetBuilder { get; } - protected ContentIndexPopulator ContentIndexPopulator { get; } - protected PublishedContentIndexPopulator PublishedContentIndexPopulator { get; } - protected MediaIndexPopulator MediaIndexPopulator { get; } protected ILocalizationService LanguageService { get; } protected IPublicAccessService PublicAccessService { get; } protected IMemberService MemberService { get; } - - public const string InternalIndexPath = "Internal"; - public const string ExternalIndexPath = "External"; - public const string MembersIndexPath = "Members"; /// /// By default these are the member fields we index @@ -70,19 +54,19 @@ namespace Umbraco.Web.Search { return new [] { - CreateContentIndex(InternalIndexPath, new UmbracoContentIndexerOptions(true, true, null), new CultureInvariantWhitespaceAnalyzer()), - CreateContentIndex(ExternalIndexPath, new UmbracoContentIndexerOptions(false, false, null), new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30)), + CreateContentIndex(Constants.UmbracoIndexes.InternalIndexName, Constants.UmbracoIndexes.InternalIndexPath, new UmbracoContentIndexerOptions(true, true, null), new CultureInvariantWhitespaceAnalyzer()), + CreateContentIndex(Constants.UmbracoIndexes.ExternalIndexName, Constants.UmbracoIndexes.ExternalIndexPath, new UmbracoContentIndexerOptions(false, false, null), new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30)), CreateMemberIndex() }; } - private IIndexer CreateContentIndex(string name, UmbracoContentIndexerOptions options, Analyzer analyzer) + private IIndexer CreateContentIndex(string name, string path, UmbracoContentIndexerOptions options, Analyzer analyzer) { var index = new UmbracoContentIndexer( - $"{name}Indexer", + name, //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, - GetFileSystemLuceneDirectory(name), + GetFileSystemLuceneDirectory(path), analyzer, ProfilingLogger, LanguageService, @@ -94,10 +78,10 @@ namespace Umbraco.Web.Search private IIndexer CreateMemberIndex() { var index = new UmbracoMemberIndexer( - $"{MembersIndexPath}Indexer", + Constants.UmbracoIndexes.MembersIndexName, //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, - GetFileSystemLuceneDirectory(MembersIndexPath), + GetFileSystemLuceneDirectory(Constants.UmbracoIndexes.MembersIndexPath), new CultureInvariantWhitespaceAnalyzer(), ProfilingLogger, GetMemberValueSetValidator()); From a7f6985b38de65b7ab9dfc61d47bfc3e45b0dc3a Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 28 Nov 2018 14:46:45 +1100 Subject: [PATCH 20/44] Cleans up the BaseValueSetBuilder --- src/Umbraco.Examine/BaseValueSetBuilder.cs | 13 +++++++---- src/Umbraco.Examine/ContentValueSetBuilder.cs | 14 +++--------- src/Umbraco.Examine/IValueSetBuilder.cs | 11 +++++++++- src/Umbraco.Examine/MediaValueSetBuilder.cs | 5 +++-- src/Umbraco.Examine/MemberValueSetBuilder.cs | 5 +++-- src/Umbraco.Web/Search/ExamineComponent.cs | 22 ++++++------------- 6 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/Umbraco.Examine/BaseValueSetBuilder.cs b/src/Umbraco.Examine/BaseValueSetBuilder.cs index 043e017754..d0c6069add 100644 --- a/src/Umbraco.Examine/BaseValueSetBuilder.cs +++ b/src/Umbraco.Examine/BaseValueSetBuilder.cs @@ -1,20 +1,27 @@ using System.Collections.Generic; using System.Linq; +using Examine; using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; namespace Umbraco.Examine { - public abstract class BaseValueSetBuilder + + /// + public abstract class BaseValueSetBuilder : IValueSetBuilder + where TContent : IContentBase { private readonly PropertyEditorCollection _propertyEditors; - public BaseValueSetBuilder(PropertyEditorCollection propertyEditors) + protected BaseValueSetBuilder(PropertyEditorCollection propertyEditors) { _propertyEditors = propertyEditors ?? throw new System.ArgumentNullException(nameof(propertyEditors)); } + /// + public abstract IEnumerable GetValueSets(params TContent[] content); + protected void AddPropertyValue(Property property, string culture, string segment, IDictionary values) { var editor = _propertyEditors[property.PropertyType.PropertyEditorAlias]; @@ -57,8 +64,6 @@ namespace Umbraco.Examine } } } - - } } diff --git a/src/Umbraco.Examine/ContentValueSetBuilder.cs b/src/Umbraco.Examine/ContentValueSetBuilder.cs index ec52bbbd4f..258f24c21e 100644 --- a/src/Umbraco.Examine/ContentValueSetBuilder.cs +++ b/src/Umbraco.Examine/ContentValueSetBuilder.cs @@ -8,9 +8,8 @@ using Umbraco.Core.Strings; namespace Umbraco.Examine { - public class ContentValueSetBuilder : BaseValueSetBuilder, IValueSetBuilder + public class ContentValueSetBuilder : BaseValueSetBuilder { - private readonly PropertyEditorCollection _propertyEditors; private readonly IEnumerable _urlSegmentProviders; private readonly IUserService _userService; @@ -19,19 +18,12 @@ namespace Umbraco.Examine IUserService userService) : base(propertyEditors) { - _propertyEditors = propertyEditors; _urlSegmentProviders = urlSegmentProviders; _userService = userService; } - /// - /// Creates a collection of for a collection - /// - /// - /// - /// - /// Yield returns - public IEnumerable GetValueSets(params IContent[] content) + /// + public override IEnumerable GetValueSets(params IContent[] content) { //TODO: There is a lot of boxing going on here and ultimately all values will be boxed by Lucene anyways // but I wonder if there's a way to reduce the boxing that we have to do or if it will matter in the end since diff --git a/src/Umbraco.Examine/IValueSetBuilder.cs b/src/Umbraco.Examine/IValueSetBuilder.cs index 6b1a372e09..89aa907926 100644 --- a/src/Umbraco.Examine/IValueSetBuilder.cs +++ b/src/Umbraco.Examine/IValueSetBuilder.cs @@ -4,9 +4,18 @@ using Umbraco.Core.Models; namespace Umbraco.Examine { - public interface IValueSetBuilder + /// + /// Creates a collection of to be indexed based on a collection of + /// + /// + public interface IValueSetBuilder where TContent : IContentBase { + /// + /// Creates a collection of to be indexed based on a collection of + /// + /// + /// IEnumerable GetValueSets(params TContent[] content); } diff --git a/src/Umbraco.Examine/MediaValueSetBuilder.cs b/src/Umbraco.Examine/MediaValueSetBuilder.cs index 6758d29f53..f162c07f59 100644 --- a/src/Umbraco.Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Examine/MediaValueSetBuilder.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Strings; namespace Umbraco.Examine { - public class MediaValueSetBuilder : BaseValueSetBuilder, IValueSetBuilder + public class MediaValueSetBuilder : BaseValueSetBuilder { private readonly IEnumerable _urlSegmentProviders; private readonly IUserService _userService; @@ -22,7 +22,8 @@ namespace Umbraco.Examine _userService = userService; } - public IEnumerable GetValueSets(params IMedia[] media) + /// + public override IEnumerable GetValueSets(params IMedia[] media) { foreach (var m in media) { diff --git a/src/Umbraco.Examine/MemberValueSetBuilder.cs b/src/Umbraco.Examine/MemberValueSetBuilder.cs index 50f4ee6992..fb6cb6f967 100644 --- a/src/Umbraco.Examine/MemberValueSetBuilder.cs +++ b/src/Umbraco.Examine/MemberValueSetBuilder.cs @@ -7,14 +7,15 @@ using Umbraco.Core.PropertyEditors; namespace Umbraco.Examine { - public class MemberValueSetBuilder : BaseValueSetBuilder, IValueSetBuilder + public class MemberValueSetBuilder : BaseValueSetBuilder { public MemberValueSetBuilder(PropertyEditorCollection propertyEditors) : base(propertyEditors) { } - public IEnumerable GetValueSets(params IMember[] members) + /// + public override IEnumerable GetValueSets(params IMember[] members) { foreach (var m in members) { diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 2b2a1f1938..752440a4dc 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -17,17 +16,13 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Changes; -using Umbraco.Core.Strings; using Umbraco.Core.Sync; using Umbraco.Web.Cache; -using Umbraco.Web.PropertyEditors; using Umbraco.Examine; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Web.Scheduling; using System.Threading.Tasks; -using Umbraco.Core.Persistence; using Umbraco.Core.Composing; -using LightInject; namespace Umbraco.Web.Search { @@ -42,15 +37,13 @@ namespace Umbraco.Web.Search private IValueSetBuilder _contentValueSetBuilder; private IValueSetBuilder _mediaValueSetBuilder; private IValueSetBuilder _memberValueSetBuilder; - private ContentIndexPopulator _contentIndexPopulator; - private MediaIndexPopulator _mediaIndexPopulator; private static bool _disableExamineIndexing = false; private static volatile bool _isConfigured = false; private static readonly object IsConfiguredLocker = new object(); private IScopeProvider _scopeProvider; private ServiceContext _services; private static BackgroundTaskRunner _rebuildOnStartupRunner; - private static readonly object _rebuildLocker = new object(); + private static readonly object RebuildLocker = new object(); // the default enlist priority is 100 // enlist with a lower priority to ensure that anything "default" runs after us @@ -77,15 +70,11 @@ namespace Umbraco.Web.Search IndexRebuilder indexRebuilder, ServiceContext services, IValueSetBuilder contentValueSetBuilder, IValueSetBuilder mediaValueSetBuilder, - IValueSetBuilder memberValueSetBuilder, - ContentIndexPopulator contentIndexPopulator, PublishedContentIndexPopulator publishedContentIndexPopulator, - MediaIndexPopulator mediaIndexPopulator) + IValueSetBuilder memberValueSetBuilder) { _services = services; _scopeProvider = scopeProvider; _examineManager = examineManager; - _contentIndexPopulator = contentIndexPopulator; - _mediaIndexPopulator = mediaIndexPopulator; _contentValueSetBuilder = contentValueSetBuilder; _mediaValueSetBuilder = mediaValueSetBuilder; _memberValueSetBuilder = memberValueSetBuilder; @@ -149,17 +138,20 @@ namespace Umbraco.Web.Search RebuildIndexes(indexRebuilder, profilingLogger.Logger, true, 5000); } - + /// /// Called to rebuild empty indexes on startup /// + /// /// + /// + /// public static void RebuildIndexes(IndexRebuilder indexRebuilder, ILogger logger, bool onlyEmptyIndexes, int waitMilliseconds = 0) { //TODO: need a way to disable rebuilding on startup - lock(_rebuildLocker) + lock(RebuildLocker) { if (_rebuildOnStartupRunner != null && _rebuildOnStartupRunner.IsRunning) { From 2b05d54fb735f8a1c3f7272a8d0bff41fc18703e Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 28 Nov 2018 14:55:05 +1100 Subject: [PATCH 21/44] Fixes tests, adds test, renames class --- ...lidator.cs => ContentValueSetValidator.cs} | 10 ++++-- src/Umbraco.Examine/IndexTypes.cs | 8 +---- src/Umbraco.Examine/Umbraco.Examine.csproj | 2 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 2 +- .../UmbracoExamine/IndexInitializer.cs | 2 +- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 6 +++- .../UmbracoExamine/SearchTests.cs | 1 + .../UmbracoContentValueSetValidatorTests.cs | 36 ++++++++++++++----- .../Search/UmbracoIndexesBuilder.cs | 2 +- 9 files changed, 45 insertions(+), 24 deletions(-) rename src/Umbraco.Examine/{UmbracoContentValueSetValidator.cs => ContentValueSetValidator.cs} (89%) diff --git a/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs b/src/Umbraco.Examine/ContentValueSetValidator.cs similarity index 89% rename from src/Umbraco.Examine/UmbracoContentValueSetValidator.cs rename to src/Umbraco.Examine/ContentValueSetValidator.cs index 220f082e48..4fbb039bae 100644 --- a/src/Umbraco.Examine/UmbracoContentValueSetValidator.cs +++ b/src/Umbraco.Examine/ContentValueSetValidator.cs @@ -11,16 +11,17 @@ namespace Umbraco.Examine { /// - /// Used to validate a ValueSet for content - based on permissions, parent id, etc.... + /// Used to validate a ValueSet for content/media - based on permissions, parent id, etc.... /// - public class UmbracoContentValueSetValidator : IValueSetValidator + public class ContentValueSetValidator : IValueSetValidator { private readonly UmbracoContentIndexerOptions _options; private readonly IPublicAccessService _publicAccessService; private const string PathKey = "path"; + private static readonly IEnumerable ValidIndexTypes = new[] {IndexTypes.Content, IndexTypes.Media}; - public UmbracoContentValueSetValidator(UmbracoContentIndexerOptions options, IPublicAccessService publicAccessService) + public ContentValueSetValidator(UmbracoContentIndexerOptions options, IPublicAccessService publicAccessService) { _options = options; _publicAccessService = publicAccessService; @@ -28,6 +29,9 @@ namespace Umbraco.Examine public bool Validate(ValueSet valueSet) { + if (!ValidIndexTypes.Contains(valueSet.Category)) + return false; + //check for published content if (valueSet.Category == IndexTypes.Content && !_options.SupportUnpublishedContent) { diff --git a/src/Umbraco.Examine/IndexTypes.cs b/src/Umbraco.Examine/IndexTypes.cs index fab72eca25..3fa00e234c 100644 --- a/src/Umbraco.Examine/IndexTypes.cs +++ b/src/Umbraco.Examine/IndexTypes.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Lucene.Net.Store; - -namespace Umbraco.Examine +namespace Umbraco.Examine { /// /// The index types stored in the Lucene Index diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 0bb12ae00a..e36f911f47 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -81,7 +81,7 @@ - + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 0433a75d41..8e8208479c 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -119,7 +119,7 @@ namespace Umbraco.Examine parentId = indexSet.IndexParentId; } - ValueSetValidator = new UmbracoContentValueSetValidator( + ValueSetValidator = new ContentValueSetValidator( new UmbracoContentIndexerOptions( SupportUnpublishedContent, SupportProtectedContent, parentId, ConfigIndexCriteria.IncludeItemTypes, ConfigIndexCriteria.ExcludeItemTypes), diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index c8a5cbceb4..f66e55cbbd 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -169,7 +169,7 @@ namespace Umbraco.Tests.UmbracoExamine analyzer, profilingLogger, languageService, - new UmbracoContentValueSetValidator(options, Mock.Of()), + new ContentValueSetValidator(options, Mock.Of()), options); i.IndexingError += IndexingError; diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 5ad542bf19..6ef3cc376f 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -130,6 +130,9 @@ namespace Umbraco.Tests.UmbracoExamine options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { + contentRebuilder.RegisterIndex(indexer.Name); + mediaRebuilder.RegisterIndex(indexer.Name); + var searcher = indexer.GetSearcher(); //create the whole thing @@ -278,12 +281,13 @@ namespace Umbraco.Tests.UmbracoExamine public void Index_Reindex_Content() { var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext); - using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, options: new UmbracoContentIndexerOptions(true, false, null))) using (indexer.ProcessNonAsync()) { + rebuilder.RegisterIndex(indexer.Name); + var searcher = indexer.GetSearcher(); //create the whole thing diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index fc95592d3d..648a5d7c86 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -62,6 +62,7 @@ namespace Umbraco.Tests.UmbracoExamine using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir)) using (indexer.ProcessNonAsync()) { + rebuilder.RegisterIndex(indexer.Name); indexer.CreateIndex(); rebuilder.Populate(indexer); diff --git a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs index d06b2b4fff..63ca0141f2 100644 --- a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs @@ -14,10 +14,28 @@ namespace Umbraco.Tests.UmbracoExamine [TestFixture] public class UmbracoContentValueSetValidatorTests { + [Test] + public void Invalid_Category() + { + var validator = new ContentValueSetValidator( + new UmbracoContentIndexerOptions(true, true, null), + Mock.Of()); + + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.IsTrue(result); + + result = validator.Validate(new ValueSet("777", IndexTypes.Media, new { hello = "world", path = "-1,555" })); + Assert.IsTrue(result); + + result = validator.Validate(new ValueSet("555", "invalid-category", new { hello = "world", path = "-1,555" })); + Assert.IsFalse(result); + + } + [Test] public void Must_Have_Path() { - var validator = new UmbracoContentValueSetValidator( + var validator = new ContentValueSetValidator( new UmbracoContentIndexerOptions(true, true, null), Mock.Of()); @@ -31,7 +49,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Parent_Id() { - var validator = new UmbracoContentValueSetValidator( + var validator = new ContentValueSetValidator( new UmbracoContentIndexerOptions(true, true, 555), Mock.Of()); @@ -51,7 +69,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Inclusion_List() { - var validator = new UmbracoContentValueSetValidator( + var validator = new ContentValueSetValidator( new UmbracoContentIndexerOptions(true, true, includeContentTypes: new List { "include-content" }), Mock.Of()); @@ -68,7 +86,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Exclusion_List() { - var validator = new UmbracoContentValueSetValidator( + var validator = new ContentValueSetValidator( new UmbracoContentIndexerOptions(true, true, excludeContentTypes: new List { "exclude-content" }), Mock.Of()); @@ -85,7 +103,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Inclusion_Exclusion_List() { - var validator = new UmbracoContentValueSetValidator( + var validator = new ContentValueSetValidator( new UmbracoContentIndexerOptions(true, true, includeContentTypes: new List { "include-content", "exclude-content" }, excludeContentTypes: new List { "exclude-content" }), @@ -107,7 +125,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Recycle_Bin() { - var validator = new UmbracoContentValueSetValidator( + var validator = new ContentValueSetValidator( new UmbracoContentIndexerOptions(false, true, null), Mock.Of()); @@ -133,7 +151,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Published_Only() { - var validator = new UmbracoContentValueSetValidator( + var validator = new ContentValueSetValidator( new UmbracoContentIndexerOptions(false, true, null), Mock.Of()); @@ -162,7 +180,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Published_Only_With_Variants() { - var validator = new UmbracoContentValueSetValidator( + var validator = new ContentValueSetValidator( new UmbracoContentIndexerOptions(false, true, null), Mock.Of()); @@ -222,7 +240,7 @@ namespace Umbraco.Tests.UmbracoExamine .Returns(Attempt.Succeed(new PublicAccessEntry(Guid.NewGuid(), 555, 444, 333, Enumerable.Empty()))); publicAccessService.Setup(x => x.IsProtected("-1,777")) .Returns(Attempt.Fail()); - var validator = new UmbracoContentValueSetValidator( + var validator = new ContentValueSetValidator( new UmbracoContentIndexerOptions(true, false, null), publicAccessService.Object); diff --git a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs index 339fa2f2be..bdfb259c10 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs @@ -104,7 +104,7 @@ namespace Umbraco.Web.Search public virtual IValueSetValidator GetContentValueSetValidator(UmbracoContentIndexerOptions options) { - return new UmbracoContentValueSetValidator(options, PublicAccessService); + return new ContentValueSetValidator(options, PublicAccessService); } /// From 6ab5d6ec9fdac787f1792ad6d13ffb5df2351c12 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 28 Nov 2018 18:39:03 +1100 Subject: [PATCH 22/44] More cleanup, removes options since it was irrelevant --- .../ContentValueSetValidator.cs | 50 +++++----- src/Umbraco.Examine/IUmbracoIndexer.cs | 7 +- .../MemberValueSetValidator.cs | 32 +++++++ src/Umbraco.Examine/Umbraco.Examine.csproj | 3 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 62 ++++--------- .../UmbracoContentIndexerOptions.cs | 52 ----------- src/Umbraco.Examine/UmbracoExamineIndexer.cs | 26 +----- src/Umbraco.Examine/UmbracoMemberIndexer.cs | 8 +- src/Umbraco.Examine/ValueSetValidator.cs | 91 +++++++++++++++++++ .../PublishedContent/PublishedMediaTests.cs | 28 +++--- .../UmbracoExamine/EventsTest.cs | 4 +- .../UmbracoExamine/IndexInitializer.cs | 9 +- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 14 +-- .../UmbracoContentValueSetValidatorTests.cs | 48 +++------- src/Umbraco.Web/Search/ExamineComponent.cs | 10 +- .../Search/UmbracoIndexesBuilder.cs | 56 ++++++------ 16 files changed, 259 insertions(+), 241 deletions(-) create mode 100644 src/Umbraco.Examine/MemberValueSetValidator.cs delete mode 100644 src/Umbraco.Examine/UmbracoContentIndexerOptions.cs create mode 100644 src/Umbraco.Examine/ValueSetValidator.cs diff --git a/src/Umbraco.Examine/ContentValueSetValidator.cs b/src/Umbraco.Examine/ContentValueSetValidator.cs index 4fbb039bae..9355d6d695 100644 --- a/src/Umbraco.Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Examine/ContentValueSetValidator.cs @@ -9,31 +9,45 @@ using Umbraco.Core.Services; namespace Umbraco.Examine { - /// /// Used to validate a ValueSet for content/media - based on permissions, parent id, etc.... /// - public class ContentValueSetValidator : IValueSetValidator + public class ContentValueSetValidator : ValueSetValidator { - private readonly UmbracoContentIndexerOptions _options; private readonly IPublicAccessService _publicAccessService; private const string PathKey = "path"; - private static readonly IEnumerable ValidIndexTypes = new[] {IndexTypes.Content, IndexTypes.Media}; + private static readonly IEnumerable ValidCategories = new[] {IndexTypes.Content, IndexTypes.Media}; + protected override IEnumerable ValidIndexCategories => ValidCategories; - public ContentValueSetValidator(UmbracoContentIndexerOptions options, IPublicAccessService publicAccessService) + public bool SupportUnpublishedContent { get; } + public bool SupportProtectedContent { get; } + public int? ParentId { get; } + + public ContentValueSetValidator(bool supportUnpublishedContent, int? parentId = null, + IEnumerable includeItemTypes = null, IEnumerable excludeItemTypes = null) + : this(supportUnpublishedContent, true, null, parentId, includeItemTypes, excludeItemTypes) { - _options = options; + } + + public ContentValueSetValidator(bool supportUnpublishedContent, bool supportProtectedContent, + IPublicAccessService publicAccessService, int? parentId = null, + IEnumerable includeItemTypes = null, IEnumerable excludeItemTypes = null) + : base(includeItemTypes, excludeItemTypes, null, null) + { + SupportUnpublishedContent = supportUnpublishedContent; + SupportProtectedContent = supportProtectedContent; + ParentId = parentId; _publicAccessService = publicAccessService; } - public bool Validate(ValueSet valueSet) + public override bool Validate(ValueSet valueSet) { - if (!ValidIndexTypes.Contains(valueSet.Category)) + if (!ValidCategories.Contains(valueSet.Category)) return false; //check for published content - if (valueSet.Category == IndexTypes.Content && !_options.SupportUnpublishedContent) + if (valueSet.Category == IndexTypes.Content && !SupportUnpublishedContent) { if (!valueSet.Values.TryGetValue(UmbracoExamineIndexer.PublishedFieldName, out var published)) return false; @@ -70,36 +84,28 @@ namespace Umbraco.Examine // return nothing if we're not supporting protected content and it is protected, and we're not supporting unpublished content if (valueSet.Category == IndexTypes.Content - && !_options.SupportProtectedContent + && !SupportProtectedContent && _publicAccessService.IsProtected(path)) { return false; } //check if this document is a descendent of the parent - if (_options.ParentId.HasValue && _options.ParentId.Value > 0) + if (ParentId.HasValue && ParentId.Value > 0) { - if (!path.Contains(string.Concat(",", _options.ParentId.Value, ","))) + if (!path.Contains(string.Concat(",", ParentId.Value, ","))) return false; } //check for recycle bin - if (!_options.SupportUnpublishedContent) + if (!SupportUnpublishedContent) { var recycleBinId = valueSet.Category == IndexTypes.Content ? Constants.System.RecycleBinContent : Constants.System.RecycleBinMedia; if (path.Contains(string.Concat(",", recycleBinId, ","))) return false; } - //check if this document is of a correct type of node type alias - if (_options.IncludeContentTypes != null && !_options.IncludeContentTypes.Contains(valueSet.ItemType)) - return false; - - //if this node type is part of our exclusion list - if (_options.ExcludeContentTypes != null && _options.ExcludeContentTypes.Contains(valueSet.ItemType)) - return false; - - return true; + return base.Validate(valueSet); } } } diff --git a/src/Umbraco.Examine/IUmbracoIndexer.cs b/src/Umbraco.Examine/IUmbracoIndexer.cs index 936c78c71d..1785cd5a92 100644 --- a/src/Umbraco.Examine/IUmbracoIndexer.cs +++ b/src/Umbraco.Examine/IUmbracoIndexer.cs @@ -14,11 +14,8 @@ namespace Umbraco.Examine bool EnableDefaultEventHandler { get; } /// - /// When set to true data will not be deleted from the index if the data is being unpublished (not deleted) + /// When set to true data will not be deleted from the index if the data is being soft deleted (unpublished or trashed) /// - /// - /// Generally used only for published content - /// - bool SupportUnpublishedContent { get; } + bool SupportSoftDelete { get; } } } diff --git a/src/Umbraco.Examine/MemberValueSetValidator.cs b/src/Umbraco.Examine/MemberValueSetValidator.cs new file mode 100644 index 0000000000..b8e96dc68d --- /dev/null +++ b/src/Umbraco.Examine/MemberValueSetValidator.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Linq; +using Examine; + +namespace Umbraco.Examine +{ + public class MemberValueSetValidator : ValueSetValidator + { + public MemberValueSetValidator() : base(null, null, DefaultMemberIndexFields, null) + { + } + + public MemberValueSetValidator(IEnumerable includeItemTypes, IEnumerable excludeItemTypes) + : base(includeItemTypes, excludeItemTypes, DefaultMemberIndexFields, null) + { + } + + public MemberValueSetValidator(IEnumerable includeItemTypes, IEnumerable excludeItemTypes, IEnumerable includeFields, IEnumerable excludeFields) + : base(includeItemTypes, excludeItemTypes, includeFields, excludeFields) + { + } + + /// + /// By default these are the member fields we index + /// + public static readonly string[] DefaultMemberIndexFields = { "id", "nodeName", "updateDate", "writerName", "loginName", "email", "nodeTypeAlias" }; + + private static readonly IEnumerable ValidCategories = new[] { IndexTypes.Member }; + protected override IEnumerable ValidIndexCategories => ValidCategories; + + } +} diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index e36f911f47..5c206250b1 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -74,13 +74,13 @@ + - @@ -88,6 +88,7 @@ Properties\SolutionInfo.cs + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 8e8208479c..ddf5f23b4f 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; +using System.Linq; using Examine; using Umbraco.Core; using Umbraco.Core.Services; @@ -22,11 +23,9 @@ namespace Umbraco.Examine /// public class UmbracoContentIndexer : UmbracoExamineIndexer, IUmbracoContentIndexer, IUmbracoMediaIndexer { - public const string VariesByCultureFieldName = UmbracoExamineIndexer.SpecialFieldPrefix + "VariesByCulture"; + public const string VariesByCultureFieldName = SpecialFieldPrefix + "VariesByCulture"; protected ILocalizationService LanguageService { get; } - private int? _parentId; - #region Constructors /// @@ -36,6 +35,8 @@ namespace Umbraco.Examine public UmbracoContentIndexer() { LanguageService = Current.Services.LocalizationService; + + //note: The validator for this config based indexer is set in the Initialize method } /// @@ -46,8 +47,8 @@ namespace Umbraco.Examine /// /// /// + /// /// - /// /// public UmbracoContentIndexer( string name, @@ -57,19 +58,15 @@ namespace Umbraco.Examine ProfilingLogger profilingLogger, ILocalizationService languageService, IValueSetValidator validator, - UmbracoContentIndexerOptions options, IReadOnlyDictionary> indexValueTypes = null) : base(name, fieldDefinitions, luceneDirectory, defaultAnalyzer, profilingLogger, validator, indexValueTypes) { if (validator == null) throw new ArgumentNullException(nameof(validator)); - if (options == null) throw new ArgumentNullException(nameof(options)); - - SupportProtectedContent = options.SupportProtectedContent; - SupportUnpublishedContent = options.SupportUnpublishedContent; - ParentId = options.ParentId; LanguageService = languageService ?? throw new ArgumentNullException(nameof(languageService)); - } + if (validator is ContentValueSetValidator contentValueSetValidator) + SupportSoftDelete = contentValueSetValidator.SupportUnpublishedContent; + } #endregion @@ -97,19 +94,19 @@ namespace Umbraco.Examine { base.Initialize(name, config); + var supportUnpublished = false; + var supportProtected = false; + //check if there's a flag specifying to support unpublished content, //if not, set to false; - if (config["supportUnpublished"] != null && bool.TryParse(config["supportUnpublished"], out var supportUnpublished)) - SupportUnpublishedContent = supportUnpublished; - else - SupportUnpublishedContent = false; + if (config["supportUnpublished"] != null) + bool.TryParse(config["supportUnpublished"], out supportUnpublished); //check if there's a flag specifying to support protected content, //if not, set to false; - if (config["supportProtected"] != null && bool.TryParse(config["supportProtected"], out var supportProtected)) - SupportProtectedContent = supportProtected; - else - SupportProtectedContent = false; + if (config["supportProtected"] != null) + bool.TryParse(config["supportProtected"], out supportProtected); + //now we need to build up the indexer options so we can create our validator int? parentId = null; @@ -120,32 +117,13 @@ namespace Umbraco.Examine } ValueSetValidator = new ContentValueSetValidator( - new UmbracoContentIndexerOptions( - SupportUnpublishedContent, SupportProtectedContent, parentId, - ConfigIndexCriteria.IncludeItemTypes, ConfigIndexCriteria.ExcludeItemTypes), + supportUnpublished, supportProtected, //Using a singleton here, we can't inject this when using config based providers and we don't use this //anywhere else in this class - Current.Services.PublicAccessService); + Current.Services.PublicAccessService, + parentId, ConfigIndexCriteria.IncludeItemTypes, ConfigIndexCriteria.ExcludeItemTypes); - } - - #endregion - - #region Properties - - /// - /// By default this is false, if set to true then the indexer will include indexing content that is flagged as publicly protected. - /// This property is ignored if SupportUnpublishedContent is set to true. - /// - public bool SupportProtectedContent { get; protected set; } - - /// - /// If set this will filter the content items allowed to be indexed - /// - public int? ParentId - { - get => _parentId ?? ConfigIndexCriteria?.ParentNodeId; - protected set => _parentId = value; + SupportSoftDelete = supportUnpublished; } #endregion diff --git a/src/Umbraco.Examine/UmbracoContentIndexerOptions.cs b/src/Umbraco.Examine/UmbracoContentIndexerOptions.cs deleted file mode 100644 index 55fd35e012..0000000000 --- a/src/Umbraco.Examine/UmbracoContentIndexerOptions.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Umbraco.Examine -{ - - /// - /// Options used to configure the umbraco content indexer - /// - public class UmbracoContentIndexerOptions - { - public bool SupportUnpublishedContent { get; private set; } - public bool SupportProtectedContent { get; private set; } - public int? ParentId { get; private set; } - - /// - /// Optional inclusion list of content types to index - /// - /// - /// All other types will be ignored if they do not match this list - /// - public IEnumerable IncludeContentTypes { get; private set; } - - /// - /// Optional exclusion list of content types to ignore - /// - /// - /// Any content type alias matched in this will not be included in the index - /// - public IEnumerable ExcludeContentTypes { get; private set; } - - /// - /// Creates a new - /// - /// If the index supports unpublished content - /// If the index supports protected content - /// Optional value indicating to only index content below this ID - /// Optional content type alias inclusion list - /// Optional content type alias exclusion list - public UmbracoContentIndexerOptions(bool supportUnpublishedContent, bool supportProtectedContent, - int? parentId = null, IEnumerable includeContentTypes = null, IEnumerable excludeContentTypes = null) - { - SupportUnpublishedContent = supportUnpublishedContent; - SupportProtectedContent = supportProtectedContent; - ParentId = parentId; - IncludeContentTypes = includeContentTypes; - ExcludeContentTypes = excludeContentTypes; - } - - - } -} diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index 19929d15fe..b62f527450 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -54,23 +54,6 @@ namespace Umbraco.Examine { ProfilingLogger = Current.ProfilingLogger; _configBased = true; - - //This is using the config so we'll validate based on that - ValueSetValidator = new ValueSetValidatorDelegate(set => - { - - //check if this document is of a correct type of node type alias - if (ConfigIndexCriteria.IncludeItemTypes.Any()) - if (!ConfigIndexCriteria.IncludeItemTypes.Contains(set.ItemType)) - return false; - - //if this node type is part of our exclusion list, do not validate - if (ConfigIndexCriteria.ExcludeItemTypes.Any()) - if (ConfigIndexCriteria.ExcludeItemTypes.Contains(set.ItemType)) - return false; - - return true; - }); } /// @@ -135,7 +118,6 @@ namespace Umbraco.Examine /// /// Overridden to ensure that the umbraco system field definitions are in place /// - /// /// /// protected override FieldValueTypeCollection CreateFieldValueTypes(IReadOnlyDictionary> indexValueTypesFactory = null) @@ -158,13 +140,7 @@ namespace Umbraco.Examine /// public bool EnableDefaultEventHandler { get; set; } = true; - /// - /// When set to true data will not be deleted from the index if the data is being unpublished (not deleted) - /// - /// - /// Generally used only for published content - /// - public bool SupportUnpublishedContent { get; protected set; } = false; + public bool SupportSoftDelete { get; protected set; } = false; protected ConfigIndexCriteria ConfigIndexCriteria { get; private set; } diff --git a/src/Umbraco.Examine/UmbracoMemberIndexer.cs b/src/Umbraco.Examine/UmbracoMemberIndexer.cs index 4c59b4d4ca..2b0d3a0c66 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndexer.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndexer.cs @@ -5,6 +5,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Services; using System.Collections.Generic; +using System.Collections.Specialized; using System.ComponentModel; using Examine; using Examine.LuceneEngine; @@ -51,11 +52,16 @@ namespace Umbraco.Examine { } + public override void Initialize(string name, NameValueCollection config) + { + base.Initialize(name, config); + + ValueSetValidator = new MemberValueSetValidator(ConfigIndexCriteria.IncludeItemTypes, ConfigIndexCriteria.ExcludeItemTypes); + } /// /// Overridden to ensure that the umbraco system field definitions are in place /// - /// /// /// protected override FieldValueTypeCollection CreateFieldValueTypes(IReadOnlyDictionary> indexValueTypesFactory = null) diff --git a/src/Umbraco.Examine/ValueSetValidator.cs b/src/Umbraco.Examine/ValueSetValidator.cs new file mode 100644 index 0000000000..1bf7ec2d7a --- /dev/null +++ b/src/Umbraco.Examine/ValueSetValidator.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Examine; +using Examine.LuceneEngine.Providers; +using Umbraco.Core; + +namespace Umbraco.Examine +{ + /// + /// Performing basic validation of a value set + /// + public abstract class ValueSetValidator : IValueSetValidator + { + protected ValueSetValidator( + IEnumerable includeItemTypes, + IEnumerable excludeItemTypes, + IEnumerable includeFields, + IEnumerable excludeFields) + { + IncludeItemTypes = includeItemTypes; + ExcludeItemTypes = excludeItemTypes; + IncludeFields = includeFields; + ExcludeFields = excludeFields; + } + + protected abstract IEnumerable ValidIndexCategories { get; } + + /// + /// Optional inclusion list of content types to index + /// + /// + /// All other types will be ignored if they do not match this list + /// + public IEnumerable IncludeItemTypes { get; } + + /// + /// Optional exclusion list of content types to ignore + /// + /// + /// Any content type alias matched in this will not be included in the index + /// + public IEnumerable ExcludeItemTypes { get; } + + /// + /// Optional inclusion list of index fields to index + /// + /// + /// If specified, all other fields in a will be filtered + /// + public IEnumerable IncludeFields { get; } + + /// + /// Optional exclusion list of index fields + /// + /// + /// If specified, all fields matching these field names will be filtered from the + /// + public IEnumerable ExcludeFields { get; } + + public virtual bool Validate(ValueSet valueSet) + { + if (!ValidIndexCategories.InvariantContains(valueSet.Category)) + return false; + + //check if this document is of a correct type of node type alias + if (IncludeItemTypes != null && !IncludeItemTypes.InvariantContains(valueSet.ItemType)) + return false; + + //if this node type is part of our exclusion list + if (ExcludeItemTypes != null && ExcludeItemTypes.InvariantContains(valueSet.ItemType)) + return false; + + //filter based on the fields provided (if any) + if (IncludeFields != null || ExcludeFields != null) + { + foreach (var key in valueSet.Values.Keys.ToList()) + { + if (IncludeFields != null && !IncludeFields.InvariantContains(key)) + valueSet.Values.Remove(key); //remove any value with a key that doesn't match the inclusion list + + if (ExcludeFields != null && ExcludeFields.InvariantContains(key)) + valueSet.Values.Remove(key); //remove any value with a key that matches the exclusion list + } + } + + + return true; + } + } +} diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 4b49fad3e4..784f534af4 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -116,8 +116,8 @@ namespace Umbraco.Tests.PublishedContent var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, + validator: new ContentValueSetValidator(true))) { rebuilder.Populate(indexer); @@ -143,9 +143,9 @@ namespace Umbraco.Tests.PublishedContent var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, //include unpublished content since this uses the 'internal' indexer, it's up to the media cache to filter - options: new UmbracoContentIndexerOptions(true, false, null))) + validator: new ContentValueSetValidator(true))) using (indexer.ProcessNonAsync()) { rebuilder.Populate(indexer); @@ -190,8 +190,8 @@ namespace Umbraco.Tests.PublishedContent var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, + validator: new ContentValueSetValidator(true))) using (indexer.ProcessNonAsync()) { rebuilder.Populate(indexer); @@ -217,8 +217,8 @@ namespace Umbraco.Tests.PublishedContent var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, + validator: new ContentValueSetValidator(true))) using (indexer.ProcessNonAsync()) { rebuilder.Populate(indexer); @@ -244,8 +244,8 @@ namespace Umbraco.Tests.PublishedContent var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, + validator: new ContentValueSetValidator(true))) using (indexer.ProcessNonAsync()) { rebuilder.Populate(indexer); @@ -272,8 +272,8 @@ namespace Umbraco.Tests.PublishedContent using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, + validator: new ContentValueSetValidator(true))) using (indexer.ProcessNonAsync()) { rebuilder.Populate(indexer); @@ -296,8 +296,8 @@ namespace Umbraco.Tests.PublishedContent var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, + validator: new ContentValueSetValidator(true))) using (indexer.ProcessNonAsync()) { rebuilder.Populate(indexer); diff --git a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs index 7f613753a5..47fa32cbaa 100644 --- a/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/EventsTest.cs @@ -19,9 +19,9 @@ namespace Umbraco.Tests.UmbracoExamine public void Events_Ignoring_Node() { using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, //make parent id 999 so all are ignored - options: new UmbracoContentIndexerOptions(false, false, 999))) + validator: new ContentValueSetValidator(false, 999))) using (indexer.ProcessNonAsync()) { var searcher = indexer.GetSearcher(); diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index f66e55cbbd..e5d7598080 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -151,7 +151,7 @@ namespace Umbraco.Tests.UmbracoExamine Directory luceneDir, Analyzer analyzer = null, ILocalizationService languageService = null, - UmbracoContentIndexerOptions options = null) + IValueSetValidator validator = null) { if (languageService == null) languageService = GetMockLocalizationService(); @@ -159,8 +159,8 @@ namespace Umbraco.Tests.UmbracoExamine if (analyzer == null) analyzer = new StandardAnalyzer(Version.LUCENE_30); - if (options == null) - options = new UmbracoContentIndexerOptions(false, false, null); + if (validator == null) + validator = new ContentValueSetValidator(false); var i = new UmbracoContentIndexer( "testIndexer", @@ -169,8 +169,7 @@ namespace Umbraco.Tests.UmbracoExamine analyzer, profilingLogger, languageService, - new ContentValueSetValidator(options, Mock.Of()), - options); + validator); i.IndexingError += IndexingError; diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 6ef3cc376f..2132c9b19e 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -33,8 +33,8 @@ namespace Umbraco.Tests.UmbracoExamine var contentValueSetBuilder = IndexInitializer.GetContentValueSetBuilder(Container.GetInstance()); using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, + validator: new ContentValueSetValidator(true))) using (indexer.ProcessNonAsync()) { indexer.CreateIndex(); @@ -126,8 +126,8 @@ namespace Umbraco.Tests.UmbracoExamine var mediaRebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - options: new UmbracoContentIndexerOptions(true, false, null))) + using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, + validator: new ContentValueSetValidator(true))) using (indexer.ProcessNonAsync()) { contentRebuilder.RegisterIndex(indexer.Name); @@ -191,7 +191,7 @@ namespace Umbraco.Tests.UmbracoExamine using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, //make parent id 1116 - options: new UmbracoContentIndexerOptions(false, false, 1116))) + validator: new ContentValueSetValidator(false, 1116))) using (indexer.ProcessNonAsync()) { var searcher = indexer.GetSearcher(); @@ -233,7 +233,7 @@ namespace Umbraco.Tests.UmbracoExamine using (var luceneDir = new RandomIdRamDirectory()) using (var indexer1 = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, //make parent id 2222 - options: new UmbracoContentIndexerOptions(false, false, 2222))) + validator: new ContentValueSetValidator(false, 2222))) using (indexer1.ProcessNonAsync()) { var searcher = indexer1.GetSearcher(); @@ -283,7 +283,7 @@ namespace Umbraco.Tests.UmbracoExamine var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - options: new UmbracoContentIndexerOptions(true, false, null))) + validator: new ContentValueSetValidator(true))) using (indexer.ProcessNonAsync()) { rebuilder.RegisterIndex(indexer.Name); diff --git a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs index 63ca0141f2..daac5f5778 100644 --- a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs @@ -17,9 +17,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Invalid_Category() { - var validator = new ContentValueSetValidator( - new UmbracoContentIndexerOptions(true, true, null), - Mock.Of()); + var validator = new ContentValueSetValidator(true, true, Mock.Of()); var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); Assert.IsTrue(result); @@ -35,9 +33,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Must_Have_Path() { - var validator = new ContentValueSetValidator( - new UmbracoContentIndexerOptions(true, true, null), - Mock.Of()); + var validator = new ContentValueSetValidator(true, true, Mock.Of()); var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world" })); Assert.IsFalse(result); @@ -49,9 +45,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Parent_Id() { - var validator = new ContentValueSetValidator( - new UmbracoContentIndexerOptions(true, true, 555), - Mock.Of()); + var validator = new ContentValueSetValidator(true, true, Mock.Of(), 555); var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); Assert.IsFalse(result); @@ -69,9 +63,8 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Inclusion_List() { - var validator = new ContentValueSetValidator( - new UmbracoContentIndexerOptions(true, true, includeContentTypes: new List { "include-content" }), - Mock.Of()); + var validator = new ContentValueSetValidator(true, true, Mock.Of(), + includeItemTypes: new List { "include-content" }); var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); Assert.IsFalse(result); @@ -86,9 +79,8 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Exclusion_List() { - var validator = new ContentValueSetValidator( - new UmbracoContentIndexerOptions(true, true, excludeContentTypes: new List { "exclude-content" }), - Mock.Of()); + var validator = new ContentValueSetValidator(true, true, Mock.Of(), + excludeItemTypes: new List { "exclude-content" }); var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); Assert.IsTrue(result); @@ -103,12 +95,10 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Inclusion_Exclusion_List() { - var validator = new ContentValueSetValidator( - new UmbracoContentIndexerOptions(true, true, - includeContentTypes: new List { "include-content", "exclude-content" }, - excludeContentTypes: new List { "exclude-content" }), - Mock.Of()); - + var validator = new ContentValueSetValidator(true, true, Mock.Of(), + includeItemTypes: new List { "include-content", "exclude-content" }, + excludeItemTypes: new List { "exclude-content" }); + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); Assert.IsFalse(result); @@ -125,9 +115,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Recycle_Bin() { - var validator = new ContentValueSetValidator( - new UmbracoContentIndexerOptions(false, true, null), - Mock.Of()); + var validator = new ContentValueSetValidator(false, true, Mock.Of()); var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555" })); Assert.IsFalse(result); @@ -151,9 +139,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Published_Only() { - var validator = new ContentValueSetValidator( - new UmbracoContentIndexerOptions(false, true, null), - Mock.Of()); + var validator = new ContentValueSetValidator(false, true, Mock.Of()); var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); Assert.IsFalse(result); @@ -180,9 +166,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Published_Only_With_Variants() { - var validator = new ContentValueSetValidator( - new UmbracoContentIndexerOptions(false, true, null), - Mock.Of()); + var validator = new ContentValueSetValidator(false, true, Mock.Of()); var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new Dictionary @@ -240,9 +224,7 @@ namespace Umbraco.Tests.UmbracoExamine .Returns(Attempt.Succeed(new PublicAccessEntry(Guid.NewGuid(), 555, 444, 333, Enumerable.Empty()))); publicAccessService.Setup(x => x.IsProtected("-1,777")) .Returns(Attempt.Fail()); - var validator = new ContentValueSetValidator( - new UmbracoContentIndexerOptions(true, false, null), - publicAccessService.Object); + var validator = new ContentValueSetValidator(true, false, publicAccessService.Object); var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); Assert.IsFalse(result); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 752440a4dc..8d1bf2898a 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -695,9 +695,9 @@ namespace Umbraco.Web.Search { var valueSet = examineComponent._contentValueSetBuilder.GetValueSets(content).ToList(); - foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() // only for the specified indexers - .Where(x => supportUnpublished.HasValue == false || supportUnpublished.Value == x.SupportUnpublishedContent) + .Where(x => supportUnpublished.HasValue == false || supportUnpublished.Value == x.SupportSoftDelete) .Where(x => x.EnableDefaultEventHandler)) { index.IndexItems(valueSet); @@ -727,10 +727,10 @@ namespace Umbraco.Web.Search { var valueSet = examineComponent._mediaValueSetBuilder.GetValueSets(media).ToList(); - foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() // index this item for all indexers if the media is not trashed, otherwise if the item is trashed // then only index this for indexers supporting unpublished media - .Where(x => isPublished || (x.SupportUnpublishedContent)) + .Where(x => isPublished || (x.SupportSoftDelete)) .Where(x => x.EnableDefaultEventHandler)) { index.IndexItems(valueSet); @@ -790,7 +790,7 @@ namespace Umbraco.Web.Search foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() // if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content, // otherwise if keepIfUnpublished == false then remove from all indexes - .Where(x => keepIfUnpublished == false || x.SupportUnpublishedContent == false) + .Where(x => keepIfUnpublished == false || x.SupportSoftDelete == false) .Where(x => x.EnableDefaultEventHandler)) { index.DeleteFromIndex(strId); diff --git a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs index bdfb259c10..fbe4d618e6 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs @@ -41,11 +41,6 @@ namespace Umbraco.Web.Search protected IPublicAccessService PublicAccessService { get; } protected IMemberService MemberService { get; } - /// - /// By default these are the member fields we index - /// - public static readonly string[] DefaultMemberIndexFields = new[] { "id", "nodeName", "updateDate", "writerName", "loginName", "email", "nodeTypeAlias" }; - /// /// Creates the Umbraco indexes /// @@ -54,24 +49,37 @@ namespace Umbraco.Web.Search { return new [] { - CreateContentIndex(Constants.UmbracoIndexes.InternalIndexName, Constants.UmbracoIndexes.InternalIndexPath, new UmbracoContentIndexerOptions(true, true, null), new CultureInvariantWhitespaceAnalyzer()), - CreateContentIndex(Constants.UmbracoIndexes.ExternalIndexName, Constants.UmbracoIndexes.ExternalIndexPath, new UmbracoContentIndexerOptions(false, false, null), new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30)), + CreateInternalIndex(), + CreateExternalIndex(), CreateMemberIndex() }; } - private IIndexer CreateContentIndex(string name, string path, UmbracoContentIndexerOptions options, Analyzer analyzer) + private IIndexer CreateInternalIndex() { var index = new UmbracoContentIndexer( - name, + Constants.UmbracoIndexes.InternalIndexName, //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, - GetFileSystemLuceneDirectory(path), - analyzer, + GetFileSystemLuceneDirectory(Constants.UmbracoIndexes.InternalIndexPath), + new CultureInvariantWhitespaceAnalyzer(), ProfilingLogger, LanguageService, - GetContentValueSetValidator(options), - options); + GetContentValueSetValidator()); + return index; + } + + private IIndexer CreateExternalIndex() + { + var index = new UmbracoContentIndexer( + Constants.UmbracoIndexes.ExternalIndexName, + //fixme - how to deal with languages like in UmbracoContentIndexer.CreateFieldValueTypes + UmbracoExamineIndexer.UmbracoIndexFieldDefinitions, + GetFileSystemLuceneDirectory(Constants.UmbracoIndexes.ExternalIndexPath), + new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), + ProfilingLogger, + LanguageService, + GetPublishedContentValueSetValidator()); return index; } @@ -102,9 +110,14 @@ namespace Umbraco.Web.Search return luceneDir; } - public virtual IValueSetValidator GetContentValueSetValidator(UmbracoContentIndexerOptions options) + public virtual IValueSetValidator GetContentValueSetValidator() { - return new ContentValueSetValidator(options, PublicAccessService); + return new ContentValueSetValidator(true, true, PublicAccessService); + } + + public virtual IValueSetValidator GetPublishedContentValueSetValidator() + { + return new ContentValueSetValidator(false, false, PublicAccessService); } /// @@ -113,18 +126,7 @@ namespace Umbraco.Web.Search /// public virtual IValueSetValidator GetMemberValueSetValidator() { - //This validator is used purely to filter the value set - return new ValueSetValidatorDelegate(valueSet => - { - - foreach(var key in valueSet.Values.Keys.ToList()) - { - if (!DefaultMemberIndexFields.InvariantContains(key)) - valueSet.Values.Remove(key); //remove any value with a key that doesn't match our list - } - - return true; - }); + return new MemberValueSetValidator(); } } From 8ba694c4fb40bd787ab1409d0d1c02abf4e3c2a5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 28 Nov 2018 19:10:43 +1100 Subject: [PATCH 23/44] more tests --- .../ContentValueSetValidator.cs | 3 +- src/Umbraco.Examine/ValueSetValidator.cs | 9 ++- .../UmbracoContentValueSetValidatorTests.cs | 80 +++++++++++++++++-- 3 files changed, 79 insertions(+), 13 deletions(-) diff --git a/src/Umbraco.Examine/ContentValueSetValidator.cs b/src/Umbraco.Examine/ContentValueSetValidator.cs index 9355d6d695..e12e20e224 100644 --- a/src/Umbraco.Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Examine/ContentValueSetValidator.cs @@ -85,7 +85,8 @@ namespace Umbraco.Examine // return nothing if we're not supporting protected content and it is protected, and we're not supporting unpublished content if (valueSet.Category == IndexTypes.Content && !SupportProtectedContent - && _publicAccessService.IsProtected(path)) + //if the service is null we can't look this up so we'll return false + && (_publicAccessService == null || _publicAccessService.IsProtected(path))) { return false; } diff --git a/src/Umbraco.Examine/ValueSetValidator.cs b/src/Umbraco.Examine/ValueSetValidator.cs index 1bf7ec2d7a..a0e926fed0 100644 --- a/src/Umbraco.Examine/ValueSetValidator.cs +++ b/src/Umbraco.Examine/ValueSetValidator.cs @@ -10,9 +10,9 @@ namespace Umbraco.Examine /// /// Performing basic validation of a value set /// - public abstract class ValueSetValidator : IValueSetValidator + public class ValueSetValidator : IValueSetValidator { - protected ValueSetValidator( + public ValueSetValidator( IEnumerable includeItemTypes, IEnumerable excludeItemTypes, IEnumerable includeFields, @@ -22,9 +22,10 @@ namespace Umbraco.Examine ExcludeItemTypes = excludeItemTypes; IncludeFields = includeFields; ExcludeFields = excludeFields; + ValidIndexCategories = null; } - protected abstract IEnumerable ValidIndexCategories { get; } + protected virtual IEnumerable ValidIndexCategories { get; } /// /// Optional inclusion list of content types to index @@ -60,7 +61,7 @@ namespace Umbraco.Examine public virtual bool Validate(ValueSet valueSet) { - if (!ValidIndexCategories.InvariantContains(valueSet.Category)) + if (ValidIndexCategories != null && !ValidIndexCategories.InvariantContains(valueSet.Category)) return false; //check if this document is of a correct type of node type alias diff --git a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs index daac5f5778..0df4750051 100644 --- a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs @@ -61,12 +61,60 @@ namespace Umbraco.Tests.UmbracoExamine } [Test] - public void Inclusion_List() + public void Inclusion_Field_List() + { + var validator = new ValueSetValidator(null, null, + new[] { "hello", "world" }, + null); + + var valueSet = new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); + var result = validator.Validate(valueSet); + Assert.IsTrue(result); + + Assert.IsFalse(valueSet.Values.ContainsKey("path")); + Assert.IsTrue(valueSet.Values.ContainsKey("hello")); + Assert.IsTrue(valueSet.Values.ContainsKey("world")); + } + + [Test] + public void Exclusion_Field_List() + { + var validator = new ValueSetValidator(null, null, + null, + new[] { "hello", "world" }); + + var valueSet = new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); + var result = validator.Validate(valueSet); + Assert.IsTrue(result); + + Assert.IsTrue(valueSet.Values.ContainsKey("path")); + Assert.IsFalse(valueSet.Values.ContainsKey("hello")); + Assert.IsFalse(valueSet.Values.ContainsKey("world")); + } + + [Test] + public void Inclusion_Exclusion_Field_List() + { + var validator = new ValueSetValidator(null, null, + new[] { "hello", "world" }, + new[] { "world" }); + + var valueSet = new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); + var result = validator.Validate(valueSet); + Assert.IsTrue(result); + + Assert.IsFalse(valueSet.Values.ContainsKey("path")); + Assert.IsTrue(valueSet.Values.ContainsKey("hello")); + Assert.IsFalse(valueSet.Values.ContainsKey("world")); + } + + [Test] + public void Inclusion_Type_List() { var validator = new ContentValueSetValidator(true, true, Mock.Of(), includeItemTypes: new List { "include-content" }); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); Assert.IsFalse(result); result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); @@ -77,7 +125,7 @@ namespace Umbraco.Tests.UmbracoExamine } [Test] - public void Exclusion_List() + public void Exclusion_Type_List() { var validator = new ContentValueSetValidator(true, true, Mock.Of(), excludeItemTypes: new List { "exclude-content" }); @@ -93,12 +141,12 @@ namespace Umbraco.Tests.UmbracoExamine } [Test] - public void Inclusion_Exclusion_List() + public void Inclusion_Exclusion_Type_List() { var validator = new ContentValueSetValidator(true, true, Mock.Of(), includeItemTypes: new List { "include-content", "exclude-content" }, excludeItemTypes: new List { "exclude-content" }); - + var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); Assert.IsFalse(result); @@ -113,9 +161,9 @@ namespace Umbraco.Tests.UmbracoExamine } [Test] - public void Recycle_Bin() + public void Recycle_Bin_Content() { - var validator = new ContentValueSetValidator(false, true, Mock.Of()); + var validator = new ContentValueSetValidator(false, false, Mock.Of()); var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555" })); Assert.IsFalse(result); @@ -136,6 +184,22 @@ namespace Umbraco.Tests.UmbracoExamine Assert.IsTrue(result); } + [Test] + public void Recycle_Bin_Media() + { + var validator = new ContentValueSetValidator(false, false, Mock.Of()); + + var result = validator.Validate(new ValueSet("555", IndexTypes.Media, new { hello = "world", path = "-1,-21,555" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Media, new { hello = "world", path = "-1,-21,555,777" })); + Assert.IsFalse(result); + + result = validator.Validate(new ValueSet("555", IndexTypes.Media, new { hello = "world", path = "-1,555" })); + Assert.IsTrue(result); + + } + [Test] public void Published_Only() { @@ -202,7 +266,7 @@ namespace Umbraco.Tests.UmbracoExamine ["title_es-ES"] = "my title", [UmbracoExamineIndexer.PublishedFieldName] = 1 }); - Assert.AreEqual(10, valueSet.Values.Count()); + Assert.AreEqual(10, valueSet.Values.Count()); Assert.IsTrue(valueSet.Values.ContainsKey($"{UmbracoExamineIndexer.PublishedFieldName}_es-es")); Assert.IsTrue(valueSet.Values.ContainsKey("hello_es-ES")); Assert.IsTrue(valueSet.Values.ContainsKey("title_es-ES")); From 011209bd03ee32ece319bf86413ca7b3413a7e6a Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 28 Nov 2018 19:53:25 +1100 Subject: [PATCH 24/44] Fixes bool check and member index fields --- src/Umbraco.Examine/ContentValueSetValidator.cs | 4 ++-- src/Umbraco.Examine/IndexRebuilder.cs | 2 +- src/Umbraco.Examine/MemberValueSetBuilder.cs | 2 +- src/Umbraco.Examine/MemberValueSetValidator.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Examine/ContentValueSetValidator.cs b/src/Umbraco.Examine/ContentValueSetValidator.cs index e12e20e224..e78fe749db 100644 --- a/src/Umbraco.Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Examine/ContentValueSetValidator.cs @@ -43,7 +43,7 @@ namespace Umbraco.Examine public override bool Validate(ValueSet valueSet) { - if (!ValidCategories.Contains(valueSet.Category)) + if (!base.Validate(valueSet)) return false; //check for published content @@ -106,7 +106,7 @@ namespace Umbraco.Examine return false; } - return base.Validate(valueSet); + return true; } } } diff --git a/src/Umbraco.Examine/IndexRebuilder.cs b/src/Umbraco.Examine/IndexRebuilder.cs index 92b30537bf..4c21f8416f 100644 --- a/src/Umbraco.Examine/IndexRebuilder.cs +++ b/src/Umbraco.Examine/IndexRebuilder.cs @@ -21,7 +21,7 @@ namespace Umbraco.Examine public void RebuildIndexes(bool onlyEmptyIndexes) { var indexes = (onlyEmptyIndexes - ? ExamineManager.IndexProviders.Values.Where(x => x.IndexExists()) + ? ExamineManager.IndexProviders.Values.Where(x => !x.IndexExists()) : ExamineManager.IndexProviders.Values).ToArray(); foreach(var index in indexes) diff --git a/src/Umbraco.Examine/MemberValueSetBuilder.cs b/src/Umbraco.Examine/MemberValueSetBuilder.cs index fb6cb6f967..14af7de8cb 100644 --- a/src/Umbraco.Examine/MemberValueSetBuilder.cs +++ b/src/Umbraco.Examine/MemberValueSetBuilder.cs @@ -42,7 +42,7 @@ namespace Umbraco.Examine AddPropertyValue(property, null, null, values); } - var vs = new ValueSet(m.Id.ToInvariantString(), IndexTypes.Content, m.ContentType.Alias, values); + var vs = new ValueSet(m.Id.ToInvariantString(), IndexTypes.Member, m.ContentType.Alias, values); yield return vs; } diff --git a/src/Umbraco.Examine/MemberValueSetValidator.cs b/src/Umbraco.Examine/MemberValueSetValidator.cs index b8e96dc68d..71de6001ce 100644 --- a/src/Umbraco.Examine/MemberValueSetValidator.cs +++ b/src/Umbraco.Examine/MemberValueSetValidator.cs @@ -23,7 +23,7 @@ namespace Umbraco.Examine /// /// By default these are the member fields we index /// - public static readonly string[] DefaultMemberIndexFields = { "id", "nodeName", "updateDate", "writerName", "loginName", "email", "nodeTypeAlias" }; + public static readonly string[] DefaultMemberIndexFields = { "id", "nodeName", "updateDate", "loginName", "email" }; private static readonly IEnumerable ValidCategories = new[] { IndexTypes.Member }; protected override IEnumerable ValidIndexCategories => ValidCategories; From dd571060b924717d8335755427eae3ce8476fb10 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 28 Nov 2018 21:02:45 +1100 Subject: [PATCH 25/44] Starts integrating changes into the Examine mgmt dashboard --- src/Umbraco.Core/ObjectExtensions.cs | 4 +- src/Umbraco.Examine/ExamineExtensions.cs | 72 ++++++++++ src/Umbraco.Examine/IIndexDiagnostics.cs | 41 ++++++ src/Umbraco.Examine/IUmbracoIndexer.cs | 1 - src/Umbraco.Examine/Umbraco.Examine.csproj | 2 + src/Umbraco.Examine/UmbracoExamineIndexer.cs | 59 +++++++- .../Editors/ExamineManagementController.cs | 83 +++-------- src/Umbraco.Web/Search/ExamineComponent.cs | 1 - src/Umbraco.Web/Search/ExamineIndexModel.cs | 21 +++ src/Umbraco.Web/Search/ExamineIndexerModel.cs | 32 ----- .../Search/GenericIndexDiagnostics.cs | 66 +++++++++ .../Search/LuceneIndexerExtensions.cs | 131 ------------------ src/Umbraco.Web/Umbraco.Web.csproj | 4 +- 13 files changed, 287 insertions(+), 230 deletions(-) create mode 100644 src/Umbraco.Examine/ExamineExtensions.cs create mode 100644 src/Umbraco.Examine/IIndexDiagnostics.cs create mode 100644 src/Umbraco.Web/Search/ExamineIndexModel.cs delete mode 100644 src/Umbraco.Web/Search/ExamineIndexerModel.cs create mode 100644 src/Umbraco.Web/Search/GenericIndexDiagnostics.cs delete mode 100644 src/Umbraco.Web/Search/LuceneIndexerExtensions.cs diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs index 44e5968a9f..318a826b25 100644 --- a/src/Umbraco.Core/ObjectExtensions.cs +++ b/src/Umbraco.Core/ObjectExtensions.cs @@ -724,8 +724,8 @@ namespace Umbraco.Core { return typeConverter; } - - TypeConverter converter = TypeDescriptor.GetConverter(target); + + var converter = TypeDescriptor.GetConverter(target); if (converter.CanConvertFrom(source)) { return DestinationTypeConverterCache[key] = converter; diff --git a/src/Umbraco.Examine/ExamineExtensions.cs b/src/Umbraco.Examine/ExamineExtensions.cs new file mode 100644 index 0000000000..b0657bc502 --- /dev/null +++ b/src/Umbraco.Examine/ExamineExtensions.cs @@ -0,0 +1,72 @@ +using System; +using Examine.LuceneEngine.Providers; +using Lucene.Net.Index; +using Lucene.Net.Search; +using Lucene.Net.Store; + +namespace Umbraco.Examine +{ + /// + /// Extension methods for the LuceneIndexer + /// + internal static class ExamineExtensions + { + /// + /// Checks if the index can be read/opened + /// + /// + /// The exception returned if there was an error + /// + public static bool IsHealthy(this LuceneIndexer indexer, out Exception ex) + { + try + { + using (indexer.GetIndexWriter().GetReader()) + { + ex = null; + return true; + } + } + catch (Exception e) + { + ex = e; + return false; + } + } + + /// + /// Return the number of indexed documents in Lucene + /// + /// + /// + public static int GetIndexDocumentCount(this LuceneIndexer indexer) + { + if (!((indexer.GetSearcher() as LuceneSearcher)?.GetLuceneSearcher() is IndexSearcher searcher)) + return 0; + + using (searcher) + using (var reader = searcher.IndexReader) + { + return reader.NumDocs(); + } + } + + /// + /// Return the total number of fields in the index + /// + /// + /// + public static int GetIndexFieldCount(this LuceneIndexer indexer) + { + if (!((indexer.GetSearcher() as LuceneSearcher)?.GetLuceneSearcher() is IndexSearcher searcher)) + return 0; + + using (searcher) + using (var reader = searcher.IndexReader) + { + return reader.GetFieldNames(IndexReader.FieldOption.ALL).Count; + } + } + + } +} diff --git a/src/Umbraco.Examine/IIndexDiagnostics.cs b/src/Umbraco.Examine/IIndexDiagnostics.cs new file mode 100644 index 0000000000..04ca4a6ab9 --- /dev/null +++ b/src/Umbraco.Examine/IIndexDiagnostics.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using Examine; +using Umbraco.Core; + +namespace Umbraco.Examine +{ + + + /// + /// Exposes diagnostic information about an index + /// + public interface IIndexDiagnostics + { + /// + /// The number of documents in the index + /// + int DocumentCount { get; } + + /// + /// The number of fields in the index + /// + int FieldCount { get; } + + /// + /// If the index can be open/read + /// + /// + /// A successful attempt if it is healthy, else a failed attempt with a message if unhealthy + /// + Attempt IsHealthy(); + + /// + /// A key/value collection of diagnostic properties for the index + /// + /// + /// Used to display in the UI + /// + IReadOnlyDictionary Metadata { get; } + } +} diff --git a/src/Umbraco.Examine/IUmbracoIndexer.cs b/src/Umbraco.Examine/IUmbracoIndexer.cs index 1785cd5a92..5649a3a866 100644 --- a/src/Umbraco.Examine/IUmbracoIndexer.cs +++ b/src/Umbraco.Examine/IUmbracoIndexer.cs @@ -2,7 +2,6 @@ namespace Umbraco.Examine { - /// /// A Marker interface for defining an Umbraco indexer /// diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 5c206250b1..a8898f7fe5 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -63,6 +63,8 @@ + + diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index b62f527450..cfea6de628 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -11,6 +11,7 @@ using Umbraco.Core; using Examine; using Examine.LuceneEngine; using Examine.LuceneEngine.Indexing; +using Lucene.Net.Store; using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Core.Xml; @@ -24,7 +25,7 @@ namespace Umbraco.Examine /// An abstract provider containing the basic functionality to be able to query against /// Umbraco data. /// - public abstract class UmbracoExamineIndexer : LuceneIndexer, IUmbracoIndexer + public abstract class UmbracoExamineIndexer : LuceneIndexer, IUmbracoIndexer, IIndexDiagnostics { // note // wrapping all operations that end up calling base.SafelyProcessQueueItems in a safe call @@ -77,6 +78,10 @@ namespace Umbraco.Examine : base(name, fieldDefinitions, luceneDirectory, defaultAnalyzer, validator, indexValueTypes) { ProfilingLogger = profilingLogger ?? throw new ArgumentNullException(nameof(profilingLogger)); + + //try to set the value of `LuceneIndexFolder` for diagnostic reasons + if (luceneDirectory is FSDirectory fsDir) + LuceneIndexFolder = fsDir.Directory; } private readonly bool _configBased = false; @@ -348,5 +353,57 @@ namespace Umbraco.Examine indexSet.ExcludeNodeTypes.ToList().Select(x => x.Name).ToArray(), indexSet.IndexParentId); } + + #region IIndexDiagnostics + + public int DocumentCount + { + get + { + try + { + return this.GetIndexDocumentCount(); + } + catch (AlreadyClosedException) + { + ProfilingLogger.Logger.Warn(typeof(UmbracoContentIndexer), "Cannot get GetIndexDocumentCount, the writer is already closed"); + return 0; + } + } + } + + public int FieldCount + { + get + { + try + { + return this.GetIndexFieldCount(); + } + catch (AlreadyClosedException) + { + ProfilingLogger.Logger.Warn(typeof(UmbracoContentIndexer), "Cannot get GetIndexFieldCount, the writer is already closed"); + return 0; + } + } + } + + public Attempt IsHealthy() + { + var isHealthy = this.IsHealthy(out var indexError); + return isHealthy ? Attempt.Succeed() : Attempt.Fail(indexError.Message); + } + + public virtual IReadOnlyDictionary Metadata => new Dictionary + { + [nameof(CommitCount)] = CommitCount, + [nameof(DefaultAnalyzer)] = DefaultAnalyzer.GetType(), + [nameof(DirectoryFactory)] = DirectoryFactory, + [nameof(EnableDefaultEventHandler)] = EnableDefaultEventHandler, + [nameof(LuceneIndexFolder)] = LuceneIndexFolder?.ToString(), + [nameof(SupportSoftDelete)] = SupportSoftDelete + }; + + #endregion } } diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs index 2c29f63613..ec6f1841b0 100644 --- a/src/Umbraco.Web/Editors/ExamineManagementController.cs +++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs @@ -13,6 +13,7 @@ using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Logging; +using Umbraco.Examine; using Umbraco.Web.Mvc; using Umbraco.Web.Search; @@ -37,13 +38,9 @@ namespace Umbraco.Web.Editors /// Get the details for indexers /// /// - public IEnumerable GetIndexerDetails() + public IEnumerable GetIndexerDetails() { - return _examineManager.IndexProviders.Select(CreateModel).OrderBy(x => - { - //order by name , but strip the "Indexer" from the end if it exists - return x.Name.TrimEnd("Indexer"); - }); + return _examineManager.IndexProviders.Select(CreateModel).OrderBy(x => x.Name.TrimEnd("Indexer")); } /// @@ -122,7 +119,7 @@ namespace Umbraco.Web.Editors /// This is kind of rudimentary since there's no way we can know that the index has rebuilt, we /// have a listener for the index op complete so we'll just check if that key is no longer there in the runtime cache /// - public ExamineIndexerModel PostCheckRebuildIndex(string indexerName) + public ExamineIndexModel PostCheckRebuildIndex(string indexerName) { var msg = ValidateLuceneIndexer(indexerName, out LuceneIndexer indexer); if (msg.IsSuccessStatusCode) @@ -186,66 +183,32 @@ namespace Umbraco.Web.Editors return msg; } - private ExamineIndexerModel CreateModel(KeyValuePair indexerKeyVal) + + + private ExamineIndexModel CreateModel(KeyValuePair indexerKeyVal) { var indexer = indexerKeyVal.Value; var indexName = indexerKeyVal.Key; - var indexerModel = new ExamineIndexerModel + + if (!(indexer is IIndexDiagnostics indexDiag)) + indexDiag = new GenericIndexDiagnostics(indexer); + + + var isHealth = indexDiag.IsHealthy(); + var properties = new Dictionary { - FieldDefinitions = indexer.FieldDefinitionCollection, - Name = indexName + [nameof(IIndexDiagnostics.DocumentCount)] = indexDiag.DocumentCount, + [nameof(IIndexDiagnostics.FieldCount)] = indexDiag.FieldCount, }; + foreach (var p in indexDiag.Metadata) + properties[p.Key] = p.Value; - var props = TypeHelper.CachedDiscoverableProperties(indexer.GetType(), mustWrite: false) - //ignore these properties - .Where(x => new[] { "IndexerData", "Description", "WorkingFolder" } - .InvariantContains(x.Name) == false) - .OrderBy(x => x.Name); - - foreach (var p in props) + var indexerModel = new ExamineIndexModel { - var val = p.GetValue(indexer, null); - if (val == null) - { - // Do not warn for new new attribute that is optional - if (string.Equals(p.Name, "DirectoryFactory", StringComparison.InvariantCultureIgnoreCase) == false) - { - Logger - .Warn("Property value was null when setting up property on indexer: " + indexName + - " property: " + p.Name); - } - - val = string.Empty; - } - - indexerModel.ProviderProperties.Add(p.Name, val.ToString()); - } - - if (indexer is LuceneIndexer luceneIndexer) - { - indexerModel.IsLuceneIndex = true; - - if (luceneIndexer.IndexExists()) - { - indexerModel.IsHealthy = luceneIndexer.IsHealthy(out var indexError); - - if (indexerModel.IsHealthy == false) - { - //we cannot continue at this point - indexerModel.Error = indexError.ToString(); - return indexerModel; - } - - indexerModel.DocumentCount = luceneIndexer.GetIndexDocumentCount(); - indexerModel.FieldCount = luceneIndexer.GetIndexFieldCount(); - } - else - { - indexerModel.DocumentCount = 0; - indexerModel.FieldCount = 0; - } - } + Name = indexName, + HealthStatus = isHealth.Success ? (isHealth.Result ?? "Healthy") : (isHealth.Result ?? "Unhealthy"), + ProviderProperties = properties + }; return indexerModel; } diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 8d1bf2898a..a7c7bf8dae 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -26,7 +26,6 @@ using Umbraco.Core.Composing; namespace Umbraco.Web.Search { - /// /// Configures and installs Examine. /// diff --git a/src/Umbraco.Web/Search/ExamineIndexModel.cs b/src/Umbraco.Web/Search/ExamineIndexModel.cs new file mode 100644 index 0000000000..1851e696f4 --- /dev/null +++ b/src/Umbraco.Web/Search/ExamineIndexModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using Examine; + +namespace Umbraco.Web.Search +{ + [DataContract(Name = "indexer", Namespace = "")] + public class ExamineIndexModel + { + [DataMember(Name = "name")] + public string Name { get; set; } + + [DataMember(Name = "healthStatus")] + public string HealthStatus { get; set; } + + [DataMember(Name = "providerProperties")] + public IReadOnlyDictionary ProviderProperties { get; set; } + + } +} diff --git a/src/Umbraco.Web/Search/ExamineIndexerModel.cs b/src/Umbraco.Web/Search/ExamineIndexerModel.cs deleted file mode 100644 index 3ad4edf7e8..0000000000 --- a/src/Umbraco.Web/Search/ExamineIndexerModel.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; -using Examine; - -namespace Umbraco.Web.Search -{ - [DataContract(Name = "indexer", Namespace = "")] - public class ExamineIndexerModel : ExamineSearcherModel - { - [DataMember(Name = "fieldDefinitions")] - public IEnumerable FieldDefinitions { get; set; } - - /// - /// The number of docs in the index - /// - [DataMember(Name = "documentCount")] - public int DocumentCount { get; set; } - - /// - /// The number of fields in the index - /// - [DataMember(Name = "fieldCount")] - public int FieldCount { get; set; } - - /// - /// Generally will always be true unless someone has created a new non-lucene index - /// - [DataMember(Name = "isLuceneIndex")] - public bool IsLuceneIndex { get; set; } - } -} diff --git a/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs b/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs new file mode 100644 index 0000000000..6d3482837e --- /dev/null +++ b/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Examine; +using Umbraco.Core; +using Umbraco.Core.Composing; +using Umbraco.Examine; + +namespace Umbraco.Web.Search +{ + /// + /// Used to return diagnostic data for any index + /// + public class GenericIndexDiagnostics : IIndexDiagnostics + { + private readonly IIndexer _index; + private static readonly string[] IgnoreProperties = { "Description" }; + + public GenericIndexDiagnostics(IIndexer index) + { + _index = index; + } + + public int DocumentCount => -1; //unknown + + public int FieldCount => -1; //unknown + + public Attempt IsHealthy() + { + if (!_index.IndexExists()) + return Attempt.Fail("Does not exist"); + + try + { + var searcher = _index.GetSearcher(); + var result = searcher.Search("test", false); + return Attempt.Succeed(); //if we can search we'll assume it's healthy + } + catch (Exception e) + { + return Attempt.Fail($"Error: {e.Message}"); + } + } + + public IReadOnlyDictionary Metadata + { + get + { + var result = new Dictionary(); + + var props = TypeHelper.CachedDiscoverableProperties(_index.GetType(), mustWrite: false) + .Where(x => IgnoreProperties.InvariantContains(x.Name) == false) + .OrderBy(x => x.Name); + + foreach (var p in props) + { + var val = p.GetValue(_index, null) ?? string.Empty; + + result.Add(p.Name, val); + } + + return result; + } + } + } +} diff --git a/src/Umbraco.Web/Search/LuceneIndexerExtensions.cs b/src/Umbraco.Web/Search/LuceneIndexerExtensions.cs deleted file mode 100644 index 0b0ca2a768..0000000000 --- a/src/Umbraco.Web/Search/LuceneIndexerExtensions.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System; -using System.Linq; -using Examine; -using Examine.LuceneEngine.Providers; -using Examine.Providers; -using Lucene.Net.Index; -using Lucene.Net.Search; -using Lucene.Net.Store; -using Umbraco.Core.Logging; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.Search -{ - /// - /// Extension methods for the LuceneIndexer - /// - internal static class ExamineExtensions - { - /// - /// Checks if the index can be read/opened - /// - /// - /// The exception returned if there was an error - /// - public static bool IsHealthy(this LuceneIndexer indexer, out Exception ex) - { - try - { - using (indexer.GetIndexWriter().GetReader()) - { - ex = null; - return true; - } - } - catch (Exception e) - { - ex = e; - return false; - } - } - - /// - /// Return the number of indexed documents in Lucene - /// - /// - /// - public static int GetIndexDocumentCount(this LuceneIndexer indexer) - { - try - { - if (!((indexer.GetSearcher() as LuceneSearcher)?.GetLuceneSearcher() is IndexSearcher searcher)) - return 0; - - using (searcher) - using (var reader = searcher.IndexReader) - { - return reader.NumDocs(); - } - } - catch (AlreadyClosedException) - { - Current.Logger.Warn(typeof(ExamineExtensions), "Cannot get GetIndexDocumentCount, the writer is already closed"); - return 0; - } - } - - /// - /// Return the total number of fields in the index - /// - /// - /// - public static int GetIndexFieldCount(this LuceneIndexer indexer) - { - try - { - if (!((indexer.GetSearcher() as LuceneSearcher)?.GetLuceneSearcher() is IndexSearcher searcher)) - return 0; - - using (searcher) - using (var reader = searcher.IndexReader) - { - return reader.GetFieldNames(IndexReader.FieldOption.ALL).Count; - } - } - catch (AlreadyClosedException) - { - Current.Logger.Warn(typeof(ExamineExtensions), "Cannot get GetIndexFieldCount, the writer is already closed"); - return 0; - } - } - - /// - /// Check if the index is locked - /// - /// - /// - /// - /// If the index does not exist we'll consider it locked - /// - public static bool IsIndexLocked(this LuceneIndexer indexer) - { - return indexer.IndexExists() == false - || IndexWriter.IsLocked(indexer.GetLuceneDirectory()); - } - - /// - /// The number of documents deleted in the index - /// - /// - /// - public static int GetDeletedDocumentsCount(this LuceneIndexer indexer) - { - try - { - if (!((indexer.GetSearcher() as LuceneSearcher)?.GetLuceneSearcher() is IndexSearcher searcher)) - return 0; - - using (searcher) - using (var reader = searcher.IndexReader) - { - return reader.NumDeletedDocs; - } - } - catch (AlreadyClosedException) - { - Current.Logger.Warn(typeof(ExamineExtensions), "Cannot get GetDeletedDocumentsCount, the writer is already closed"); - return 0; - } - } - } -} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a626cd8ebd..3e54107759 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -160,6 +160,7 @@ + @@ -993,8 +994,7 @@ - - + From 340b8e6cefa77a556fc54c78968d2ecdf1593f64 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 29 Nov 2018 13:55:51 +1100 Subject: [PATCH 26/44] Latest examine, updates for dashboard, adds UmbracoExamineIndexDiagnostics --- src/Umbraco.Core/EnumerableExtensions.cs | 16 +- src/Umbraco.Examine/BaseValueSetBuilder.cs | 6 +- src/Umbraco.Examine/ContentIndexPopulator.cs | 2 +- src/Umbraco.Examine/ContentValueSetBuilder.cs | 18 +- src/Umbraco.Examine/ExamineExtensions.cs | 8 +- src/Umbraco.Examine/IIndexPopulator.cs | 2 +- src/Umbraco.Examine/IUmbracoContentIndexer.cs | 4 +- src/Umbraco.Examine/IUmbracoIndexer.cs | 2 +- src/Umbraco.Examine/IUmbracoMediaIndexer.cs | 2 +- src/Umbraco.Examine/IndexPopulator.cs | 6 +- src/Umbraco.Examine/MediaIndexPopulator.cs | 2 +- src/Umbraco.Examine/MediaValueSetBuilder.cs | 12 +- src/Umbraco.Examine/MemberIndexPopulator.cs | 2 +- src/Umbraco.Examine/MemberValueSetBuilder.cs | 12 +- src/Umbraco.Examine/Umbraco.Examine.csproj | 3 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 2 +- .../UmbracoExamineIndexDiagnostics.cs | 70 ++++++ src/Umbraco.Examine/UmbracoExamineIndexer.cs | 73 ++---- src/Umbraco.Examine/UmbracoExamineSearcher.cs | 2 +- src/Umbraco.Examine/UmbracoMemberIndexer.cs | 4 +- .../TestHelpers/Stubs/TestExamineManager.cs | 8 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 10 +- .../developer/developerdashboardintro.html | 14 -- .../dashboard/settings/examinemanagement.html | 114 ++-------- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- .../Editors/ExamineManagementController.cs | 12 +- src/Umbraco.Web/ExamineStartup.cs | 212 ------------------ .../Models/Mapping/EntityMapperProfile.cs | 8 +- .../DictionaryPublishedContent.cs | 2 +- .../XmlPublishedCache/PublishedMediaCache.cs | 2 +- src/Umbraco.Web/PublishedContentQuery.cs | 6 +- src/Umbraco.Web/Search/ExamineComponent.cs | 4 +- src/Umbraco.Web/Search/ExamineIndexModel.cs | 3 + .../Search/GenericIndexDiagnostics.cs | 4 +- .../Search/IUmbracoIndexesBuilder.cs | 2 +- .../Search/UmbracoIndexesBuilder.cs | 8 +- src/Umbraco.Web/Umbraco.Web.csproj | 3 +- 38 files changed, 201 insertions(+), 463 deletions(-) create mode 100644 src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs delete mode 100644 src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardintro.html delete mode 100644 src/Umbraco.Web/ExamineStartup.cs diff --git a/src/Umbraco.Core/EnumerableExtensions.cs b/src/Umbraco.Core/EnumerableExtensions.cs index c455fadad7..2563d2e5dc 100644 --- a/src/Umbraco.Core/EnumerableExtensions.cs +++ b/src/Umbraco.Core/EnumerableExtensions.cs @@ -1,11 +1,6 @@ using System; -using System.Collections; using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Text; -using Umbraco.Core.Logging; namespace Umbraco.Core { @@ -14,6 +9,17 @@ namespace Umbraco.Core /// public static class EnumerableExtensions { + /// + /// Wraps this object instance into an IEnumerable{T} consisting of a single item. + /// + /// Type of the object. + /// The instance that will be wrapped. + /// An IEnumerable{T} consisting of a single item. + public static IEnumerable Yield(this T item) + { + yield return item; + } + public static IEnumerable> InGroupsOf(this IEnumerable source, int groupSize) { if (source == null) diff --git a/src/Umbraco.Examine/BaseValueSetBuilder.cs b/src/Umbraco.Examine/BaseValueSetBuilder.cs index d0c6069add..9e36f43793 100644 --- a/src/Umbraco.Examine/BaseValueSetBuilder.cs +++ b/src/Umbraco.Examine/BaseValueSetBuilder.cs @@ -22,7 +22,7 @@ namespace Umbraco.Examine /// public abstract IEnumerable GetValueSets(params TContent[] content); - protected void AddPropertyValue(Property property, string culture, string segment, IDictionary values) + protected void AddPropertyValue(Property property, string culture, string segment, IDictionary> values) { var editor = _propertyEditors[property.PropertyType.PropertyEditorAlias]; if (editor == null) return; @@ -48,7 +48,7 @@ namespace Umbraco.Examine if (values.TryGetValue(key, out var v)) values[key] = new List(v) { val }.ToArray(); else - values.Add($"{keyVal.Key}{cultureSuffix}", new[] { val }); + values.Add($"{keyVal.Key}{cultureSuffix}", val.Yield()); } break; default: @@ -57,7 +57,7 @@ namespace Umbraco.Examine if (values.TryGetValue(key, out var v)) values[key] = new List(v) { val }.ToArray(); else - values.Add($"{keyVal.Key}{cultureSuffix}", new[] { val }); + values.Add($"{keyVal.Key}{cultureSuffix}", val.Yield()); } break; diff --git a/src/Umbraco.Examine/ContentIndexPopulator.cs b/src/Umbraco.Examine/ContentIndexPopulator.cs index c84b251786..8127a8c3d3 100644 --- a/src/Umbraco.Examine/ContentIndexPopulator.cs +++ b/src/Umbraco.Examine/ContentIndexPopulator.cs @@ -61,7 +61,7 @@ namespace Umbraco.Examine RegisterIndex(Constants.UmbracoIndexes.ExternalIndexName); } - protected override void PopulateIndexes(IEnumerable indexes) + protected override void PopulateIndexes(IEnumerable indexes) { const int pageSize = 10000; var pageIndex = 0; diff --git a/src/Umbraco.Examine/ContentValueSetBuilder.cs b/src/Umbraco.Examine/ContentValueSetBuilder.cs index 258f24c21e..056e137c07 100644 --- a/src/Umbraco.Examine/ContentValueSetBuilder.cs +++ b/src/Umbraco.Examine/ContentValueSetBuilder.cs @@ -35,9 +35,9 @@ namespace Umbraco.Examine var isVariant = c.ContentType.VariesByCulture(); var urlValue = c.GetUrlSegment(_urlSegmentProviders); //Always add invariant urlName - var values = new Dictionary + var values = new Dictionary> { - {"icon", new [] {c.ContentType.Icon}}, + {"icon", c.ContentType.Icon.Yield()}, {UmbracoExamineIndexer.PublishedFieldName, new object[] {c.Published ? 1 : 0}}, //Always add invariant published value {"id", new object[] {c.Id}}, {"key", new object[] {c.Key}}, @@ -47,12 +47,12 @@ namespace Umbraco.Examine {"sortOrder", new object[] {c.SortOrder}}, {"createDate", new object[] {c.CreateDate}}, //Always add invariant createDate {"updateDate", new object[] {c.UpdateDate}}, //Always add invariant updateDate - {"nodeName", new object[] {c.Name}}, //Always add invariant nodeName - {"urlName", new object[] {urlValue}}, //Always add invariant urlName - {"path", new object[] {c.Path}}, + {"nodeName", c.Name.Yield()}, //Always add invariant nodeName + {"urlName", urlValue.Yield()}, //Always add invariant urlName + {"path", c.Path.Yield()}, {"nodeType", new object[] {c.ContentType.Id}}, - {"creatorName", new object[] {c.GetCreatorProfile(_userService)?.Name ?? "??"}}, - {"writerName", new object[] {c.GetWriterProfile(_userService)?.Name ?? "??"}}, + {"creatorName", (c.GetCreatorProfile(_userService)?.Name ?? "??").Yield() }, + {"writerName",(c.GetWriterProfile(_userService)?.Name ?? "??").Yield() }, {"writerID", new object[] {c.WriterId}}, {"template", new object[] {c.Template?.Id ?? 0}}, {UmbracoContentIndexer.VariesByCultureFieldName, new object[] {0}}, @@ -66,8 +66,8 @@ namespace Umbraco.Examine { var variantUrl = c.GetUrlSegment(_urlSegmentProviders, culture); var lowerCulture = culture.ToLowerInvariant(); - values[$"urlName_{lowerCulture}"] = new object[] { variantUrl }; - values[$"nodeName_{lowerCulture}"] = new object[] { c.GetCultureName(culture) }; + values[$"urlName_{lowerCulture}"] = variantUrl.Yield(); + values[$"nodeName_{lowerCulture}"] = c.GetCultureName(culture).Yield(); values[$"{UmbracoExamineIndexer.PublishedFieldName}_{lowerCulture}"] = new object[] { c.IsCulturePublished(culture) ? 1 : 0 }; values[$"updateDate_{lowerCulture}"] = new object[] { c.GetUpdateDate(culture) }; } diff --git a/src/Umbraco.Examine/ExamineExtensions.cs b/src/Umbraco.Examine/ExamineExtensions.cs index b0657bc502..3681979267 100644 --- a/src/Umbraco.Examine/ExamineExtensions.cs +++ b/src/Umbraco.Examine/ExamineExtensions.cs @@ -7,7 +7,7 @@ using Lucene.Net.Store; namespace Umbraco.Examine { /// - /// Extension methods for the LuceneIndexer + /// Extension methods for the LuceneIndex /// internal static class ExamineExtensions { @@ -17,7 +17,7 @@ namespace Umbraco.Examine /// /// The exception returned if there was an error /// - public static bool IsHealthy(this LuceneIndexer indexer, out Exception ex) + public static bool IsHealthy(this LuceneIndex indexer, out Exception ex) { try { @@ -39,7 +39,7 @@ namespace Umbraco.Examine /// /// /// - public static int GetIndexDocumentCount(this LuceneIndexer indexer) + public static int GetIndexDocumentCount(this LuceneIndex indexer) { if (!((indexer.GetSearcher() as LuceneSearcher)?.GetLuceneSearcher() is IndexSearcher searcher)) return 0; @@ -56,7 +56,7 @@ namespace Umbraco.Examine /// /// /// - public static int GetIndexFieldCount(this LuceneIndexer indexer) + public static int GetIndexFieldCount(this LuceneIndex indexer) { if (!((indexer.GetSearcher() as LuceneSearcher)?.GetLuceneSearcher() is IndexSearcher searcher)) return 0; diff --git a/src/Umbraco.Examine/IIndexPopulator.cs b/src/Umbraco.Examine/IIndexPopulator.cs index f4c27902eb..ebb977114a 100644 --- a/src/Umbraco.Examine/IIndexPopulator.cs +++ b/src/Umbraco.Examine/IIndexPopulator.cs @@ -9,7 +9,7 @@ namespace Umbraco.Examine /// Populate indexers /// /// - void Populate(params IIndexer[] indexes); + void Populate(params IIndex[] indexes); } } diff --git a/src/Umbraco.Examine/IUmbracoContentIndexer.cs b/src/Umbraco.Examine/IUmbracoContentIndexer.cs index a7cc9f9c2f..09520af1f8 100644 --- a/src/Umbraco.Examine/IUmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/IUmbracoContentIndexer.cs @@ -2,11 +2,11 @@ namespace Umbraco.Examine { - //TODO: Rethink this, need a better way of rebuilding + /// /// A Marker interface for defining an Umbraco content indexer /// - public interface IUmbracoContentIndexer : IIndexer + public interface IUmbracoContentIndexer : IIndex { } } diff --git a/src/Umbraco.Examine/IUmbracoIndexer.cs b/src/Umbraco.Examine/IUmbracoIndexer.cs index 5649a3a866..c3b4a5d629 100644 --- a/src/Umbraco.Examine/IUmbracoIndexer.cs +++ b/src/Umbraco.Examine/IUmbracoIndexer.cs @@ -5,7 +5,7 @@ namespace Umbraco.Examine /// /// A Marker interface for defining an Umbraco indexer /// - public interface IUmbracoIndexer : IIndexer + public interface IUmbracoIndexer : IIndex { /// /// When set to true Umbraco will keep the index in sync with Umbraco data automatically diff --git a/src/Umbraco.Examine/IUmbracoMediaIndexer.cs b/src/Umbraco.Examine/IUmbracoMediaIndexer.cs index 05ab46bec3..c31ab560ff 100644 --- a/src/Umbraco.Examine/IUmbracoMediaIndexer.cs +++ b/src/Umbraco.Examine/IUmbracoMediaIndexer.cs @@ -6,7 +6,7 @@ namespace Umbraco.Examine /// /// A Marker interface for defining an Umbraco media indexer /// - public interface IUmbracoMediaIndexer : IIndexer + public interface IUmbracoMediaIndexer : IIndex { } } diff --git a/src/Umbraco.Examine/IndexPopulator.cs b/src/Umbraco.Examine/IndexPopulator.cs index af5301c230..877f261913 100644 --- a/src/Umbraco.Examine/IndexPopulator.cs +++ b/src/Umbraco.Examine/IndexPopulator.cs @@ -22,11 +22,11 @@ namespace Umbraco.Examine /// public IEnumerable RegisteredIndexes => _registeredIndexes; - public void Populate(params IIndexer[] indexes) + public void Populate(params IIndex[] indexes) { PopulateIndexes(indexes.Where(x => RegisteredIndexes.Contains(x.Name))); } - protected abstract void PopulateIndexes(IEnumerable indexes); + protected abstract void PopulateIndexes(IEnumerable indexes); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Examine/MediaIndexPopulator.cs b/src/Umbraco.Examine/MediaIndexPopulator.cs index 99fa22acea..39a0b3f7c8 100644 --- a/src/Umbraco.Examine/MediaIndexPopulator.cs +++ b/src/Umbraco.Examine/MediaIndexPopulator.cs @@ -42,7 +42,7 @@ namespace Umbraco.Examine RegisterIndex(Constants.UmbracoIndexes.ExternalIndexName); } - protected override void PopulateIndexes(IEnumerable indexes) + protected override void PopulateIndexes(IEnumerable indexes) { const int pageSize = 10000; var pageIndex = 0; diff --git a/src/Umbraco.Examine/MediaValueSetBuilder.cs b/src/Umbraco.Examine/MediaValueSetBuilder.cs index f162c07f59..af41f574fb 100644 --- a/src/Umbraco.Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Examine/MediaValueSetBuilder.cs @@ -28,9 +28,9 @@ namespace Umbraco.Examine foreach (var m in media) { var urlValue = m.GetUrlSegment(_urlSegmentProviders); - var values = new Dictionary + var values = new Dictionary> { - {"icon", new object[] {m.ContentType.Icon}}, + {"icon", m.ContentType.Icon.Yield()}, {"id", new object[] {m.Id}}, {"key", new object[] {m.Key}}, {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, @@ -39,11 +39,11 @@ namespace Umbraco.Examine {"sortOrder", new object[] {m.SortOrder}}, {"createDate", new object[] {m.CreateDate}}, {"updateDate", new object[] {m.UpdateDate}}, - {"nodeName", new object[] {m.Name}}, - {"urlName", new object[] {urlValue}}, - {"path", new object[] {m.Path}}, + {"nodeName", m.Name.Yield()}, + {"urlName", urlValue.Yield()}, + {"path", m.Path.Yield()}, {"nodeType", new object[] {m.ContentType.Id}}, - {"creatorName", new object[] {m.GetCreatorProfile(_userService).Name}} + {"creatorName", m.GetCreatorProfile(_userService).Name.Yield()} }; foreach (var property in m.Properties) diff --git a/src/Umbraco.Examine/MemberIndexPopulator.cs b/src/Umbraco.Examine/MemberIndexPopulator.cs index 79dd31436b..b94100f6e3 100644 --- a/src/Umbraco.Examine/MemberIndexPopulator.cs +++ b/src/Umbraco.Examine/MemberIndexPopulator.cs @@ -19,7 +19,7 @@ namespace Umbraco.Examine RegisterIndex(Core.Constants.UmbracoIndexes.MembersIndexName); } - protected override void PopulateIndexes(IEnumerable indexes) + protected override void PopulateIndexes(IEnumerable indexes) { const int pageSize = 1000; var pageIndex = 0; diff --git a/src/Umbraco.Examine/MemberValueSetBuilder.cs b/src/Umbraco.Examine/MemberValueSetBuilder.cs index 14af7de8cb..85a8fa13c5 100644 --- a/src/Umbraco.Examine/MemberValueSetBuilder.cs +++ b/src/Umbraco.Examine/MemberValueSetBuilder.cs @@ -19,9 +19,9 @@ namespace Umbraco.Examine { foreach (var m in members) { - var values = new Dictionary + var values = new Dictionary> { - {"icon", new object[] {m.ContentType.Icon}}, + {"icon", m.ContentType.Icon.Yield()}, {"id", new object[] {m.Id}}, {"key", new object[] {m.Key}}, {"parentID", new object[] {m.Level > 1 ? m.ParentId : -1}}, @@ -30,11 +30,11 @@ namespace Umbraco.Examine {"sortOrder", new object[] {m.SortOrder}}, {"createDate", new object[] {m.CreateDate}}, {"updateDate", new object[] {m.UpdateDate}}, - {"nodeName", new object[] {m.Name}}, - {"path", new object[] {m.Path}}, + {"nodeName", m.Name.Yield()}, + {"path", m.Path.Yield()}, {"nodeType", new object[] {m.ContentType.Id}}, - {"loginName", new object[] {m.Username}}, - {"email", new object[] {m.Email}}, + {"loginName", m.Username.Yield()}, + {"email", m.Email.Yield()}, }; foreach (var property in m.Properties) diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index a8898f7fe5..8435025063 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + @@ -84,6 +84,7 @@ + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index ddf5f23b4f..00b5a03f1d 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -153,7 +153,7 @@ namespace Umbraco.Examine //need to queue a delete item for each one found foreach (var r in results) { - QueueIndexOperation(new IndexOperation(IndexItem.ForId(r.Id), IndexOperationType.Delete)); + QueueIndexOperation(new IndexOperation(new ValueSet(r.Id, null), IndexOperationType.Delete)); } base.DeleteFromIndex(nodeId); diff --git a/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs b/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs new file mode 100644 index 0000000000..d5d8f9df4b --- /dev/null +++ b/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using Lucene.Net.Store; +using Umbraco.Core; +using Umbraco.Core.Logging; + +namespace Umbraco.Examine +{ + public class UmbracoExamineIndexDiagnostics : IIndexDiagnostics + { + private readonly UmbracoExamineIndexer _index; + private readonly ILogger _logger; + + public UmbracoExamineIndexDiagnostics(UmbracoExamineIndexer index, ILogger logger) + { + _index = index; + _logger = logger; + } + + public int DocumentCount + { + get + { + try + { + return _index.GetIndexDocumentCount(); + } + catch (AlreadyClosedException) + { + _logger.Warn(typeof(UmbracoContentIndexer), "Cannot get GetIndexDocumentCount, the writer is already closed"); + return 0; + } + } + } + + public int FieldCount + { + get + { + try + { + return _index.GetIndexFieldCount(); + } + catch (AlreadyClosedException) + { + _logger.Warn(typeof(UmbracoContentIndexer), "Cannot get GetIndexFieldCount, the writer is already closed"); + return 0; + } + } + } + + public Attempt IsHealthy() + { + var isHealthy = _index.IsHealthy(out var indexError); + return isHealthy ? Attempt.Succeed() : Attempt.Fail(indexError.Message); + } + + public virtual IReadOnlyDictionary Metadata => new Dictionary + { + [nameof(UmbracoExamineIndexer.CommitCount)] = _index.CommitCount, + [nameof(UmbracoExamineIndexer.DefaultAnalyzer)] = _index.DefaultAnalyzer.GetType(), + [nameof(UmbracoExamineIndexer.DirectoryFactory)] = _index.DirectoryFactory, + [nameof(UmbracoExamineIndexer.EnableDefaultEventHandler)] = _index.EnableDefaultEventHandler, + [nameof(UmbracoExamineIndexer.LuceneIndexFolder)] = _index.LuceneIndexFolder?.ToString(), + [nameof(UmbracoExamineIndexer.SupportSoftDelete)] = _index.SupportSoftDelete, + [nameof(UmbracoExamineIndexer.FieldDefinitionCollection)] = _index.FieldDefinitionCollection, + [nameof(UmbracoExamineIndexer.FieldValueTypeCollection)] = _index.FieldValueTypeCollection, + + }; + } +} diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index cfea6de628..0bd6be5855 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -25,7 +25,7 @@ namespace Umbraco.Examine /// An abstract provider containing the basic functionality to be able to query against /// Umbraco data. /// - public abstract class UmbracoExamineIndexer : LuceneIndexer, IUmbracoIndexer, IIndexDiagnostics + public abstract class UmbracoExamineIndexer : LuceneIndex, IUmbracoIndexer, IIndexDiagnostics { // note // wrapping all operations that end up calling base.SafelyProcessQueueItems in a safe call @@ -82,6 +82,8 @@ namespace Umbraco.Examine //try to set the value of `LuceneIndexFolder` for diagnostic reasons if (luceneDirectory is FSDirectory fsDir) LuceneIndexFolder = fsDir.Directory; + + _diagnostics = new UmbracoExamineIndexDiagnostics(this, ProfilingLogger.Logger); } private readonly bool _configBased = false; @@ -315,15 +317,15 @@ namespace Umbraco.Examine /// /// Overridden for logging. /// - protected override void AddDocument(Document doc, IndexItem item, IndexWriter writer) + protected override void AddDocument(Document doc, ValueSet valueSet, IndexWriter writer) { ProfilingLogger.Logger.Debug(GetType(), "Write lucene doc id:{DocumentId}, category:{DocumentCategory}, type:{DocumentItemType}", - item.ValueSet.Id, - item.ValueSet.Category, - item.ValueSet.ItemType); + valueSet.Id, + valueSet.Category, + valueSet.ItemType); - base.AddDocument(doc, item, writer); + base.AddDocument(doc, valueSet, writer); } protected override void OnTransformingIndexValues(IndexingItemEventArgs e) @@ -331,16 +333,16 @@ namespace Umbraco.Examine base.OnTransformingIndexValues(e); //ensure special __Path field - var path = e.IndexItem.ValueSet.GetValue("path"); + var path = e.ValueSet.GetValue("path"); if (path != null) { - e.IndexItem.ValueSet.Set(IndexPathFieldName, path); + e.ValueSet.Set(IndexPathFieldName, path); } //icon - if (e.IndexItem.ValueSet.Values.TryGetValue("icon", out var icon) && e.IndexItem.ValueSet.Values.ContainsKey(IconFieldName) == false) + if (e.ValueSet.Values.TryGetValue("icon", out var icon) && e.ValueSet.Values.ContainsKey(IconFieldName) == false) { - e.IndexItem.ValueSet.Values[IconFieldName] = icon; + e.ValueSet.Values[IconFieldName] = icon; } } @@ -356,53 +358,12 @@ namespace Umbraco.Examine #region IIndexDiagnostics - public int DocumentCount - { - get - { - try - { - return this.GetIndexDocumentCount(); - } - catch (AlreadyClosedException) - { - ProfilingLogger.Logger.Warn(typeof(UmbracoContentIndexer), "Cannot get GetIndexDocumentCount, the writer is already closed"); - return 0; - } - } - } + private readonly UmbracoExamineIndexDiagnostics _diagnostics; - public int FieldCount - { - get - { - try - { - return this.GetIndexFieldCount(); - } - catch (AlreadyClosedException) - { - ProfilingLogger.Logger.Warn(typeof(UmbracoContentIndexer), "Cannot get GetIndexFieldCount, the writer is already closed"); - return 0; - } - } - } - - public Attempt IsHealthy() - { - var isHealthy = this.IsHealthy(out var indexError); - return isHealthy ? Attempt.Succeed() : Attempt.Fail(indexError.Message); - } - - public virtual IReadOnlyDictionary Metadata => new Dictionary - { - [nameof(CommitCount)] = CommitCount, - [nameof(DefaultAnalyzer)] = DefaultAnalyzer.GetType(), - [nameof(DirectoryFactory)] = DirectoryFactory, - [nameof(EnableDefaultEventHandler)] = EnableDefaultEventHandler, - [nameof(LuceneIndexFolder)] = LuceneIndexFolder?.ToString(), - [nameof(SupportSoftDelete)] = SupportSoftDelete - }; + public int DocumentCount => _diagnostics.DocumentCount; + public int FieldCount => _diagnostics.FieldCount; + public Attempt IsHealthy() => _diagnostics.IsHealthy(); + public virtual IReadOnlyDictionary Metadata => _diagnostics.Metadata; #endregion } diff --git a/src/Umbraco.Examine/UmbracoExamineSearcher.cs b/src/Umbraco.Examine/UmbracoExamineSearcher.cs index b23d2bcb29..2474bc6429 100644 --- a/src/Umbraco.Examine/UmbracoExamineSearcher.cs +++ b/src/Umbraco.Examine/UmbracoExamineSearcher.cs @@ -137,7 +137,7 @@ namespace Umbraco.Examine var fields = base.GetAllIndexedFields(); return fields .Where(x => x != UmbracoExamineIndexer.IndexPathFieldName) - .Where(x => x != LuceneIndexer.ItemTypeFieldName) + .Where(x => x != LuceneIndex.ItemTypeFieldName) .ToArray(); } diff --git a/src/Umbraco.Examine/UmbracoMemberIndexer.cs b/src/Umbraco.Examine/UmbracoMemberIndexer.cs index 2b0d3a0c66..4943f49825 100644 --- a/src/Umbraco.Examine/UmbracoMemberIndexer.cs +++ b/src/Umbraco.Examine/UmbracoMemberIndexer.cs @@ -80,10 +80,10 @@ namespace Umbraco.Examine { base.OnTransformingIndexValues(e); - if (e.IndexItem.ValueSet.Values.TryGetValue("key", out var key) && e.IndexItem.ValueSet.Values.ContainsKey("__key") == false) + if (e.ValueSet.Values.TryGetValue("key", out var key) && e.ValueSet.Values.ContainsKey("__key") == false) { //double __ prefix means it will be indexed as culture invariant - e.IndexItem.ValueSet.Values["__key"] = key; + e.ValueSet.Values["__key"] = key; } } diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs index f0abe053ac..ac5671fceb 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs @@ -6,10 +6,10 @@ namespace Umbraco.Tests.TestHelpers.Stubs { internal class TestExamineManager : IExamineManager { - private readonly ConcurrentDictionary _indexers = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _indexers = new ConcurrentDictionary(); private readonly ConcurrentDictionary _searchers = new ConcurrentDictionary(); - public void AddIndexer(IIndexer indexer) + public void AddIndex(IIndex indexer) { _indexers.TryAdd(indexer.Name, indexer); } @@ -24,7 +24,7 @@ namespace Umbraco.Tests.TestHelpers.Stubs //noop } - public IIndexer GetIndexer(string indexerName) + public IIndex GetIndex(string indexerName) { return _indexers.TryGetValue(indexerName, out var indexer) ? indexer : null; } @@ -34,6 +34,6 @@ namespace Umbraco.Tests.TestHelpers.Stubs return _searchers.TryGetValue(searcherName, out var indexer) ? indexer : null; } - public IReadOnlyDictionary IndexProviders => _indexers; + public IReadOnlyDictionary IndexProviders => _indexers; } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 5fa0cf3469..3b98e778e4 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/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index 2132c9b19e..fe8732eeeb 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -167,12 +167,12 @@ namespace Umbraco.Tests.UmbracoExamine var protectedQuery = new BooleanQuery(); protectedQuery.Add( new BooleanClause( - new TermQuery(new Term(LuceneIndexer.CategoryFieldName, IndexTypes.Content)), + new TermQuery(new Term(LuceneIndex.CategoryFieldName, IndexTypes.Content)), Occur.MUST)); protectedQuery.Add( new BooleanClause( - new TermQuery(new Term(LuceneIndexer.ItemIdFieldName, ExamineDemoDataContentService.ProtectedNode.ToString())), + new TermQuery(new Term(LuceneIndex.ItemIdFieldName, ExamineDemoDataContentService.ProtectedNode.ToString())), Occur.MUST)); var collector = TopScoreDocCollector.Create(100, true); @@ -293,7 +293,7 @@ namespace Umbraco.Tests.UmbracoExamine //create the whole thing rebuilder.Populate(indexer); - var result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndexer.CategoryFieldName, IndexTypes.Content).Compile()); + var result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndex.CategoryFieldName, IndexTypes.Content).Compile()); Assert.AreEqual(21, result.TotalItemCount); //delete all content @@ -304,13 +304,13 @@ namespace Umbraco.Tests.UmbracoExamine //ensure it's all gone - result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndexer.CategoryFieldName, IndexTypes.Content).Compile()); + result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndex.CategoryFieldName, IndexTypes.Content).Compile()); Assert.AreEqual(0, result.TotalItemCount); //call our indexing methods rebuilder.Populate(indexer); - result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndexer.CategoryFieldName, IndexTypes.Content).Compile()); + result = searcher.Search(searcher.CreateCriteria().Field(LuceneIndex.CategoryFieldName, IndexTypes.Content).Compile()); Assert.AreEqual(21, result.TotalItemCount); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardintro.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardintro.html deleted file mode 100644 index 5b4bc988c0..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/developer/developerdashboardintro.html +++ /dev/null @@ -1,14 +0,0 @@ -

Start here

-

This section contains the tools to add advanced features to your Umbraco site

-

From here you can explore and install packages, create macros, add data types, and much more. Start by exploring the below links or videos.

- -

Find out more:

- - diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html index b5f1fd12ed..a64f69e345 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html @@ -28,124 +28,48 @@
- +
-
- {{indexer.name}} -
- - {{indexer.name}} - -
+ +
The index cannot be read and will need to be rebuilt
+ + + {{indexer.name}} + +
-
-
- - -
-
- -
+
  • Index info & tools - -
    +

    -
    - +
    + +
    The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation
    - - - - - - - - - - -
     
    Documents in index{{indexer.documentCount}}
    Fields in index{{indexer.fieldCount}}
  • -
  • - Node types - - - - - - - - - - - - - -
    Include node types{{indexer.indexCriteria.IncludeNodeTypes | json}}
    Exclude node types{{indexer.indexCriteria.ExcludeNodeTypes | json}}
    Parent node id{{indexer.indexCriteria.ParentNodeId}}
    -
  • -
  • - System fields - - - - - - - - - - - - - - - - -
     
    NameEnable sortingType
    {{field.Name}}{{field.EnableSorting}}{{field.Type}}
    -
  • -
  • - User fields - - - - - - - - - - - - - - - - -
     
    NameEnable sortingType
    {{field.Name}}{{field.EnableSorting}}{{field.Type}}
    -
  • - Provider properties + Index properties diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index f33df18a54..a17168b42c 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,7 +88,7 @@ - + diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs index ec6f1841b0..d54c67ddb7 100644 --- a/src/Umbraco.Web/Editors/ExamineManagementController.cs +++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs @@ -121,7 +121,7 @@ namespace Umbraco.Web.Editors /// public ExamineIndexModel PostCheckRebuildIndex(string indexerName) { - var msg = ValidateLuceneIndexer(indexerName, out LuceneIndexer indexer); + var msg = ValidateLuceneIndexer(indexerName, out LuceneIndex indexer); if (msg.IsSuccessStatusCode) { var cacheKey = "temp_indexing_op_" + indexerName; @@ -130,7 +130,7 @@ namespace Umbraco.Web.Editors //if its still there then it's not done return found != null ? null - : CreateModel(new KeyValuePair(indexerName, indexer)); + : CreateModel(new KeyValuePair(indexerName, indexer)); } throw new HttpResponseException(msg); @@ -143,7 +143,7 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage PostRebuildIndex(string indexerName) { - var msg = ValidateLuceneIndexer(indexerName, out LuceneIndexer indexer); + var msg = ValidateLuceneIndexer(indexerName, out LuceneIndex indexer); if (msg.IsSuccessStatusCode) { _logger.Info("Rebuilding index '{IndexerName}'", indexerName); @@ -185,7 +185,7 @@ namespace Umbraco.Web.Editors - private ExamineIndexModel CreateModel(KeyValuePair indexerKeyVal) + private ExamineIndexModel CreateModel(KeyValuePair indexerKeyVal) { var indexer = indexerKeyVal.Value; var indexName = indexerKeyVal.Key; @@ -249,7 +249,7 @@ namespace Umbraco.Web.Editors } private HttpResponseMessage ValidateLuceneIndexer(string indexerName, out T indexer) - where T : class, IIndexer + where T : class, IIndex { indexer = null; @@ -270,7 +270,7 @@ namespace Umbraco.Web.Editors //static listener so it's not GC'd private void Indexer_IndexOperationComplete(object sender, EventArgs e) { - var indexer = (LuceneIndexer) sender; + var indexer = (LuceneIndex) sender; //ensure it's not listening anymore indexer.IndexOperationComplete -= Indexer_IndexOperationComplete; diff --git a/src/Umbraco.Web/ExamineStartup.cs b/src/Umbraco.Web/ExamineStartup.cs deleted file mode 100644 index f3a8bc3ef5..0000000000 --- a/src/Umbraco.Web/ExamineStartup.cs +++ /dev/null @@ -1,212 +0,0 @@ -// fixme - Examine is borked -// this cannot compile because it seems to require some changes that are not in Examine v2 -// this should *not* exist, see ExamineComponent instead, which handles everything about Examine - -//using System; -//using System.Collections.Generic; -//using System.Linq; -//using Examine; -//using Examine.Config; -//using Examine.LuceneEngine; -//using Examine.LuceneEngine.Providers; -//using Examine.Providers; -//using Lucene.Net.Index; -//using Lucene.Net.Store; -//using Umbraco.Core; -//using Umbraco.Core.Logging; -//using UmbracoExamine; - -//namespace Umbraco.Web -//{ -// /// -// /// Used to configure Examine during startup for the web application -// /// -// internal class ExamineStartup -// { -// private readonly List _indexesToRebuild = new List(); -// private readonly ApplicationContext _appCtx; -// private readonly ProfilingLogger _profilingLogger; -// private static bool _isConfigured = false; - -// //this is used if we are not the MainDom, in which case we need to ensure that if indexes need rebuilding that this -// //doesn't occur since that should only occur when we are MainDom -// private bool _disableExamineIndexing = false; - -// public ExamineStartup(ApplicationContext appCtx) -// { -// _appCtx = appCtx; -// _profilingLogger = appCtx.ProfilingLogger; -// } - -// /// -// /// Called during the initialize operation of the boot manager process -// /// -// public void Initialize() -// { -// //We want to manage Examine's appdomain shutdown sequence ourselves so first we'll disable Examine's default behavior -// //and then we'll use MainDom to control Examine's shutdown -// ExamineManager.DisableDefaultHostingEnvironmentRegistration(); - -// //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the appdomain -// //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock -// //which simply checks the existence of the lock file -// DirectoryTracker.DefaultLockFactory = d => -// { -// var simpleFsLockFactory = new NoPrefixSimpleFsLockFactory(d); -// return simpleFsLockFactory; -// }; - -// //This is basically a hack for this item: http://issues.umbraco.org/issue/U4-5976 -// // when Examine initializes it will try to rebuild if the indexes are empty, however in many cases not all of Examine's -// // event handlers will be assigned during bootup when the rebuilding starts which is a problem. So with the examine 0.1.58.2941 build -// // it has an event we can subscribe to in order to cancel this rebuilding process, but what we'll do is cancel it and postpone the rebuilding until the -// // boot process has completed. It's a hack but it works. -// ExamineManager.Instance.BuildingEmptyIndexOnStartup += OnInstanceOnBuildingEmptyIndexOnStartup; - -// //let's deal with shutting down Examine with MainDom -// var examineShutdownRegistered = _appCtx.MainDom.Register(() => -// { -// using (_profilingLogger.TraceDuration("Examine shutting down")) -// { -// //Due to the way Examine's own IRegisteredObject works, we'll first run it with immediate=false and then true so that -// //it's correct subroutines are executed (otherwise we'd have to run this logic manually ourselves) -// ExamineManager.Instance.Stop(false); -// ExamineManager.Instance.Stop(true); -// } -// }); -// if (examineShutdownRegistered) -// { -// _profilingLogger.Logger.Debug("Examine shutdown registered with MainDom"); -// } -// else -// { -// _profilingLogger.Logger.Debug("Examine shutdown not registered, this appdomain is not the MainDom"); - -// //if we could not register the shutdown examine ourselves, it means we are not maindom! in this case all of examine should be disabled -// //from indexing anything on startup!! -// _disableExamineIndexing = true; -// Suspendable.ExamineEvents.SuspendIndexers(); -// } -// } - -// /// -// /// Called during the Complete operation of the boot manager process -// /// -// public void Complete() -// { -// EnsureUnlockedAndConfigured(); - -// //Ok, now that everything is complete we'll check if we've stored any references to index that need rebuilding and run them -// // (see the initialize method for notes) - we'll ensure we remove the event handler too in case examine manager doesn't actually -// // initialize during startup, in which case we want it to rebuild the indexes itself. -// ExamineManager.Instance.BuildingEmptyIndexOnStartup -= OnInstanceOnBuildingEmptyIndexOnStartup; - -// //don't do anything if we have disabled this -// if (_disableExamineIndexing == false) -// { -// foreach (var indexer in _indexesToRebuild) -// { -// indexer.RebuildIndex(); -// } -// } -// } - -// /// -// /// Called to perform the rebuilding indexes on startup if the indexes don't exist -// /// -// public void RebuildIndexes() -// { -// //don't do anything if we have disabled this -// if (_disableExamineIndexing) return; - -// EnsureUnlockedAndConfigured(); - -// //If the developer has explicitly opted out of rebuilding indexes on startup then we -// // should adhere to that and not do it, this means that if they are load balancing things will be -// // out of sync if they are auto-scaling but there's not much we can do about that. -// if (ExamineSettings.Instance.RebuildOnAppStart == false) return; - -// foreach (var indexer in GetIndexesForColdBoot()) -// { -// indexer.RebuildIndex(); -// } -// } - -// /// -// /// The method used to create indexes on a cold boot -// /// -// /// -// /// A cold boot is when the server determines it will not (or cannot) process instructions in the cache table and -// /// will rebuild it's own caches itself. -// /// -// public IEnumerable GetIndexesForColdBoot() -// { -// // NOTE: This is IMPORTANT! ... we don't want to rebuild any index that is already flagged to be re-indexed -// // on startup based on our _indexesToRebuild variable and how Examine auto-rebuilds when indexes are empty. -// // This callback is used above for the DatabaseServerMessenger startup options. - -// // all indexes -// IEnumerable indexes = ExamineManager.Instance.IndexProviderCollection; - -// // except those that are already flagged -// // and are processed in Complete() -// if (_indexesToRebuild.Any()) -// indexes = indexes.Except(_indexesToRebuild); - -// // return -// foreach (var index in indexes) -// yield return index; -// } - -// /// -// /// Must be called to configure each index and ensure it's unlocked before any indexing occurs -// /// -// /// -// /// Indexing rebuilding can occur on a normal boot if the indexes are empty or on a cold boot by the database server messenger. Before -// /// either of these happens, we need to configure the indexes. -// /// -// private void EnsureUnlockedAndConfigured() -// { -// if (_isConfigured) return; - -// _isConfigured = true; - -// foreach (var luceneIndexer in ExamineManager.Instance.IndexProviderCollection.OfType()) -// { -// //We now need to disable waiting for indexing for Examine so that the appdomain is shutdown immediately and doesn't wait for pending -// //indexing operations. We used to wait for indexing operations to complete but this can cause more problems than that is worth because -// //that could end up halting shutdown for a very long time causing overlapping appdomains and many other problems. -// luceneIndexer.WaitForIndexQueueOnShutdown = false; - -// //we should check if the index is locked ... it shouldn't be! We are using simple fs lock now and we are also ensuring that -// //the indexes are not operational unless MainDom is true so if _disableExamineIndexing is false then we should be in charge -// if (_disableExamineIndexing == false) -// { -// var dir = luceneIndexer.GetLuceneDirectory(); -// if (IndexWriter.IsLocked(dir)) -// { -// _profilingLogger.Logger.Info("Forcing index " + luceneIndexer.IndexSetName + " to be unlocked since it was left in a locked state"); -// IndexWriter.Unlock(dir); -// } -// } -// } -// } - -// private void OnInstanceOnBuildingEmptyIndexOnStartup(object sender, BuildingEmptyIndexOnStartupEventArgs args) -// { -// //store the indexer that needs rebuilding because it's empty for when the boot process -// // is complete and cancel this current event so the rebuild process doesn't start right now. -// args.Cancel = true; -// _indexesToRebuild.Add((BaseIndexProvider)args.Indexer); - -// //check if the index is rebuilding due to an error and log it -// if (args.IsHealthy == false) -// { -// var baseIndex = args.Indexer as BaseIndexProvider; -// var name = baseIndex != null ? baseIndex.Name : "[UKNOWN]"; - -// _profilingLogger.Logger.Error(string.Format("The index {0} is rebuilding due to being unreadable/corrupt", name), args.UnhealthyException); -// } -// } -// } -//} diff --git a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs index 5d41c8cb0c..4028d480db 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs @@ -136,9 +136,9 @@ namespace Umbraco.Web.Models.Mapping dest.Key = key; //need to set the UDI - if (src.Values.ContainsKey(LuceneIndexer.CategoryFieldName)) + if (src.Values.ContainsKey(LuceneIndex.CategoryFieldName)) { - switch (src.Values[LuceneIndexer.CategoryFieldName]) + switch (src.Values[LuceneIndex.CategoryFieldName]) { case IndexTypes.Member: dest.Udi = new GuidUdi(Constants.UdiEntityType.Member, dest.Key); @@ -168,9 +168,9 @@ namespace Umbraco.Web.Models.Mapping } dest.Path = src.Values.ContainsKey(UmbracoExamineIndexer.IndexPathFieldName) ? src.Values[UmbracoExamineIndexer.IndexPathFieldName] : ""; - if (src.Values.ContainsKey(LuceneIndexer.ItemTypeFieldName)) + if (src.Values.ContainsKey(LuceneIndex.ItemTypeFieldName)) { - dest.AdditionalData.Add("contentType", src.Values[LuceneIndexer.ItemTypeFieldName]); + dest.AdditionalData.Add("contentType", src.Values[LuceneIndex.ItemTypeFieldName]); } }); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs index 49e0d1e9d2..7a8ce65ae3 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs @@ -57,7 +57,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache ValidateAndSetProperty(valueDictionary, val => _sortOrder = Int32.Parse(val), "sortOrder"); ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName", "__nodeName"); ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName"); - ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", LuceneIndexer.ItemTypeFieldName); + ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", LuceneIndex.ItemTypeFieldName); ValidateAndSetProperty(valueDictionary, val => _documentTypeId = Int32.Parse(val), "nodeType"); //ValidateAndSetProperty(valueDictionary, val => _writerName = val, "writerName"); ValidateAndSetProperty(valueDictionary, val => _creatorName = val, "creatorName", "writerName"); //this is a bit of a hack fix for: U4-1132 diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index d334c6ab48..45f32353da 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -38,7 +38,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // method GetExamineManagerSafe(). // private readonly ISearcher _searchProvider; - private readonly IIndexer _indexProvider; + private readonly IIndex _indexProvider; private readonly XmlStore _xmlStore; private readonly PublishedContentTypeCache _contentTypeCache; diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs index 363607fbcb..8ad40887cc 100644 --- a/src/Umbraco.Web/PublishedContentQuery.cs +++ b/src/Umbraco.Web/PublishedContentQuery.cs @@ -234,8 +234,8 @@ namespace Umbraco.Web if (_query != null) return _query.Search(skip, take, out totalRecords, term, useWildCards, indexName); var indexer = string.IsNullOrEmpty(indexName) - ? Examine.ExamineManager.Instance.GetIndexer(Constants.Examine.ExternalIndexer) - : Examine.ExamineManager.Instance.GetIndexer(indexName); + ? Examine.ExamineManager.Instance.GetIndex(Constants.Examine.ExternalIndexer) + : Examine.ExamineManager.Instance.GetIndex(indexName); if (indexer == null) throw new InvalidOperationException("No index found by name " + indexName); @@ -278,7 +278,7 @@ namespace Umbraco.Web /// /// Creates an ISearchCriteria for searching all fields in a . /// - private ISearchCriteria SearchAllFields(string searchText, bool useWildcards, Examine.ISearcher searcher, Examine.IIndexer indexer) + private ISearchCriteria SearchAllFields(string searchText, bool useWildcards, Examine.ISearcher searcher, Examine.IIndex indexer) { var sc = searcher.CreateCriteria(); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index a7c7bf8dae..9c659582ea 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -113,7 +113,7 @@ namespace Umbraco.Web.Search //create the indexes and register them with the manager foreach(var index in indexBuilder.Create()) { - _examineManager.AddIndexer(index); + _examineManager.AddIndex(index); } profilingLogger.Logger.Debug("Examine shutdown registered with MainDom"); @@ -192,7 +192,7 @@ namespace Umbraco.Web.Search _isConfigured = true; - foreach (var luceneIndexer in examineManager.IndexProviders.Values.OfType()) + foreach (var luceneIndexer in examineManager.IndexProviders.Values.OfType()) { //We now need to disable waiting for indexing for Examine so that the appdomain is shutdown immediately and doesn't wait for pending //indexing operations. We used to wait for indexing operations to complete but this can cause more problems than that is worth because diff --git a/src/Umbraco.Web/Search/ExamineIndexModel.cs b/src/Umbraco.Web/Search/ExamineIndexModel.cs index 1851e696f4..aa4c84ce9b 100644 --- a/src/Umbraco.Web/Search/ExamineIndexModel.cs +++ b/src/Umbraco.Web/Search/ExamineIndexModel.cs @@ -14,6 +14,9 @@ namespace Umbraco.Web.Search [DataMember(Name = "healthStatus")] public string HealthStatus { get; set; } + [DataMember(Name = "isHealthy")] + public bool IsHealthy => false; + [DataMember(Name = "providerProperties")] public IReadOnlyDictionary ProviderProperties { get; set; } diff --git a/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs b/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs index 6d3482837e..32c8d43575 100644 --- a/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs +++ b/src/Umbraco.Web/Search/GenericIndexDiagnostics.cs @@ -13,10 +13,10 @@ namespace Umbraco.Web.Search /// public class GenericIndexDiagnostics : IIndexDiagnostics { - private readonly IIndexer _index; + private readonly IIndex _index; private static readonly string[] IgnoreProperties = { "Description" }; - public GenericIndexDiagnostics(IIndexer index) + public GenericIndexDiagnostics(IIndex index) { _index = index; } diff --git a/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs index 8193117d69..ec8d923abc 100644 --- a/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs +++ b/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs @@ -5,6 +5,6 @@ namespace Umbraco.Web.Search { internal interface IUmbracoIndexesBuilder { - IEnumerable Create(); + IEnumerable Create(); } } diff --git a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs index fbe4d618e6..18ea85162c 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs @@ -45,7 +45,7 @@ namespace Umbraco.Web.Search /// Creates the Umbraco indexes /// /// - public IEnumerable Create() + public IEnumerable Create() { return new [] { @@ -55,7 +55,7 @@ namespace Umbraco.Web.Search }; } - private IIndexer CreateInternalIndex() + private IIndex CreateInternalIndex() { var index = new UmbracoContentIndexer( Constants.UmbracoIndexes.InternalIndexName, @@ -69,7 +69,7 @@ namespace Umbraco.Web.Search return index; } - private IIndexer CreateExternalIndex() + private IIndex CreateExternalIndex() { var index = new UmbracoContentIndexer( Constants.UmbracoIndexes.ExternalIndexName, @@ -83,7 +83,7 @@ namespace Umbraco.Web.Search return index; } - private IIndexer CreateMemberIndex() + private IIndex CreateMemberIndex() { var index = new UmbracoMemberIndexer( Constants.UmbracoIndexes.MembersIndexName, diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 3e54107759..90d9a58ef8 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -62,7 +62,7 @@ - + 2.6.2.25 @@ -219,7 +219,6 @@ - From afe234e21c16ebf7cc94750fe0a4be091c221aff Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 29 Nov 2018 14:54:35 +1100 Subject: [PATCH 27/44] better index diag --- .../UmbracoExamineIndexDiagnostics.cs | 49 ++++++++++++++----- .../dashboard/settings/examinemanagement.html | 4 +- src/Umbraco.Web/Search/ExamineIndexModel.cs | 2 +- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs b/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs index d5d8f9df4b..7e8bd13323 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; +using System.Linq; using Lucene.Net.Store; using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Core.Logging; namespace Umbraco.Examine @@ -54,17 +56,42 @@ namespace Umbraco.Examine return isHealthy ? Attempt.Succeed() : Attempt.Fail(indexError.Message); } - public virtual IReadOnlyDictionary Metadata => new Dictionary + public virtual IReadOnlyDictionary Metadata { - [nameof(UmbracoExamineIndexer.CommitCount)] = _index.CommitCount, - [nameof(UmbracoExamineIndexer.DefaultAnalyzer)] = _index.DefaultAnalyzer.GetType(), - [nameof(UmbracoExamineIndexer.DirectoryFactory)] = _index.DirectoryFactory, - [nameof(UmbracoExamineIndexer.EnableDefaultEventHandler)] = _index.EnableDefaultEventHandler, - [nameof(UmbracoExamineIndexer.LuceneIndexFolder)] = _index.LuceneIndexFolder?.ToString(), - [nameof(UmbracoExamineIndexer.SupportSoftDelete)] = _index.SupportSoftDelete, - [nameof(UmbracoExamineIndexer.FieldDefinitionCollection)] = _index.FieldDefinitionCollection, - [nameof(UmbracoExamineIndexer.FieldValueTypeCollection)] = _index.FieldValueTypeCollection, - - }; + get + { + var d = new Dictionary + { + [nameof(UmbracoExamineIndexer.CommitCount)] = _index.CommitCount, + [nameof(UmbracoExamineIndexer.DefaultAnalyzer)] = _index.DefaultAnalyzer.GetType().Name, + [nameof(UmbracoExamineIndexer.DirectoryFactory)] = _index.DirectoryFactory, + [nameof(UmbracoExamineIndexer.EnableDefaultEventHandler)] = _index.EnableDefaultEventHandler, + [nameof(UmbracoExamineIndexer.LuceneIndexFolder)] = + _index.LuceneIndexFolder == null + ? string.Empty + : _index.LuceneIndexFolder.ToString().ToLowerInvariant().TrimStart(IOHelper.MapPath(SystemDirectories.Root).ToLowerInvariant()).Replace("\\", "/").EnsureStartsWith('/'), + [nameof(UmbracoExamineIndexer.SupportSoftDelete)] = _index.SupportSoftDelete, + //There's too much info here + //[nameof(UmbracoExamineIndexer.FieldDefinitionCollection)] = _index.FieldDefinitionCollection, + }; + + if (_index.ValueSetValidator is ValueSetValidator vsv) + { + d[nameof(ValueSetValidator.IncludeItemTypes)] = vsv.IncludeItemTypes; + d[nameof(ContentValueSetValidator.ExcludeItemTypes)] = vsv.ExcludeItemTypes; + d[nameof(ContentValueSetValidator.IncludeFields)] = vsv.IncludeFields; + d[nameof(ContentValueSetValidator.ExcludeFields)] = vsv.ExcludeFields; + } + + if (_index.ValueSetValidator is ContentValueSetValidator cvsv) + { + d[nameof(ContentValueSetValidator.SupportUnpublishedContent)] = cvsv.SupportUnpublishedContent; + d[nameof(ContentValueSetValidator.SupportProtectedContent)] = cvsv.SupportProtectedContent; + d[nameof(ContentValueSetValidator.ParentId)] = cvsv.ParentId; + } + + return d.Where(x => x.Value != null).ToDictionary(x => x.Key, x => x.Value); + } + } } } diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html index a64f69e345..a2febfc72a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html @@ -112,14 +112,14 @@
    - +
    - +
    • diff --git a/src/Umbraco.Web/Search/ExamineIndexModel.cs b/src/Umbraco.Web/Search/ExamineIndexModel.cs index aa4c84ce9b..df02fee1ed 100644 --- a/src/Umbraco.Web/Search/ExamineIndexModel.cs +++ b/src/Umbraco.Web/Search/ExamineIndexModel.cs @@ -15,7 +15,7 @@ namespace Umbraco.Web.Search public string HealthStatus { get; set; } [DataMember(Name = "isHealthy")] - public bool IsHealthy => false; + public bool IsHealthy => HealthStatus == "Healthy"; [DataMember(Name = "providerProperties")] public IReadOnlyDictionary ProviderProperties { get; set; } From 1767bff255cad443509e145a31657b08beed120d Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 29 Nov 2018 16:54:13 +1100 Subject: [PATCH 28/44] Quite far revamping the examine dashboard --- ...ler.js => examinemanagement.controller.js} | 101 +++-- .../dashboard/settings/examinemanagement.html | 412 ++++++++++-------- 2 files changed, 308 insertions(+), 205 deletions(-) rename src/Umbraco.Web.UI.Client/src/views/dashboard/settings/{examinemgmt.controller.js => examinemanagement.controller.js} (60%) diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemgmt.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js similarity index 60% rename from src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemgmt.controller.js rename to src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js index f4e78a9dc3..4e5b2dd688 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemgmt.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js @@ -1,8 +1,37 @@ -function ExamineMgmtController($scope, umbRequestHelper, $log, $http, $q, $timeout) { +function ExamineManagementController($scope, umbRequestHelper, $http, $q, $timeout) { - $scope.indexerDetails = []; - $scope.searcherDetails = []; - $scope.loading = true; + var vm = this; + + vm.indexerDetails = []; + vm.searcherDetails = []; + vm.loading = true; + vm.viewState = "list"; + vm.selectedIndex = null; + + vm.showIndexInfo = showIndexInfo; + vm.showSearcherInfo = showSearcherInfo; + vm.search = search; + vm.toggle = toggle; + vm.rebuildIndex = rebuildIndex; + vm.closeSearch = closeSearch; + vm.setViewState = setViewState; + + + vm.infoOverlay = null; + + function setViewState(state) { + vm.viewState = state; + } + + function showIndexInfo(index) { + vm.selectedIndex = index; + setViewState("index-details"); + } + + function showSearcherInfo(searcher) { + vm.selectedSearcher = searcher; + setViewState("searcher-details"); + } function checkProcessing(indexer, checkActionName) { umbRequestHelper.resourcePromise( @@ -36,7 +65,7 @@ function ExamineMgmtController($scope, umbRequestHelper, $log, $http, $q, $timeo }); } - $scope.search = function(searcher, e) { + function search(searcher, e) { if (e && e.keyCode !== 13) { return; } @@ -56,7 +85,7 @@ function ExamineMgmtController($scope, umbRequestHelper, $log, $http, $q, $timeo }); } - $scope.toggle = function(provider, propName) { + function toggle(provider, propName) { if (provider[propName] !== undefined) { provider[propName] = !provider[propName]; } else { @@ -64,7 +93,7 @@ function ExamineMgmtController($scope, umbRequestHelper, $log, $http, $q, $timeo } } - $scope.rebuildIndex = function(indexer) { + function rebuildIndex(indexer) { if (confirm("This will cause the index to be rebuilt. " + "Depending on how much content there is in your site this could take a while. " + "It is not recommended to rebuild an index during times of high website traffic " + @@ -91,38 +120,42 @@ function ExamineMgmtController($scope, umbRequestHelper, $log, $http, $q, $timeo } } - $scope.closeSearch = function(searcher) { + function closeSearch(searcher) { searcher.isSearching = true; } - //go get the data + function init() { + //go get the data - //combine two promises and execute when they are both done - $q.all([ + //combine two promises and execute when they are both done + $q.all([ - //get the indexer details - umbRequestHelper.resourcePromise( - $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetIndexerDetails")), - 'Failed to retrieve indexer details') - .then(function(data) { - $scope.indexerDetails = data; - }), + //get the indexer details + umbRequestHelper.resourcePromise( + $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetIndexerDetails")), + 'Failed to retrieve indexer details') + .then(function (data) { + vm.indexerDetails = data; + }), - //get the searcher details - umbRequestHelper.resourcePromise( - $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearcherDetails")), - 'Failed to retrieve searcher details') - .then(function(data) { - $scope.searcherDetails = data; - for (var s in $scope.searcherDetails) { - $scope.searcherDetails[s].searchType = "text"; - } - }) - ]) - .then(function() { - //all init loading is complete - $scope.loading = false; - }); + //get the searcher details + umbRequestHelper.resourcePromise( + $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearcherDetails")), + 'Failed to retrieve searcher details') + .then(function (data) { + vm.searcherDetails = data; + for (var s in vm.searcherDetails) { + vm.searcherDetails[s].searchType = "text"; + } + }) + ]) + .then(function () { + //all init loading is complete + vm.loading = false; + }); + } + + init(); } -angular.module("umbraco").controller("Umbraco.Dashboard.ExamineMgmtController", ExamineMgmtController); \ No newline at end of file +angular.module("umbraco").controller("Umbraco.Dashboard.ExamineManagementController", ExamineManagementController); diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html index a2febfc72a..e547c1c139 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html @@ -1,210 +1,280 @@ -
      +
      - - -

      Examine Management

      -
      -
      +
      + + +

      Examine Management

      +
      +
      +
      -
      +
      +
      -
      +
      -
      -
      Indexers
      -
      +
      +
      Indexers
      +
      -
      -
      -
      -
      Manage Examine's indexes
      -
      Allows you to view the details of each index and provides some tools for managing the indexes
      -
      - -
      - -
      - +
      +
      +
      +
      Manage Examine's indexes
      +
      Allows you to view the details of each index and provides some tools for managing the indexes
      -
      -
      +
      -
      - The index cannot be read and will need to be rebuilt +
      + +
      + + + +
      +
      +
      + +
      + +
      + +
      + +
      +
      Searchers
      +
      + +
      +
      +
      +
      Configured Searchers
      +
      Shows properties and tools for any configured Searcher (i.e. such as a multi-index searcher)
      +
      + +
      + +
      + +
      + + +
      +
      +
      + +
      + +
      + +
      + + +
      + +
      + + + + ← Back to overview + + + + +
      + +
      + +
      +
      {{ vm.selectedIndex.name }}
      +
      + +
      + +
      + +
      +
      Health status
      +
      The health status of the index and if it can be read
      +
      + +
      + +
      +
      - - {{indexer.name}} - +
      -
      - -
      -
        -
      • - Index info & tools -
        -
        -
        - -
        - - -
        - -
        - The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation -
        -
        +
        +
        {{vm.selectedIndex.healthStatus}}
        +
        + The index cannot be read and will need to be rebuilt
        -
      • -
      • - Index properties -
     
    - - - - - -
     
    {{key}}{{val}}
    -
  • -
-
-
+ +
+
-
-
- -
-
- - - - - -
- -
- -
-
Searchers
-
- -
-
-
-
Search indexes
-
Allows you to search the indexes and view the searcher properties
-
- -
- -
- -
- -
- -
-
    -
  • +
- Search tools +
-
- Hide search results +
+
Index info
+
Lists the properties of the index
+
-
+
+ +
+ + + + + + + +
 
{{key}}{{val}}
+ +
+ +
+ +
+ +
+ +
+
Search
+
Search the index and view the results
+
+ +
+ +
+ +
+
+ -
- -
  • - Provider properties - - - - - - -
     
    {{key}}{{val}}
    -
  • - +
    + +
    +
    +
    + +
    + +
    +
    Tools
    +
    Tools to help manage the index
    +
    + +
    + +
    + +
    +
    + + + + + +
    +
    + +
    + +
    + The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation +
    + +
    +
    +
    + +
    + +
    + +
    + + +
    +
    +
    - -
    - -
    From 6103832b8a76c30669704d763a587327845c1681 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 30 Nov 2018 12:22:23 +1100 Subject: [PATCH 29/44] Gets index rebuilding working --- .../Collections/ConcurrentHashSet.cs | 10 +- src/Umbraco.Examine/IIndexPopulator.cs | 3 + src/Umbraco.Examine/IndexPopulator.cs | 15 +- src/Umbraco.Examine/Umbraco.Examine.csproj | 2 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 6 +- src/Umbraco.Examine/UmbracoExamineIndexer.cs | 4 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../settings/examinemanagement.controller.js | 26 ++-- .../dashboard/settings/examinemanagement.html | 11 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- src/Umbraco.Web/Editors/EntityController.cs | 3 +- .../Editors/ExamineManagementController.cs | 143 ++++++++++-------- src/Umbraco.Web/Search/ExamineComponent.cs | 25 ++- src/Umbraco.Web/Search/ExamineIndexModel.cs | 3 + ...esBuilder.cs => IUmbracoIndexesCreator.cs} | 5 +- .../Search/SearchableTreeCollection.cs | 6 +- ...xesBuilder.cs => UmbracoIndexesCreator.cs} | 4 +- src/Umbraco.Web/Umbraco.Web.csproj | 6 +- 18 files changed, 159 insertions(+), 117 deletions(-) rename src/Umbraco.Web/Search/{IUmbracoIndexesBuilder.cs => IUmbracoIndexesCreator.cs} (52%) rename src/Umbraco.Web/Search/{UmbracoIndexesBuilder.cs => UmbracoIndexesCreator.cs} (97%) diff --git a/src/Umbraco.Core/Collections/ConcurrentHashSet.cs b/src/Umbraco.Core/Collections/ConcurrentHashSet.cs index 4cad6e9f15..54367ed588 100644 --- a/src/Umbraco.Core/Collections/ConcurrentHashSet.cs +++ b/src/Umbraco.Core/Collections/ConcurrentHashSet.cs @@ -70,10 +70,7 @@ namespace Umbraco.Core.Collections /// The number of elements contained in the . /// /// 2 - public int Count - { - get { return GetThreadSafeClone().Count; } - } + public int Count => GetThreadSafeClone().Count; /// /// Gets a value indicating whether the is read-only. @@ -81,10 +78,7 @@ namespace Umbraco.Core.Collections /// /// true if the is read-only; otherwise, false. /// - public bool IsReadOnly - { - get { return false; } - } + public bool IsReadOnly => false; /// /// Adds an item to the . diff --git a/src/Umbraco.Examine/IIndexPopulator.cs b/src/Umbraco.Examine/IIndexPopulator.cs index ebb977114a..9e73de7260 100644 --- a/src/Umbraco.Examine/IIndexPopulator.cs +++ b/src/Umbraco.Examine/IIndexPopulator.cs @@ -5,6 +5,9 @@ namespace Umbraco.Examine { public interface IIndexPopulator { + bool IsRegistered(string indexName); + void RegisterIndex(string indexName); + /// /// Populate indexers /// diff --git a/src/Umbraco.Examine/IndexPopulator.cs b/src/Umbraco.Examine/IndexPopulator.cs index 877f261913..5f701312ea 100644 --- a/src/Umbraco.Examine/IndexPopulator.cs +++ b/src/Umbraco.Examine/IndexPopulator.cs @@ -1,12 +1,18 @@ using System.Collections.Generic; using System.Linq; using Examine; +using Umbraco.Core.Collections; namespace Umbraco.Examine { public abstract class IndexPopulator : IIndexPopulator { - private readonly HashSet _registeredIndexes = new HashSet(); + private readonly ConcurrentHashSet _registeredIndexes = new ConcurrentHashSet(); + + public bool IsRegistered(string indexName) + { + return _registeredIndexes.Contains(indexName); + } /// /// Registers an index for this populator @@ -17,14 +23,9 @@ namespace Umbraco.Examine _registeredIndexes.Add(indexName); } - /// - /// Returns a list of index names that his populate is associated with - /// - public IEnumerable RegisteredIndexes => _registeredIndexes; - public void Populate(params IIndex[] indexes) { - PopulateIndexes(indexes.Where(x => RegisteredIndexes.Contains(x.Name))); + PopulateIndexes(indexes.Where(x => IsRegistered(x.Name))); } protected abstract void PopulateIndexes(IEnumerable indexes); diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 8435025063..cae45edcdb 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 00b5a03f1d..ae6d607c63 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -130,6 +130,7 @@ namespace Umbraco.Examine #region Public methods + /// /// /// Deletes a node from the index. /// @@ -138,7 +139,8 @@ namespace Umbraco.Examine /// custom Lucene search to find all decendents and create Delete item queues for them too. /// /// ID of the node to delete - public override void DeleteFromIndex(string nodeId) + /// + protected override void PerformDeleteFromIndex(string nodeId, Action onComplete) { //find all descendants based on path var descendantPath = $@"\-1\,*{nodeId}\,*"; @@ -156,7 +158,7 @@ namespace Umbraco.Examine QueueIndexOperation(new IndexOperation(new ValueSet(r.Id, null), IndexOperationType.Delete)); } - base.DeleteFromIndex(nodeId); + base.PerformDeleteFromIndex(nodeId, onComplete); } #endregion diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index 0bd6be5855..05958e185d 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -255,13 +255,13 @@ namespace Umbraco.Examine /// /// This check is required since the base examine lib will try to rebuild on startup /// - public override void DeleteFromIndex(string nodeId) + protected override void PerformDeleteFromIndex(string nodeId, Action onComplete) { if (CanInitialize()) { using (new SafeCallContext()) { - base.DeleteFromIndex(nodeId); + base.PerformDeleteFromIndex(nodeId, onComplete); } } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 3b98e778e4..9e6a7734f3 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.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js index 4e5b2dd688..5b26f198b0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js @@ -33,11 +33,11 @@ function ExamineManagementController($scope, umbRequestHelper, $http, $q, $timeo setViewState("searcher-details"); } - function checkProcessing(indexer, checkActionName) { + function checkProcessing(index, checkActionName) { umbRequestHelper.resourcePromise( $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", checkActionName, - { indexerName: indexer.name })), + { indexName: index.name })), 'Failed to check index processing') .then(function(data) { @@ -45,19 +45,19 @@ function ExamineManagementController($scope, umbRequestHelper, $http, $q, $timeo //copy all resulting properties for (var k in data) { - indexer[k] = data[k]; + index[k] = data[k]; } - indexer.isProcessing = false; + index.isProcessing = false; } else { $timeout(function() { //don't continue if we've tried 100 times - if (indexer.processingAttempts < 100) { - checkProcessing(indexer, checkActionName); + if (index.processingAttempts < 100) { + checkProcessing(index, checkActionName); //add an attempt - indexer.processingAttempts++; + index.processingAttempts++; } else { //we've exceeded 100 attempts, stop processing - indexer.isProcessing = false; + index.isProcessing = false; } }, 1000); @@ -93,26 +93,26 @@ function ExamineManagementController($scope, umbRequestHelper, $http, $q, $timeo } } - function rebuildIndex(indexer) { + function rebuildIndex(index) { if (confirm("This will cause the index to be rebuilt. " + "Depending on how much content there is in your site this could take a while. " + "It is not recommended to rebuild an index during times of high website traffic " + "or when editors are editing content.")) { - indexer.isProcessing = true; - indexer.processingAttempts = 0; + index.isProcessing = true; + index.processingAttempts = 0; umbRequestHelper.resourcePromise( $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "PostRebuildIndex", - { indexerName: indexer.name })), + { indexName: index.name })), 'Failed to rebuild index') .then(function() { //rebuilding has started, nothing is returned accept a 200 status code. //lets poll to see if it is done. $timeout(function() { - checkProcessing(indexer, "PostCheckRebuildIndex"); + checkProcessing(index, "PostCheckRebuildIndex"); }, 1000); diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html index e547c1c139..ac8e990ef6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html @@ -183,7 +183,7 @@
    -
    Tools
    -
    Tools to help manage the index
    +
    Tools to manage the index
    @@ -241,9 +241,10 @@ @@ -257,6 +258,10 @@
    + +
    + This index cannot be rebuilt because it has no assigned IIndexPopulator +
    diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index a17168b42c..5f3b95844c 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,7 +88,7 @@ - + diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 5444aadcec..bf5181ba5e 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -121,9 +121,8 @@ namespace Umbraco.Web.Editors return result; var allowedSections = Security.CurrentUser.AllowedSections.ToArray(); - var searchableTrees = _searchableTreeCollection.AsReadOnlyDictionary(); - foreach (var searchableTree in searchableTrees) + foreach (var searchableTree in _searchableTreeCollection.SearchableApplicationTrees) { if (allowedSections.Contains(searchableTree.Value.AppAlias)) { diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs index d54c67ddb7..999b5bd41e 100644 --- a/src/Umbraco.Web/Editors/ExamineManagementController.cs +++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs @@ -25,13 +25,16 @@ namespace Umbraco.Web.Editors private readonly IExamineManager _examineManager; private readonly ILogger _logger; private readonly IRuntimeCacheProvider _runtimeCacheProvider; + private readonly IEnumerable _populators; public ExamineManagementController(IExamineManager examineManager, ILogger logger, - IRuntimeCacheProvider runtimeCacheProvider) + IRuntimeCacheProvider runtimeCacheProvider, + IEnumerable populators) { _examineManager = examineManager; _logger = logger; _runtimeCacheProvider = runtimeCacheProvider; + _populators = populators; } /// @@ -113,74 +116,83 @@ namespace Umbraco.Web.Editors /// /// Check if the index has been rebuilt /// - /// + /// /// /// /// This is kind of rudimentary since there's no way we can know that the index has rebuilt, we /// have a listener for the index op complete so we'll just check if that key is no longer there in the runtime cache /// - public ExamineIndexModel PostCheckRebuildIndex(string indexerName) + public ExamineIndexModel PostCheckRebuildIndex(string indexName) { - var msg = ValidateLuceneIndexer(indexerName, out LuceneIndex indexer); - if (msg.IsSuccessStatusCode) - { - var cacheKey = "temp_indexing_op_" + indexerName; - var found = ApplicationCache.RuntimeCache.GetCacheItem(cacheKey); + var validate = ValidateIndex(indexName, out var index); + if (!validate.IsSuccessStatusCode) + throw new HttpResponseException(validate); - //if its still there then it's not done - return found != null - ? null - : CreateModel(new KeyValuePair(indexerName, indexer)); - } + validate = ValidatePopulator(indexName); + if (!validate.IsSuccessStatusCode) + throw new HttpResponseException(validate); + + var cacheKey = "temp_indexing_op_" + indexName; + var found = ApplicationCache.RuntimeCache.GetCacheItem(cacheKey); + + //if its still there then it's not done + return found != null + ? null + : CreateModel(new KeyValuePair(indexName, index)); - throw new HttpResponseException(msg); } /// - /// Rebuilds the index + /// Rebuilds the index /// - /// + /// /// - public HttpResponseMessage PostRebuildIndex(string indexerName) + public HttpResponseMessage PostRebuildIndex(string indexName) { - var msg = ValidateLuceneIndexer(indexerName, out LuceneIndex indexer); - if (msg.IsSuccessStatusCode) + var validate = ValidateIndex(indexName, out var index); + if (!validate.IsSuccessStatusCode) + return validate; + + validate = ValidatePopulator(indexName); + if (!validate.IsSuccessStatusCode) + return validate; + + _logger.Info("Rebuilding index '{IndexName}'", indexName); + + //remove it in case there's a handler there alraedy + index.IndexOperationComplete -= Indexer_IndexOperationComplete; + + //now add a single handler + index.IndexOperationComplete += Indexer_IndexOperationComplete; + + var cacheKey = "temp_indexing_op_" + index.Name; + + //put temp val in cache which is used as a rudimentary way to know when the indexing is done + ApplicationCache.RuntimeCache.InsertCacheItem(cacheKey, () => "tempValue", TimeSpan.FromMinutes(5)); + + try { - _logger.Info("Rebuilding index '{IndexerName}'", indexerName); + //clear and replace + index.CreateIndex(); - //remove it in case there's a handler there alraedy - indexer.IndexOperationComplete -= Indexer_IndexOperationComplete; + //populate it + foreach (var populator in _populators.Where(x => x.IsRegistered(indexName))) + populator.Populate(index); - //now add a single handler - indexer.IndexOperationComplete += Indexer_IndexOperationComplete; - - var cacheKey = "temp_indexing_op_" + indexer.Name; - - //put temp val in cache which is used as a rudimentary way to know when the indexing is done - ApplicationCache.RuntimeCache.InsertCacheItem(cacheKey, () => "tempValue", TimeSpan.FromMinutes(5), - false); - - try - { - //TODO: Rebuilding isn't build directly into an index, we need a new IRebuildIndex or similar interface that can be registered - throw new NotImplementedException("Implement rebuilding!"); - //indexer.RebuildIndex(); - } - catch (Exception ex) - { - //ensure it's not listening - indexer.IndexOperationComplete -= Indexer_IndexOperationComplete; - Logger.Error(ex, "An error occurred rebuilding index"); - var response = Request.CreateResponse(HttpStatusCode.Conflict); - response.Content = - new - StringContent($"The index could not be rebuilt at this time, most likely there is another thread currently writing to the index. Error: {ex}"); - response.ReasonPhrase = "Could Not Rebuild"; - return response; - } + return Request.CreateResponse(HttpStatusCode.OK); + } + catch (Exception ex) + { + //ensure it's not listening + index.IndexOperationComplete -= Indexer_IndexOperationComplete; + Logger.Error(ex, "An error occurred rebuilding index"); + var response = Request.CreateResponse(HttpStatusCode.Conflict); + response.Content = + new + StringContent($"The index could not be rebuilt at this time, most likely there is another thread currently writing to the index. Error: {ex}"); + response.ReasonPhrase = "Could Not Rebuild"; + return response; } - - return msg; } @@ -207,8 +219,10 @@ namespace Umbraco.Web.Editors { Name = indexName, HealthStatus = isHealth.Success ? (isHealth.Result ?? "Healthy") : (isHealth.Result ?? "Unhealthy"), - ProviderProperties = properties + ProviderProperties = properties, + CanRebuild = _populators.Any(x => x.IsRegistered(indexName)) }; + return indexerModel; } @@ -248,22 +262,31 @@ namespace Umbraco.Web.Editors return response1; } - private HttpResponseMessage ValidateLuceneIndexer(string indexerName, out T indexer) - where T : class, IIndex + private HttpResponseMessage ValidatePopulator(string indexName) { - indexer = null; + if (_populators.Any(x => x.IsRegistered(indexName))) + return Request.CreateResponse(HttpStatusCode.OK); - if (_examineManager.IndexProviders.ContainsKey(indexerName) - && _examineManager.IndexProviders[indexerName] is T casted) + var response = Request.CreateResponse(HttpStatusCode.BadRequest); + response.Content = new StringContent($"The index {indexName} cannot be rebuilt because it does not have an associated {typeof(IIndexPopulator)}"); + response.ReasonPhrase = "Index cannot be rebuilt"; + return response; + } + + private HttpResponseMessage ValidateIndex(string indexName, out IIndex index) + { + index = null; + + if (_examineManager.IndexProviders.ContainsKey(indexName)) { //return Ok! - indexer = casted; + index = _examineManager.GetIndex(indexName); return Request.CreateResponse(HttpStatusCode.OK); } var response = Request.CreateResponse(HttpStatusCode.BadRequest); - response.Content = new StringContent($"No indexer found with name = {indexerName} of type {typeof(T)}"); - response.ReasonPhrase = "Indexer Not Found"; + response.Content = new StringContent($"No index found with name = {indexName}"); + response.ReasonPhrase = "Index Not Found"; return response; } diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 9c659582ea..d6d5a5f444 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -22,6 +22,7 @@ using Umbraco.Examine; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Web.Scheduling; using System.Threading.Tasks; +using LightInject; using Umbraco.Core.Composing; namespace Umbraco.Web.Search @@ -53,11 +54,23 @@ namespace Umbraco.Web.Search { base.Compose(composition); - composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(); + //fixme: I cannot do this since RegisterSingleton acts like TryRegisterSingleton and only allows one + //composition.Container.RegisterSingleton(); + //composition.Container.RegisterSingleton(); + //composition.Container.RegisterSingleton(); + //composition.Container.RegisterSingleton(); + + //fixme: Instead i have to do this, but this means that developers adding their own will also need to do this which isn't ideal + composition.Container.RegisterMany(new[] + { + typeof(MemberIndexPopulator), + typeof(ContentIndexPopulator), + typeof(PublishedContentIndexPopulator), + typeof(MediaIndexPopulator), + }); + composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton(); + composition.Container.RegisterSingleton(); composition.Container.RegisterSingleton, ContentValueSetBuilder>(); composition.Container.RegisterSingleton, MediaValueSetBuilder>(); composition.Container.RegisterSingleton, MemberValueSetBuilder>(); @@ -65,7 +78,7 @@ namespace Umbraco.Web.Search internal void Initialize(IRuntimeState runtime, MainDom mainDom, PropertyEditorCollection propertyEditors, IExamineManager examineManager, ProfilingLogger profilingLogger, - IScopeProvider scopeProvider, IUmbracoIndexesBuilder indexBuilder, + IScopeProvider scopeProvider, IUmbracoIndexesCreator indexCreator, IndexRebuilder indexRebuilder, ServiceContext services, IValueSetBuilder contentValueSetBuilder, IValueSetBuilder mediaValueSetBuilder, @@ -111,7 +124,7 @@ namespace Umbraco.Web.Search } //create the indexes and register them with the manager - foreach(var index in indexBuilder.Create()) + foreach(var index in indexCreator.Create()) { _examineManager.AddIndex(index); } diff --git a/src/Umbraco.Web/Search/ExamineIndexModel.cs b/src/Umbraco.Web/Search/ExamineIndexModel.cs index df02fee1ed..13cd2cbc12 100644 --- a/src/Umbraco.Web/Search/ExamineIndexModel.cs +++ b/src/Umbraco.Web/Search/ExamineIndexModel.cs @@ -20,5 +20,8 @@ namespace Umbraco.Web.Search [DataMember(Name = "providerProperties")] public IReadOnlyDictionary ProviderProperties { get; set; } + [DataMember(Name = "canRebuild")] + public bool CanRebuild { get; set; } + } } diff --git a/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/IUmbracoIndexesCreator.cs similarity index 52% rename from src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs rename to src/Umbraco.Web/Search/IUmbracoIndexesCreator.cs index ec8d923abc..58014597d2 100644 --- a/src/Umbraco.Web/Search/IUmbracoIndexesBuilder.cs +++ b/src/Umbraco.Web/Search/IUmbracoIndexesCreator.cs @@ -3,7 +3,10 @@ using Examine; namespace Umbraco.Web.Search { - internal interface IUmbracoIndexesBuilder + /// + /// Used to create the Umbraco indexes + /// + public interface IUmbracoIndexesCreator { IEnumerable Create(); } diff --git a/src/Umbraco.Web/Search/SearchableTreeCollection.cs b/src/Umbraco.Web/Search/SearchableTreeCollection.cs index d9368c973e..86f4494353 100644 --- a/src/Umbraco.Web/Search/SearchableTreeCollection.cs +++ b/src/Umbraco.Web/Search/SearchableTreeCollection.cs @@ -33,11 +33,7 @@ namespace Umbraco.Web.Search return dictionary; } - // fixme - oh why?! - public IReadOnlyDictionary AsReadOnlyDictionary() - { - return new ReadOnlyDictionary(_dictionary); - } + public IReadOnlyDictionary SearchableApplicationTrees => _dictionary; public SearchableApplicationTree this[string key] => _dictionary[key]; } diff --git a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs similarity index 97% rename from src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs rename to src/Umbraco.Web/Search/UmbracoIndexesCreator.cs index 18ea85162c..53bac7769c 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesBuilder.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs @@ -21,11 +21,11 @@ namespace Umbraco.Web.Search /// /// Creates the indexes used by Umbraco /// - public class UmbracoIndexesBuilder : IUmbracoIndexesBuilder + public class UmbracoIndexesCreator : IUmbracoIndexesCreator { //TODO: we should inject the different IValueSetValidator so devs can just register them instead of overriding this class? - public UmbracoIndexesBuilder(ProfilingLogger profilingLogger, + public UmbracoIndexesCreator(ProfilingLogger profilingLogger, ILocalizationService languageService, IPublicAccessService publicAccessService, IMemberService memberService) diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 90d9a58ef8..19c1e59bd1 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -62,7 +62,7 @@ - + 2.6.2.25 @@ -161,8 +161,8 @@ - - + + From f8d203abff9f2ab18b3063714e21a2336d47ce13 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 3 Dec 2018 22:10:56 +1100 Subject: [PATCH 30/44] Updates latest examine + api updates + fixes tests, gets different validation working (valid, failed, filtered) --- .../ContentValueSetValidator.cs | 96 +++++++----- .../IContentValueSetValidator.cs | 18 +++ src/Umbraco.Examine/IndexRebuilder.cs | 4 +- src/Umbraco.Examine/Umbraco.Examine.csproj | 3 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 51 +++++-- src/Umbraco.Examine/ValueSetValidator.cs | 20 ++- .../TestHelpers/Stubs/TestExamineManager.cs | 12 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../UmbracoExamine/IndexInitializer.cs | 2 +- .../UmbracoContentValueSetValidatorTests.cs | 138 +++++++++--------- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 3 +- src/Umbraco.Web/Editors/EntityController.cs | 28 ++-- .../Editors/ExamineManagementController.cs | 50 ++----- src/Umbraco.Web/IPublishedContentQuery.cs | 2 +- .../XmlPublishedCache/PublishedMediaCache.cs | 5 +- src/Umbraco.Web/PublishedContentExtensions.cs | 43 +++--- src/Umbraco.Web/PublishedContentQuery.cs | 33 +++-- .../Runtime/WebRuntimeComponent.cs | 2 + src/Umbraco.Web/Search/ExamineComponent.cs | 16 +- .../Search/ExamineSearcherModel.cs | 15 -- .../Search/UmbracoIndexesCreator.cs | 4 +- src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 48 +++--- .../Trees/ContentTreeController.cs | 9 +- src/Umbraco.Web/Trees/MediaTreeController.cs | 9 +- src/Umbraco.Web/Trees/MemberTreeController.cs | 7 +- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- 26 files changed, 343 insertions(+), 279 deletions(-) create mode 100644 src/Umbraco.Examine/IContentValueSetValidator.cs diff --git a/src/Umbraco.Examine/ContentValueSetValidator.cs b/src/Umbraco.Examine/ContentValueSetValidator.cs index e78fe749db..46e4f48c9a 100644 --- a/src/Umbraco.Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Examine/ContentValueSetValidator.cs @@ -12,7 +12,7 @@ namespace Umbraco.Examine /// /// Used to validate a ValueSet for content/media - based on permissions, parent id, etc.... /// - public class ContentValueSetValidator : ValueSetValidator + public class ContentValueSetValidator : ValueSetValidator, IContentValueSetValidator { private readonly IPublicAccessService _publicAccessService; @@ -24,6 +24,46 @@ namespace Umbraco.Examine public bool SupportProtectedContent { get; } public int? ParentId { get; } + public bool ValidatePath(string path, string category) + { + //check if this document is a descendent of the parent + if (ParentId.HasValue && ParentId.Value > 0) + { + // we cannot return FAILED here because we need the value set to get into the indexer and then deal with it from there + // because we need to remove anything that doesn't pass by parent Id in the cases that umbraco data is moved to an illegal parent. + if (!path.Contains(string.Concat(",", ParentId.Value, ","))) + return false; + } + + return true; + } + + public bool ValidateRecycleBin(string path, string category) + { + var recycleBinId = category == IndexTypes.Content ? Constants.System.RecycleBinContent : Constants.System.RecycleBinMedia; + + //check for recycle bin + if (!SupportUnpublishedContent) + { + if (path.Contains(string.Concat(",", recycleBinId, ","))) + return false; + } + return true; + } + + public bool ValidateProtectedContent(string path, string category) + { + if (category == IndexTypes.Content + && !SupportProtectedContent + //if the service is null we can't look this up so we'll return false + && (_publicAccessService == null || _publicAccessService.IsProtected(path))) + { + return false; + } + + return true; + } + public ContentValueSetValidator(bool supportUnpublishedContent, int? parentId = null, IEnumerable includeItemTypes = null, IEnumerable excludeItemTypes = null) : this(supportUnpublishedContent, true, null, parentId, includeItemTypes, excludeItemTypes) @@ -41,19 +81,22 @@ namespace Umbraco.Examine _publicAccessService = publicAccessService; } - public override bool Validate(ValueSet valueSet) + public override ValueSetValidationResult Validate(ValueSet valueSet) { - if (!base.Validate(valueSet)) - return false; + var baseValidate = base.Validate(valueSet); + if (baseValidate == ValueSetValidationResult.Failed) + return ValueSetValidationResult.Failed; + + var isFiltered = baseValidate == ValueSetValidationResult.Filtered; //check for published content if (valueSet.Category == IndexTypes.Content && !SupportUnpublishedContent) { if (!valueSet.Values.TryGetValue(UmbracoExamineIndexer.PublishedFieldName, out var published)) - return false; + return ValueSetValidationResult.Failed; if (!published[0].Equals(1)) - return false; + return ValueSetValidationResult.Failed; //deal with variants, if there are unpublished variants than we need to remove them from the value set if (valueSet.Values.TryGetValue(UmbracoContentIndexer.VariesByCultureFieldName, out var variesByCulture) @@ -69,6 +112,7 @@ namespace Umbraco.Examine foreach (var cultureField in valueSet.Values.Where(x => x.Key.InvariantEndsWith(cultureSuffix)).ToList()) { valueSet.Values.Remove(cultureField.Key); + isFiltered = true; } } } @@ -76,37 +120,21 @@ namespace Umbraco.Examine } //must have a 'path' - if (!valueSet.Values.TryGetValue(PathKey, out var pathValues)) return false; - if (pathValues.Count == 0) return false; - if (pathValues[0] == null) return false; - if (pathValues[0].ToString().IsNullOrWhiteSpace()) return false; + if (!valueSet.Values.TryGetValue(PathKey, out var pathValues)) return ValueSetValidationResult.Failed; + if (pathValues.Count == 0) return ValueSetValidationResult.Failed; + if (pathValues[0] == null) return ValueSetValidationResult.Failed; + if (pathValues[0].ToString().IsNullOrWhiteSpace()) return ValueSetValidationResult.Failed; var path = pathValues[0].ToString(); - // return nothing if we're not supporting protected content and it is protected, and we're not supporting unpublished content - if (valueSet.Category == IndexTypes.Content - && !SupportProtectedContent - //if the service is null we can't look this up so we'll return false - && (_publicAccessService == null || _publicAccessService.IsProtected(path))) - { - return false; - } + // We need to validate the path of the content based on ParentId, protected content and recycle bin rules. + // We cannot return FAILED here because we need the value set to get into the indexer and then deal with it from there + // because we need to remove anything that doesn't pass by protected content in the cases that umbraco data is moved to an illegal parent. + if (!ValidatePath(path, valueSet.Category) + || !ValidateRecycleBin(path, valueSet.Category) + || !ValidateProtectedContent(path, valueSet.Category)) + return ValueSetValidationResult.Filtered; - //check if this document is a descendent of the parent - if (ParentId.HasValue && ParentId.Value > 0) - { - if (!path.Contains(string.Concat(",", ParentId.Value, ","))) - return false; - } - - //check for recycle bin - if (!SupportUnpublishedContent) - { - var recycleBinId = valueSet.Category == IndexTypes.Content ? Constants.System.RecycleBinContent : Constants.System.RecycleBinMedia; - if (path.Contains(string.Concat(",", recycleBinId, ","))) - return false; - } - - return true; + return isFiltered ? ValueSetValidationResult.Filtered: ValueSetValidationResult.Valid; } } } diff --git a/src/Umbraco.Examine/IContentValueSetValidator.cs b/src/Umbraco.Examine/IContentValueSetValidator.cs new file mode 100644 index 0000000000..a7164773bb --- /dev/null +++ b/src/Umbraco.Examine/IContentValueSetValidator.cs @@ -0,0 +1,18 @@ +using Examine; + +namespace Umbraco.Examine +{ + /// + /// An extended for content indexes + /// + public interface IContentValueSetValidator : IValueSetValidator + { + bool SupportUnpublishedContent { get; } + bool SupportProtectedContent { get; } + int? ParentId { get; } + + bool ValidatePath(string path, string category); + bool ValidateRecycleBin(string path, string category); + bool ValidateProtectedContent(string path, string category); + } +} diff --git a/src/Umbraco.Examine/IndexRebuilder.cs b/src/Umbraco.Examine/IndexRebuilder.cs index 4c21f8416f..525b57cc7d 100644 --- a/src/Umbraco.Examine/IndexRebuilder.cs +++ b/src/Umbraco.Examine/IndexRebuilder.cs @@ -21,8 +21,8 @@ namespace Umbraco.Examine public void RebuildIndexes(bool onlyEmptyIndexes) { var indexes = (onlyEmptyIndexes - ? ExamineManager.IndexProviders.Values.Where(x => !x.IndexExists()) - : ExamineManager.IndexProviders.Values).ToArray(); + ? ExamineManager.Indexes.Where(x => !x.IndexExists()) + : ExamineManager.Indexes).ToArray(); foreach(var index in indexes) index.CreateIndex(); // clear the index diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index cae45edcdb..d2336eedf4 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + @@ -64,6 +64,7 @@ + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index ae6d607c63..1387357463 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -57,7 +57,7 @@ namespace Umbraco.Examine Analyzer defaultAnalyzer, ProfilingLogger profilingLogger, ILocalizationService languageService, - IValueSetValidator validator, + IContentValueSetValidator validator, IReadOnlyDictionary> indexValueTypes = null) : base(name, fieldDefinitions, luceneDirectory, defaultAnalyzer, profilingLogger, validator, indexValueTypes) { @@ -121,14 +121,50 @@ namespace Umbraco.Examine //Using a singleton here, we can't inject this when using config based providers and we don't use this //anywhere else in this class Current.Services.PublicAccessService, - parentId, ConfigIndexCriteria.IncludeItemTypes, ConfigIndexCriteria.ExcludeItemTypes); + parentId, + ConfigIndexCriteria.IncludeItemTypes, ConfigIndexCriteria.ExcludeItemTypes); SupportSoftDelete = supportUnpublished; } #endregion - #region Public methods + /// + /// Special check for invalid paths + /// + /// + /// + protected override void PerformIndexItems(IEnumerable values, Action onComplete) + { + var valid = true; + + // ReSharper disable once PossibleMultipleEnumeration + foreach (var v in values) + { + if (v.Values.TryGetValue("path", out var paths) && paths.Count > 0 && paths[0] != null) + { + //we know this is an IContentValueSetValidator + var validator = (IContentValueSetValidator) ValueSetValidator; + var path = paths[0].ToString(); + + if (!validator.ValidatePath(path, v.Category) + || !validator.ValidateRecycleBin(path, v.Category) + || !validator.ValidateProtectedContent(path, v.Category)) + { + //since the path is not valid we need to delete this item in case it exists in the index already and has now + //been moved to an invalid parent. + PerformDeleteFromIndex(v.Id, x => { /*noop*/ }); + valid = false; + } + } + } + + if (valid) + { + // ReSharper disable once PossibleMultipleEnumeration + base.PerformIndexItems(values, onComplete); + } + } /// /// @@ -155,14 +191,11 @@ namespace Umbraco.Examine //need to queue a delete item for each one found foreach (var r in results) { - QueueIndexOperation(new IndexOperation(new ValueSet(r.Id, null), IndexOperationType.Delete)); + QueueIndexOperation(new IndexOperation(new ValueSet(r.Id), IndexOperationType.Delete)); } base.PerformDeleteFromIndex(nodeId, onComplete); } - #endregion - - #region Protected /// /// Overridden to ensure that the variant system fields have the right value types @@ -184,8 +217,6 @@ namespace Umbraco.Examine return base.CreateFieldValueTypes(indexValueTypesFactory); } - - - #endregion + } } diff --git a/src/Umbraco.Examine/ValueSetValidator.cs b/src/Umbraco.Examine/ValueSetValidator.cs index a0e926fed0..4db251c0f1 100644 --- a/src/Umbraco.Examine/ValueSetValidator.cs +++ b/src/Umbraco.Examine/ValueSetValidator.cs @@ -59,18 +59,20 @@ namespace Umbraco.Examine /// public IEnumerable ExcludeFields { get; } - public virtual bool Validate(ValueSet valueSet) + public virtual ValueSetValidationResult Validate(ValueSet valueSet) { if (ValidIndexCategories != null && !ValidIndexCategories.InvariantContains(valueSet.Category)) - return false; + return ValueSetValidationResult.Failed; //check if this document is of a correct type of node type alias if (IncludeItemTypes != null && !IncludeItemTypes.InvariantContains(valueSet.ItemType)) - return false; + return ValueSetValidationResult.Failed; //if this node type is part of our exclusion list if (ExcludeItemTypes != null && ExcludeItemTypes.InvariantContains(valueSet.ItemType)) - return false; + return ValueSetValidationResult.Failed; + + var isFiltered = false; //filter based on the fields provided (if any) if (IncludeFields != null || ExcludeFields != null) @@ -78,15 +80,21 @@ namespace Umbraco.Examine foreach (var key in valueSet.Values.Keys.ToList()) { if (IncludeFields != null && !IncludeFields.InvariantContains(key)) + { valueSet.Values.Remove(key); //remove any value with a key that doesn't match the inclusion list + isFiltered = true; + } if (ExcludeFields != null && ExcludeFields.InvariantContains(key)) + { valueSet.Values.Remove(key); //remove any value with a key that matches the exclusion list + isFiltered = true; + } + } } - - return true; + return isFiltered ? ValueSetValidationResult.Filtered : ValueSetValidationResult.Valid; } } } diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs index ac5671fceb..f8d48c5703 100644 --- a/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs +++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs @@ -24,16 +24,20 @@ namespace Umbraco.Tests.TestHelpers.Stubs //noop } - public IIndex GetIndex(string indexerName) + public bool TryGetIndex(string indexName, out IIndex index) { - return _indexers.TryGetValue(indexerName, out var indexer) ? indexer : null; + return _indexers.TryGetValue(indexName, out index); } - public ISearcher GetSearcher(string searcherName) + public bool TryGetSearcher(string searcherName, out ISearcher searcher) { - return _searchers.TryGetValue(searcherName, out var indexer) ? indexer : null; + return _searchers.TryGetValue(searcherName, out searcher); } + public IEnumerable Indexes => _indexers.Values; + + public IEnumerable RegisteredSearchers => _searchers.Values; + public IReadOnlyDictionary IndexProviders => _indexers; } } diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 9e6a7734f3..6ddf99822b 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/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index e5d7598080..b478b8f8fc 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -151,7 +151,7 @@ namespace Umbraco.Tests.UmbracoExamine Directory luceneDir, Analyzer analyzer = null, ILocalizationService languageService = null, - IValueSetValidator validator = null) + IContentValueSetValidator validator = null) { if (languageService == null) languageService = GetMockLocalizationService(); diff --git a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs index 0df4750051..6458cd2a80 100644 --- a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs @@ -19,14 +19,14 @@ namespace Umbraco.Tests.UmbracoExamine { var validator = new ContentValueSetValidator(true, true, Mock.Of()); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); - result = validator.Validate(new ValueSet("777", IndexTypes.Media, new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("777", IndexTypes.Media, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); - result = validator.Validate(new ValueSet("555", "invalid-category", new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", "invalid-category", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); } @@ -35,11 +35,11 @@ namespace Umbraco.Tests.UmbracoExamine { var validator = new ContentValueSetValidator(true, true, Mock.Of()); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } [Test] @@ -47,17 +47,17 @@ namespace Umbraco.Tests.UmbracoExamine { var validator = new ContentValueSetValidator(true, true, Mock.Of(), 555); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,444" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,444" })); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555,777" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555,777" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555,777,999" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555,777,999" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } [Test] @@ -67,9 +67,9 @@ namespace Umbraco.Tests.UmbracoExamine new[] { "hello", "world" }, null); - var valueSet = new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); + var valueSet = ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); var result = validator.Validate(valueSet); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); Assert.IsFalse(valueSet.Values.ContainsKey("path")); Assert.IsTrue(valueSet.Values.ContainsKey("hello")); @@ -83,9 +83,9 @@ namespace Umbraco.Tests.UmbracoExamine null, new[] { "hello", "world" }); - var valueSet = new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); + var valueSet = ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); var result = validator.Validate(valueSet); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); Assert.IsTrue(valueSet.Values.ContainsKey("path")); Assert.IsFalse(valueSet.Values.ContainsKey("hello")); @@ -99,9 +99,9 @@ namespace Umbraco.Tests.UmbracoExamine new[] { "hello", "world" }, new[] { "world" }); - var valueSet = new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); + var valueSet = ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555", world = "your oyster" }); var result = validator.Validate(valueSet); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); Assert.IsFalse(valueSet.Values.ContainsKey("path")); Assert.IsTrue(valueSet.Values.ContainsKey("hello")); @@ -114,14 +114,14 @@ namespace Umbraco.Tests.UmbracoExamine var validator = new ContentValueSetValidator(true, true, Mock.Of(), includeItemTypes: new List { "include-content" }); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, "include-content", new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "include-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } [Test] @@ -130,14 +130,14 @@ namespace Umbraco.Tests.UmbracoExamine var validator = new ContentValueSetValidator(true, true, Mock.Of(), excludeItemTypes: new List { "exclude-content" }); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, "exclude-content", new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "exclude-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); } [Test] @@ -147,17 +147,17 @@ namespace Umbraco.Tests.UmbracoExamine includeItemTypes: new List { "include-content", "exclude-content" }, excludeItemTypes: new List { "exclude-content" }); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, "exclude-content", new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "exclude-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, "include-content", new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "include-content", new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } [Test] @@ -165,14 +165,14 @@ namespace Umbraco.Tests.UmbracoExamine { var validator = new ContentValueSetValidator(false, false, Mock.Of()); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555,777" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555,777" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); result = validator.Validate(new ValueSet("555", IndexTypes.Content, new Dictionary @@ -181,7 +181,7 @@ namespace Umbraco.Tests.UmbracoExamine ["path"] = "-1,555", [UmbracoExamineIndexer.PublishedFieldName] = 1 })); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } [Test] @@ -189,15 +189,15 @@ namespace Umbraco.Tests.UmbracoExamine { var validator = new ContentValueSetValidator(false, false, Mock.Of()); - var result = validator.Validate(new ValueSet("555", IndexTypes.Media, new { hello = "world", path = "-1,-21,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Media, new { hello = "world", path = "-1,-21,555" })); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Media, new { hello = "world", path = "-1,-21,555,777" })); - Assert.IsFalse(result); + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Media, new { hello = "world", path = "-1,-21,555,777" })); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); + + result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Media, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); - result = validator.Validate(new ValueSet("555", IndexTypes.Media, new { hello = "world", path = "-1,555" })); - Assert.IsTrue(result); - } [Test] @@ -205,8 +205,8 @@ namespace Umbraco.Tests.UmbracoExamine { var validator = new ContentValueSetValidator(false, true, Mock.Of()); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Failed, result); result = validator.Validate(new ValueSet("555", IndexTypes.Content, new Dictionary @@ -215,7 +215,7 @@ namespace Umbraco.Tests.UmbracoExamine ["path"] = "-1,555", [UmbracoExamineIndexer.PublishedFieldName] = 0 })); - Assert.IsFalse(result); + Assert.AreEqual(ValueSetValidationResult.Failed, result); result = validator.Validate(new ValueSet("555", IndexTypes.Content, new Dictionary @@ -224,7 +224,7 @@ namespace Umbraco.Tests.UmbracoExamine ["path"] = "-1,555", [UmbracoExamineIndexer.PublishedFieldName] = 1 })); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } [Test] @@ -240,7 +240,7 @@ namespace Umbraco.Tests.UmbracoExamine [UmbracoContentIndexer.VariesByCultureFieldName] = 1, [UmbracoExamineIndexer.PublishedFieldName] = 0 })); - Assert.IsFalse(result); + Assert.AreEqual(ValueSetValidationResult.Failed, result); result = validator.Validate(new ValueSet("555", IndexTypes.Content, new Dictionary @@ -250,7 +250,7 @@ namespace Umbraco.Tests.UmbracoExamine [UmbracoContentIndexer.VariesByCultureFieldName] = 1, [UmbracoExamineIndexer.PublishedFieldName] = 1 })); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Valid, result); var valueSet = new ValueSet("555", IndexTypes.Content, new Dictionary @@ -272,7 +272,7 @@ namespace Umbraco.Tests.UmbracoExamine Assert.IsTrue(valueSet.Values.ContainsKey("title_es-ES")); result = validator.Validate(valueSet); - Assert.IsTrue(result); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); Assert.AreEqual(7, valueSet.Values.Count()); //filtered to 7 values (removes es-es values) Assert.IsFalse(valueSet.Values.ContainsKey($"{UmbracoExamineIndexer.PublishedFieldName}_es-es")); @@ -290,11 +290,11 @@ namespace Umbraco.Tests.UmbracoExamine .Returns(Attempt.Fail()); var validator = new ContentValueSetValidator(true, false, publicAccessService.Object); - var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); - Assert.IsFalse(result); + var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); + Assert.AreEqual(ValueSetValidationResult.Filtered, result); - result = validator.Validate(new ValueSet("777", IndexTypes.Content, new { hello = "world", path = "-1,777" })); - Assert.IsTrue(result); + result = validator.Validate(ValueSet.FromObject("777", IndexTypes.Content, new { hello = "world", path = "-1,777" })); + Assert.AreEqual(ValueSetValidationResult.Valid, result); } } } diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 5f3b95844c..8278dac171 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,10 +88,9 @@ - + - diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index bf5181ba5e..31109485f6 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Models; using Constants = Umbraco.Core.Constants; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using System.Web.Http.Controllers; +using Examine; using Umbraco.Core.Models.Entities; using Umbraco.Core.Xml; using Umbraco.Web.Models.Mapping; @@ -53,12 +54,13 @@ namespace Umbraco.Web.Editors } } - private readonly UmbracoTreeSearcher _treeSearcher = new UmbracoTreeSearcher(); + private readonly UmbracoTreeSearcher _treeSearcher; private readonly SearchableTreeCollection _searchableTreeCollection; - public EntityController(SearchableTreeCollection searchableTreeCollection) + public EntityController(SearchableTreeCollection searchableTreeCollection, UmbracoTreeSearcher treeSearcher) { _searchableTreeCollection = searchableTreeCollection; + _treeSearcher = treeSearcher; } /// @@ -410,22 +412,18 @@ namespace Umbraco.Web.Editors Direction orderDirection = Direction.Ascending, string filter = "") { - int intId; - - if (int.TryParse(id, out intId)) + if (int.TryParse(id, out var intId)) { return GetPagedChildren(intId, type, pageNumber, pageSize, orderBy, orderDirection, filter); } - Guid guidId; - if (Guid.TryParse(id, out guidId)) + if (Guid.TryParse(id, out _)) { //Not supported currently throw new HttpResponseException(HttpStatusCode.NotFound); } - Udi udiId; - if (Udi.TryParse(id, out udiId)) + if (Udi.TryParse(id, out _)) { //Not supported currently throw new HttpResponseException(HttpStatusCode.NotFound); @@ -443,8 +441,7 @@ namespace Umbraco.Web.Editors //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 - long total; - var searchResult = _treeSearcher.ExamineSearch(Umbraco, filter ?? "", type, pageSize, pageNumber - 1, out total, id); + var searchResult = _treeSearcher.ExamineSearch(filter ?? "", type, pageSize, pageNumber - 1, out long total, id); return new PagedResult(total, pageNumber, pageSize) { @@ -480,8 +477,7 @@ namespace Umbraco.Web.Editors var objectType = ConvertToObjectType(type); if (objectType.HasValue) { - long totalRecords; - var entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out totalRecords, orderBy, orderDirection, filter); + var entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out var totalRecords, orderBy, orderDirection, filter); if (totalRecords == 0) { @@ -596,13 +592,9 @@ namespace Umbraco.Web.Editors /// private IEnumerable ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null) { - long total; - return _treeSearcher.ExamineSearch(Umbraco, query, entityType, 200, 0, out total, searchFrom); + return _treeSearcher.ExamineSearch(query, entityType, 200, 0, out _, searchFrom); } - - - private IEnumerable GetResultForChildren(int id, UmbracoEntityTypes entityType) { var objectType = ConvertToObjectType(entityType); diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs index 999b5bd41e..c166162930 100644 --- a/src/Umbraco.Web/Editors/ExamineManagementController.cs +++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs @@ -43,7 +43,7 @@ namespace Umbraco.Web.Editors /// public IEnumerable GetIndexerDetails() { - return _examineManager.IndexProviders.Select(CreateModel).OrderBy(x => x.Name.TrimEnd("Indexer")); + return _examineManager.Indexes.Select(CreateModel).OrderBy(x => x.Name.TrimEnd("Indexer")); } /// @@ -53,32 +53,8 @@ namespace Umbraco.Web.Editors public IEnumerable GetSearcherDetails() { var model = new List( - _examineManager.IndexProviders.Select(indexer => - { - var searcher = indexer.Value.GetSearcher(); - var searcherName = (searcher as BaseLuceneSearcher)?.Name ?? string.Concat(indexer.Key, "Searcher"); - - var indexerModel = new ExamineSearcherModel - { - Name = searcherName - }; - var props = TypeHelper.CachedDiscoverableProperties(searcher.GetType(), mustWrite: false) - //ignore these properties - .Where(x => new[] { "Description" }.InvariantContains(x.Name) == false) - .Where(x => x.GetCustomAttribute() - ?.State != EditorBrowsableState.Never) - .OrderBy(x => x.Name); - foreach (var p in props) - { - indexerModel.ProviderProperties.Add(p.Name, p.GetValue(searcher, null)?.ToString()); - } - - return indexerModel; - }).OrderBy(x => - { - //order by name , but strip the "Searcher" from the end if it exists - return x.Name.TrimEnd("Searcher"); - })); + _examineManager.RegisteredSearchers.Select(searcher => new ExamineSearcherModel{Name = searcher.Name}) + .OrderBy(x => x.Name.TrimEnd("Searcher"))); //order by name , but strip the "Searcher" from the end if it exists return model; } @@ -138,7 +114,7 @@ namespace Umbraco.Web.Editors //if its still there then it's not done return found != null ? null - : CreateModel(new KeyValuePair(indexName, index)); + : CreateModel(index); } @@ -197,13 +173,12 @@ namespace Umbraco.Web.Editors - private ExamineIndexModel CreateModel(KeyValuePair indexerKeyVal) + private ExamineIndexModel CreateModel(IIndex index) { - var indexer = indexerKeyVal.Value; - var indexName = indexerKeyVal.Key; + var indexName = index.Name; - if (!(indexer is IIndexDiagnostics indexDiag)) - indexDiag = new GenericIndexDiagnostics(indexer); + if (!(index is IIndexDiagnostics indexDiag)) + indexDiag = new GenericIndexDiagnostics(index); var isHealth = indexDiag.IsHealthy(); @@ -229,10 +204,10 @@ namespace Umbraco.Web.Editors private HttpResponseMessage ValidateLuceneSearcher(string searcherName, out LuceneSearcher searcher) { - foreach (var indexer in _examineManager.IndexProviders) + foreach (var indexer in _examineManager.Indexes) { - var s = indexer.Value.GetSearcher(); - var sName = (s as BaseLuceneSearcher)?.Name ?? string.Concat(indexer.Key, "Searcher"); + var s = indexer.GetSearcher(); + var sName = (s as BaseLuceneSearcher)?.Name ?? string.Concat(indexer.Name, "Searcher"); if (sName != searcherName) { continue; @@ -277,10 +252,9 @@ namespace Umbraco.Web.Editors { index = null; - if (_examineManager.IndexProviders.ContainsKey(indexName)) + if (_examineManager.TryGetIndex(indexName, out index)) { //return Ok! - index = _examineManager.GetIndex(indexName); return Request.CreateResponse(HttpStatusCode.OK); } diff --git a/src/Umbraco.Web/IPublishedContentQuery.cs b/src/Umbraco.Web/IPublishedContentQuery.cs index 055dcbe181..80af19c941 100644 --- a/src/Umbraco.Web/IPublishedContentQuery.cs +++ b/src/Umbraco.Web/IPublishedContentQuery.cs @@ -49,6 +49,6 @@ namespace Umbraco.Web /// /// Searches content. /// - IEnumerable Search(int skip, int take, out int totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.ISearcher searchProvider = null); + IEnumerable Search(int skip, int take, out int totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.ISearcher searcher = null); } } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 45f32353da..ccfe947515 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -240,8 +240,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache try { - //by default use the internal index - return eMgr.GetSearcher(Constants.Examine.InternalIndexer); + if (eMgr.TryGetIndex(Constants.Examine.InternalIndexer, out var index)) + return index.GetSearcher(); + throw new InvalidOperationException($"No index found by name {Constants.Examine.InternalIndexer}"); } catch (FileNotFoundException) { diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index f0ddf62074..2bc0d7be3f 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -282,16 +282,15 @@ namespace Umbraco.Web #region Search - public static IEnumerable Search(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null) + public static IEnumerable SearchDescendants(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null) { - //TODO: we should pass in the IExamineManager? + //fixme: pass in the IExamineManager - var searcher = string.IsNullOrEmpty(indexName) - ? ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer) - : ExamineManager.Instance.GetSearcher(indexName); + indexName = string.IsNullOrEmpty(indexName) ? Constants.Examine.ExternalIndexer : indexName; + if (!ExamineManager.Instance.TryGetIndex(indexName, out var index)) + throw new InvalidOperationException("No index found with name " + indexName); - if (searcher == null) - throw new InvalidOperationException("No searcher found for index " + indexName); + var searcher = index.GetSearcher(); var t = term.Escape().Value; if (useWildCards) @@ -303,21 +302,15 @@ namespace Umbraco.Web return content.Search(crit, searcher); } - public static IEnumerable SearchDescendants(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null) - { - return content.Search(term, useWildCards, indexName); - } - public static IEnumerable SearchChildren(this IPublishedContent content, string term, bool useWildCards = true, string indexName = null) { - //TODO: we should pass in the IExamineManager? + //fixme: pass in the IExamineManager - var searcher = string.IsNullOrEmpty(indexName) - ? ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer) - : ExamineManager.Instance.GetSearcher(indexName); + indexName = string.IsNullOrEmpty(indexName) ? Constants.Examine.ExternalIndexer : indexName; + if (!ExamineManager.Instance.TryGetIndex(indexName, out var index)) + throw new InvalidOperationException("No index found with name " + indexName); - if (searcher == null) - throw new InvalidOperationException("No searcher found for index " + indexName); + var searcher = index.GetSearcher(); var t = term.Escape().Value; if (useWildCards) @@ -329,13 +322,17 @@ namespace Umbraco.Web return content.Search(crit, searcher); } - public static IEnumerable Search(this IPublishedContent content, Examine.SearchCriteria.ISearchCriteria criteria, Examine.ISearcher searchProvider = null) + public static IEnumerable Search(this IPublishedContent content, Examine.SearchCriteria.ISearchCriteria criteria, ISearcher searchProvider = null) { - //TODO: we should pass in the IExamineManager? + //fixme: pass in the IExamineManager - var s = searchProvider ?? ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer); - - var results = s.Search(criteria); + if (searchProvider == null) + { + if (!ExamineManager.Instance.TryGetIndex(Constants.Examine.ExternalIndexer, out var index)) + throw new InvalidOperationException("No index found with name " + Constants.Examine.ExternalIndexer); + searchProvider = index.GetSearcher(); + } + var results = searchProvider.Search(criteria); return results.ToPublishedSearchResults(UmbracoContext.Current.ContentCache); } diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs index 8ad40887cc..ed42181234 100644 --- a/src/Umbraco.Web/PublishedContentQuery.cs +++ b/src/Umbraco.Web/PublishedContentQuery.cs @@ -229,17 +229,18 @@ namespace Umbraco.Web /// public IEnumerable Search(int skip, int take, out int totalRecords, string term, bool useWildCards = true, string indexName = null) { - //TODO: Can we inject IExamineManager? + //fixme: inject IExamineManager if (_query != null) return _query.Search(skip, take, out totalRecords, term, useWildCards, indexName); - var indexer = string.IsNullOrEmpty(indexName) - ? Examine.ExamineManager.Instance.GetIndex(Constants.Examine.ExternalIndexer) - : Examine.ExamineManager.Instance.GetIndex(indexName); + indexName = string.IsNullOrEmpty(indexName) + ? Constants.Examine.ExternalIndexer + : indexName; - if (indexer == null) throw new InvalidOperationException("No index found by name " + indexName); + if (!ExamineManager.Instance.TryGetIndex(indexName, out var index)) + throw new InvalidOperationException($"No index found by name {indexName}"); - var searcher = indexer.GetSearcher(); + var searcher = index.GetSearcher(); if (skip == 0 && take == 0) { @@ -248,24 +249,28 @@ namespace Umbraco.Web return results.ToPublishedSearchResults(_contentCache); } - var criteria = SearchAllFields(term, useWildCards, searcher, indexer); + var criteria = SearchAllFields(term, useWildCards, searcher, index); return Search(skip, take, out totalRecords, criteria, searcher); } /// - public IEnumerable Search(ISearchCriteria criteria, Examine.ISearcher searchProvider = null) + public IEnumerable Search(ISearchCriteria criteria, ISearcher searchProvider = null) { return Search(0, 0, out _, criteria, searchProvider); } /// - public IEnumerable Search(int skip, int take, out int totalRecords, ISearchCriteria criteria, Examine.ISearcher searchProvider = null) + public IEnumerable Search(int skip, int take, out int totalRecords, ISearchCriteria criteria, ISearcher searcher = null) { - if (_query != null) return _query.Search(skip, take, out totalRecords, criteria, searchProvider); + if (_query != null) return _query.Search(skip, take, out totalRecords, criteria, searcher); - //TODO: Can we inject IExamineManager? - - var searcher = searchProvider ?? Examine.ExamineManager.Instance.GetSearcher(Constants.Examine.ExternalIndexer); + //fixme: inject IExamineManager + if (searcher == null) + { + if (!ExamineManager.Instance.TryGetIndex(Constants.Examine.ExternalIndexer, out var index)) + throw new InvalidOperationException($"No index found by name {Constants.Examine.ExternalIndexer}"); + searcher = index.GetSearcher(); + } var results = skip == 0 && take == 0 ? searcher.Search(criteria) @@ -278,7 +283,7 @@ namespace Umbraco.Web /// /// Creates an ISearchCriteria for searching all fields in a . /// - private ISearchCriteria SearchAllFields(string searchText, bool useWildcards, Examine.ISearcher searcher, Examine.IIndex indexer) + private ISearchCriteria SearchAllFields(string searchText, bool useWildcards, ISearcher searcher, IIndex indexer) { var sc = searcher.CreateCriteria(); diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 1af24db636..1f6210b6a1 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -129,6 +129,8 @@ namespace Umbraco.Web.Runtime composition.Container.RegisterCollectionBuilder() .Add(() => typeLoader.GetTypes()); // fixme which searchable trees?! + composition.Container.Register(new PerRequestLifeTime()); + composition.Container.RegisterCollectionBuilder() .Add(() => typeLoader.GetTypes()); diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index d6d5a5f444..fa22da5ae1 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -125,13 +125,11 @@ namespace Umbraco.Web.Search //create the indexes and register them with the manager foreach(var index in indexCreator.Create()) - { _examineManager.AddIndex(index); - } profilingLogger.Logger.Debug("Examine shutdown registered with MainDom"); - var registeredIndexers = examineManager.IndexProviders.Values.OfType().Count(x => x.EnableDefaultEventHandler); + var registeredIndexers = examineManager.Indexes.OfType().Count(x => x.EnableDefaultEventHandler); profilingLogger.Logger.Info("Adding examine event handlers for {RegisteredIndexers} index providers.", registeredIndexers); @@ -205,7 +203,7 @@ namespace Umbraco.Web.Search _isConfigured = true; - foreach (var luceneIndexer in examineManager.IndexProviders.Values.OfType()) + foreach (var luceneIndexer in examineManager.Indexes.OfType()) { //We now need to disable waiting for indexing for Examine so that the appdomain is shutdown immediately and doesn't wait for pending //indexing operations. We used to wait for indexing operations to complete but this can cause more problems than that is worth because @@ -377,7 +375,7 @@ namespace Umbraco.Web.Search //Delete all content of this content/media/member type that is in any content indexer by looking up matched examine docs foreach (var id in ci.Value.removedIds) { - foreach (var index in _examineManager.IndexProviders.Values.OfType()) + foreach (var index in _examineManager.Indexes.OfType()) { var searcher = index.GetSearcher(); @@ -707,7 +705,7 @@ namespace Umbraco.Web.Search { var valueSet = examineComponent._contentValueSetBuilder.GetValueSets(content).ToList(); - foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + foreach (var index in examineComponent._examineManager.Indexes.OfType() // only for the specified indexers .Where(x => supportUnpublished.HasValue == false || supportUnpublished.Value == x.SupportSoftDelete) .Where(x => x.EnableDefaultEventHandler)) @@ -739,7 +737,7 @@ namespace Umbraco.Web.Search { var valueSet = examineComponent._mediaValueSetBuilder.GetValueSets(media).ToList(); - foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + foreach (var index in examineComponent._examineManager.Indexes.OfType() // index this item for all indexers if the media is not trashed, otherwise if the item is trashed // then only index this for indexers supporting unpublished media .Where(x => isPublished || (x.SupportSoftDelete)) @@ -769,7 +767,7 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, IMember member) { var valueSet = examineComponent._memberValueSetBuilder.GetValueSets(member).ToList(); - foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + foreach (var index in examineComponent._examineManager.Indexes.OfType() //ensure that only the providers are flagged to listen execute .Where(x => x.EnableDefaultEventHandler)) { @@ -799,7 +797,7 @@ namespace Umbraco.Web.Search public static void Execute(ExamineComponent examineComponent, int id, bool keepIfUnpublished) { var strId = id.ToString(CultureInfo.InvariantCulture); - foreach (var index in examineComponent._examineManager.IndexProviders.Values.OfType() + foreach (var index in examineComponent._examineManager.Indexes.OfType() // if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content, // otherwise if keepIfUnpublished == false then remove from all indexes .Where(x => keepIfUnpublished == false || x.SupportSoftDelete == false) diff --git a/src/Umbraco.Web/Search/ExamineSearcherModel.cs b/src/Umbraco.Web/Search/ExamineSearcherModel.cs index 3ad932efa2..8b9badfcfa 100644 --- a/src/Umbraco.Web/Search/ExamineSearcherModel.cs +++ b/src/Umbraco.Web/Search/ExamineSearcherModel.cs @@ -11,26 +11,11 @@ namespace Umbraco.Web.Search { public ExamineSearcherModel() { - ProviderProperties = new Dictionary(); } - /// - /// If the index is not healthy this represents the index error state - /// - [DataMember(Name = "error")] - public string Error { get; set; } - - /// - /// If the index can be open/read - /// - [DataMember(Name = "isHealthy")] - public bool IsHealthy { get; set; } - [DataMember(Name = "name")] public string Name { get; set; } - [DataMember(Name = "providerProperties")] - public IDictionary ProviderProperties { get; private set; } } } diff --git a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs index 53bac7769c..dd64d9b142 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs @@ -110,12 +110,12 @@ namespace Umbraco.Web.Search return luceneDir; } - public virtual IValueSetValidator GetContentValueSetValidator() + public virtual IContentValueSetValidator GetContentValueSetValidator() { return new ContentValueSetValidator(true, true, PublicAccessService); } - public virtual IValueSetValidator GetPublishedContentValueSetValidator() + public virtual IContentValueSetValidator GetPublishedContentValueSetValidator() { return new ContentValueSetValidator(false, false, PublicAccessService); } diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index 6290307926..9b98d02a10 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -11,15 +11,27 @@ using Umbraco.Core.Composing; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Trees; namespace Umbraco.Web.Search { - internal class UmbracoTreeSearcher + /// + /// Used for internal Umbraco implementations of + /// + public class UmbracoTreeSearcher { + private readonly IExamineManager _examineManager; + private readonly UmbracoHelper _umbracoHelper; + + public UmbracoTreeSearcher(IExamineManager examineManager, UmbracoHelper umbracoHelper) + { + _examineManager = examineManager ?? throw new ArgumentNullException(nameof(examineManager)); + _umbracoHelper = umbracoHelper ?? throw new ArgumentNullException(nameof(umbracoHelper)); + } + /// /// Searches for results based on the entity type /// - /// /// /// /// @@ -30,7 +42,6 @@ namespace Umbraco.Web.Search /// /// public IEnumerable ExamineSearch( - UmbracoHelper umbracoHelper, string query, UmbracoEntityTypes entityType, int pageSize, @@ -39,16 +50,16 @@ namespace Umbraco.Web.Search var sb = new StringBuilder(); string type; - var indexer = Constants.Examine.InternalIndexer; + var indexName = Constants.Examine.InternalIndexer; var fields = new[] { "id", "__NodeId" }; - var umbracoContext = umbracoHelper.UmbracoContext; + var umbracoContext = _umbracoHelper.UmbracoContext; //TODO: WE should really just allow passing in a lucene raw query switch (entityType) { case UmbracoEntityTypes.Member: - indexer = Constants.Examine.InternalMemberIndexer; + indexName = Constants.Examine.InternalMemberIndexer; type = "member"; fields = new[] { "id", "__NodeId", "email", "loginName" }; if (searchFrom != null && searchFrom != Constants.Conventions.MemberTypes.AllMembersListId && searchFrom.Trim() != "-1") @@ -72,7 +83,10 @@ namespace Umbraco.Web.Search throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType); } - var internalSearcher = ExamineManager.Instance.GetSearcher(indexer); + if (!_examineManager.TryGetIndex(indexName, out var index)) + throw new InvalidOperationException("No index found by name " + indexName); + + var internalSearcher = index.GetSearcher(); //build a lucene query: // the __nodeName will be boosted 10x without wildcards @@ -197,7 +211,7 @@ namespace Umbraco.Web.Search case UmbracoEntityTypes.Media: return MediaFromSearchResults(pagedResult); case UmbracoEntityTypes.Document: - return ContentFromSearchResults(umbracoHelper, pagedResult); + return ContentFromSearchResults(pagedResult); default: throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType); } @@ -205,15 +219,13 @@ namespace Umbraco.Web.Search private void AppendPath(StringBuilder sb, UmbracoObjectTypes objectType, int[] startNodeIds, string searchFrom, IEntityService entityService) { - if (sb == null) throw new ArgumentNullException("sb"); - if (entityService == null) throw new ArgumentNullException("entityService"); + if (sb == null) throw new ArgumentNullException(nameof(sb)); + if (entityService == null) throw new ArgumentNullException(nameof(entityService)); - Udi udi; - Udi.TryParse(searchFrom, true, out udi); + Udi.TryParse(searchFrom, true, out var udi); searchFrom = udi == null ? searchFrom : entityService.GetId(udi).Result.ToString(); - int searchFromId; - var entityPath = int.TryParse(searchFrom, out searchFromId) && searchFromId > 0 + var entityPath = int.TryParse(searchFrom, out var searchFromId) && searchFromId > 0 ? entityService.GetAllPaths(objectType, searchFromId).FirstOrDefault() : null; if (entityPath != null) @@ -284,8 +296,7 @@ namespace Umbraco.Web.Search } if (searchResult.Values.ContainsKey("__key") && searchResult.Values["__key"] != null) { - Guid key; - if (Guid.TryParse(searchResult.Values["__key"], out key)) + if (Guid.TryParse(searchResult.Values["__key"], out var key)) { m.Key = key; } @@ -317,10 +328,9 @@ namespace Umbraco.Web.Search /// /// Returns a collection of entities for content based on search results /// - /// /// /// - private IEnumerable ContentFromSearchResults(UmbracoHelper umbracoHelper, IEnumerable results) + private IEnumerable ContentFromSearchResults(IEnumerable results) { var mapped = Mapper.Map>(results).ToArray(); //add additional data @@ -329,7 +339,7 @@ namespace Umbraco.Web.Search var intId = m.Id.TryConvertTo(); if (intId.Success) { - m.AdditionalData["Url"] = umbracoHelper.Url(intId.Result); + m.AdditionalData["Url"] = _umbracoHelper.Url(intId.Result); } } return mapped; diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index c0b2126d5e..35c335f06d 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -35,7 +35,12 @@ namespace Umbraco.Web.Trees [SearchableTree("searchResultFormatter", "configureContentResult")] public class ContentTreeController : ContentTreeControllerBase, ISearchableTree { - private readonly UmbracoTreeSearcher _treeSearcher = new UmbracoTreeSearcher(); + private readonly UmbracoTreeSearcher _treeSearcher; + + public ContentTreeController(UmbracoTreeSearcher treeSearcher) + { + _treeSearcher = treeSearcher; + } protected override int RecycleBinId => Constants.System.RecycleBinContent; @@ -316,7 +321,7 @@ namespace Umbraco.Web.Trees public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) { - return _treeSearcher.ExamineSearch(Umbraco, query, UmbracoEntityTypes.Document, pageSize, pageIndex, out totalFound, searchFrom); + return _treeSearcher.ExamineSearch(query, UmbracoEntityTypes.Document, pageSize, pageIndex, out totalFound, searchFrom); } } } diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index 20804d4cf8..4efddfb4b3 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -35,7 +35,12 @@ namespace Umbraco.Web.Trees [SearchableTree("searchResultFormatter", "configureMediaResult")] public class MediaTreeController : ContentTreeControllerBase, ISearchableTree { - private readonly UmbracoTreeSearcher _treeSearcher = new UmbracoTreeSearcher(); + private readonly UmbracoTreeSearcher _treeSearcher; + + public MediaTreeController(UmbracoTreeSearcher treeSearcher) + { + _treeSearcher = treeSearcher; + } protected override int RecycleBinId => Constants.System.RecycleBinMedia; @@ -161,7 +166,7 @@ namespace Umbraco.Web.Trees public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) { - return _treeSearcher.ExamineSearch(Umbraco, query, UmbracoEntityTypes.Media, pageSize, pageIndex, out totalFound, searchFrom); + return _treeSearcher.ExamineSearch(query, UmbracoEntityTypes.Media, pageSize, pageIndex, out totalFound, searchFrom); } internal override IEnumerable GetChildrenFromEntityService(int entityId) diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index 68819351c0..3317bfbdf9 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -33,13 +33,14 @@ namespace Umbraco.Web.Trees [SearchableTree("searchResultFormatter", "configureMemberResult")] public class MemberTreeController : TreeController, ISearchableTree { - public MemberTreeController() + public MemberTreeController(UmbracoTreeSearcher treeSearcher) { + _treeSearcher = treeSearcher; _provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); _isUmbracoProvider = _provider.IsUmbracoMembershipProvider(); } - private readonly UmbracoTreeSearcher _treeSearcher = new UmbracoTreeSearcher(); + private readonly UmbracoTreeSearcher _treeSearcher; private readonly MembershipProvider _provider; private readonly bool _isUmbracoProvider; @@ -193,7 +194,7 @@ namespace Umbraco.Web.Trees public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) { - return _treeSearcher.ExamineSearch(Umbraco, query, UmbracoEntityTypes.Member, pageSize, pageIndex, out totalFound, searchFrom); + return _treeSearcher.ExamineSearch(query, UmbracoEntityTypes.Member, pageSize, pageIndex, out totalFound, searchFrom); } } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 19c1e59bd1..bca87b02ac 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -62,7 +62,7 @@ - + 2.6.2.25 From 69c939c44477b9ac829a41c28c67bbe194e3b535 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 3 Dec 2018 22:19:35 +1100 Subject: [PATCH 31/44] Some cleanup and renames --- src/Umbraco.Core/PropertyEditors/DataEditor.cs | 2 +- ...ValueIndexer.cs => DefaultPropertyIndexValues.cs} | 2 +- src/Umbraco.Core/PropertyEditors/IDataEditor.cs | 2 +- .../{IValueIndexer.cs => IPropertyIndexValues.cs} | 3 +-- src/Umbraco.Core/Umbraco.Core.csproj | 4 ++-- src/Umbraco.Examine/BaseValueSetBuilder.cs | 2 +- src/Umbraco.Examine/IUmbracoContentIndexer.cs | 12 ------------ src/Umbraco.Examine/IUmbracoMediaIndexer.cs | 12 ------------ src/Umbraco.Examine/MemberIndexPopulator.cs | 2 -- src/Umbraco.Examine/Umbraco.Examine.csproj | 2 -- src/Umbraco.Examine/UmbracoContentIndexer.cs | 2 +- .../PropertyEditors/GridPropertyEditor.cs | 2 +- ...ridValueIndexer.cs => GridPropertyIndexValues.cs} | 2 +- .../PropertyEditors/RichTextPropertyEditor.cs | 8 ++++---- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- 15 files changed, 15 insertions(+), 44 deletions(-) rename src/Umbraco.Core/PropertyEditors/{DefaultValueIndexer.cs => DefaultPropertyIndexValues.cs} (88%) rename src/Umbraco.Core/PropertyEditors/{IValueIndexer.cs => IPropertyIndexValues.cs} (74%) delete mode 100644 src/Umbraco.Examine/IUmbracoContentIndexer.cs delete mode 100644 src/Umbraco.Examine/IUmbracoMediaIndexer.cs rename src/Umbraco.Web/PropertyEditors/{GridValueIndexer.cs => GridPropertyIndexValues.cs} (98%) diff --git a/src/Umbraco.Core/PropertyEditors/DataEditor.cs b/src/Umbraco.Core/PropertyEditors/DataEditor.cs index f3a9c7f4c6..3e9d9be720 100644 --- a/src/Umbraco.Core/PropertyEditors/DataEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataEditor.cs @@ -156,7 +156,7 @@ namespace Umbraco.Core.PropertyEditors /// /// Returns the value indexer for this editor /// - public virtual IValueIndexer ValueIndexer => new DefaultValueIndexer(); + public virtual IPropertyIndexValues PropertyIndexValues => new DefaultPropertyIndexValues(); /// /// Creates a value editor instance. diff --git a/src/Umbraco.Core/PropertyEditors/DefaultValueIndexer.cs b/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValues.cs similarity index 88% rename from src/Umbraco.Core/PropertyEditors/DefaultValueIndexer.cs rename to src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValues.cs index 072847896f..bf39adf93b 100644 --- a/src/Umbraco.Core/PropertyEditors/DefaultValueIndexer.cs +++ b/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValues.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.PropertyEditors /// /// Returns a single field to index containing the property value /// - public class DefaultValueIndexer : IValueIndexer + public class DefaultPropertyIndexValues : IPropertyIndexValues { public IEnumerable> GetIndexValues(Property property, string culture, string segment) { diff --git a/src/Umbraco.Core/PropertyEditors/IDataEditor.cs b/src/Umbraco.Core/PropertyEditors/IDataEditor.cs index f967f6f269..2b5d6e8bf3 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataEditor.cs @@ -67,6 +67,6 @@ namespace Umbraco.Core.PropertyEditors /// IConfigurationEditor GetConfigurationEditor(); - IValueIndexer ValueIndexer { get; } + IPropertyIndexValues PropertyIndexValues { get; } } } diff --git a/src/Umbraco.Core/PropertyEditors/IValueIndexer.cs b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValues.cs similarity index 74% rename from src/Umbraco.Core/PropertyEditors/IValueIndexer.cs rename to src/Umbraco.Core/PropertyEditors/IPropertyIndexValues.cs index eb5538b928..5ebd0a29b7 100644 --- a/src/Umbraco.Core/PropertyEditors/IValueIndexer.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValues.cs @@ -6,9 +6,8 @@ namespace Umbraco.Core.PropertyEditors /// /// Returns indexable data for the property /// - public interface IValueIndexer + public interface IPropertyIndexValues { - //fixme: What about segments and whether we want the published value? IEnumerable> GetIndexValues(Property property, string culture, string segment); } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 7e99042562..e22841ccd6 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -440,7 +440,7 @@ - + @@ -449,7 +449,7 @@ - + diff --git a/src/Umbraco.Examine/BaseValueSetBuilder.cs b/src/Umbraco.Examine/BaseValueSetBuilder.cs index 9e36f43793..6191297aff 100644 --- a/src/Umbraco.Examine/BaseValueSetBuilder.cs +++ b/src/Umbraco.Examine/BaseValueSetBuilder.cs @@ -27,7 +27,7 @@ namespace Umbraco.Examine var editor = _propertyEditors[property.PropertyType.PropertyEditorAlias]; if (editor == null) return; - var indexVals = editor.ValueIndexer.GetIndexValues(property, culture, segment); + var indexVals = editor.PropertyIndexValues.GetIndexValues(property, culture, segment); foreach (var keyVal in indexVals) { if (keyVal.Key.IsNullOrWhiteSpace()) continue; diff --git a/src/Umbraco.Examine/IUmbracoContentIndexer.cs b/src/Umbraco.Examine/IUmbracoContentIndexer.cs deleted file mode 100644 index 09520af1f8..0000000000 --- a/src/Umbraco.Examine/IUmbracoContentIndexer.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Examine; - -namespace Umbraco.Examine -{ - - /// - /// A Marker interface for defining an Umbraco content indexer - /// - public interface IUmbracoContentIndexer : IIndex - { - } -} diff --git a/src/Umbraco.Examine/IUmbracoMediaIndexer.cs b/src/Umbraco.Examine/IUmbracoMediaIndexer.cs deleted file mode 100644 index c31ab560ff..0000000000 --- a/src/Umbraco.Examine/IUmbracoMediaIndexer.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Examine; - -namespace Umbraco.Examine -{ - //TODO: Rethink this, need a better way of rebuilding - /// - /// A Marker interface for defining an Umbraco media indexer - /// - public interface IUmbracoMediaIndexer : IIndex - { - } -} diff --git a/src/Umbraco.Examine/MemberIndexPopulator.cs b/src/Umbraco.Examine/MemberIndexPopulator.cs index b94100f6e3..cf04861372 100644 --- a/src/Umbraco.Examine/MemberIndexPopulator.cs +++ b/src/Umbraco.Examine/MemberIndexPopulator.cs @@ -26,8 +26,6 @@ namespace Umbraco.Examine IMember[] members; - //TODO: Add validators for member indexers for ConfigIndexCriteria.IncludeItemTypes - //no node types specified, do all members do { diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index d2336eedf4..e3434cc9aa 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -69,9 +69,7 @@ - - diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index 1387357463..f37429a5c7 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -21,7 +21,7 @@ namespace Umbraco.Examine /// /// An indexer for Umbraco content and media /// - public class UmbracoContentIndexer : UmbracoExamineIndexer, IUmbracoContentIndexer, IUmbracoMediaIndexer + public class UmbracoContentIndexer : UmbracoExamineIndexer { public const string VariesByCultureFieldName = SpecialFieldPrefix + "VariesByCulture"; protected ILocalizationService LanguageService { get; } diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index 1002261ca4..88a6cfb33d 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.PropertyEditors : base(logger) { } - public override IValueIndexer ValueIndexer => new GridValueIndexer(); + public override IPropertyIndexValues PropertyIndexValues => new GridPropertyIndexValues(); /// /// Overridden to ensure that the value is validated diff --git a/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValues.cs similarity index 98% rename from src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs rename to src/Umbraco.Web/PropertyEditors/GridPropertyIndexValues.cs index d2e01d0c73..4c03fe2ccd 100644 --- a/src/Umbraco.Web/PropertyEditors/GridValueIndexer.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValues.cs @@ -16,7 +16,7 @@ namespace Umbraco.Web.PropertyEditors /// /// Parses the grid value into indexable values /// - public class GridValueIndexer : IValueIndexer + public class GridPropertyIndexValues : IPropertyIndexValues { public IEnumerable> GetIndexValues(Property property, string culture, string segment) { diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index eac2a2066c..fa490433ba 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -32,7 +32,7 @@ namespace Umbraco.Web.PropertyEditors protected override IConfigurationEditor CreateConfigurationEditor() => new RichTextConfigurationEditor(); - public override IValueIndexer ValueIndexer => new RichTextValueIndexer(); + public override IPropertyIndexValues PropertyIndexValues => new RichTextPropertyIndexValues(); /// /// A custom value editor to ensure that macro syntax is parsed when being persisted and formatted correctly for display in the editor @@ -93,7 +93,7 @@ namespace Umbraco.Web.PropertyEditors } } - internal class RichTextValueIndexer : IValueIndexer + internal class RichTextPropertyIndexValues : IPropertyIndexValues { public IEnumerable> GetIndexValues(Property property, string culture, string segment) { @@ -102,9 +102,9 @@ namespace Umbraco.Web.PropertyEditors if (!(val is string strVal)) yield break; //index the stripped html values - yield return new KeyValuePair(property.Alias, new[] { strVal.StripHtml() }); + yield return new KeyValuePair(property.Alias, new object[] { strVal.StripHtml() }); //store the raw value - yield return new KeyValuePair($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new[] { strVal }); + yield return new KeyValuePair($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new object[] { strVal }); } } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index bca87b02ac..cda21b029c 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -159,7 +159,7 @@ - + From fce5b29b1c20a8a53f360e96d4710bdad7d083e6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 3 Dec 2018 23:15:18 +1100 Subject: [PATCH 32/44] index rebuilding updates --- src/Umbraco.Examine/IIndexPopulator.cs | 2 + src/Umbraco.Examine/IndexRebuilder.cs | 33 +++++++++--- src/Umbraco.Examine/UmbracoContentIndexer.cs | 52 +++++++++++-------- .../Editors/ExamineManagementController.cs | 20 +++---- 4 files changed, 70 insertions(+), 37 deletions(-) diff --git a/src/Umbraco.Examine/IIndexPopulator.cs b/src/Umbraco.Examine/IIndexPopulator.cs index 9e73de7260..aeb3514578 100644 --- a/src/Umbraco.Examine/IIndexPopulator.cs +++ b/src/Umbraco.Examine/IIndexPopulator.cs @@ -3,6 +3,8 @@ using Examine; namespace Umbraco.Examine { + + public interface IIndexPopulator { bool IsRegistered(string indexName); diff --git a/src/Umbraco.Examine/IndexRebuilder.cs b/src/Umbraco.Examine/IndexRebuilder.cs index 525b57cc7d..39b29c5a2b 100644 --- a/src/Umbraco.Examine/IndexRebuilder.cs +++ b/src/Umbraco.Examine/IndexRebuilder.cs @@ -1,5 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Examine; namespace Umbraco.Examine @@ -18,19 +20,36 @@ namespace Umbraco.Examine ExamineManager = examineManager; } + public bool CanRebuild(string indexName) + { + return _populators.Any(x => x.IsRegistered(indexName)); + } + + public void RebuildIndex(string indexName) + { + if (!ExamineManager.TryGetIndex(indexName, out var index)) + throw new InvalidOperationException($"No index found with name {indexName}"); + index.CreateIndex(); // clear the index + foreach (var populator in _populators) + { + populator.Populate(index); + } + } + public void RebuildIndexes(bool onlyEmptyIndexes) { var indexes = (onlyEmptyIndexes ? ExamineManager.Indexes.Where(x => !x.IndexExists()) : ExamineManager.Indexes).ToArray(); - foreach(var index in indexes) - index.CreateIndex(); // clear the index - - foreach (var populator in _populators) + foreach (var index in indexes) { - populator.Populate(indexes); - } + index.CreateIndex(); // clear the index + } + + //run the populators in parallel against all indexes + Parallel.ForEach(_populators, populator => populator.Populate(indexes)); } + } } diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index f37429a5c7..feea0a4efd 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -136,33 +136,43 @@ namespace Umbraco.Examine /// protected override void PerformIndexItems(IEnumerable values, Action onComplete) { - var valid = true; - - // ReSharper disable once PossibleMultipleEnumeration - foreach (var v in values) + //We don't want to re-enumerate this list, but we need to split it into 2x enumerables: invalid and valid items. + // The Invalid items will be deleted, these are items that have invalid paths (i.e. moved to the recycle bin, etc...) + // Then we'll index the Value group all together. + // We return 0 or 1 here so we can order the results and do the invalid first and then the valid. + var invalidOrValid = values.GroupBy(v => { - if (v.Values.TryGetValue("path", out var paths) && paths.Count > 0 && paths[0] != null) - { - //we know this is an IContentValueSetValidator - var validator = (IContentValueSetValidator) ValueSetValidator; - var path = paths[0].ToString(); - - if (!validator.ValidatePath(path, v.Category) + if (!v.Values.TryGetValue("path", out var paths) || paths.Count <= 0 || paths[0] == null) + return 0; + + //we know this is an IContentValueSetValidator + var validator = (IContentValueSetValidator)ValueSetValidator; + var path = paths[0].ToString(); + + return (!validator.ValidatePath(path, v.Category) || !validator.ValidateRecycleBin(path, v.Category) || !validator.ValidateProtectedContent(path, v.Category)) + ? 0 + : 1; + }); + + foreach (var group in invalidOrValid.OrderBy(x => x.Key)) + { + if (group.Key == 0) + { + //these are the invalid items so we'll delete them + //since the path is not valid we need to delete this item in case it exists in the index already and has now + //been moved to an invalid parent. + foreach (var i in group) { - //since the path is not valid we need to delete this item in case it exists in the index already and has now - //been moved to an invalid parent. - PerformDeleteFromIndex(v.Id, x => { /*noop*/ }); - valid = false; + PerformDeleteFromIndex(i.Id, x => { /*noop*/ }); } } - } - - if (valid) - { - // ReSharper disable once PossibleMultipleEnumeration - base.PerformIndexItems(values, onComplete); + else + { + //these are the valid ones, so just index them all at once + base.PerformIndexItems(group, onComplete); + } } } diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs index c166162930..ab60f64e49 100644 --- a/src/Umbraco.Web/Editors/ExamineManagementController.cs +++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs @@ -25,16 +25,17 @@ namespace Umbraco.Web.Editors private readonly IExamineManager _examineManager; private readonly ILogger _logger; private readonly IRuntimeCacheProvider _runtimeCacheProvider; - private readonly IEnumerable _populators; + private readonly IndexRebuilder _indexRebuilder; + public ExamineManagementController(IExamineManager examineManager, ILogger logger, IRuntimeCacheProvider runtimeCacheProvider, - IEnumerable populators) + IndexRebuilder indexRebuilder) { _examineManager = examineManager; _logger = logger; _runtimeCacheProvider = runtimeCacheProvider; - _populators = populators; + _indexRebuilder = indexRebuilder; } /// @@ -151,9 +152,11 @@ namespace Umbraco.Web.Editors //clear and replace index.CreateIndex(); - //populate it - foreach (var populator in _populators.Where(x => x.IsRegistered(indexName))) - populator.Populate(index); + _indexRebuilder.RebuildIndex(indexName); + + ////populate it + //foreach (var populator in _populators.Where(x => x.IsRegistered(indexName))) + // populator.Populate(index); return Request.CreateResponse(HttpStatusCode.OK); } @@ -195,7 +198,7 @@ namespace Umbraco.Web.Editors Name = indexName, HealthStatus = isHealth.Success ? (isHealth.Result ?? "Healthy") : (isHealth.Result ?? "Unhealthy"), ProviderProperties = properties, - CanRebuild = _populators.Any(x => x.IsRegistered(indexName)) + CanRebuild = _indexRebuilder.CanRebuild(indexName) }; @@ -239,7 +242,7 @@ namespace Umbraco.Web.Editors private HttpResponseMessage ValidatePopulator(string indexName) { - if (_populators.Any(x => x.IsRegistered(indexName))) + if (_indexRebuilder.CanRebuild(indexName)) return Request.CreateResponse(HttpStatusCode.OK); var response = Request.CreateResponse(HttpStatusCode.BadRequest); @@ -264,7 +267,6 @@ namespace Umbraco.Web.Editors return response; } - //static listener so it's not GC'd private void Indexer_IndexOperationComplete(object sender, EventArgs e) { var indexer = (LuceneIndex) sender; From 56d1a317c04205ea822fc366526e8d8978af6f1e Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 4 Dec 2018 14:25:37 +1100 Subject: [PATCH 33/44] Gets searching working in the dashboard, updates to latest examine --- src/Umbraco.Core/Services/IContentService.cs | 4 +- src/Umbraco.Examine/Umbraco.Examine.csproj | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../UmbracoExamine/SearchTests.cs | 2 +- .../settings/examinemanagement.controller.js | 59 ++++++---- .../dashboard/settings/examinemanagement.html | 74 +++++++----- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- src/Umbraco.Web/Editors/EntityController.cs | 2 +- .../Editors/ExamineManagementController.cs | 111 ++++++++++-------- src/Umbraco.Web/ExamineExtensions.cs | 2 +- src/Umbraco.Web/IPublishedContentQuery.cs | 4 +- .../Models/ContentEditing/SearchResult.cs | 21 ++++ ...rchResultItem.cs => SearchResultEntity.cs} | 2 +- .../Models/ContentEditing/SearchResults.cs | 22 ++++ .../Models/ContentEditing/TreeSearchResult.cs | 2 +- .../Models/Mapping/EntityMapperProfile.cs | 12 +- .../XmlPublishedCache/PublishedMediaCache.cs | 2 +- src/Umbraco.Web/PublishedContentQuery.cs | 4 +- src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 19 +-- .../Trees/ContentTreeController.cs | 2 +- .../Trees/ContentTypeTreeController.cs | 4 +- .../Trees/DataTypeTreeController.cs | 4 +- src/Umbraco.Web/Trees/ISearchableTree.cs | 2 +- src/Umbraco.Web/Trees/MediaTreeController.cs | 2 +- .../Trees/MediaTypeTreeController.cs | 4 +- src/Umbraco.Web/Trees/MemberTreeController.cs | 2 +- .../Trees/TemplatesTreeController.cs | 4 +- src/Umbraco.Web/Umbraco.Web.csproj | 6 +- src/Umbraco.Web/UmbracoHelper.cs | 4 +- 29 files changed, 235 insertions(+), 147 deletions(-) create mode 100644 src/Umbraco.Web/Models/ContentEditing/SearchResult.cs rename src/Umbraco.Web/Models/ContentEditing/{SearchResultItem.cs => SearchResultEntity.cs} (86%) create mode 100644 src/Umbraco.Web/Models/ContentEditing/SearchResults.cs diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 22138a5e8c..a9a0412a18 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -176,10 +176,8 @@ namespace Umbraco.Core.Services /// The page number. /// The page size. /// Total number of documents. - /// A field to order by. - /// The ordering direction. - /// A flag indicating whether the ordering field is a system field. /// Query filter. + /// IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null); diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index e3434cc9aa..5c18da00ef 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 6ddf99822b..3fd6393a3c 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/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index 648a5d7c86..85cb14e1a9 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -86,7 +86,7 @@ namespace Umbraco.Tests.UmbracoExamine } } - private bool IsSortedByNumber(IEnumerable results) + private bool IsSortedByNumber(IEnumerable results) { var currentSort = 0; foreach (var searchResult in results) diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js index 5b26f198b0..3d1773def5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js @@ -7,19 +7,33 @@ function ExamineManagementController($scope, umbRequestHelper, $http, $q, $timeo vm.loading = true; vm.viewState = "list"; vm.selectedIndex = null; + vm.selectedSearcher = null; + vm.searchResults = null; vm.showIndexInfo = showIndexInfo; vm.showSearcherInfo = showSearcherInfo; vm.search = search; vm.toggle = toggle; vm.rebuildIndex = rebuildIndex; - vm.closeSearch = closeSearch; vm.setViewState = setViewState; - + vm.nextSearchResultPage = nextSearchResultPage; + vm.prevSearchResultPage = prevSearchResultPage; + vm.goToPageSearchResultPage = goToPageSearchResultPage; vm.infoOverlay = null; + function nextSearchResultPage(pageNumber) { + search(vm.selectedIndex ? vm.selectedIndex : vm.selectedSearcher, null, pageNumber); + } + function prevSearchResultPage(pageNumber) { + search(vm.selectedIndex ? vm.selectedIndex : vm.selectedSearcher, null, pageNumber); + } + function goToPageSearchResultPage(pageNumber) { + search(vm.selectedIndex ? vm.selectedIndex : vm.selectedSearcher, null, pageNumber); + } + function setViewState(state) { + vm.searchResults = null; vm.viewState = state; } @@ -49,7 +63,7 @@ function ExamineManagementController($scope, umbRequestHelper, $http, $q, $timeo } index.isProcessing = false; } else { - $timeout(function() { + $timeout(() => { //don't continue if we've tried 100 times if (index.processingAttempts < 100) { checkProcessing(index, checkActionName); @@ -65,23 +79,34 @@ function ExamineManagementController($scope, umbRequestHelper, $http, $q, $timeo }); } - function search(searcher, e) { + function search(searcher, e, pageNumber) { + + //deal with accepting pressing the enter key if (e && e.keyCode !== 13) { return; } + if (!searcher) { + throw "searcher parameter is required"; + } + + searcher.isProcessing = true; + umbRequestHelper.resourcePromise( $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearchResults", { searcherName: searcher.name, - query: encodeURIComponent(searcher.searchText), - queryType: searcher.searchType + query: encodeURIComponent(vm.searchText), + pageIndex: pageNumber ? (pageNumber - 1) : 0 })), 'Failed to search') - .then(function(searchResults) { - searcher.isSearching = true; - searcher.searchResults = searchResults; + .then(searchResults => { + searcher.isProcessing = false; + vm.searchResults = searchResults + vm.searchResults.pageNumber = pageNumber ? pageNumber : 1; + //20 is page size + vm.searchResults.totalPages = Math.ceil(vm.searchResults.totalRecords / 20); }); } @@ -111,19 +136,12 @@ function ExamineManagementController($scope, umbRequestHelper, $http, $q, $timeo //rebuilding has started, nothing is returned accept a 200 status code. //lets poll to see if it is done. - $timeout(function() { - checkProcessing(index, "PostCheckRebuildIndex"); - }, - 1000); + $timeout(() => { checkProcessing(index, "PostCheckRebuildIndex"), 1000 }); }); } } - function closeSearch(searcher) { - searcher.isSearching = true; - } - function init() { //go get the data @@ -142,17 +160,14 @@ function ExamineManagementController($scope, umbRequestHelper, $http, $q, $timeo umbRequestHelper.resourcePromise( $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearcherDetails")), 'Failed to retrieve searcher details') - .then(function (data) { + .then(data => { vm.searcherDetails = data; for (var s in vm.searcherDetails) { vm.searcherDetails[s].searchType = "text"; } }) ]) - .then(function () { - //all init loading is complete - vm.loading = false; - }); + .then(() => { vm.loading = false }); } init(); diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html index ac8e990ef6..4174fc5642 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html @@ -8,11 +8,11 @@ -
    +
    -
    +
    @@ -109,6 +109,8 @@
    + +
    @@ -138,6 +140,8 @@
    + +
    @@ -163,6 +167,8 @@
    + +
    @@ -178,43 +184,57 @@
    - +
    +
    + + + + + + + + + + + + + + + + +
    ScoreIdName
    {{result.score}}{{result.id}} + {{result.values['nodeName']}}  + ({{result.fieldCount}} fields) +
    + +
    + + +
    + +
    +
    diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 8278dac171..13db7ce7cb 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,7 +88,7 @@ - + diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index 31109485f6..4d7a21ffef 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -590,7 +590,7 @@ namespace Umbraco.Web.Editors /// /// /// - private IEnumerable ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null) + private IEnumerable ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null) { return _treeSearcher.ExamineSearch(query, entityType, 200, 0, out _, searchFrom); } diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs index ab60f64e49..f624f2cee3 100644 --- a/src/Umbraco.Web/Editors/ExamineManagementController.cs +++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs @@ -9,13 +9,18 @@ using System.Web.Http; using Examine; using Examine.LuceneEngine; using Examine.LuceneEngine.Providers; +using Lucene.Net.Analysis; +using Lucene.Net.QueryParsers; using Umbraco.Core; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Logging; using Umbraco.Examine; +using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using Umbraco.Web.Search; +using SearchResult = Umbraco.Web.Models.ContentEditing.SearchResult; +using Version = Lucene.Net.Util.Version; namespace Umbraco.Web.Editors { @@ -54,40 +59,59 @@ namespace Umbraco.Web.Editors public IEnumerable GetSearcherDetails() { var model = new List( - _examineManager.RegisteredSearchers.Select(searcher => new ExamineSearcherModel{Name = searcher.Name}) + _examineManager.RegisteredSearchers.Select(searcher => new ExamineSearcherModel { Name = searcher.Name }) .OrderBy(x => x.Name.TrimEnd("Searcher"))); //order by name , but strip the "Searcher" from the end if it exists return model; } - public ISearchResults GetSearchResults(string searcherName, string query, string queryType) + public SearchResults GetSearchResults(string searcherName, string query, int pageIndex = 0, int pageSize = 20) { - if (queryType == null) - { - throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); - } - if (query.IsNullOrWhiteSpace()) + return SearchResults.Empty(); + + var msg = ValidateSearcher(searcherName, out var searcher); + if (!msg.IsSuccessStatusCode) + throw new HttpResponseException(msg); + + var results = TryParseLuceneQuery(query) + ? searcher.Search(searcher.CreateCriteria().RawQuery(query), maxResults: pageSize * (pageIndex + 1)) + : searcher.Search(query, true, maxResults: pageSize * (pageIndex + 1)); + + var pagedResults = results.Skip(pageIndex * pageSize); + + return new SearchResults { - return LuceneSearchResults.Empty(); - } + TotalRecords = results.TotalItemCount, + Results = pagedResults.Select(x => new SearchResult + { + Id = x.Id, + Score = x.Score, + Values = x.Values + }) + }; + } - var msg = ValidateLuceneSearcher(searcherName, out var searcher); - if (msg.IsSuccessStatusCode) + private bool TryParseLuceneQuery(string query) + { + //TODO: I'd assume there would be a more strict way to parse the query but not that i can find yet, for now we'll + // also do this rudimentary check + if (!query.Contains(":")) + return false; + + try { - if (queryType.InvariantEquals("text")) - { - return searcher.Search(query, false); - } - - if (queryType.InvariantEquals("lucene")) - { - return searcher.Search(searcher.CreateCriteria().RawQuery(query)); - } - - throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); + //This will pass with a plain old string without any fields, need to figure out a way to have it properly parse + var parsed = new QueryParser(Version.LUCENE_30, "nodeName", new KeywordAnalyzer()).Parse(query); + return true; + } + catch (ParseException) + { + return false; + } + catch (Exception) + { + return false; } - - throw new HttpResponseException(msg); } /// @@ -174,7 +198,7 @@ namespace Umbraco.Web.Editors } } - + private ExamineIndexModel CreateModel(IIndex index) { @@ -200,39 +224,24 @@ namespace Umbraco.Web.Editors ProviderProperties = properties, CanRebuild = _indexRebuilder.CanRebuild(indexName) }; - + return indexerModel; } - private HttpResponseMessage ValidateLuceneSearcher(string searcherName, out LuceneSearcher searcher) + private HttpResponseMessage ValidateSearcher(string searcherName, out ISearcher searcher) { - foreach (var indexer in _examineManager.Indexes) + //try to get the searcher from the indexes + if (_examineManager.TryGetIndex(searcherName, out var index)) { - var s = indexer.GetSearcher(); - var sName = (s as BaseLuceneSearcher)?.Name ?? string.Concat(indexer.Name, "Searcher"); - if (sName != searcherName) - { - continue; - } - - searcher = s as LuceneSearcher; - - //Found it, return OK - if (searcher != null) - { - return Request.CreateResponse(HttpStatusCode.OK); - } - - //Return an error since it's not the right type - var response = Request.CreateResponse(HttpStatusCode.BadRequest); - response.Content = - new StringContent($"The searcher {searcherName} is not of type {typeof(LuceneSearcher)}"); - response.ReasonPhrase = "Wrong Searcher Type"; - return response; + searcher = index.GetSearcher(); + return Request.CreateResponse(HttpStatusCode.OK); } - searcher = null; + + //if we didn't find anything try to find it by an explicitly declared searcher + if (_examineManager.TryGetSearcher(searcherName, out searcher)) + return Request.CreateResponse(HttpStatusCode.OK); var response1 = Request.CreateResponse(HttpStatusCode.BadRequest); response1.Content = new StringContent($"No searcher found with name = {searcherName}"); @@ -269,7 +278,7 @@ namespace Umbraco.Web.Editors private void Indexer_IndexOperationComplete(object sender, EventArgs e) { - var indexer = (LuceneIndex) sender; + var indexer = (LuceneIndex)sender; //ensure it's not listening anymore indexer.IndexOperationComplete -= Indexer_IndexOperationComplete; diff --git a/src/Umbraco.Web/ExamineExtensions.cs b/src/Umbraco.Web/ExamineExtensions.cs index fa5ce64426..f1ed6c0659 100644 --- a/src/Umbraco.Web/ExamineExtensions.cs +++ b/src/Umbraco.Web/ExamineExtensions.cs @@ -12,7 +12,7 @@ namespace Umbraco.Web /// public static class ExamineExtensions { - public static IEnumerable ToPublishedSearchResults(this IEnumerable results, IPublishedCache cache) + public static IEnumerable ToPublishedSearchResults(this IEnumerable results, IPublishedCache cache) { var list = new List(); diff --git a/src/Umbraco.Web/IPublishedContentQuery.cs b/src/Umbraco.Web/IPublishedContentQuery.cs index 80af19c941..3ab1c5e3ae 100644 --- a/src/Umbraco.Web/IPublishedContentQuery.cs +++ b/src/Umbraco.Web/IPublishedContentQuery.cs @@ -39,7 +39,7 @@ namespace Umbraco.Web /// /// Searches content. /// - IEnumerable Search(int skip, int take, out int totalRecords, string term, bool useWildCards = true, string indexName = null); + IEnumerable Search(int skip, int take, out long totalRecords, string term, bool useWildCards = true, string indexName = null); /// /// Searches content. @@ -49,6 +49,6 @@ namespace Umbraco.Web /// /// Searches content. /// - IEnumerable Search(int skip, int take, out int totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.ISearcher searcher = null); + IEnumerable Search(int skip, int take, out long totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.ISearcher searcher = null); } } diff --git a/src/Umbraco.Web/Models/ContentEditing/SearchResult.cs b/src/Umbraco.Web/Models/ContentEditing/SearchResult.cs new file mode 100644 index 0000000000..1cdd539165 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/SearchResult.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "result", Namespace = "")] + public class SearchResult + { + [DataMember(Name = "id")] + public string Id { get; set; } + + [DataMember(Name = "score")] + public float Score { get; set; } + + [DataMember(Name = "fieldCount")] + public int FieldCount => Values?.Count ?? 0; + + [DataMember(Name = "values")] + public IReadOnlyDictionary Values { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/SearchResultItem.cs b/src/Umbraco.Web/Models/ContentEditing/SearchResultEntity.cs similarity index 86% rename from src/Umbraco.Web/Models/ContentEditing/SearchResultItem.cs rename to src/Umbraco.Web/Models/ContentEditing/SearchResultEntity.cs index 5ba89806d0..a1cca9763d 100644 --- a/src/Umbraco.Web/Models/ContentEditing/SearchResultItem.cs +++ b/src/Umbraco.Web/Models/ContentEditing/SearchResultEntity.cs @@ -3,7 +3,7 @@ namespace Umbraco.Web.Models.ContentEditing { [DataContract(Name = "searchResult", Namespace = "")] - public class SearchResultItem : EntityBasic + public class SearchResultEntity : EntityBasic { /// /// The score of the search result diff --git a/src/Umbraco.Web/Models/ContentEditing/SearchResults.cs b/src/Umbraco.Web/Models/ContentEditing/SearchResults.cs new file mode 100644 index 0000000000..f791d55cab --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/SearchResults.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "results", Namespace = "")] + public class SearchResults + { + public static SearchResults Empty() => new SearchResults + { + Results = Enumerable.Empty(), + TotalRecords = 0 + }; + + [DataMember(Name = "totalRecords")] + public long TotalRecords { get; set; } + + [DataMember(Name = "results")] + public IEnumerable Results { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/TreeSearchResult.cs b/src/Umbraco.Web/Models/ContentEditing/TreeSearchResult.cs index 964ed62c88..22c177190d 100644 --- a/src/Umbraco.Web/Models/ContentEditing/TreeSearchResult.cs +++ b/src/Umbraco.Web/Models/ContentEditing/TreeSearchResult.cs @@ -30,6 +30,6 @@ namespace Umbraco.Web.Models.ContentEditing public string JsFormatterMethod { get; set; } [DataMember(Name = "results")] - public IEnumerable Results { get; set; } + public IEnumerable Results { get; set; } } } diff --git a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs index 4028d480db..3e42178fbd 100644 --- a/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/EntityMapperProfile.cs @@ -84,7 +84,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.Trashed, opt => opt.Ignore()) .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); - CreateMap() + CreateMap() .ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(ObjectTypes.GetUdiType(src.NodeObjectType), src.Key))) .ForMember(dest => dest.Icon, opt => opt.MapFrom(src=> GetContentTypeIcon(src))) .ForMember(dest => dest.Trashed, opt => opt.Ignore()) @@ -107,7 +107,7 @@ namespace Umbraco.Web.Models.Mapping } }); - CreateMap() + CreateMap() //default to document icon .ForMember(dest => dest.Score, opt => opt.MapFrom(result => result.Score)) .ForMember(dest => dest.Udi, opt => opt.Ignore()) @@ -174,11 +174,11 @@ namespace Umbraco.Web.Models.Mapping } }); - CreateMap>() - .ConvertUsing(results => results.Select(Mapper.Map).ToList()); + CreateMap>() + .ConvertUsing(results => results.Select(Mapper.Map).ToList()); - CreateMap, IEnumerable>() - .ConvertUsing(results => results.Select(Mapper.Map).ToList()); + CreateMap, IEnumerable>() + .ConvertUsing(results => results.Select(Mapper.Map).ToList()); } /// diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index ccfe947515..d681d9296e 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -354,7 +354,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return null; } - internal CacheValues ConvertFromSearchResult(SearchResult searchResult) + internal CacheValues ConvertFromSearchResult(ISearchResult searchResult) { // note: fixing fields in 7.x, removed by Shan for 8.0 diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs index ed42181234..1883010582 100644 --- a/src/Umbraco.Web/PublishedContentQuery.cs +++ b/src/Umbraco.Web/PublishedContentQuery.cs @@ -227,7 +227,7 @@ namespace Umbraco.Web } /// - public IEnumerable Search(int skip, int take, out int totalRecords, string term, bool useWildCards = true, string indexName = null) + public IEnumerable Search(int skip, int take, out long totalRecords, string term, bool useWildCards = true, string indexName = null) { //fixme: inject IExamineManager @@ -260,7 +260,7 @@ namespace Umbraco.Web } /// - public IEnumerable Search(int skip, int take, out int totalRecords, ISearchCriteria criteria, ISearcher searcher = null) + public IEnumerable Search(int skip, int take, out long totalRecords, ISearchCriteria criteria, ISearcher searcher = null) { if (_query != null) return _query.Search(skip, take, out totalRecords, criteria, searcher); diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index 9b98d02a10..9aab30edae 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -12,6 +12,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Trees; +using SearchResult = Examine.SearchResult; namespace Umbraco.Web.Search { @@ -41,7 +42,7 @@ namespace Umbraco.Web.Search /// /// /// - public IEnumerable ExamineSearch( + public IEnumerable ExamineSearch( string query, UmbracoEntityTypes entityType, int pageSize, @@ -109,7 +110,7 @@ namespace Umbraco.Web.Search if (searchFrom.IsNullOrWhiteSpace() && query.IsNullOrWhiteSpace()) { totalFound = 0; - return new List(); + return new List(); } //update the query with the query term @@ -143,7 +144,7 @@ namespace Umbraco.Web.Search if (searchFrom.IsNullOrWhiteSpace() && trimmed.IsNullOrWhiteSpace()) { totalFound = 0; - return new List(); + return new List(); } //update the query with the query term @@ -277,9 +278,9 @@ namespace Umbraco.Web.Search /// /// /// - private IEnumerable MemberFromSearchResults(SearchResult[] results) + private IEnumerable MemberFromSearchResults(ISearchResult[] results) { - var mapped = Mapper.Map>(results).ToArray(); + var mapped = Mapper.Map>(results).ToArray(); //add additional data foreach (var m in mapped) { @@ -310,9 +311,9 @@ namespace Umbraco.Web.Search /// /// /// - private IEnumerable MediaFromSearchResults(IEnumerable results) + private IEnumerable MediaFromSearchResults(IEnumerable results) { - var mapped = Mapper.Map>(results).ToArray(); + var mapped = Mapper.Map>(results).ToArray(); //add additional data foreach (var m in mapped) { @@ -330,9 +331,9 @@ namespace Umbraco.Web.Search /// /// /// - private IEnumerable ContentFromSearchResults(IEnumerable 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/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index 35c335f06d..d2b94c815b 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -319,7 +319,7 @@ namespace Umbraco.Web.Trees menuItem.OpensDialog = opensDialog; } - public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) + public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) { return _treeSearcher.ExamineSearch(query, UmbracoEntityTypes.Document, pageSize, pageIndex, out totalFound, searchFrom); } diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs index 43e5b03f2f..0ef3c073eb 100644 --- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs @@ -142,10 +142,10 @@ namespace Umbraco.Web.Trees return menu; } - public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) + 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: query); - return Mapper.Map>(results); + return Mapper.Map>(results); } } } diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index 6c89f4a1dc..0970481357 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -142,10 +142,10 @@ namespace Umbraco.Web.Trees return menu; } - public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) + 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: query); - return Mapper.Map>(results); + return Mapper.Map>(results); } } } diff --git a/src/Umbraco.Web/Trees/ISearchableTree.cs b/src/Umbraco.Web/Trees/ISearchableTree.cs index 6e52b054b4..4146bfaf45 100644 --- a/src/Umbraco.Web/Trees/ISearchableTree.cs +++ b/src/Umbraco.Web/Trees/ISearchableTree.cs @@ -21,6 +21,6 @@ namespace Umbraco.Web.Trees /// 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); + IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null); } } diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index 4efddfb4b3..8533081dde 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -164,7 +164,7 @@ namespace Umbraco.Web.Trees return HasPathAccess(entity, queryStrings); } - public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) + public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) { return _treeSearcher.ExamineSearch(query, UmbracoEntityTypes.Media, pageSize, pageIndex, out totalFound, searchFrom); } diff --git a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs index 547199676a..8b3ad5e8cd 100644 --- a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs @@ -133,10 +133,10 @@ namespace Umbraco.Web.Trees return menu; } - public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) + 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: query); - return Mapper.Map>(results); + return Mapper.Map>(results); } } } diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index 3317bfbdf9..ae2ad1824d 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -192,7 +192,7 @@ namespace Umbraco.Web.Trees return menu; } - public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) + public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) { return _treeSearcher.ExamineSearch(query, UmbracoEntityTypes.Member, pageSize, pageIndex, out totalFound, searchFrom); } diff --git a/src/Umbraco.Web/Trees/TemplatesTreeController.cs b/src/Umbraco.Web/Trees/TemplatesTreeController.cs index 3fc005d670..56f47695a0 100644 --- a/src/Umbraco.Web/Trees/TemplatesTreeController.cs +++ b/src/Umbraco.Web/Trees/TemplatesTreeController.cs @@ -133,10 +133,10 @@ namespace Umbraco.Web.Trees : null; } - public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null) + 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: query); - return Mapper.Map>(results); + return Mapper.Map>(results); } } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index cda21b029c..1ebb54248d 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -62,7 +62,7 @@ - + 2.6.2.25 @@ -151,6 +151,8 @@ + + @@ -944,7 +946,7 @@ - + diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index aa30fff0ec..357c8a30a6 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -836,7 +836,7 @@ namespace Umbraco.Web /// /// /// - public IEnumerable TypedSearch(int skip, int take, out int totalRecords, string term, bool useWildCards = true, string searchProvider = null) + public IEnumerable TypedSearch(int skip, int take, out long totalRecords, string term, bool useWildCards = true, string searchProvider = null) { return ContentQuery.Search(skip, take, out totalRecords, term, useWildCards, searchProvider); } @@ -850,7 +850,7 @@ namespace Umbraco.Web /// /// /// - public IEnumerable TypedSearch(int skip, int take, out int totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) + public IEnumerable TypedSearch(int skip, int take, out long totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) { return ContentQuery.Search(skip, take, out totalRecords, criteria, searchProvider); } From 1717f94fcf067ea0b0893668c6c1740ed4df636a Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 4 Dec 2018 15:41:54 +1100 Subject: [PATCH 34/44] Gets searching on dashboard working with paging and viewing field values --- .../settings/examinemanagement.controller.js | 14 ++++++ .../dashboard/settings/examinemanagement.html | 49 +++++++++++-------- .../settings/examinemanagementresults.html | 18 +++++++ 3 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagementresults.html diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js index 3d1773def5..4b54663a4d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js @@ -10,6 +10,7 @@ function ExamineManagementController($scope, umbRequestHelper, $http, $q, $timeo vm.selectedSearcher = null; vm.searchResults = null; + vm.showSearchResultDialog = showSearchResultDialog; vm.showIndexInfo = showIndexInfo; vm.showSearcherInfo = showSearcherInfo; vm.search = search; @@ -22,6 +23,19 @@ function ExamineManagementController($scope, umbRequestHelper, $http, $q, $timeo vm.infoOverlay = null; + function showSearchResultDialog(values) { + if (vm.searchResults) { + vm.searchResults.overlay = { + title: "Field values", + searchResultValues: values, + view: "views/dashboard/settings/examinemanagementresults.html", + close: function () { + vm.searchResults.overlay = null; + } + }; + } + } + function nextSearchResultPage(pageNumber) { search(vm.selectedIndex ? vm.selectedIndex : vm.selectedSearcher, null, pageNumber); } diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html index 4174fc5642..b685396ffd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html @@ -188,7 +188,7 @@
    + ng-keypress="vm.search(vm.selectedIndex, $event)" />
    -
    +
    - - - - - + + + + + - - - - - + + + + +
    ScoreIdName
    ScoreIdName
    {{result.score}}{{result.id}} - {{result.values['nodeName']}}  - ({{result.fieldCount}} fields) -
    {{result.score}}{{result.id}} + {{result.values['nodeName']}}  + + ({{result.fieldCount}} fields) + +
    - +
    @@ -302,4 +303,10 @@
    + + +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagementresults.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagementresults.html new file mode 100644 index 0000000000..1df737816f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagementresults.html @@ -0,0 +1,18 @@ +
    + + + + + + + + + + + + + + +
    FieldValue
    {{key}}{{val}}
    + +
    From b48a7fa4a967f32dc9c11fc42336343eec666fa1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 5 Dec 2018 16:53:25 +1100 Subject: [PATCH 35/44] Gets search tools working for explicitly defined searchers, have tested with a multi index searcher, refactors SupportsUnpublished vs SupportSoftDelete vs OnlyPublishedContent booleans and made some sense out of the reindexing items in ExamineComponent which didn't really work, cleans up IPublishedContentQuery implementation --- src/Umbraco.Core/Constants-Indexes.cs | 6 +- src/Umbraco.Core/Models/PagedResult.cs | 2 +- .../DefaultPropertyIndexValues.cs | 6 +- .../PropertyEditors/IPropertyIndexValues.cs | 2 +- src/Umbraco.Examine/BaseValueSetBuilder.cs | 6 +- src/Umbraco.Examine/Config/IndexSet.cs | 42 --- src/Umbraco.Examine/ContentIndexPopulator.cs | 14 +- src/Umbraco.Examine/ContentValueSetBuilder.cs | 26 +- .../ContentValueSetValidator.cs | 14 +- .../IContentValueSetBuilder.cs | 13 + .../IContentValueSetValidator.cs | 15 +- .../IPublishedContentValueSetBuilder.cs | 12 + src/Umbraco.Examine/IUmbracoIndexer.cs | 9 +- src/Umbraco.Examine/MediaValueSetBuilder.cs | 2 +- src/Umbraco.Examine/MemberValueSetBuilder.cs | 2 +- .../PublishedContentIndexPopulator.cs | 4 +- src/Umbraco.Examine/Umbraco.Examine.csproj | 4 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 6 +- .../UmbracoExamineExtensions.cs | 15 +- .../UmbracoExamineIndexDiagnostics.cs | 4 +- src/Umbraco.Examine/UmbracoExamineIndexer.cs | 55 ++- src/Umbraco.Examine/UmbracoExamineSearcher.cs | 189 +++-------- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- .../UmbracoExamine/IndexInitializer.cs | 4 +- .../settings/examinemanagement.controller.js | 3 - .../dashboard/settings/examinemanagement.html | 165 +++++++-- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- src/Umbraco.Web.UI/config/ExamineIndex.config | 6 +- .../config/ExamineSettings.config | 8 +- .../Editors/ExamineManagementController.cs | 3 +- .../GridPropertyIndexValues.cs | 12 +- .../PropertyEditors/RichTextPropertyEditor.cs | 8 +- src/Umbraco.Web/PublishedContentQuery.cs | 35 +- src/Umbraco.Web/Search/ExamineComponent.cs | 312 +++++++++--------- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- src/Umbraco.Web/UmbracoHelper.cs | 75 ++--- 36 files changed, 517 insertions(+), 568 deletions(-) create mode 100644 src/Umbraco.Examine/IContentValueSetBuilder.cs create mode 100644 src/Umbraco.Examine/IPublishedContentValueSetBuilder.cs diff --git a/src/Umbraco.Core/Constants-Indexes.cs b/src/Umbraco.Core/Constants-Indexes.cs index d9d86884c4..ecf143e83f 100644 --- a/src/Umbraco.Core/Constants-Indexes.cs +++ b/src/Umbraco.Core/Constants-Indexes.cs @@ -8,9 +8,9 @@ namespace Umbraco.Core public static class UmbracoIndexes { - public const string InternalIndexName = InternalIndexPath + "Indexer"; - public const string ExternalIndexName = ExternalIndexPath + "Indexer"; - public const string MembersIndexName = MembersIndexPath + "Indexer"; + public const string InternalIndexName = InternalIndexPath + "Index"; + public const string ExternalIndexName = ExternalIndexPath + "Index"; + public const string MembersIndexName = MembersIndexPath + "Index"; public const string InternalIndexPath = "Internal"; public const string ExternalIndexPath = "External"; diff --git a/src/Umbraco.Core/Models/PagedResult.cs b/src/Umbraco.Core/Models/PagedResult.cs index 653712d9f8..ef4d4efdfd 100644 --- a/src/Umbraco.Core/Models/PagedResult.cs +++ b/src/Umbraco.Core/Models/PagedResult.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Models if (pageSize > 0) { - TotalPages = (long)Math.Ceiling(totalItems / (Decimal)pageSize); + TotalPages = (long)Math.Ceiling(totalItems / (decimal)pageSize); } else { diff --git a/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValues.cs b/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValues.cs index bf39adf93b..c6caab86d8 100644 --- a/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValues.cs +++ b/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValues.cs @@ -8,9 +8,11 @@ namespace Umbraco.Core.PropertyEditors ///
    public class DefaultPropertyIndexValues : IPropertyIndexValues { - public IEnumerable> GetIndexValues(Property property, string culture, string segment) + public IEnumerable>> GetIndexValues(Property property, string culture, string segment, bool published) { - yield return new KeyValuePair(property.Alias, new[] { property.GetValue(culture, segment) }); + yield return new KeyValuePair>( + property.Alias, + property.GetValue(culture, segment, published).Yield()); } } } diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyIndexValues.cs b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValues.cs index 5ebd0a29b7..15580ccb80 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyIndexValues.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValues.cs @@ -8,6 +8,6 @@ namespace Umbraco.Core.PropertyEditors ///
    public interface IPropertyIndexValues { - IEnumerable> GetIndexValues(Property property, string culture, string segment); + IEnumerable>> GetIndexValues(Property property, string culture, string segment, bool published); } } diff --git a/src/Umbraco.Examine/BaseValueSetBuilder.cs b/src/Umbraco.Examine/BaseValueSetBuilder.cs index 6191297aff..f0261f7cd9 100644 --- a/src/Umbraco.Examine/BaseValueSetBuilder.cs +++ b/src/Umbraco.Examine/BaseValueSetBuilder.cs @@ -12,10 +12,12 @@ namespace Umbraco.Examine public abstract class BaseValueSetBuilder : IValueSetBuilder where TContent : IContentBase { + protected bool PublishedValuesOnly { get; } private readonly PropertyEditorCollection _propertyEditors; - protected BaseValueSetBuilder(PropertyEditorCollection propertyEditors) + protected BaseValueSetBuilder(PropertyEditorCollection propertyEditors, bool publishedValuesOnly) { + PublishedValuesOnly = publishedValuesOnly; _propertyEditors = propertyEditors ?? throw new System.ArgumentNullException(nameof(propertyEditors)); } @@ -27,7 +29,7 @@ namespace Umbraco.Examine var editor = _propertyEditors[property.PropertyType.PropertyEditorAlias]; if (editor == null) return; - var indexVals = editor.PropertyIndexValues.GetIndexValues(property, culture, segment); + var indexVals = editor.PropertyIndexValues.GetIndexValues(property, culture, segment, PublishedValuesOnly); foreach (var keyVal in indexVals) { if (keyVal.Key.IsNullOrWhiteSpace()) continue; diff --git a/src/Umbraco.Examine/Config/IndexSet.cs b/src/Umbraco.Examine/Config/IndexSet.cs index 33b67412f2..65b1cd5aec 100644 --- a/src/Umbraco.Examine/Config/IndexSet.cs +++ b/src/Umbraco.Examine/Config/IndexSet.cs @@ -11,48 +11,6 @@ namespace Umbraco.Examine.Config [ConfigurationProperty("SetName", IsRequired = true, IsKey = true)] public string SetName => (string)this["SetName"]; - private string _indexPath = ""; - - /// - /// The folder path of where the lucene index is stored - /// - /// The index path. - /// - /// This can be set at runtime but will not be persisted to the configuration file - /// - [ConfigurationProperty("IndexPath", IsRequired = true, IsKey = false)] - public string IndexPath - { - get - { - if (string.IsNullOrEmpty(_indexPath)) - _indexPath = (string)this["IndexPath"]; - - return _indexPath; - } - set => _indexPath = value; - } - - /// - /// Returns the DirectoryInfo object for the index path. - /// - /// The index directory. - public DirectoryInfo IndexDirectory - { - get - { - //TODO: Get this out of the index set. We need to use the Indexer's DataService to lookup the folder so it can be unit tested. Probably need DataServices on the searcher then too - - //we need to de-couple the context - if (HttpContext.Current != null) - return new DirectoryInfo(HttpContext.Current.Server.MapPath(this.IndexPath)); - else if (HostingEnvironment.ApplicationID != null) - return new DirectoryInfo(HostingEnvironment.MapPath(this.IndexPath)); - else - return new DirectoryInfo(this.IndexPath); - } - } - /// /// When this property is set, the indexing will only index documents that are descendants of this node. /// diff --git a/src/Umbraco.Examine/ContentIndexPopulator.cs b/src/Umbraco.Examine/ContentIndexPopulator.cs index 8127a8c3d3..d1255b4d46 100644 --- a/src/Umbraco.Examine/ContentIndexPopulator.cs +++ b/src/Umbraco.Examine/ContentIndexPopulator.cs @@ -25,7 +25,7 @@ namespace Umbraco.Examine ///
    private static IQuery _publishedQuery; - private readonly bool _supportUnpublishedContent; + private readonly bool _publishedValuesOnly; private readonly int? _parentId; /// @@ -34,27 +34,27 @@ namespace Umbraco.Examine /// /// /// - public ContentIndexPopulator(IContentService contentService, ISqlContext sqlContext, IValueSetBuilder contentValueSetBuilder) - : this(true, null, contentService, sqlContext, contentValueSetBuilder) + public ContentIndexPopulator(IContentService contentService, ISqlContext sqlContext, IContentValueSetBuilder contentValueSetBuilder) + : this(false, null, contentService, sqlContext, contentValueSetBuilder) { } /// /// Optional constructor allowing specifying custom query parameters /// - /// + /// /// /// /// /// - public ContentIndexPopulator(bool supportUnpublishedContent, int? parentId, IContentService contentService, ISqlContext sqlContext, IValueSetBuilder contentValueSetBuilder) + public ContentIndexPopulator(bool publishedValuesOnly, int? parentId, IContentService contentService, ISqlContext sqlContext, IValueSetBuilder contentValueSetBuilder) { if (sqlContext == null) throw new ArgumentNullException(nameof(sqlContext)); _contentService = contentService ?? throw new ArgumentNullException(nameof(contentService)); _contentValueSetBuilder = contentValueSetBuilder ?? throw new ArgumentNullException(nameof(contentValueSetBuilder)); if (_publishedQuery != null) _publishedQuery = sqlContext.Query().Where(x => x.Published); - _supportUnpublishedContent = supportUnpublishedContent; + _publishedValuesOnly = publishedValuesOnly; _parentId = parentId; RegisterIndex(Constants.UmbracoIndexes.InternalIndexName); @@ -75,7 +75,7 @@ namespace Umbraco.Examine do { - if (_supportUnpublishedContent) + if (!_publishedValuesOnly) { content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out _).ToArray(); } diff --git a/src/Umbraco.Examine/ContentValueSetBuilder.cs b/src/Umbraco.Examine/ContentValueSetBuilder.cs index 056e137c07..39ffab98d9 100644 --- a/src/Umbraco.Examine/ContentValueSetBuilder.cs +++ b/src/Umbraco.Examine/ContentValueSetBuilder.cs @@ -8,15 +8,19 @@ using Umbraco.Core.Strings; namespace Umbraco.Examine { - public class ContentValueSetBuilder : BaseValueSetBuilder + /// + /// Builds s for items + /// + public class ContentValueSetBuilder : BaseValueSetBuilder, IContentValueSetBuilder, IPublishedContentValueSetBuilder { private readonly IEnumerable _urlSegmentProviders; private readonly IUserService _userService; public ContentValueSetBuilder(PropertyEditorCollection propertyEditors, IEnumerable urlSegmentProviders, - IUserService userService) - : base(propertyEditors) + IUserService userService, + bool publishedValuesOnly) + : base(propertyEditors, publishedValuesOnly) { _urlSegmentProviders = urlSegmentProviders; _userService = userService; @@ -47,8 +51,10 @@ namespace Umbraco.Examine {"sortOrder", new object[] {c.SortOrder}}, {"createDate", new object[] {c.CreateDate}}, //Always add invariant createDate {"updateDate", new object[] {c.UpdateDate}}, //Always add invariant updateDate - {"nodeName", c.Name.Yield()}, //Always add invariant nodeName - {"urlName", urlValue.Yield()}, //Always add invariant urlName + {"nodeName", PublishedValuesOnly //Always add invariant nodeName + ? c.PublishName.Yield() + : c.Name.Yield()}, + {"urlName", urlValue.Yield()}, //Always add invariant urlName {"path", c.Path.Yield()}, {"nodeType", new object[] {c.ContentType.Id}}, {"creatorName", (c.GetCreatorProfile(_userService)?.Name ?? "??").Yield() }, @@ -67,9 +73,13 @@ namespace Umbraco.Examine var variantUrl = c.GetUrlSegment(_urlSegmentProviders, culture); var lowerCulture = culture.ToLowerInvariant(); values[$"urlName_{lowerCulture}"] = variantUrl.Yield(); - values[$"nodeName_{lowerCulture}"] = c.GetCultureName(culture).Yield(); - values[$"{UmbracoExamineIndexer.PublishedFieldName}_{lowerCulture}"] = new object[] { c.IsCulturePublished(culture) ? 1 : 0 }; - values[$"updateDate_{lowerCulture}"] = new object[] { c.GetUpdateDate(culture) }; + values[$"nodeName_{lowerCulture}"] = PublishedValuesOnly + ? c.GetPublishName(culture).Yield() + : c.GetCultureName(culture).Yield(); + values[$"{UmbracoExamineIndexer.PublishedFieldName}_{lowerCulture}"] = (c.IsCulturePublished(culture) ? 1 : 0).Yield(); + values[$"updateDate_{lowerCulture}"] = PublishedValuesOnly + ? c.GetPublishDate(culture).Yield() + : c.GetUpdateDate(culture).Yield(); } } diff --git a/src/Umbraco.Examine/ContentValueSetValidator.cs b/src/Umbraco.Examine/ContentValueSetValidator.cs index 46e4f48c9a..d671d101d6 100644 --- a/src/Umbraco.Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Examine/ContentValueSetValidator.cs @@ -20,7 +20,7 @@ namespace Umbraco.Examine private static readonly IEnumerable ValidCategories = new[] {IndexTypes.Content, IndexTypes.Media}; protected override IEnumerable ValidIndexCategories => ValidCategories; - public bool SupportUnpublishedContent { get; } + public bool PublishedValuesOnly { get; } public bool SupportProtectedContent { get; } public int? ParentId { get; } @@ -43,7 +43,7 @@ namespace Umbraco.Examine var recycleBinId = category == IndexTypes.Content ? Constants.System.RecycleBinContent : Constants.System.RecycleBinMedia; //check for recycle bin - if (!SupportUnpublishedContent) + if (!PublishedValuesOnly) { if (path.Contains(string.Concat(",", recycleBinId, ","))) return false; @@ -64,18 +64,18 @@ namespace Umbraco.Examine return true; } - public ContentValueSetValidator(bool supportUnpublishedContent, int? parentId = null, + public ContentValueSetValidator(bool publishedValuesOnly, int? parentId = null, IEnumerable includeItemTypes = null, IEnumerable excludeItemTypes = null) - : this(supportUnpublishedContent, true, null, parentId, includeItemTypes, excludeItemTypes) + : this(publishedValuesOnly, true, null, parentId, includeItemTypes, excludeItemTypes) { } - public ContentValueSetValidator(bool supportUnpublishedContent, bool supportProtectedContent, + public ContentValueSetValidator(bool publishedValuesOnly, bool supportProtectedContent, IPublicAccessService publicAccessService, int? parentId = null, IEnumerable includeItemTypes = null, IEnumerable excludeItemTypes = null) : base(includeItemTypes, excludeItemTypes, null, null) { - SupportUnpublishedContent = supportUnpublishedContent; + PublishedValuesOnly = publishedValuesOnly; SupportProtectedContent = supportProtectedContent; ParentId = parentId; _publicAccessService = publicAccessService; @@ -90,7 +90,7 @@ namespace Umbraco.Examine var isFiltered = baseValidate == ValueSetValidationResult.Filtered; //check for published content - if (valueSet.Category == IndexTypes.Content && !SupportUnpublishedContent) + if (valueSet.Category == IndexTypes.Content && !PublishedValuesOnly) { if (!valueSet.Values.TryGetValue(UmbracoExamineIndexer.PublishedFieldName, out var published)) return ValueSetValidationResult.Failed; diff --git a/src/Umbraco.Examine/IContentValueSetBuilder.cs b/src/Umbraco.Examine/IContentValueSetBuilder.cs new file mode 100644 index 0000000000..fed706d592 --- /dev/null +++ b/src/Umbraco.Examine/IContentValueSetBuilder.cs @@ -0,0 +1,13 @@ +using Examine; +using Umbraco.Core.Models; + +namespace Umbraco.Examine +{ + /// + /// + /// Marker interface for a builder for supporting unpublished content + /// + public interface IContentValueSetBuilder : IValueSetBuilder + { + } +} diff --git a/src/Umbraco.Examine/IContentValueSetValidator.cs b/src/Umbraco.Examine/IContentValueSetValidator.cs index a7164773bb..fa85a0d32b 100644 --- a/src/Umbraco.Examine/IContentValueSetValidator.cs +++ b/src/Umbraco.Examine/IContentValueSetValidator.cs @@ -7,8 +7,21 @@ namespace Umbraco.Examine /// public interface IContentValueSetValidator : IValueSetValidator { - bool SupportUnpublishedContent { get; } + /// + /// When set to true the index will only retain published values + /// + /// + /// Any non-published values will not be put or kept in the index: + /// * Deleted, Trashed, non-published Content items + /// * non-published Variants + /// + bool PublishedValuesOnly { get; } + + /// + /// If true, protected content will be indexed otherwise it will not be put or kept in the index + /// bool SupportProtectedContent { get; } + int? ParentId { get; } bool ValidatePath(string path, string category); diff --git a/src/Umbraco.Examine/IPublishedContentValueSetBuilder.cs b/src/Umbraco.Examine/IPublishedContentValueSetBuilder.cs new file mode 100644 index 0000000000..c337a7a1e6 --- /dev/null +++ b/src/Umbraco.Examine/IPublishedContentValueSetBuilder.cs @@ -0,0 +1,12 @@ +using Examine; +using Umbraco.Core.Models; + +namespace Umbraco.Examine +{ + /// + /// Marker interface for a builder for only published content + /// + public interface IPublishedContentValueSetBuilder : IValueSetBuilder + { + } +} \ No newline at end of file diff --git a/src/Umbraco.Examine/IUmbracoIndexer.cs b/src/Umbraco.Examine/IUmbracoIndexer.cs index c3b4a5d629..00f772b1e2 100644 --- a/src/Umbraco.Examine/IUmbracoIndexer.cs +++ b/src/Umbraco.Examine/IUmbracoIndexer.cs @@ -13,8 +13,13 @@ namespace Umbraco.Examine bool EnableDefaultEventHandler { get; } /// - /// When set to true data will not be deleted from the index if the data is being soft deleted (unpublished or trashed) + /// When set to true the index will only retain published values /// - bool SupportSoftDelete { get; } + /// + /// Any non-published values will not be put or kept in the index: + /// * Deleted, Trashed, non-published Content items + /// * non-published Variants + /// + bool PublishedValuesOnly { get; } } } diff --git a/src/Umbraco.Examine/MediaValueSetBuilder.cs b/src/Umbraco.Examine/MediaValueSetBuilder.cs index af41f574fb..8df51570c1 100644 --- a/src/Umbraco.Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Examine/MediaValueSetBuilder.cs @@ -16,7 +16,7 @@ namespace Umbraco.Examine public MediaValueSetBuilder(PropertyEditorCollection propertyEditors, IEnumerable urlSegmentProviders, IUserService userService) - : base(propertyEditors) + : base(propertyEditors, false) { _urlSegmentProviders = urlSegmentProviders; _userService = userService; diff --git a/src/Umbraco.Examine/MemberValueSetBuilder.cs b/src/Umbraco.Examine/MemberValueSetBuilder.cs index 85a8fa13c5..9864aba18d 100644 --- a/src/Umbraco.Examine/MemberValueSetBuilder.cs +++ b/src/Umbraco.Examine/MemberValueSetBuilder.cs @@ -10,7 +10,7 @@ namespace Umbraco.Examine public class MemberValueSetBuilder : BaseValueSetBuilder { public MemberValueSetBuilder(PropertyEditorCollection propertyEditors) - : base(propertyEditors) + : base(propertyEditors, false) { } diff --git a/src/Umbraco.Examine/PublishedContentIndexPopulator.cs b/src/Umbraco.Examine/PublishedContentIndexPopulator.cs index a29031ca3c..143e2db630 100644 --- a/src/Umbraco.Examine/PublishedContentIndexPopulator.cs +++ b/src/Umbraco.Examine/PublishedContentIndexPopulator.cs @@ -14,8 +14,8 @@ namespace Umbraco.Examine /// public class PublishedContentIndexPopulator : ContentIndexPopulator { - public PublishedContentIndexPopulator(IContentService contentService, ISqlContext sqlContext, IValueSetBuilder contentValueSetBuilder) : - base(false, null, contentService, sqlContext, contentValueSetBuilder) + public PublishedContentIndexPopulator(IContentService contentService, ISqlContext sqlContext, IPublishedContentValueSetBuilder contentValueSetBuilder) : + base(true, null, contentService, sqlContext, contentValueSetBuilder) { } } diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 5c18da00ef..0b0e02df49 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + @@ -64,11 +64,13 @@ + + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index feea0a4efd..e39a9d5990 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -64,8 +64,8 @@ namespace Umbraco.Examine if (validator == null) throw new ArgumentNullException(nameof(validator)); LanguageService = languageService ?? throw new ArgumentNullException(nameof(languageService)); - if (validator is ContentValueSetValidator contentValueSetValidator) - SupportSoftDelete = contentValueSetValidator.SupportUnpublishedContent; + if (validator is IContentValueSetValidator contentValueSetValidator) + PublishedValuesOnly = contentValueSetValidator.PublishedValuesOnly; } #endregion @@ -124,7 +124,7 @@ namespace Umbraco.Examine parentId, ConfigIndexCriteria.IncludeItemTypes, ConfigIndexCriteria.ExcludeItemTypes); - SupportSoftDelete = supportUnpublished; + PublishedValuesOnly = supportUnpublished; } #endregion diff --git a/src/Umbraco.Examine/UmbracoExamineExtensions.cs b/src/Umbraco.Examine/UmbracoExamineExtensions.cs index ab8819b276..8be5a6c1e3 100644 --- a/src/Umbraco.Examine/UmbracoExamineExtensions.cs +++ b/src/Umbraco.Examine/UmbracoExamineExtensions.cs @@ -76,17 +76,6 @@ namespace Umbraco.Examine return fieldQuery; } - /// - /// Used to replace any available tokens in the index path before the lucene directory is assigned to the path - /// - /// - internal static void ReplaceTokensInIndexPath(this IndexSet indexSet) - { - if (indexSet == null) return; - indexSet.IndexPath = indexSet.IndexPath - .Replace("{machinename}", NetworkHelper.FileSafeMachineName) - .Replace("{appdomainappid}", (HttpRuntime.AppDomainAppId ?? string.Empty).ReplaceNonAlphanumericChars("")) - .EnsureEndsWith('/'); - } + } -} \ No newline at end of file +} diff --git a/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs b/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs index 7e8bd13323..0812d93931 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexDiagnostics.cs @@ -70,7 +70,7 @@ namespace Umbraco.Examine _index.LuceneIndexFolder == null ? string.Empty : _index.LuceneIndexFolder.ToString().ToLowerInvariant().TrimStart(IOHelper.MapPath(SystemDirectories.Root).ToLowerInvariant()).Replace("\\", "/").EnsureStartsWith('/'), - [nameof(UmbracoExamineIndexer.SupportSoftDelete)] = _index.SupportSoftDelete, + [nameof(UmbracoExamineIndexer.PublishedValuesOnly)] = _index.PublishedValuesOnly, //There's too much info here //[nameof(UmbracoExamineIndexer.FieldDefinitionCollection)] = _index.FieldDefinitionCollection, }; @@ -85,7 +85,7 @@ namespace Umbraco.Examine if (_index.ValueSetValidator is ContentValueSetValidator cvsv) { - d[nameof(ContentValueSetValidator.SupportUnpublishedContent)] = cvsv.SupportUnpublishedContent; + d[nameof(ContentValueSetValidator.PublishedValuesOnly)] = cvsv.PublishedValuesOnly; d[nameof(ContentValueSetValidator.SupportProtectedContent)] = cvsv.SupportProtectedContent; d[nameof(ContentValueSetValidator.ParentId)] = cvsv.ParentId; } diff --git a/src/Umbraco.Examine/UmbracoExamineIndexer.cs b/src/Umbraco.Examine/UmbracoExamineIndexer.cs index 05958e185d..84c8a7d8c5 100644 --- a/src/Umbraco.Examine/UmbracoExamineIndexer.cs +++ b/src/Umbraco.Examine/UmbracoExamineIndexer.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.IO; using System.Linq; using Examine.LuceneEngine.Providers; using Lucene.Net.Analysis; @@ -14,7 +13,6 @@ using Examine.LuceneEngine.Indexing; using Lucene.Net.Store; using Umbraco.Core.Composing; using Umbraco.Core.Logging; -using Umbraco.Core.Xml; using Umbraco.Examine.Config; using Directory = Lucene.Net.Store.Directory; @@ -147,7 +145,7 @@ namespace Umbraco.Examine /// public bool EnableDefaultEventHandler { get; set; } = true; - public bool SupportSoftDelete { get; protected set; } = false; + public bool PublishedValuesOnly { get; protected set; } = false; protected ConfigIndexCriteria ConfigIndexCriteria { get; private set; } @@ -177,38 +175,35 @@ namespace Umbraco.Examine } //Need to check if the index set or IndexerData is specified... - if (config["indexSet"] == null && (FieldDefinitionCollection.Count == 0)) + if (config["indexSet"] == null && FieldDefinitionCollection.Count == 0) { //if we don't have either, then we'll try to set the index set by naming conventions var found = false; - if (name.EndsWith("Indexer")) + + var possibleSuffixes = new[] {"Index", "Indexer"}; + foreach (var suffix in possibleSuffixes) { - var setNameByConvension = name.Remove(name.LastIndexOf("Indexer")) + "IndexSet"; + if (!name.EndsWith(suffix)) continue; + + var setNameByConvension = name.Remove(name.LastIndexOf(suffix, StringComparison.Ordinal)) + "IndexSet"; //check if we can assign the index set by naming convention var set = IndexSets.Instance.Sets.Cast().SingleOrDefault(x => x.SetName == setNameByConvension); - if (set != null) + if (set == null) continue; + + //we've found an index set by naming conventions :) + IndexSetName = set.SetName; + + 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)) { - //we've found an index set by naming conventions :) - IndexSetName = set.SetName; - - var indexSet = IndexSets.Instance.Sets[IndexSetName]; - - //if tokens are declared in the path, then use them (i.e. {machinename} ) - indexSet.ReplaceTokensInIndexPath(); - - //get the index criteria and ensure folder - ConfigIndexCriteria = CreateFieldDefinitionsFromConfig(indexSet); - foreach (var fieldDefinition in ConfigIndexCriteria.StandardFields.Union(ConfigIndexCriteria.UserFields)) - { - FieldDefinitionCollection.TryAdd(fieldDefinition.Name, fieldDefinition); - } - - //now set the index folder - LuceneIndexFolder = new DirectoryInfo(Path.Combine(IndexSets.Instance.Sets[IndexSetName].IndexDirectory.FullName, "Index")); - - found = true; + FieldDefinitionCollection.TryAdd(fieldDefinition.Name, fieldDefinition); } + found = true; + break; } if (!found) @@ -229,24 +224,18 @@ namespace Umbraco.Examine var indexSet = IndexSets.Instance.Sets[IndexSetName]; - //if tokens are declared in the path, then use them (i.e. {machinename} ) - indexSet.ReplaceTokensInIndexPath(); - //get the index criteria and ensure folder ConfigIndexCriteria = CreateFieldDefinitionsFromConfig(indexSet); foreach (var fieldDefinition in ConfigIndexCriteria.StandardFields.Union(ConfigIndexCriteria.UserFields)) { FieldDefinitionCollection.TryAdd(fieldDefinition.Name, fieldDefinition); } - - //now set the index folder - LuceneIndexFolder = new DirectoryInfo(Path.Combine(IndexSets.Instance.Sets[IndexSetName].IndexDirectory.FullName, "Index")); } } base.Initialize(name, config); } - + #endregion /// diff --git a/src/Umbraco.Examine/UmbracoExamineSearcher.cs b/src/Umbraco.Examine/UmbracoExamineSearcher.cs index 2474bc6429..50c6c21e37 100644 --- a/src/Umbraco.Examine/UmbracoExamineSearcher.cs +++ b/src/Umbraco.Examine/UmbracoExamineSearcher.cs @@ -1,145 +1,52 @@ -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; +//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 - { - private readonly bool _configBased = false; +//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) +// { +// } - /// - /// Default constructor for config based construction - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public UmbracoExamineSearcher() - { - _configBased = true; - } +// /// +// public UmbracoExamineSearcher(string name, IndexWriter writer, Analyzer analyzer, FieldValueTypeCollection fieldValueTypeCollection) +// : base(name, writer, analyzer, fieldValueTypeCollection) +// { +// } - /// - /// Constructor to allow for creating an indexer at runtime - /// - /// - /// - /// - public UmbracoExamineSearcher(string name, Directory luceneDirectory, Analyzer analyzer, FieldValueTypeCollection fieldValueTypeCollection) - : base(name, luceneDirectory, analyzer, fieldValueTypeCollection) - { - _configBased = false; - } +// /// +// /// 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(); +// } - /// - public UmbracoExamineSearcher(string name, IndexWriter writer, Analyzer analyzer, FieldValueTypeCollection fieldValueTypeCollection) - : base(name, writer, analyzer, fieldValueTypeCollection) - { - _configBased = false; - } - - /// - /// Name of the Lucene.NET index set - /// - public string IndexSetName { get; private set; } - - /// - /// Method used for initializing based on a configuration based searcher - /// - /// - /// - public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) - { - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(name)); - - //We need to check if we actually can initialize, if not then don't continue - if (CanInitialize() == false) - { - return; - } - - //need to check if the index set is specified, if it's not, we'll see if we can find one by convension - //if the folder is not null and the index set is null, we'll assume that this has been created at runtime. - //NOTE: Don't proceed if the _luceneDirectory is set since we already know where to look. - var luceneDirectory = GetLuceneDirectory(); - if (config["indexSet"] == null && (LuceneIndexFolder == null && luceneDirectory == null)) - { - //if we don't have either, then we'll try to set the index set by naming convensions - var found = false; - if (name.EndsWith("Searcher")) - { - var setNameByConvension = name.Remove(name.LastIndexOf("Searcher")) + "IndexSet"; - //check if we can assign the index set by naming convension - var set = IndexSets.Instance.Sets.Cast().SingleOrDefault(x => x.SetName == setNameByConvension); - - if (set != null) - { - set.ReplaceTokensInIndexPath(); - - //we've found an index set by naming convensions :) - IndexSetName = set.SetName; - found = true; - } - } - - if (!found) - throw new ArgumentNullException("indexSet on LuceneExamineIndexer provider has not been set in configuration"); - - //get the folder to index - LuceneIndexFolder = new DirectoryInfo(Path.Combine(IndexSets.Instance.Sets[IndexSetName].IndexDirectory.FullName, "Index")); - } - else if (config["indexSet"] != null && luceneDirectory == null) - { - if (IndexSets.Instance.Sets[config["indexSet"]] == null) - throw new ArgumentException("The indexSet specified for the LuceneExamineIndexer provider does not exist"); - - IndexSetName = config["indexSet"]; - - var indexSet = IndexSets.Instance.Sets[IndexSetName]; - - indexSet.ReplaceTokensInIndexPath(); - - //get the folder to index - LuceneIndexFolder = new DirectoryInfo(Path.Combine(indexSet.IndexDirectory.FullName, "Index")); - } - - base.Initialize(name, config); - - } - - /// - /// Returns true if the Umbraco application is in a state that we can initialize the examine indexes - /// - - protected bool CanInitialize() - { - // only affects indexers that are config file based, if an index was created via code then - // this has no effect, it is assumed the index would not be created if it could not be initialized - return _configBased == false || Current.RuntimeState.Level == RuntimeLevel.Run; - } - - /// - /// 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.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 3fd6393a3c..3f9ab2ddbd 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/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index b478b8f8fc..8b54619c1e 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -31,7 +31,7 @@ namespace Umbraco.Tests.UmbracoExamine { public static ContentValueSetBuilder GetContentValueSetBuilder(PropertyEditorCollection propertyEditors) { - var contentValueSetBuilder = new ContentValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, IndexInitializer.GetMockUserService()); + var contentValueSetBuilder = new ContentValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, GetMockUserService(), true); return contentValueSetBuilder; } @@ -44,7 +44,7 @@ namespace Umbraco.Tests.UmbracoExamine public static MediaIndexPopulator GetMediaIndexRebuilder(PropertyEditorCollection propertyEditors, IMediaService mediaService) { - var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, IndexInitializer.GetMockUserService()); + var mediaValueSetBuilder = new MediaValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, GetMockUserService()); var mediaIndexDataSource = new MediaIndexPopulator(null, mediaService, mediaValueSetBuilder); return mediaIndexDataSource; } diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js index 4b54663a4d..a5e4125742 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.controller.js @@ -176,9 +176,6 @@ function ExamineManagementController($scope, umbRequestHelper, $http, $q, $timeo 'Failed to retrieve searcher details') .then(data => { vm.searcherDetails = data; - for (var s in vm.searcherDetails) { - vm.searcherDetails[s].searchType = "text"; - } }) ]) .then(() => { vm.loading = false }); diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html index b685396ffd..5f226771e9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/settings/examinemanagement.html @@ -86,8 +86,107 @@
    - - + + + + ← Back to overview + + + +
    + +
    + +
    +
    {{ vm.selectedSearcher.name }}
    +
    + +
    + + + +
    + +
    +
    Search
    +
    Search the index and view the results
    +
    + +
    + +
    + +
    +
    + + + + +
    +
    + + + + + + + + + + + + + + + + +
    ScoreIdName
    {{result.score}}{{result.id}} + {{result.values['nodeName']}}  + + ({{result.fieldCount}} fields) + +
    + +
    + + +
    + +
    + +
    +
    +
    + +
    + +
    + +
    + +
    + +
    + +
    @@ -140,33 +239,6 @@
    - - -
    - -
    -
    Index info
    -
    Lists the properties of the index
    -
    - -
    - -
    - - - - - - - -
     
    {{key}}{{val}}
    - -
    - -
    - -
    -
    @@ -200,7 +272,7 @@
    -
    +

    @@ -212,7 +284,7 @@ - +
    {{result.score}} {{result.id}} @@ -246,6 +318,35 @@ + + +
    + +
    +
    Index info
    +
    Lists the properties of the index
    +
    + +
    + +
    + + + + + + + +
     
    {{key}}{{val}}
    + +
    + +
    + +
    + + +
    @@ -292,8 +393,6 @@
    - -
    diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 13db7ce7cb..a16e9544bb 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,7 +88,7 @@ - + diff --git a/src/Umbraco.Web.UI/config/ExamineIndex.config b/src/Umbraco.Web.UI/config/ExamineIndex.config index ec0d18aa2d..833a65c8d1 100644 --- a/src/Umbraco.Web.UI/config/ExamineIndex.config +++ b/src/Umbraco.Web.UI/config/ExamineIndex.config @@ -4,7 +4,11 @@ Umbraco examine is an extensible indexer and search engine. This configuration file can be extended to create your own index sets. Index/Search providers can be defined in the UmbracoSettings.config -More information and documentation can be found on GitHub: https://github.com/Shazwazza/Examine/ +More information and documentation can be found on Our: +https://our.umbraco.com/Documentation/Reference/Searching/Examine/ +https://our.umbraco.com/Documentation/Reference/Config/ExamineIndex/ +https://our.umbraco.com/Documentation/Reference/Config/ExamineSettings/ + --> diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.config b/src/Umbraco.Web.UI/config/ExamineSettings.config index 265e5ff788..09a8f90207 100644 --- a/src/Umbraco.Web.UI/config/ExamineSettings.config +++ b/src/Umbraco.Web.UI/config/ExamineSettings.config @@ -4,7 +4,10 @@ Umbraco examine is an extensible indexer and search engine. This configuration file can be extended to add your own search/index providers. Index sets can be defined in the ExamineIndex.config if you're using the standard provider model. -More information and documentation can be found on GitHub: https://github.com/Shazwazza/Examine/ +More information and documentation can be found on Our: +https://our.umbraco.com/Documentation/Reference/Searching/Examine/ +https://our.umbraco.com/Documentation/Reference/Config/ExamineIndex/ +https://our.umbraco.com/Documentation/Reference/Config/ExamineSettings/ --> @@ -14,7 +17,8 @@ More information and documentation can be found on GitHub: https://github.com/Sh + - + diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs index f624f2cee3..c72b4439f0 100644 --- a/src/Umbraco.Web/Editors/ExamineManagementController.cs +++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs @@ -86,7 +86,8 @@ namespace Umbraco.Web.Editors { Id = x.Id, Score = x.Score, - Values = x.Values + //order the values by key + Values = new Dictionary(x.Values.OrderBy(y => y.Key).ToDictionary(y => y.Key, y => y.Value)) }) }; } diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValues.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValues.cs index 4c03fe2ccd..04876ec118 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValues.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValues.cs @@ -18,11 +18,11 @@ namespace Umbraco.Web.PropertyEditors /// public class GridPropertyIndexValues : IPropertyIndexValues { - public IEnumerable> GetIndexValues(Property property, string culture, string segment) + public IEnumerable>> GetIndexValues(Property property, string culture, string segment, bool published) { - var result = new List>(); + var result = new List>>(); - var val = property.GetValue(culture, segment); + var val = property.GetValue(culture, segment, published); //if there is a value, it's a string and it's detected as json if (val is string rawVal && rawVal.DetectIsJson()) @@ -51,7 +51,7 @@ namespace Umbraco.Web.PropertyEditors sb.Append(" "); //add the row name as an individual field - result.Add(new KeyValuePair($"{property.Alias}.{rowName}", new[] { str })); + result.Add(new KeyValuePair>($"{property.Alias}.{rowName}", new[] { str })); } } } @@ -59,10 +59,10 @@ namespace Umbraco.Web.PropertyEditors if (sb.Length > 0) { //First save the raw value to a raw field - result.Add(new KeyValuePair($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new[] { rawVal })); + result.Add(new KeyValuePair>($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new[] { rawVal })); //index the property with the combined/cleaned value - result.Add(new KeyValuePair(property.Alias, new[] { sb.ToString() })); + result.Add(new KeyValuePair>(property.Alias, new[] { sb.ToString() })); } } catch (InvalidCastException) diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index fa490433ba..6dd63e6123 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -95,16 +95,16 @@ namespace Umbraco.Web.PropertyEditors internal class RichTextPropertyIndexValues : IPropertyIndexValues { - public IEnumerable> GetIndexValues(Property property, string culture, string segment) + public IEnumerable>> GetIndexValues(Property property, string culture, string segment, bool published) { - var val = property.GetValue(culture, segment); + var val = property.GetValue(culture, segment, published); if (!(val is string strVal)) yield break; //index the stripped html values - yield return new KeyValuePair(property.Alias, new object[] { strVal.StripHtml() }); + yield return new KeyValuePair>(property.Alias, new object[] { strVal.StripHtml() }); //store the raw value - yield return new KeyValuePair($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new object[] { strVal }); + yield return new KeyValuePair>($"{UmbracoExamineIndexer.RawFieldPrefix}{property.Alias}", new object[] { strVal }); } } } diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs index 1883010582..c2fb84a3da 100644 --- a/src/Umbraco.Web/PublishedContentQuery.cs +++ b/src/Umbraco.Web/PublishedContentQuery.cs @@ -242,15 +242,12 @@ namespace Umbraco.Web var searcher = index.GetSearcher(); - if (skip == 0 && take == 0) - { - var results = searcher.Search(term, useWildCards); - totalRecords = results.TotalItemCount; - return results.ToPublishedSearchResults(_contentCache); - } + var results = skip == 0 && take == 0 + ? searcher.Search(term, true) + : searcher.Search(term, true, maxResults: skip + take); - var criteria = SearchAllFields(term, useWildCards, searcher, index); - return Search(skip, take, out totalRecords, criteria, searcher); + totalRecords = results.TotalItemCount; + return results.ToPublishedSearchResults(_contentCache); } /// @@ -280,28 +277,6 @@ namespace Umbraco.Web return results.ToPublishedSearchResults(_contentCache); } - /// - /// Creates an ISearchCriteria for searching all fields in a . - /// - private ISearchCriteria SearchAllFields(string searchText, bool useWildcards, ISearcher searcher, IIndex indexer) - { - var sc = searcher.CreateCriteria(); - - //if we're dealing with a lucene searcher, we can get all of it's indexed fields, - //else we can get the defined fields for the index. - var searchFields = (searcher is BaseLuceneSearcher luceneSearcher) - ? luceneSearcher.GetAllIndexedFields() - : indexer.FieldDefinitionCollection.Keys; - - //this is what Examine does internally to create ISearchCriteria for searching all fields - var strArray = searchText.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - - sc = useWildcards == false - ? sc.GroupedOr(searchFields, strArray).Compile() - : sc.GroupedOr(searchFields, strArray.Select(x => (IExamineValue)new ExamineValue(Examineness.ComplexWildcard, x.MultipleCharacterWildcard().Value)).ToArray()).Compile(); - return sc; - } - #endregion } diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index fa22da5ae1..1a842525a4 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -22,8 +22,10 @@ using Umbraco.Examine; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Web.Scheduling; using System.Threading.Tasks; +using Examine.LuceneEngine.Directories; using LightInject; using Umbraco.Core.Composing; +using Umbraco.Core.Strings; namespace Umbraco.Web.Search { @@ -34,7 +36,8 @@ namespace Umbraco.Web.Search public sealed class ExamineComponent : UmbracoComponentBase, IUmbracoCoreComponent { private IExamineManager _examineManager; - private IValueSetBuilder _contentValueSetBuilder; + private IContentValueSetBuilder _contentValueSetBuilder; + private IPublishedContentValueSetBuilder _publishedContentValueSetBuilder; private IValueSetBuilder _mediaValueSetBuilder; private IValueSetBuilder _memberValueSetBuilder; private static bool _disableExamineIndexing = false; @@ -71,7 +74,18 @@ namespace Umbraco.Web.Search composition.Container.RegisterSingleton(); composition.Container.RegisterSingleton(); - composition.Container.RegisterSingleton, ContentValueSetBuilder>(); + composition.Container.Register(factory => + new ContentValueSetBuilder( + factory.GetInstance(), + factory.GetInstance>(), + factory.GetInstance(), + false)); + composition.Container.Register(factory => + new ContentValueSetBuilder( + factory.GetInstance(), + factory.GetInstance>(), + factory.GetInstance(), + true)); composition.Container.RegisterSingleton, MediaValueSetBuilder>(); composition.Container.RegisterSingleton, MemberValueSetBuilder>(); } @@ -80,7 +94,8 @@ namespace Umbraco.Web.Search IExamineManager examineManager, ProfilingLogger profilingLogger, IScopeProvider scopeProvider, IUmbracoIndexesCreator indexCreator, IndexRebuilder indexRebuilder, ServiceContext services, - IValueSetBuilder contentValueSetBuilder, + IContentValueSetBuilder contentValueSetBuilder, + IPublishedContentValueSetBuilder publishedContentValueSetBuilder, IValueSetBuilder mediaValueSetBuilder, IValueSetBuilder memberValueSetBuilder) { @@ -88,6 +103,7 @@ namespace Umbraco.Web.Search _scopeProvider = scopeProvider; _examineManager = examineManager; _contentValueSetBuilder = contentValueSetBuilder; + _publishedContentValueSetBuilder = publishedContentValueSetBuilder; _mediaValueSetBuilder = mediaValueSetBuilder; _memberValueSetBuilder = memberValueSetBuilder; @@ -98,7 +114,7 @@ namespace Umbraco.Web.Search //we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the appdomain //terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock //which simply checks the existence of the lock file - DirectoryTracker.DefaultLockFactory = d => + DirectoryFactory.DefaultLockFactory = d => { var simpleFsLockFactory = new NoPrefixSimpleFsLockFactory(d); return simpleFsLockFactory; @@ -221,8 +237,106 @@ namespace Umbraco.Web.Search } } } - + #region Cache refresher updated event handlers + + /// + /// Updates indexes based on content changes + /// + /// + /// + private void ContentCacheRefresherUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) + { + if (Suspendable.ExamineEvents.CanIndex == false) + return; + + if (args.MessageType != MessageType.RefreshByPayload) + throw new NotSupportedException(); + + var contentService = _services.ContentService; + + foreach (var payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject) + { + if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) + { + // delete content entirely (with descendants) + // false: remove entirely from all indexes + DeleteIndexForEntity(payload.Id, false); + } + else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) + { + // ExamineEvents does not support RefreshAll + // just ignore that payload + // so what?! + + //fixme: Rebuild the index at this point? + } + else // RefreshNode or RefreshBranch (maybe trashed) + { + // don't try to be too clever - refresh entirely + // there has to be race conds in there ;-( + + var content = contentService.GetById(payload.Id); + if (content == null) + { + // gone fishing, remove entirely from all indexes (with descendants) + DeleteIndexForEntity(payload.Id, false); + continue; + } + + IContent published = null; + if (content.Published && contentService.IsPathPublished(content)) + published = content; + + if (published == null) + DeleteIndexForEntity(payload.Id, true); + + // just that content + ReIndexForContent(content, published != null); + + // branch + if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) + { + var masked = published == null ? null : new List(); + const int pageSize = 500; + var page = 0; + var total = long.MaxValue; + while (page * pageSize < total) + { + var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total, + //order by shallowest to deepest, this allows us to check it's published state without checking every item + ordering: Ordering.By("Path", Direction.Ascending)); + + foreach (var descendant in descendants) + { + published = null; + if (masked != null) // else everything is masked + { + if (masked.Contains(descendant.ParentId) || !descendant.Published) + masked.Add(descendant.Id); + else + published = descendant; + } + + ReIndexForContent(descendant, published != null); + } + } + } + } + + // NOTE + // + // DeleteIndexForEntity is handled by UmbracoContentIndexer.DeleteFromIndex() which takes + // care of also deleting the descendants + // + // ReIndexForContent is NOT taking care of descendants so we have to reload everything + // again in order to process the branch - we COULD improve that by just reloading the + // XML from database instead of reloading content & re-serializing! + // + // BUT ... pretty sure it is! see test "Index_Delete_Index_Item_Ensure_Heirarchy_Removed" + } + } + private void MemberCacheRefresherUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs args) { if (Suspendable.ExamineEvents.CanIndex == false) @@ -292,15 +406,18 @@ namespace Umbraco.Web.Search else // RefreshNode or RefreshBranch (maybe trashed) { var media = mediaService.GetById(payload.Id); - if (media == null || media.Trashed) + if (media == null) { // gone fishing, remove entirely DeleteIndexForEntity(payload.Id, false); continue; } + if (media.Trashed) + DeleteIndexForEntity(payload.Id, true); + // just that media - ReIndexForMedia(media, media.Trashed == false); + ReIndexForMedia(media, !media.Trashed); // branch if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) @@ -313,7 +430,7 @@ namespace Umbraco.Web.Search var descendants = mediaService.GetPagedDescendants(media.Id, page++, pageSize, out total); foreach (var descendant in descendants) { - ReIndexForMedia(descendant, descendant.Trashed == false); + ReIndexForMedia(descendant, !descendant.Trashed); } } } @@ -461,155 +578,33 @@ namespace Umbraco.Web.Search foreach (var c in contentToRefresh) { - IContent published = null; + var isPublished = false; if (c.Published) { - if (publishChecked.TryGetValue(c.ParentId, out var isPublished)) - { - //if the parent's published path has already been verified then this is published - if (isPublished) - published = c; - } - else + if (!publishChecked.TryGetValue(c.ParentId, out isPublished)) { //nothing by parent id, so query the service and cache the result for the next child to check against isPublished = _services.ContentService.IsPathPublished(c); publishChecked[c.Id] = isPublished; - if (isPublished) - published = c; } } - ReIndexForContent(c, published); + ReIndexForContent(c, isPublished); } } } - /// - /// Updates indexes based on content changes - /// - /// - /// - private void ContentCacheRefresherUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args) - { - if (Suspendable.ExamineEvents.CanIndex == false) - return; - - if (args.MessageType != MessageType.RefreshByPayload) - throw new NotSupportedException(); - - var contentService = _services.ContentService; - - foreach (var payload in (ContentCacheRefresher.JsonPayload[])args.MessageObject) - { - if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) - { - // delete content entirely (with descendants) - // false: remove entirely from all indexes - DeleteIndexForEntity(payload.Id, false); - } - else if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) - { - // ExamineEvents does not support RefreshAll - // just ignore that payload - // so what?! - - //fixme: Rebuild the index at this point? - } - else // RefreshNode or RefreshBranch (maybe trashed) - { - // don't try to be too clever - refresh entirely - // there has to be race conds in there ;-( - - var content = contentService.GetById(payload.Id); - if (content == null || content.Trashed) - { - // gone fishing, remove entirely from all indexes (with descendants) - DeleteIndexForEntity(payload.Id, false); - continue; - } - - IContent published = null; - if (content.Published && contentService.IsPathPublished(content)) - published = content; - - // just that content - ReIndexForContent(content, published); - - // branch - if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch)) - { - var masked = published == null ? null : new List(); - const int pageSize = 500; - var page = 0; - var total = long.MaxValue; - while (page * pageSize < total) - { - var descendants = contentService.GetPagedDescendants(content.Id, page++, pageSize, out total, - //order by shallowest to deepest, this allows us to check it's published state without checking every item - ordering: Ordering.By("Path", Direction.Ascending)); - - foreach (var descendant in descendants) - { - published = null; - if (masked != null) // else everything is masked - { - if (masked.Contains(descendant.ParentId) || !descendant.Published) - masked.Add(descendant.Id); - else - published = descendant; - } - - ReIndexForContent(descendant, published); - } - } - } - } - - // NOTE - // - // DeleteIndexForEntity is handled by UmbracoContentIndexer.DeleteFromIndex() which takes - // care of also deleting the descendants - // - // ReIndexForContent is NOT taking care of descendants so we have to reload everything - // again in order to process the branch - we COULD improve that by just reloading the - // XML from database instead of reloading content & re-serializing! - // - // BUT ... pretty sure it is! see test "Index_Delete_Index_Item_Ensure_Heirarchy_Removed" - } - } + #endregion #region ReIndex/Delete for entity - private void ReIndexForContent(IContent content, IContent published) - { - if (published != null && content.VersionId == published.VersionId) - { - ReIndexForContent(content); // same = both - } - else - { - if (published == null) - { - // remove 'published' - keep 'draft' - DeleteIndexForEntity(content.Id, true); - } - else - { - // index 'published' - don't overwrite 'draft' - ReIndexForContent(published, false); - } - ReIndexForContent(content, true); // index 'draft' - } - } - - private void ReIndexForContent(IContent sender, bool? supportUnpublished = null) + private void ReIndexForContent(IContent sender, bool isPublished) { var actions = DeferedActions.Get(_scopeProvider); if (actions != null) - actions.Add(new DeferedReIndexForContent(this, sender, supportUnpublished)); + actions.Add(new DeferedReIndexForContent(this, sender, isPublished)); else - DeferedReIndexForContent.Execute(this, sender, supportUnpublished); + DeferedReIndexForContent.Execute(this, sender, isPublished); } private void ReIndexForMember(IMember member) @@ -621,17 +616,17 @@ namespace Umbraco.Web.Search DeferedReIndexForMember.Execute(this, member); } - private void ReIndexForMedia(IMedia sender, bool isMediaPublished) + private void ReIndexForMedia(IMedia sender, bool isPublished) { var actions = DeferedActions.Get(_scopeProvider); if (actions != null) - actions.Add(new DeferedReIndexForMedia(this, sender, isMediaPublished)); + actions.Add(new DeferedReIndexForMedia(this, sender, isPublished)); else - DeferedReIndexForMedia.Execute(this, sender, isMediaPublished); + DeferedReIndexForMedia.Execute(this, sender, isPublished); } /// - /// Remove items from any index that doesn't support unpublished content + /// Remove items from an index /// /// /// @@ -687,30 +682,33 @@ namespace Umbraco.Web.Search { private readonly ExamineComponent _examineComponent; private readonly IContent _content; - private readonly bool? _supportUnpublished; + private readonly bool _isPublished; - public DeferedReIndexForContent(ExamineComponent examineComponent, IContent content, bool? supportUnpublished) + public DeferedReIndexForContent(ExamineComponent examineComponent, IContent content, bool isPublished) { _examineComponent = examineComponent; _content = content; - _supportUnpublished = supportUnpublished; + _isPublished = isPublished; } public override void Execute() { - Execute(_examineComponent, _content, _supportUnpublished); + Execute(_examineComponent, _content, _isPublished); } - public static void Execute(ExamineComponent examineComponent, IContent content, bool? supportUnpublished) + public static void Execute(ExamineComponent examineComponent, IContent content, bool isPublished) { - var valueSet = examineComponent._contentValueSetBuilder.GetValueSets(content).ToList(); - foreach (var index in examineComponent._examineManager.Indexes.OfType() - // only for the specified indexers - .Where(x => supportUnpublished.HasValue == false || supportUnpublished.Value == x.SupportSoftDelete) + //filter the indexers + .Where(x => isPublished || !x.PublishedValuesOnly) .Where(x => x.EnableDefaultEventHandler)) { - index.IndexItems(valueSet); + //for content we have a different builder for published vs unpublished + var builder = index.PublishedValuesOnly + ? examineComponent._publishedContentValueSetBuilder + : (IValueSetBuilder)examineComponent._contentValueSetBuilder; + + index.IndexItems(builder.GetValueSets(content)); } } } @@ -738,9 +736,8 @@ namespace Umbraco.Web.Search var valueSet = examineComponent._mediaValueSetBuilder.GetValueSets(media).ToList(); foreach (var index in examineComponent._examineManager.Indexes.OfType() - // index this item for all indexers if the media is not trashed, otherwise if the item is trashed - // then only index this for indexers supporting unpublished media - .Where(x => isPublished || (x.SupportSoftDelete)) + //filter the indexers + .Where(x => isPublished || !x.PublishedValuesOnly) .Where(x => x.EnableDefaultEventHandler)) { index.IndexItems(valueSet); @@ -768,7 +765,7 @@ namespace Umbraco.Web.Search { var valueSet = examineComponent._memberValueSetBuilder.GetValueSets(member).ToList(); foreach (var index in examineComponent._examineManager.Indexes.OfType() - //ensure that only the providers are flagged to listen execute + //filter the indexers .Where(x => x.EnableDefaultEventHandler)) { index.IndexItems(valueSet); @@ -798,9 +795,8 @@ namespace Umbraco.Web.Search { var strId = id.ToString(CultureInfo.InvariantCulture); foreach (var index in examineComponent._examineManager.Indexes.OfType() - // if keepIfUnpublished == true then only delete this item from indexes not supporting unpublished content, - // otherwise if keepIfUnpublished == false then remove from all indexes - .Where(x => keepIfUnpublished == false || x.SupportSoftDelete == false) + + .Where(x => (keepIfUnpublished && !x.PublishedValuesOnly) || !keepIfUnpublished) .Where(x => x.EnableDefaultEventHandler)) { index.DeleteFromIndex(strId); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 1ebb54248d..63908a23f9 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -62,7 +62,7 @@ - + 2.6.2.25 diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index 357c8a30a6..fbb739b5c2 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -30,13 +30,11 @@ namespace Umbraco.Web private readonly IPublishedContent _currentPage; private readonly IPublishedContentQuery _iQuery; private readonly ServiceContext _services; - private readonly CacheHelper _appCache; - + private IUmbracoComponentRenderer _componentRenderer; private PublishedContentQuery _query; private MembershipHelper _membershipHelper; private ITagQuery _tag; - private IDataTypeService _dataTypeService; private ICultureDictionary _cultureDictionary; #region Constructors @@ -48,34 +46,21 @@ namespace Umbraco.Web internal UmbracoHelper(UmbracoContext umbracoContext, IPublishedContent content, IPublishedContentQuery query, ITagQuery tagQuery, - IDataTypeService dataTypeService, ICultureDictionary cultureDictionary, IUmbracoComponentRenderer componentRenderer, MembershipHelper membershipHelper, - ServiceContext services, - CacheHelper appCache) + ServiceContext services) { - if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext)); - if (content == null) throw new ArgumentNullException(nameof(content)); - if (query == null) throw new ArgumentNullException(nameof(query)); if (tagQuery == null) throw new ArgumentNullException(nameof(tagQuery)); - if (dataTypeService == null) throw new ArgumentNullException(nameof(dataTypeService)); - if (cultureDictionary == null) throw new ArgumentNullException(nameof(cultureDictionary)); - if (componentRenderer == null) throw new ArgumentNullException(nameof(componentRenderer)); - if (membershipHelper == null) throw new ArgumentNullException(nameof(membershipHelper)); - if (services == null) throw new ArgumentNullException(nameof(services)); - if (appCache == null) throw new ArgumentNullException(nameof(appCache)); - _umbracoContext = umbracoContext; + _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); _tag = new TagQuery(tagQuery); - _dataTypeService = dataTypeService; - _cultureDictionary = cultureDictionary; - _componentRenderer = componentRenderer; - _membershipHelper = membershipHelper; - _currentPage = content; - _iQuery = query; - _services = services; - _appCache = appCache; + _cultureDictionary = cultureDictionary ?? throw new ArgumentNullException(nameof(cultureDictionary)); + _componentRenderer = componentRenderer ?? throw new ArgumentNullException(nameof(componentRenderer)); + _membershipHelper = membershipHelper ?? throw new ArgumentNullException(nameof(membershipHelper)); + _currentPage = content ?? throw new ArgumentNullException(nameof(content)); + _iQuery = query ?? throw new ArgumentNullException(nameof(query)); + _services = services ?? throw new ArgumentNullException(nameof(services)); } /// @@ -93,13 +78,11 @@ namespace Umbraco.Web /// An Umbraco context. /// A content item. /// A services context. - /// An application cache helper. /// Sets the current page to the supplied content item. - public UmbracoHelper(UmbracoContext umbracoContext, ServiceContext services, CacheHelper appCache, IPublishedContent content) - : this(umbracoContext, services, appCache) + public UmbracoHelper(UmbracoContext umbracoContext, ServiceContext services, IPublishedContent content) + : this(umbracoContext, services) { - if (content == null) throw new ArgumentNullException(nameof(content)); - _currentPage = content; + _currentPage = content ?? throw new ArgumentNullException(nameof(content)); } /// @@ -107,19 +90,13 @@ namespace Umbraco.Web /// /// An Umbraco context. /// A services context. - /// An application cache helper. /// Sets the current page to the context's published content request's content item. - public UmbracoHelper(UmbracoContext umbracoContext, ServiceContext services, CacheHelper appCache) + public UmbracoHelper(UmbracoContext umbracoContext, ServiceContext services) { - if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext)); - if (services == null) throw new ArgumentNullException(nameof(services)); - if (appCache == null) throw new ArgumentNullException(nameof(appCache)); - - _umbracoContext = umbracoContext; + _services = services ?? throw new ArgumentNullException(nameof(services)); + _umbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); if (_umbracoContext.IsFrontEndUmbracoRequest) _currentPage = _umbracoContext.PublishedRequest.PublishedContent; - _services = services; - _appCache = appCache; } #endregion @@ -133,7 +110,7 @@ namespace Umbraco.Web /// /// Gets the query context. /// - public PublishedContentQuery ContentQuery => _query ?? + public IPublishedContentQuery ContentQuery => _query ?? (_query = _iQuery != null ? new PublishedContentQuery(_iQuery) : new PublishedContentQuery(UmbracoContext.ContentCache, UmbracoContext.MediaCache)); @@ -162,12 +139,6 @@ namespace Umbraco.Web /// public UrlProvider UrlProvider => UmbracoContext.UrlProvider; - /// - /// Gets the datatype service. - /// - private IDataTypeService DataTypeService => _dataTypeService - ?? (_dataTypeService = _services.DataTypeService); - /// /// Gets the component renderer. /// @@ -819,11 +790,11 @@ namespace Umbraco.Web /// /// /// - /// + /// /// - public IEnumerable Search(string term, bool useWildCards = true, string searchProvider = null) + public IEnumerable Search(string term, bool useWildCards = true, string indexName = null) { - return ContentQuery.Search(term, useWildCards, searchProvider); + return ContentQuery.Search(term, useWildCards, indexName); } /// @@ -834,11 +805,11 @@ namespace Umbraco.Web /// /// /// - /// + /// /// - public IEnumerable TypedSearch(int skip, int take, out long totalRecords, string term, bool useWildCards = true, string searchProvider = null) + public IEnumerable Search(int skip, int take, out long totalRecords, string term, bool useWildCards = true, string indexName = null) { - return ContentQuery.Search(skip, take, out totalRecords, term, useWildCards, searchProvider); + return ContentQuery.Search(skip, take, out totalRecords, term, useWildCards, indexName); } /// @@ -850,7 +821,7 @@ namespace Umbraco.Web /// /// /// - public IEnumerable TypedSearch(int skip, int take, out long totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) + public IEnumerable Search(int skip, int take, out long totalRecords, Examine.SearchCriteria.ISearchCriteria criteria, Examine.Providers.BaseSearchProvider searchProvider = null) { return ContentQuery.Search(skip, take, out totalRecords, criteria, searchProvider); } From 1eba69b0707fdeb99ca840ba2d4c42212e905806 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 5 Dec 2018 16:59:33 +1100 Subject: [PATCH 36/44] Fixes build with ctor change to UmbracoHelper --- src/Umbraco.Web/Editors/TemplateQueryController.cs | 7 +++---- .../Mvc/EnsurePublishedContentRequestAttribute.cs | 2 +- src/Umbraco.Web/Mvc/PluginController.cs | 2 +- src/Umbraco.Web/Mvc/UmbracoController.cs | 2 +- src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs | 4 ++-- .../ValueConverters/MacroContainerValueConverter.cs | 6 ++---- .../ValueConverters/RelatedLinksLegacyValueConverter.cs | 6 ++---- .../ValueConverters/RteMacroRenderingValueConverter.cs | 6 ++---- src/Umbraco.Web/RelatedLinksTypeConverter.cs | 2 +- src/Umbraco.Web/UI/Controls/UmbracoControl.cs | 6 +++--- src/Umbraco.Web/UI/Controls/UmbracoUserControl.cs | 7 +++---- src/Umbraco.Web/UmbracoAuthorizedHttpHandler.cs | 6 ++---- src/Umbraco.Web/UmbracoHttpHandler.cs | 6 +++--- src/Umbraco.Web/UmbracoWebService.cs | 2 +- src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs | 2 +- src/Umbraco.Web/umbraco.presentation/item.cs | 2 +- 16 files changed, 29 insertions(+), 39 deletions(-) diff --git a/src/Umbraco.Web/Editors/TemplateQueryController.cs b/src/Umbraco.Web/Editors/TemplateQueryController.cs index f614d38ecb..0d0438138a 100644 --- a/src/Umbraco.Web/Editors/TemplateQueryController.cs +++ b/src/Umbraco.Web/Editors/TemplateQueryController.cs @@ -59,8 +59,7 @@ namespace Umbraco.Web.Editors public QueryResultModel PostTemplateQuery(QueryModel model) { - var umbraco = new UmbracoHelper(UmbracoContext, Services, ApplicationCache); - + var queryResult = new QueryResultModel(); var sb = new StringBuilder(); @@ -72,7 +71,7 @@ namespace Umbraco.Web.Editors timer.Start(); - var currentPage = umbraco.ContentAtRoot().FirstOrDefault(); + var currentPage = Umbraco.ContentAtRoot().FirstOrDefault(); timer.Stop(); var pointerNode = currentPage; @@ -80,7 +79,7 @@ namespace Umbraco.Web.Editors // adjust the "FROM" if (model != null && model.Source != null && model.Source.Id > 0) { - var targetNode = umbraco.Content(model.Source.Id); + var targetNode = Umbraco.Content(model.Source.Id); if (targetNode != null) { diff --git a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs index 163284cf86..56d00e8ec9 100644 --- a/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs +++ b/src/Umbraco.Web/Mvc/EnsurePublishedContentRequestAttribute.cs @@ -80,7 +80,7 @@ namespace Umbraco.Web.Mvc /// Exposes an UmbracoHelper /// protected UmbracoHelper Umbraco => _helper - ?? (_helper = new UmbracoHelper(Current.UmbracoContext, Current.Services, Current.ApplicationCache)); + ?? (_helper = new UmbracoHelper(Current.UmbracoContext, Current.Services)); public override void OnActionExecuted(ActionExecutedContext filterContext) { diff --git a/src/Umbraco.Web/Mvc/PluginController.cs b/src/Umbraco.Web/Mvc/PluginController.cs index f2742ce9f0..2b4ebc3f71 100644 --- a/src/Umbraco.Web/Mvc/PluginController.cs +++ b/src/Umbraco.Web/Mvc/PluginController.cs @@ -82,7 +82,7 @@ namespace Umbraco.Web.Mvc get { return _umbracoHelper - ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services, ApplicationCache)); + ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services)); } internal set // tests { diff --git a/src/Umbraco.Web/Mvc/UmbracoController.cs b/src/Umbraco.Web/Mvc/UmbracoController.cs index 563b315de5..b8b55ae35d 100644 --- a/src/Umbraco.Web/Mvc/UmbracoController.cs +++ b/src/Umbraco.Web/Mvc/UmbracoController.cs @@ -83,7 +83,7 @@ namespace Umbraco.Web.Mvc /// Gets the Umbraco helper. /// public UmbracoHelper Umbraco => _umbracoHelper - ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services, ApplicationCache)); + ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services)); /// /// Gets the web security helper. diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs index 6fa6750031..bd1dd1ed6b 100644 --- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs +++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs @@ -98,8 +98,8 @@ namespace Umbraco.Web.Mvc if (content == null && model is IContentModel) content = ((IContentModel) model).Content; _helper = content == null - ? new UmbracoHelper(UmbracoContext, Services, ApplicationCache) - : new UmbracoHelper(UmbracoContext, Services, ApplicationCache, content); + ? new UmbracoHelper(UmbracoContext, Services) + : new UmbracoHelper(UmbracoContext, Services, content); return _helper; } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs index cd2f4e5384..5a0fc87cb0 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MacroContainerValueConverter.cs @@ -21,13 +21,11 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ServiceContext _services; - private readonly CacheHelper _appCache; - public MacroContainerValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, CacheHelper appCache) + public MacroContainerValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _services = services ?? throw new ArgumentNullException(nameof(services)); - _appCache = appCache ?? throw new ArgumentNullException(nameof(appCache)); } public override bool IsConverter(PublishedPropertyType propertyType) @@ -50,7 +48,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { var sb = new StringBuilder(); - var umbracoHelper = new UmbracoHelper(umbracoContext, _services, _appCache); + var umbracoHelper = new UmbracoHelper(umbracoContext, _services); MacroTagParser.ParseMacros( source, //callback for when text block is found diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksLegacyValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksLegacyValueConverter.cs index 407ea481c4..6c2a4331d0 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksLegacyValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RelatedLinksLegacyValueConverter.cs @@ -24,13 +24,11 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ILogger _logger; private readonly ServiceContext _services; - private readonly CacheHelper _appCache; - public RelatedLinksLegacyValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, CacheHelper appCache, ILogger logger) + public RelatedLinksLegacyValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, ILogger logger) { _umbracoContextAccessor = umbracoContextAccessor; _services = services; - _appCache = appCache; _logger = logger; } @@ -56,7 +54,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters //update the internal links if we have a context if (UmbracoContext.Current != null) { - var helper = new UmbracoHelper(_umbracoContextAccessor.UmbracoContext, _services, _appCache); + var helper = new UmbracoHelper(_umbracoContextAccessor.UmbracoContext, _services); foreach (var a in obj) { var type = a.Value("type"); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index 74a5fb313c..7a089b6f9e 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -22,7 +22,6 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly ServiceContext _services; - private readonly CacheHelper _appCache; public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType) { @@ -31,11 +30,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters return PropertyCacheLevel.Snapshot; } - public RteMacroRenderingValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services, CacheHelper appCache) + public RteMacroRenderingValueConverter(IUmbracoContextAccessor umbracoContextAccessor, ServiceContext services) { _umbracoContextAccessor = umbracoContextAccessor; _services = services; - _appCache = appCache; } // NOT thread-safe over a request because it modifies the @@ -49,7 +47,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters { var sb = new StringBuilder(); - var umbracoHelper = new UmbracoHelper(umbracoContext, _services, _appCache); + var umbracoHelper = new UmbracoHelper(umbracoContext, _services); MacroTagParser.ParseMacros( source, //callback for when text block is found diff --git a/src/Umbraco.Web/RelatedLinksTypeConverter.cs b/src/Umbraco.Web/RelatedLinksTypeConverter.cs index 9ed9398d2e..647959b920 100644 --- a/src/Umbraco.Web/RelatedLinksTypeConverter.cs +++ b/src/Umbraco.Web/RelatedLinksTypeConverter.cs @@ -92,7 +92,7 @@ namespace Umbraco.Web } //DO NOT assign to _umbracoHelper variable, this is a singleton class and we cannot assign this based on an UmbracoHelper which is request based - return new UmbracoHelper(UmbracoContext.Current, Current.Services, Current.ApplicationCache); + return new UmbracoHelper(UmbracoContext.Current, Current.Services); } } } diff --git a/src/Umbraco.Web/UI/Controls/UmbracoControl.cs b/src/Umbraco.Web/UI/Controls/UmbracoControl.cs index 744b77ac50..923ab00c6b 100644 --- a/src/Umbraco.Web/UI/Controls/UmbracoControl.cs +++ b/src/Umbraco.Web/UI/Controls/UmbracoControl.cs @@ -16,10 +16,10 @@ namespace Umbraco.Web.UI.Controls { private UrlHelper _url; - protected UmbracoControl(UmbracoContext umbracoContext, ServiceContext services, CacheHelper appCache) + protected UmbracoControl(UmbracoContext umbracoContext, ServiceContext services) { UmbracoContext = umbracoContext ?? throw new ArgumentNullException(nameof(umbracoContext)); - Umbraco = new UmbracoHelper(umbracoContext, services, appCache); + Umbraco = new UmbracoHelper(umbracoContext, services); // fixme inject somehow Logger = Current.Logger; @@ -31,7 +31,7 @@ namespace Umbraco.Web.UI.Controls /// Empty constructor, uses Singleton to resolve the UmbracoContext. /// protected UmbracoControl() - : this(Current.UmbracoContext, Current.Services, Current.ApplicationCache) + : this(Current.UmbracoContext, Current.Services) { } /// diff --git a/src/Umbraco.Web/UI/Controls/UmbracoUserControl.cs b/src/Umbraco.Web/UI/Controls/UmbracoUserControl.cs index 5359592578..2a2aa43bb9 100644 --- a/src/Umbraco.Web/UI/Controls/UmbracoUserControl.cs +++ b/src/Umbraco.Web/UI/Controls/UmbracoUserControl.cs @@ -27,12 +27,11 @@ namespace Umbraco.Web.UI.Controls /// /// /// - /// - protected UmbracoUserControl(UmbracoContext umbracoContext, ServiceContext services, CacheHelper appCache) + protected UmbracoUserControl(UmbracoContext umbracoContext, ServiceContext services) { if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext)); UmbracoContext = umbracoContext; - Umbraco = new UmbracoHelper(umbracoContext, services, appCache); + Umbraco = new UmbracoHelper(umbracoContext, services); Members = new MembershipHelper(umbracoContext); // fixme inject somehow @@ -45,7 +44,7 @@ namespace Umbraco.Web.UI.Controls /// Empty constructor, uses Singleton to resolve the UmbracoContext /// protected UmbracoUserControl() - : this(Current.UmbracoContext, Current.Services, Current.ApplicationCache) + : this(Current.UmbracoContext, Current.Services) { } // for debugging purposes diff --git a/src/Umbraco.Web/UmbracoAuthorizedHttpHandler.cs b/src/Umbraco.Web/UmbracoAuthorizedHttpHandler.cs index 42e3708d1c..1d8475111f 100644 --- a/src/Umbraco.Web/UmbracoAuthorizedHttpHandler.cs +++ b/src/Umbraco.Web/UmbracoAuthorizedHttpHandler.cs @@ -13,13 +13,11 @@ namespace Umbraco.Web protected UmbracoAuthorizedHttpHandler() { } - protected UmbracoAuthorizedHttpHandler(UmbracoContext umbracoContext, ServiceContext services, CacheHelper appCache) - : base(umbracoContext, services, appCache) + protected UmbracoAuthorizedHttpHandler(UmbracoContext umbracoContext, ServiceContext services) + : base(umbracoContext, services) { } - private bool _hasValidated = false; - /// /// Checks if the umbraco context id is valid /// diff --git a/src/Umbraco.Web/UmbracoHttpHandler.cs b/src/Umbraco.Web/UmbracoHttpHandler.cs index e64c4c5fe1..e3a15faf24 100644 --- a/src/Umbraco.Web/UmbracoHttpHandler.cs +++ b/src/Umbraco.Web/UmbracoHttpHandler.cs @@ -17,14 +17,14 @@ namespace Umbraco.Web private UrlHelper _url; protected UmbracoHttpHandler() - : this(Current.UmbracoContext, Current.Services, Current.ApplicationCache) + : this(Current.UmbracoContext, Current.Services) { } - protected UmbracoHttpHandler(UmbracoContext umbracoContext, ServiceContext services, CacheHelper appCache) + protected UmbracoHttpHandler(UmbracoContext umbracoContext, ServiceContext services) { if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext)); UmbracoContext = umbracoContext; - Umbraco = new UmbracoHelper(umbracoContext, services, appCache); + Umbraco = new UmbracoHelper(umbracoContext, services); // fixme inject somehow Logger = Current.Logger; diff --git a/src/Umbraco.Web/UmbracoWebService.cs b/src/Umbraco.Web/UmbracoWebService.cs index 20db7208b5..95764e40a7 100644 --- a/src/Umbraco.Web/UmbracoWebService.cs +++ b/src/Umbraco.Web/UmbracoWebService.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web protected UmbracoWebService() { UmbracoContext = Current.UmbracoContext; - Umbraco = new UmbracoHelper(UmbracoContext, Current.Services, Current.ApplicationCache); + Umbraco = new UmbracoHelper(UmbracoContext, Current.Services); Logger = Current.Logger; ProfilingLogger = Current.ProfilingLogger; diff --git a/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs b/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs index eff5a9cee7..56cc884d39 100644 --- a/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs +++ b/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs @@ -95,7 +95,7 @@ namespace Umbraco.Web.WebApi /// Gets the Umbraco helper. /// public UmbracoHelper Umbraco => _umbracoHelper - ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services, ApplicationCache)); + ?? (_umbracoHelper = new UmbracoHelper(UmbracoContext, Services)); /// /// Gets the web security helper. diff --git a/src/Umbraco.Web/umbraco.presentation/item.cs b/src/Umbraco.Web/umbraco.presentation/item.cs index d15d3d33c1..74cfb9bf25 100644 --- a/src/Umbraco.Web/umbraco.presentation/item.cs +++ b/src/Umbraco.Web/umbraco.presentation/item.cs @@ -60,7 +60,7 @@ namespace umbraco if (_fieldName.StartsWith("#")) { - var umbHelper = new UmbracoHelper(Current.UmbracoContext, Current.Services, Current.ApplicationCache); + var umbHelper = new UmbracoHelper(Current.UmbracoContext, Current.Services); _fieldContent = umbHelper.GetDictionaryValue(_fieldName.Substring(1, _fieldName.Length - 1)); } From 11d8d17156a389b1c4c76742ed17715773b15f80 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 5 Dec 2018 17:00:51 +1100 Subject: [PATCH 37/44] Fixes build with ctor change to UmbracoHelper --- .../ControllerTesting/TestControllerActivatorBase.cs | 4 +--- src/Umbraco.Tests/Testing/TestingTests/MockTests.cs | 4 +--- src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs index fe0e46941a..c65faf76c9 100644 --- a/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ b/src/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs @@ -169,12 +169,10 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting Mock.Of(), mockedTypedContent, Mock.Of(), - Mock.Of(), Mock.Of(), Mock.Of(), membershipHelper, - serviceContext, - CacheHelper.NoCache); + serviceContext); return CreateController(controllerType, request, umbHelper); } diff --git a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs index e79e504a69..d5f5778d1a 100644 --- a/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs +++ b/src/Umbraco.Tests/Testing/TestingTests/MockTests.cs @@ -62,12 +62,10 @@ namespace Umbraco.Tests.Testing.TestingTests Mock.Of(), Mock.Of(), Mock.Of(), - Mock.Of(), Mock.Of(), Mock.Of(), new MembershipHelper(umbracoContext, Mock.Of(), Mock.Of()), - new ServiceContext(), - CacheHelper.CreateDisabledCacheHelper()); + new ServiceContext()); Assert.Pass(); } diff --git a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs index 4186c56636..81f338da87 100644 --- a/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/SurfaceControllerTests.cs @@ -125,12 +125,10 @@ namespace Umbraco.Tests.Web.Mvc //return mock of IPublishedContent for any call to GetById Mock.Of(content => content.Id == 2)), Mock.Of(), - Mock.Of(), Mock.Of(), Mock.Of(), new MembershipHelper(umbracoContext, Mock.Of(), Mock.Of()), - new ServiceContext(), - CacheHelper.CreateDisabledCacheHelper()); + new ServiceContext()); var ctrl = new TestSurfaceController { UmbracoContext = umbracoContext, Umbraco = helper }; var result = ctrl.GetContent(2) as PublishedContentResult; From cb9eec97e21c3497861bcac84a6a0ce4c4709f0e Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 5 Dec 2018 17:20:48 +1100 Subject: [PATCH 38/44] Fixes tests --- .../ContentValueSetValidator.cs | 4 ++-- .../UmbracoExamine/IndexInitializer.cs | 10 ++++----- src/Umbraco.Tests/UmbracoExamine/IndexTest.cs | 16 +++++++------- .../UmbracoExamine/SearchTests.cs | 2 +- .../UmbracoContentValueSetValidatorTests.cs | 22 +++++++++---------- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Umbraco.Examine/ContentValueSetValidator.cs b/src/Umbraco.Examine/ContentValueSetValidator.cs index d671d101d6..eabf15f672 100644 --- a/src/Umbraco.Examine/ContentValueSetValidator.cs +++ b/src/Umbraco.Examine/ContentValueSetValidator.cs @@ -43,7 +43,7 @@ namespace Umbraco.Examine var recycleBinId = category == IndexTypes.Content ? Constants.System.RecycleBinContent : Constants.System.RecycleBinMedia; //check for recycle bin - if (!PublishedValuesOnly) + if (PublishedValuesOnly) { if (path.Contains(string.Concat(",", recycleBinId, ","))) return false; @@ -90,7 +90,7 @@ namespace Umbraco.Examine var isFiltered = baseValidate == ValueSetValidationResult.Filtered; //check for published content - if (valueSet.Category == IndexTypes.Content && !PublishedValuesOnly) + if (valueSet.Category == IndexTypes.Content && PublishedValuesOnly) { if (!valueSet.Values.TryGetValue(UmbracoExamineIndexer.PublishedFieldName, out var published)) return ValueSetValidationResult.Failed; diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 8b54619c1e..1f849fc1ce 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -29,15 +29,15 @@ namespace Umbraco.Tests.UmbracoExamine /// internal static class IndexInitializer { - public static ContentValueSetBuilder GetContentValueSetBuilder(PropertyEditorCollection propertyEditors) + public static ContentValueSetBuilder GetContentValueSetBuilder(PropertyEditorCollection propertyEditors, bool publishedValuesOnly) { - var contentValueSetBuilder = new ContentValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, GetMockUserService(), true); + var contentValueSetBuilder = new ContentValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, GetMockUserService(), publishedValuesOnly); return contentValueSetBuilder; } - public static ContentIndexPopulator GetContentIndexRebuilder(PropertyEditorCollection propertyEditors, IContentService contentService, ISqlContext sqlContext) + public static ContentIndexPopulator GetContentIndexRebuilder(PropertyEditorCollection propertyEditors, IContentService contentService, ISqlContext sqlContext, bool publishedValuesOnly) { - var contentValueSetBuilder = GetContentValueSetBuilder(propertyEditors); + var contentValueSetBuilder = GetContentValueSetBuilder(propertyEditors, publishedValuesOnly); var contentIndexDataSource = new ContentIndexPopulator(true, null, contentService, sqlContext, contentValueSetBuilder); return contentIndexDataSource; } @@ -160,7 +160,7 @@ namespace Umbraco.Tests.UmbracoExamine analyzer = new StandardAnalyzer(Version.LUCENE_30); if (validator == null) - validator = new ContentValueSetValidator(false); + validator = new ContentValueSetValidator(true); var i = new UmbracoContentIndexer( "testIndexer", diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs index fe8732eeeb..78bdb37cae 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexTest.cs @@ -30,11 +30,11 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Property_Data_With_Value_Indexer() { - var contentValueSetBuilder = IndexInitializer.GetContentValueSetBuilder(Container.GetInstance()); + var contentValueSetBuilder = IndexInitializer.GetContentValueSetBuilder(Container.GetInstance(), false); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - validator: new ContentValueSetValidator(true))) + validator: new ContentValueSetValidator(false))) using (indexer.ProcessNonAsync()) { indexer.CreateIndex(); @@ -122,12 +122,12 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Rebuild_Index() { - var contentRebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext); + var contentRebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false); var mediaRebuilder = IndexInitializer.GetMediaIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockMediaService()); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - validator: new ContentValueSetValidator(true))) + validator: new ContentValueSetValidator(false))) using (indexer.ProcessNonAsync()) { contentRebuilder.RegisterIndex(indexer.Name); @@ -152,7 +152,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Protected_Content_Not_Indexed() { - var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext); + var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false); using (var luceneDir = new RandomIdRamDirectory()) @@ -280,10 +280,10 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Index_Reindex_Content() { - var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext); + var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir, - validator: new ContentValueSetValidator(true))) + validator: new ContentValueSetValidator(false))) using (indexer.ProcessNonAsync()) { rebuilder.RegisterIndex(indexer.Name); @@ -322,7 +322,7 @@ namespace Umbraco.Tests.UmbracoExamine public void Index_Delete_Index_Item_Ensure_Heirarchy_Removed() { - var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext); + var rebuilder = IndexInitializer.GetContentIndexRebuilder(Container.GetInstance(), IndexInitializer.GetMockContentService(), ScopeProvider.SqlContext, false); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir)) diff --git a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs index 85cb14e1a9..a01d49cc22 100644 --- a/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/SearchTests.cs @@ -56,7 +56,7 @@ namespace Umbraco.Tests.UmbracoExamine allRecs); var propertyEditors = Container.GetInstance(); - var rebuilder = IndexInitializer.GetContentIndexRebuilder(propertyEditors, contentService, ScopeProvider.SqlContext); + var rebuilder = IndexInitializer.GetContentIndexRebuilder(propertyEditors, contentService, ScopeProvider.SqlContext, true); using (var luceneDir = new RandomIdRamDirectory()) using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, luceneDir)) diff --git a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs index 6458cd2a80..934dd34503 100644 --- a/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs +++ b/src/Umbraco.Tests/UmbracoExamine/UmbracoContentValueSetValidatorTests.cs @@ -17,7 +17,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Invalid_Category() { - var validator = new ContentValueSetValidator(true, true, Mock.Of()); + var validator = new ContentValueSetValidator(false, true, Mock.Of()); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); Assert.AreEqual(ValueSetValidationResult.Valid, result); @@ -33,7 +33,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Must_Have_Path() { - var validator = new ContentValueSetValidator(true, true, Mock.Of()); + var validator = new ContentValueSetValidator(false, true, Mock.Of()); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world" })); Assert.AreEqual(ValueSetValidationResult.Failed, result); @@ -45,7 +45,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Parent_Id() { - var validator = new ContentValueSetValidator(true, true, Mock.Of(), 555); + var validator = new ContentValueSetValidator(false, true, Mock.Of(), 555); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); Assert.AreEqual(ValueSetValidationResult.Filtered, result); @@ -111,7 +111,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Inclusion_Type_List() { - var validator = new ContentValueSetValidator(true, true, Mock.Of(), + var validator = new ContentValueSetValidator(false, true, Mock.Of(), includeItemTypes: new List { "include-content" }); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); @@ -127,7 +127,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Exclusion_Type_List() { - var validator = new ContentValueSetValidator(true, true, Mock.Of(), + var validator = new ContentValueSetValidator(false, true, Mock.Of(), excludeItemTypes: new List { "exclude-content" }); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, "test-content", new { hello = "world", path = "-1,555" })); @@ -143,7 +143,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Inclusion_Exclusion_Type_List() { - var validator = new ContentValueSetValidator(true, true, Mock.Of(), + var validator = new ContentValueSetValidator(false, true, Mock.Of(), includeItemTypes: new List { "include-content", "exclude-content" }, excludeItemTypes: new List { "exclude-content" }); @@ -163,7 +163,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Recycle_Bin_Content() { - var validator = new ContentValueSetValidator(false, false, Mock.Of()); + var validator = new ContentValueSetValidator(true, false, Mock.Of()); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,-20,555" })); Assert.AreEqual(ValueSetValidationResult.Failed, result); @@ -187,7 +187,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Recycle_Bin_Media() { - var validator = new ContentValueSetValidator(false, false, Mock.Of()); + var validator = new ContentValueSetValidator(true, false, Mock.Of()); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Media, new { hello = "world", path = "-1,-21,555" })); Assert.AreEqual(ValueSetValidationResult.Filtered, result); @@ -203,7 +203,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Published_Only() { - var validator = new ContentValueSetValidator(false, true, Mock.Of()); + var validator = new ContentValueSetValidator(true, true, Mock.Of()); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); Assert.AreEqual(ValueSetValidationResult.Failed, result); @@ -230,7 +230,7 @@ namespace Umbraco.Tests.UmbracoExamine [Test] public void Published_Only_With_Variants() { - var validator = new ContentValueSetValidator(false, true, Mock.Of()); + var validator = new ContentValueSetValidator(true, true, Mock.Of()); var result = validator.Validate(new ValueSet("555", IndexTypes.Content, new Dictionary @@ -288,7 +288,7 @@ namespace Umbraco.Tests.UmbracoExamine .Returns(Attempt.Succeed(new PublicAccessEntry(Guid.NewGuid(), 555, 444, 333, Enumerable.Empty()))); publicAccessService.Setup(x => x.IsProtected("-1,777")) .Returns(Attempt.Fail()); - var validator = new ContentValueSetValidator(true, false, publicAccessService.Object); + var validator = new ContentValueSetValidator(false, false, publicAccessService.Object); var result = validator.Validate(ValueSet.FromObject("555", IndexTypes.Content, new { hello = "world", path = "-1,555" })); Assert.AreEqual(ValueSetValidationResult.Filtered, result); From 24300e76f96e5c51464077eb86c7db721cea0213 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 5 Dec 2018 17:42:31 +1100 Subject: [PATCH 39/44] oops committed a config change --- src/Umbraco.Web.UI/config/ExamineSettings.config | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.config b/src/Umbraco.Web.UI/config/ExamineSettings.config index 09a8f90207..f0fd2b69fc 100644 --- a/src/Umbraco.Web.UI/config/ExamineSettings.config +++ b/src/Umbraco.Web.UI/config/ExamineSettings.config @@ -17,7 +17,6 @@ https://our.umbraco.com/Documentation/Reference/Config/ExamineSettings/ - From c74fc7a8f99d59b708fc945aaadae7c0c77d7a90 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 5 Dec 2018 12:57:23 +0100 Subject: [PATCH 40/44] Misc cleanup and renaming --- src/Umbraco.Core/Constants-Indexes.cs | 2 - src/Umbraco.Core/EnumerableExtensions.cs | 1 + .../PropertyEditors/DataEditor.cs | 6 +- ...cs => DefaultPropertyIndexValueFactory.cs} | 5 +- .../PropertyEditors/IDataEditor.cs | 6 +- ...alues.cs => IPropertyIndexValueFactory.cs} | 7 +- src/Umbraco.Core/Services/IContentService.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 4 +- src/Umbraco.Examine/BaseValueSetBuilder.cs | 2 +- .../EnumeratorBenchmarks.cs | 28 + .../Umbraco.Tests.Benchmarks.csproj | 3 +- src/Umbraco.Web.UI.Client/package-lock.json | 5334 ++++++++--------- .../PropertyEditors/GridPropertyEditor.cs | 2 +- ...es.cs => GridPropertyIndexValueFactory.cs} | 2 +- .../NestedContentPropertyEditor.cs | 2 +- .../PropertyEditors/RichTextPropertyEditor.cs | 4 +- src/Umbraco.Web/Search/ExamineComponent.cs | 1 + src/Umbraco.Web/Umbraco.Web.csproj | 2 +- 18 files changed, 2723 insertions(+), 2690 deletions(-) rename src/Umbraco.Core/PropertyEditors/{DefaultPropertyIndexValues.cs => DefaultPropertyIndexValueFactory.cs} (66%) rename src/Umbraco.Core/PropertyEditors/{IPropertyIndexValues.cs => IPropertyIndexValueFactory.cs} (61%) create mode 100644 src/Umbraco.Tests.Benchmarks/EnumeratorBenchmarks.cs rename src/Umbraco.Web/PropertyEditors/{GridPropertyIndexValues.cs => GridPropertyIndexValueFactory.cs} (97%) diff --git a/src/Umbraco.Core/Constants-Indexes.cs b/src/Umbraco.Core/Constants-Indexes.cs index ecf143e83f..c73a170b62 100644 --- a/src/Umbraco.Core/Constants-Indexes.cs +++ b/src/Umbraco.Core/Constants-Indexes.cs @@ -5,7 +5,6 @@ namespace Umbraco.Core { public static partial class Constants { - public static class UmbracoIndexes { public const string InternalIndexName = InternalIndexPath + "Index"; @@ -15,7 +14,6 @@ namespace Umbraco.Core public const string InternalIndexPath = "Internal"; public const string ExternalIndexPath = "External"; public const string MembersIndexPath = "Members"; - } } } diff --git a/src/Umbraco.Core/EnumerableExtensions.cs b/src/Umbraco.Core/EnumerableExtensions.cs index 2563d2e5dc..4fa013c095 100644 --- a/src/Umbraco.Core/EnumerableExtensions.cs +++ b/src/Umbraco.Core/EnumerableExtensions.cs @@ -17,6 +17,7 @@ namespace Umbraco.Core /// An IEnumerable{T} consisting of a single item. public static IEnumerable Yield(this T item) { + // see EnumeratorBenchmarks - this is faster, and allocates less, than returning an array yield return item; } diff --git a/src/Umbraco.Core/PropertyEditors/DataEditor.cs b/src/Umbraco.Core/PropertyEditors/DataEditor.cs index 3e9d9be720..f3fc4f669b 100644 --- a/src/Umbraco.Core/PropertyEditors/DataEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/DataEditor.cs @@ -153,10 +153,8 @@ namespace Umbraco.Core.PropertyEditors set => _defaultConfiguration = value; } - /// - /// Returns the value indexer for this editor - /// - public virtual IPropertyIndexValues PropertyIndexValues => new DefaultPropertyIndexValues(); + /// + public virtual IPropertyIndexValueFactory PropertyIndexValueFactory => new DefaultPropertyIndexValueFactory(); /// /// Creates a value editor instance. diff --git a/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValues.cs b/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs similarity index 66% rename from src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValues.cs rename to src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs index c6caab86d8..86e1621441 100644 --- a/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValues.cs +++ b/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs @@ -4,10 +4,11 @@ using Umbraco.Core.Models; namespace Umbraco.Core.PropertyEditors { /// - /// Returns a single field to index containing the property value + /// Provides a default implementation for , returning a single field to index containing the property value. /// - public class DefaultPropertyIndexValues : IPropertyIndexValues + public class DefaultPropertyIndexValueFactory : IPropertyIndexValueFactory { + /// public IEnumerable>> GetIndexValues(Property property, string culture, string segment, bool published) { yield return new KeyValuePair>( diff --git a/src/Umbraco.Core/PropertyEditors/IDataEditor.cs b/src/Umbraco.Core/PropertyEditors/IDataEditor.cs index 2b5d6e8bf3..917cd20dfc 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataEditor.cs @@ -3,7 +3,6 @@ using Umbraco.Core.Composing; namespace Umbraco.Core.PropertyEditors { - /// /// Represents a data editor. /// @@ -67,6 +66,9 @@ namespace Umbraco.Core.PropertyEditors /// IConfigurationEditor GetConfigurationEditor(); - IPropertyIndexValues PropertyIndexValues { get; } + /// + /// Gets the value indexer for the editor. + /// + IPropertyIndexValueFactory PropertyIndexValueFactory { get; } } } diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyIndexValues.cs b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs similarity index 61% rename from src/Umbraco.Core/PropertyEditors/IPropertyIndexValues.cs rename to src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs index 15580ccb80..bb2f2aacf3 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyIndexValues.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs @@ -4,10 +4,13 @@ using Umbraco.Core.Models; namespace Umbraco.Core.PropertyEditors { /// - /// Returns indexable data for the property + /// Represents a property value indexer. /// - public interface IPropertyIndexValues + public interface IPropertyIndexValueFactory { + /// + /// Gets the index values for a property. + /// IEnumerable>> GetIndexValues(Property property, string culture, string segment, bool published); } } diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index a9a0412a18..7915bbe24b 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -177,7 +177,7 @@ namespace Umbraco.Core.Services /// The page size. /// Total number of documents. /// Query filter. - /// + /// Ordering infos. IEnumerable GetPagedDescendants(int id, long pageIndex, int pageSize, out long totalRecords, IQuery filter = null, Ordering ordering = null); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index e737d91f02..c17ae54934 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -442,7 +442,7 @@ - + @@ -451,7 +451,7 @@ - + diff --git a/src/Umbraco.Examine/BaseValueSetBuilder.cs b/src/Umbraco.Examine/BaseValueSetBuilder.cs index f0261f7cd9..22d379d148 100644 --- a/src/Umbraco.Examine/BaseValueSetBuilder.cs +++ b/src/Umbraco.Examine/BaseValueSetBuilder.cs @@ -29,7 +29,7 @@ namespace Umbraco.Examine var editor = _propertyEditors[property.PropertyType.PropertyEditorAlias]; if (editor == null) return; - var indexVals = editor.PropertyIndexValues.GetIndexValues(property, culture, segment, PublishedValuesOnly); + var indexVals = editor.PropertyIndexValueFactory.GetIndexValues(property, culture, segment, PublishedValuesOnly); foreach (var keyVal in indexVals) { if (keyVal.Key.IsNullOrWhiteSpace()) continue; diff --git a/src/Umbraco.Tests.Benchmarks/EnumeratorBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/EnumeratorBenchmarks.cs new file mode 100644 index 0000000000..b3df1c14e8 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/EnumeratorBenchmarks.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using BenchmarkDotNet.Attributes; + +namespace Umbraco.Tests.Benchmarks +{ + [MemoryDiagnoser] + public class EnumeratorBenchmarks + { + [Benchmark(Baseline = true)] + public void WithArray() + { + foreach (var t in EnumerateOneWithArray(1)) ; + } + + [Benchmark] + public void WithYield() + { + foreach (var t in EnumerateOneWithYield(1)) ; + } + + private IEnumerable EnumerateOneWithArray(T o) => new [] { o }; + + private IEnumerable EnumerateOneWithYield(T o) + { + yield return o; + } + } +} diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 99bb768842..15a55ab6ac 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -50,6 +50,7 @@ + @@ -91,4 +92,4 @@ - + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 7ab296b042..9e10c18997 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -8,7 +8,7 @@ "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", "dev": true, "requires": { - "@babel/highlight": "^7.0.0" + "@babel/highlight": "7.0.0" } }, "@babel/core": { @@ -17,20 +17,20 @@ "integrity": "sha512-Hz6PJT6e44iUNpAn8AoyAs6B3bl60g7MJQaI0rZEar6ECzh6+srYO1xlIdssio34mPaUtAb1y+XlkkSJzok3yw==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.1.6", - "@babel/helpers": "^7.1.5", - "@babel/parser": "^7.1.6", - "@babel/template": "^7.1.2", - "@babel/traverse": "^7.1.6", - "@babel/types": "^7.1.6", - "convert-source-map": "^1.1.0", - "debug": "^4.1.0", - "json5": "^2.1.0", - "lodash": "^4.17.10", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" + "@babel/code-frame": "7.0.0", + "@babel/generator": "7.1.6", + "@babel/helpers": "7.1.5", + "@babel/parser": "7.1.6", + "@babel/template": "7.1.2", + "@babel/traverse": "7.1.6", + "@babel/types": "7.1.6", + "convert-source-map": "1.6.0", + "debug": "4.1.0", + "json5": "2.1.0", + "lodash": "4.17.11", + "resolve": "1.8.1", + "semver": "5.6.0", + "source-map": "0.5.7" } }, "@babel/generator": { @@ -39,11 +39,11 @@ "integrity": "sha512-brwPBtVvdYdGxtenbQgfCdDPmtkmUBZPjUoK5SXJEBuHaA5BCubh9ly65fzXz7R6o5rA76Rs22ES8Z+HCc0YIQ==", "dev": true, "requires": { - "@babel/types": "^7.1.6", - "jsesc": "^2.5.1", - "lodash": "^4.17.10", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "@babel/types": "7.1.6", + "jsesc": "2.5.2", + "lodash": "4.17.11", + "source-map": "0.5.7", + "trim-right": "1.0.1" } }, "@babel/helper-annotate-as-pure": { @@ -52,7 +52,7 @@ "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "7.1.6" } }, "@babel/helper-builder-binary-assignment-operator-visitor": { @@ -61,8 +61,8 @@ "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==", "dev": true, "requires": { - "@babel/helper-explode-assignable-expression": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/helper-explode-assignable-expression": "7.1.0", + "@babel/types": "7.1.6" } }, "@babel/helper-call-delegate": { @@ -71,9 +71,9 @@ "integrity": "sha512-YEtYZrw3GUK6emQHKthltKNZwszBcHK58Ygcis+gVUrF4/FmTVr5CCqQNSfmvg2y+YDEANyYoaLz/SHsnusCwQ==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.0.0", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/helper-hoist-variables": "7.0.0", + "@babel/traverse": "7.1.6", + "@babel/types": "7.1.6" } }, "@babel/helper-define-map": { @@ -82,9 +82,9 @@ "integrity": "sha512-yPPcW8dc3gZLN+U1mhYV91QU3n5uTbx7DUdf8NnPbjS0RMwBuHi9Xt2MUgppmNz7CJxTBWsGczTiEp1CSOTPRg==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/types": "^7.0.0", - "lodash": "^4.17.10" + "@babel/helper-function-name": "7.1.0", + "@babel/types": "7.1.6", + "lodash": "4.17.11" } }, "@babel/helper-explode-assignable-expression": { @@ -93,8 +93,8 @@ "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==", "dev": true, "requires": { - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/traverse": "7.1.6", + "@babel/types": "7.1.6" } }, "@babel/helper-function-name": { @@ -103,9 +103,9 @@ "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/helper-get-function-arity": "7.0.0", + "@babel/template": "7.1.2", + "@babel/types": "7.1.6" } }, "@babel/helper-get-function-arity": { @@ -114,7 +114,7 @@ "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "7.1.6" } }, "@babel/helper-hoist-variables": { @@ -123,7 +123,7 @@ "integrity": "sha512-Ggv5sldXUeSKsuzLkddtyhyHe2YantsxWKNi7A+7LeD12ExRDWTRk29JCXpaHPAbMaIPZSil7n+lq78WY2VY7w==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "7.1.6" } }, "@babel/helper-member-expression-to-functions": { @@ -132,7 +132,7 @@ "integrity": "sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "7.1.6" } }, "@babel/helper-module-imports": { @@ -141,7 +141,7 @@ "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "7.1.6" } }, "@babel/helper-module-transforms": { @@ -150,12 +150,12 @@ "integrity": "sha512-0JZRd2yhawo79Rcm4w0LwSMILFmFXjugG3yqf+P/UsKsRS1mJCmMwwlHDlMg7Avr9LrvSpp4ZSULO9r8jpCzcw==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0", - "lodash": "^4.17.10" + "@babel/helper-module-imports": "7.0.0", + "@babel/helper-simple-access": "7.1.0", + "@babel/helper-split-export-declaration": "7.0.0", + "@babel/template": "7.1.2", + "@babel/types": "7.1.6", + "lodash": "4.17.11" } }, "@babel/helper-optimise-call-expression": { @@ -164,7 +164,7 @@ "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "7.1.6" } }, "@babel/helper-plugin-utils": { @@ -179,7 +179,7 @@ "integrity": "sha512-TR0/N0NDCcUIUEbqV6dCO+LptmmSQFQ7q70lfcEB4URsjD0E1HzicrwUH+ap6BAQ2jhCX9Q4UqZy4wilujWlkg==", "dev": true, "requires": { - "lodash": "^4.17.10" + "lodash": "4.17.11" } }, "@babel/helper-remap-async-to-generator": { @@ -188,11 +188,11 @@ "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-wrap-function": "^7.1.0", - "@babel/template": "^7.1.0", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/helper-annotate-as-pure": "7.0.0", + "@babel/helper-wrap-function": "7.1.0", + "@babel/template": "7.1.2", + "@babel/traverse": "7.1.6", + "@babel/types": "7.1.6" } }, "@babel/helper-replace-supers": { @@ -201,10 +201,10 @@ "integrity": "sha512-BvcDWYZRWVuDeXTYZWxekQNO5D4kO55aArwZOTFXw6rlLQA8ZaDicJR1sO47h+HrnCiDFiww0fSPV0d713KBGQ==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.0.0", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/helper-member-expression-to-functions": "7.0.0", + "@babel/helper-optimise-call-expression": "7.0.0", + "@babel/traverse": "7.1.6", + "@babel/types": "7.1.6" } }, "@babel/helper-simple-access": { @@ -213,8 +213,8 @@ "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==", "dev": true, "requires": { - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/template": "7.1.2", + "@babel/types": "7.1.6" } }, "@babel/helper-split-export-declaration": { @@ -223,7 +223,7 @@ "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==", "dev": true, "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "7.1.6" } }, "@babel/helper-wrap-function": { @@ -232,10 +232,10 @@ "integrity": "sha512-R6HU3dete+rwsdAfrOzTlE9Mcpk4RjU3aX3gi9grtmugQY0u79X7eogUvfXA5sI81Mfq1cn6AgxihfN33STjJA==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/template": "^7.1.0", - "@babel/traverse": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/helper-function-name": "7.1.0", + "@babel/template": "7.1.2", + "@babel/traverse": "7.1.6", + "@babel/types": "7.1.6" } }, "@babel/helpers": { @@ -244,9 +244,9 @@ "integrity": "sha512-2jkcdL02ywNBry1YNFAH/fViq4fXG0vdckHqeJk+75fpQ2OH+Az6076tX/M0835zA45E0Cqa6pV5Kiv9YOqjEg==", "dev": true, "requires": { - "@babel/template": "^7.1.2", - "@babel/traverse": "^7.1.5", - "@babel/types": "^7.1.5" + "@babel/template": "7.1.2", + "@babel/traverse": "7.1.6", + "@babel/types": "7.1.6" } }, "@babel/highlight": { @@ -255,9 +255,9 @@ "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", "dev": true, "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" + "chalk": "2.4.1", + "esutils": "2.0.2", + "js-tokens": "4.0.0" } }, "@babel/parser": { @@ -272,9 +272,9 @@ "integrity": "sha512-Fq803F3Jcxo20MXUSDdmZZXrPe6BWyGcWBPPNB/M7WaUYESKDeKMOGIxEzQOjGSmW/NWb6UaPZrtTB2ekhB/ew==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.1.0", - "@babel/plugin-syntax-async-generators": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0", + "@babel/helper-remap-async-to-generator": "7.1.0", + "@babel/plugin-syntax-async-generators": "7.0.0" } }, "@babel/plugin-proposal-json-strings": { @@ -283,8 +283,8 @@ "integrity": "sha512-kfVdUkIAGJIVmHmtS/40i/fg/AGnw/rsZBCaapY5yjeO5RA9m165Xbw9KMOu2nqXP5dTFjEjHdfNdoVcHv133Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-json-strings": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0", + "@babel/plugin-syntax-json-strings": "7.0.0" } }, "@babel/plugin-proposal-object-rest-spread": { @@ -293,8 +293,8 @@ "integrity": "sha512-14fhfoPcNu7itSen7Py1iGN0gEm87hX/B+8nZPqkdmANyyYWYMY2pjA3r8WXbWVKMzfnSNS0xY8GVS0IjXi/iw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0", + "@babel/plugin-syntax-object-rest-spread": "7.0.0" } }, "@babel/plugin-proposal-optional-catch-binding": { @@ -303,8 +303,8 @@ "integrity": "sha512-JPqAvLG1s13B/AuoBjdBYvn38RqW6n1TzrQO839/sIpqLpbnXKacsAgpZHzLD83Sm8SDXMkkrAvEnJ25+0yIpw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0", + "@babel/plugin-syntax-optional-catch-binding": "7.0.0" } }, "@babel/plugin-proposal-unicode-property-regex": { @@ -313,9 +313,9 @@ "integrity": "sha512-tM3icA6GhC3ch2SkmSxv7J/hCWKISzwycub6eGsDrFDgukD4dZ/I+x81XgW0YslS6mzNuQ1Cbzh5osjIMgepPQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0", - "regexpu-core": "^4.2.0" + "@babel/helper-plugin-utils": "7.0.0", + "@babel/helper-regex": "7.0.0", + "regexpu-core": "4.2.0" } }, "@babel/plugin-syntax-async-generators": { @@ -324,7 +324,7 @@ "integrity": "sha512-im7ged00ddGKAjcZgewXmp1vxSZQQywuQXe2B1A7kajjZmDeY/ekMPmWr9zJgveSaQH0k7BcGrojQhcK06l0zA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-syntax-json-strings": { @@ -333,7 +333,7 @@ "integrity": "sha512-UlSfNydC+XLj4bw7ijpldc1uZ/HB84vw+U6BTuqMdIEmz/LDe63w/GHtpQMdXWdqQZFeAI9PjnHe/vDhwirhKA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-syntax-object-rest-spread": { @@ -342,7 +342,7 @@ "integrity": "sha512-5A0n4p6bIiVe5OvQPxBnesezsgFJdHhSs3uFSvaPdMqtsovajLZ+G2vZyvNe10EzJBWWo3AcHGKhAFUxqwp2dw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-syntax-optional-catch-binding": { @@ -351,7 +351,7 @@ "integrity": "sha512-Wc+HVvwjcq5qBg1w5RG9o9RVzmCaAg/Vp0erHCKpAYV8La6I94o4GQAmFYNmkzoMO6gzoOSulpKeSSz6mPEoZw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-arrow-functions": { @@ -360,7 +360,7 @@ "integrity": "sha512-2EZDBl1WIO/q4DIkIp4s86sdp4ZifL51MoIviLY/gG/mLSuOIEg7J8o6mhbxOTvUJkaN50n+8u41FVsr5KLy/w==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-async-to-generator": { @@ -369,9 +369,9 @@ "integrity": "sha512-rNmcmoQ78IrvNCIt/R9U+cixUHeYAzgusTFgIAv+wQb9HJU4szhpDD6e5GCACmj/JP5KxuCwM96bX3L9v4ZN/g==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-remap-async-to-generator": "^7.1.0" + "@babel/helper-module-imports": "7.0.0", + "@babel/helper-plugin-utils": "7.0.0", + "@babel/helper-remap-async-to-generator": "7.1.0" } }, "@babel/plugin-transform-block-scoped-functions": { @@ -380,7 +380,7 @@ "integrity": "sha512-AOBiyUp7vYTqz2Jibe1UaAWL0Hl9JUXEgjFvvvcSc9MVDItv46ViXFw2F7SVt1B5k+KWjl44eeXOAk3UDEaJjQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-block-scoping": { @@ -389,8 +389,8 @@ "integrity": "sha512-jlYcDrz+5ayWC7mxgpn1Wj8zj0mmjCT2w0mPIMSwO926eXBRxpEgoN/uQVRBfjtr8ayjcmS+xk2G1jaP8JjMJQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "lodash": "^4.17.10" + "@babel/helper-plugin-utils": "7.0.0", + "lodash": "4.17.11" } }, "@babel/plugin-transform-classes": { @@ -399,14 +399,14 @@ "integrity": "sha512-rNaqoD+4OCBZjM7VaskladgqnZ1LO6o2UxuWSDzljzW21pN1KXkB7BstAVweZdxQkHAujps5QMNOTWesBciKFg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-define-map": "^7.1.0", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-optimise-call-expression": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "globals": "^11.1.0" + "@babel/helper-annotate-as-pure": "7.0.0", + "@babel/helper-define-map": "7.1.0", + "@babel/helper-function-name": "7.1.0", + "@babel/helper-optimise-call-expression": "7.0.0", + "@babel/helper-plugin-utils": "7.0.0", + "@babel/helper-replace-supers": "7.1.0", + "@babel/helper-split-export-declaration": "7.0.0", + "globals": "11.9.0" } }, "@babel/plugin-transform-computed-properties": { @@ -415,7 +415,7 @@ "integrity": "sha512-ubouZdChNAv4AAWAgU7QKbB93NU5sHwInEWfp+/OzJKA02E6Woh9RVoX4sZrbRwtybky/d7baTUqwFx+HgbvMA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-destructuring": { @@ -424,7 +424,7 @@ "integrity": "sha512-Mb9M4DGIOspH1ExHOUnn2UUXFOyVTiX84fXCd+6B5iWrQg/QMeeRmSwpZ9lnjYLSXtZwiw80ytVMr3zue0ucYw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-dotall-regex": { @@ -433,9 +433,9 @@ "integrity": "sha512-00THs8eJxOJUFVx1w8i1MBF4XH4PsAjKjQ1eqN/uCH3YKwP21GCKfrn6YZFZswbOk9+0cw1zGQPHVc1KBlSxig==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0", - "regexpu-core": "^4.1.3" + "@babel/helper-plugin-utils": "7.0.0", + "@babel/helper-regex": "7.0.0", + "regexpu-core": "4.2.0" } }, "@babel/plugin-transform-duplicate-keys": { @@ -444,7 +444,7 @@ "integrity": "sha512-w2vfPkMqRkdxx+C71ATLJG30PpwtTpW7DDdLqYt2acXU7YjztzeWW2Jk1T6hKqCLYCcEA5UQM/+xTAm+QCSnuQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-exponentiation-operator": { @@ -453,8 +453,8 @@ "integrity": "sha512-uZt9kD1Pp/JubkukOGQml9tqAeI8NkE98oZnHZ2qHRElmeKCodbTZgOEUtujSCSLhHSBWbzNiFSDIMC4/RBTLQ==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-builder-binary-assignment-operator-visitor": "7.1.0", + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-for-of": { @@ -463,7 +463,7 @@ "integrity": "sha512-TlxKecN20X2tt2UEr2LNE6aqA0oPeMT1Y3cgz8k4Dn1j5ObT8M3nl9aA37LLklx0PBZKETC9ZAf9n/6SujTuXA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-function-name": { @@ -472,8 +472,8 @@ "integrity": "sha512-VxOa1TMlFMtqPW2IDYZQaHsFrq/dDoIjgN098NowhexhZcz3UGlvPgZXuE1jEvNygyWyxRacqDpCZt+par1FNg==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-function-name": "7.1.0", + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-literals": { @@ -482,7 +482,7 @@ "integrity": "sha512-1NTDBWkeNXgpUcyoVFxbr9hS57EpZYXpje92zv0SUzjdu3enaRwF/l3cmyRnXLtIdyJASyiS6PtybK+CgKf7jA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-modules-amd": { @@ -491,8 +491,8 @@ "integrity": "sha512-wt8P+xQ85rrnGNr2x1iV3DW32W8zrB6ctuBkYBbf5/ZzJY99Ob4MFgsZDFgczNU76iy9PWsy4EuxOliDjdKw6A==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-module-transforms": "7.1.0", + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-modules-commonjs": { @@ -501,9 +501,9 @@ "integrity": "sha512-wtNwtMjn1XGwM0AXPspQgvmE6msSJP15CX2RVfpTSTNPLhKhaOjaIfBaVfj4iUZ/VrFSodcFedwtPg/NxwQlPA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-simple-access": "^7.1.0" + "@babel/helper-module-transforms": "7.1.0", + "@babel/helper-plugin-utils": "7.0.0", + "@babel/helper-simple-access": "7.1.0" } }, "@babel/plugin-transform-modules-systemjs": { @@ -512,8 +512,8 @@ "integrity": "sha512-PvTxgjxQAq4pvVUZF3mD5gEtVDuId8NtWkJsZLEJZMZAW3TvgQl1pmydLLN1bM8huHFVVU43lf0uvjQj9FRkKw==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-hoist-variables": "7.0.0", + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-modules-umd": { @@ -522,8 +522,8 @@ "integrity": "sha512-enrRtn5TfRhMmbRwm7F8qOj0qEYByqUvTttPEGimcBH4CJHphjyK1Vg7sdU7JjeEmgSpM890IT/efS2nMHwYig==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.1.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-module-transforms": "7.1.0", + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-new-target": { @@ -532,7 +532,7 @@ "integrity": "sha512-yin069FYjah+LbqfGeTfzIBODex/e++Yfa0rH0fpfam9uTbuEeEOx5GLGr210ggOV77mVRNoeqSYqeuaqSzVSw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-object-super": { @@ -541,8 +541,8 @@ "integrity": "sha512-/O02Je1CRTSk2SSJaq0xjwQ8hG4zhZGNjE8psTsSNPXyLRCODv7/PBozqT5AmQMzp7MI3ndvMhGdqp9c96tTEw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-replace-supers": "^7.1.0" + "@babel/helper-plugin-utils": "7.0.0", + "@babel/helper-replace-supers": "7.1.0" } }, "@babel/plugin-transform-parameters": { @@ -551,9 +551,9 @@ "integrity": "sha512-vHV7oxkEJ8IHxTfRr3hNGzV446GAb+0hgbA7o/0Jd76s+YzccdWuTU296FOCOl/xweU4t/Ya4g41yWz80RFCRw==", "dev": true, "requires": { - "@babel/helper-call-delegate": "^7.1.0", - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-call-delegate": "7.1.0", + "@babel/helper-get-function-arity": "7.0.0", + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-regenerator": { @@ -562,7 +562,7 @@ "integrity": "sha512-sj2qzsEx8KDVv1QuJc/dEfilkg3RRPvPYx/VnKLtItVQRWt1Wqf5eVCOLZm29CiGFfYYsA3VPjfizTCV0S0Dlw==", "dev": true, "requires": { - "regenerator-transform": "^0.13.3" + "regenerator-transform": "0.13.3" } }, "@babel/plugin-transform-shorthand-properties": { @@ -571,7 +571,7 @@ "integrity": "sha512-g/99LI4vm5iOf5r1Gdxq5Xmu91zvjhEG5+yZDJW268AZELAu4J1EiFLnkSG3yuUsZyOipVOVUKoGPYwfsTymhw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-spread": { @@ -580,7 +580,7 @@ "integrity": "sha512-L702YFy2EvirrR4shTj0g2xQp7aNwZoWNCkNu2mcoU0uyzMl0XRwDSwzB/xp6DSUFiBmEXuyAyEN16LsgVqGGQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-sticky-regex": { @@ -589,8 +589,8 @@ "integrity": "sha512-LFUToxiyS/WD+XEWpkx/XJBrUXKewSZpzX68s+yEOtIbdnsRjpryDw9U06gYc6klYEij/+KQVRnD3nz3AoKmjw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0", + "@babel/helper-regex": "7.0.0" } }, "@babel/plugin-transform-template-literals": { @@ -599,8 +599,8 @@ "integrity": "sha512-vA6rkTCabRZu7Nbl9DfLZE1imj4tzdWcg5vtdQGvj+OH9itNNB6hxuRMHuIY8SGnEt1T9g5foqs9LnrHzsqEFg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-annotate-as-pure": "7.0.0", + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-typeof-symbol": { @@ -609,7 +609,7 @@ "integrity": "sha512-1r1X5DO78WnaAIvs5uC48t41LLckxsYklJrZjNKcevyz83sF2l4RHbw29qrCPr/6ksFsdfRpT/ZgxNWHXRnffg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0" + "@babel/helper-plugin-utils": "7.0.0" } }, "@babel/plugin-transform-unicode-regex": { @@ -618,9 +618,9 @@ "integrity": "sha512-uJBrJhBOEa3D033P95nPHu3nbFwFE9ZgXsfEitzoIXIwqAZWk7uXcg06yFKXz9FSxBH5ucgU/cYdX0IV8ldHKw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/helper-regex": "^7.0.0", - "regexpu-core": "^4.1.3" + "@babel/helper-plugin-utils": "7.0.0", + "@babel/helper-regex": "7.0.0", + "regexpu-core": "4.2.0" } }, "@babel/preset-env": { @@ -629,47 +629,47 @@ "integrity": "sha512-YIBfpJNQMBkb6MCkjz/A9J76SNCSuGVamOVBgoUkLzpJD/z8ghHi9I42LQ4pulVX68N/MmImz6ZTixt7Azgexw==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-async-generator-functions": "^7.1.0", - "@babel/plugin-proposal-json-strings": "^7.0.0", - "@babel/plugin-proposal-object-rest-spread": "^7.0.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.0.0", - "@babel/plugin-syntax-async-generators": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.0.0", - "@babel/plugin-transform-arrow-functions": "^7.0.0", - "@babel/plugin-transform-async-to-generator": "^7.1.0", - "@babel/plugin-transform-block-scoped-functions": "^7.0.0", - "@babel/plugin-transform-block-scoping": "^7.1.5", - "@babel/plugin-transform-classes": "^7.1.0", - "@babel/plugin-transform-computed-properties": "^7.0.0", - "@babel/plugin-transform-destructuring": "^7.0.0", - "@babel/plugin-transform-dotall-regex": "^7.0.0", - "@babel/plugin-transform-duplicate-keys": "^7.0.0", - "@babel/plugin-transform-exponentiation-operator": "^7.1.0", - "@babel/plugin-transform-for-of": "^7.0.0", - "@babel/plugin-transform-function-name": "^7.1.0", - "@babel/plugin-transform-literals": "^7.0.0", - "@babel/plugin-transform-modules-amd": "^7.1.0", - "@babel/plugin-transform-modules-commonjs": "^7.1.0", - "@babel/plugin-transform-modules-systemjs": "^7.0.0", - "@babel/plugin-transform-modules-umd": "^7.1.0", - "@babel/plugin-transform-new-target": "^7.0.0", - "@babel/plugin-transform-object-super": "^7.1.0", - "@babel/plugin-transform-parameters": "^7.1.0", - "@babel/plugin-transform-regenerator": "^7.0.0", - "@babel/plugin-transform-shorthand-properties": "^7.0.0", - "@babel/plugin-transform-spread": "^7.0.0", - "@babel/plugin-transform-sticky-regex": "^7.0.0", - "@babel/plugin-transform-template-literals": "^7.0.0", - "@babel/plugin-transform-typeof-symbol": "^7.0.0", - "@babel/plugin-transform-unicode-regex": "^7.0.0", - "browserslist": "^4.1.0", - "invariant": "^2.2.2", - "js-levenshtein": "^1.1.3", - "semver": "^5.3.0" + "@babel/helper-module-imports": "7.0.0", + "@babel/helper-plugin-utils": "7.0.0", + "@babel/plugin-proposal-async-generator-functions": "7.1.0", + "@babel/plugin-proposal-json-strings": "7.0.0", + "@babel/plugin-proposal-object-rest-spread": "7.0.0", + "@babel/plugin-proposal-optional-catch-binding": "7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "7.0.0", + "@babel/plugin-syntax-async-generators": "7.0.0", + "@babel/plugin-syntax-object-rest-spread": "7.0.0", + "@babel/plugin-syntax-optional-catch-binding": "7.0.0", + "@babel/plugin-transform-arrow-functions": "7.0.0", + "@babel/plugin-transform-async-to-generator": "7.1.0", + "@babel/plugin-transform-block-scoped-functions": "7.0.0", + "@babel/plugin-transform-block-scoping": "7.1.5", + "@babel/plugin-transform-classes": "7.1.0", + "@babel/plugin-transform-computed-properties": "7.0.0", + "@babel/plugin-transform-destructuring": "7.1.3", + "@babel/plugin-transform-dotall-regex": "7.0.0", + "@babel/plugin-transform-duplicate-keys": "7.0.0", + "@babel/plugin-transform-exponentiation-operator": "7.1.0", + "@babel/plugin-transform-for-of": "7.0.0", + "@babel/plugin-transform-function-name": "7.1.0", + "@babel/plugin-transform-literals": "7.0.0", + "@babel/plugin-transform-modules-amd": "7.1.0", + "@babel/plugin-transform-modules-commonjs": "7.1.0", + "@babel/plugin-transform-modules-systemjs": "7.1.3", + "@babel/plugin-transform-modules-umd": "7.1.0", + "@babel/plugin-transform-new-target": "7.0.0", + "@babel/plugin-transform-object-super": "7.1.0", + "@babel/plugin-transform-parameters": "7.1.0", + "@babel/plugin-transform-regenerator": "7.0.0", + "@babel/plugin-transform-shorthand-properties": "7.0.0", + "@babel/plugin-transform-spread": "7.0.0", + "@babel/plugin-transform-sticky-regex": "7.0.0", + "@babel/plugin-transform-template-literals": "7.0.0", + "@babel/plugin-transform-typeof-symbol": "7.0.0", + "@babel/plugin-transform-unicode-regex": "7.0.0", + "browserslist": "4.3.5", + "invariant": "2.2.4", + "js-levenshtein": "1.1.4", + "semver": "5.6.0" } }, "@babel/template": { @@ -678,9 +678,9 @@ "integrity": "sha512-SY1MmplssORfFiLDcOETrW7fCLl+PavlwMh92rrGcikQaRq4iWPVH0MpwPpY3etVMx6RnDjXtr6VZYr/IbP/Ag==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.1.2", - "@babel/types": "^7.1.2" + "@babel/code-frame": "7.0.0", + "@babel/parser": "7.1.6", + "@babel/types": "7.1.6" } }, "@babel/traverse": { @@ -689,15 +689,15 @@ "integrity": "sha512-CXedit6GpISz3sC2k2FsGCUpOhUqKdyL0lqNrImQojagnUMXf8hex4AxYFRuMkNGcvJX5QAFGzB5WJQmSv8SiQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/generator": "^7.1.6", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.0.0", - "@babel/parser": "^7.1.6", - "@babel/types": "^7.1.6", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.10" + "@babel/code-frame": "7.0.0", + "@babel/generator": "7.1.6", + "@babel/helper-function-name": "7.1.0", + "@babel/helper-split-export-declaration": "7.0.0", + "@babel/parser": "7.1.6", + "@babel/types": "7.1.6", + "debug": "4.1.0", + "globals": "11.9.0", + "lodash": "4.17.11" } }, "@babel/types": { @@ -706,9 +706,9 @@ "integrity": "sha512-DMiUzlY9DSjVsOylJssxLHSgj6tWM9PRFJOGW/RaOglVOK9nzTxoOMfTfRQXGUCUQ/HmlG2efwC+XqUEJ5ay4w==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.10", - "to-fast-properties": "^2.0.0" + "esutils": "2.0.2", + "lodash": "4.17.11", + "to-fast-properties": "2.0.0" } }, "@mrmlnc/readdir-enhanced": { @@ -717,8 +717,8 @@ "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", "dev": true, "requires": { - "call-me-maybe": "^1.0.1", - "glob-to-regexp": "^0.3.0" + "call-me-maybe": "1.0.1", + "glob-to-regexp": "0.3.0" } }, "@nodelib/fs.stat": { @@ -745,7 +745,7 @@ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { - "mime-types": "~2.1.18", + "mime-types": "2.1.21", "negotiator": "0.6.1" } }, @@ -755,20 +755,20 @@ "integrity": "sha512-3OOR92FTc2p5/EcOzPcXp+Cbo+3C15nV9RXHlOUBCBpHhcB+0frbSNR9ehED/o7sTcyGVtqGJpguToEdlXhD0w==", "dev": true, "requires": { - "convert-source-map": "^1.5.0", - "glob": "^7.0.5", - "indx": "^0.2.3", - "lodash.clone": "^4.3.2", - "lodash.defaults": "^4.0.1", - "lodash.flatten": "^4.2.0", - "lodash.merge": "^4.4.0", - "lodash.partialright": "^4.1.4", - "lodash.pick": "^4.2.1", - "lodash.uniq": "^4.3.0", - "resolve": "^1.5.0", - "semver": "^5.3.0", - "uglify-js": "^2.8.22", - "when": "^3.7.8" + "convert-source-map": "1.6.0", + "glob": "7.1.3", + "indx": "0.2.3", + "lodash.clone": "4.5.0", + "lodash.defaults": "4.2.0", + "lodash.flatten": "4.4.0", + "lodash.merge": "4.6.1", + "lodash.partialright": "4.2.1", + "lodash.pick": "4.4.0", + "lodash.uniq": "4.5.0", + "resolve": "1.8.1", + "semver": "5.6.0", + "uglify-js": "2.8.29", + "when": "3.7.8" }, "dependencies": { "glob": { @@ -777,12 +777,12 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" } }, "minimatch": { @@ -791,7 +791,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } } } @@ -825,10 +825,10 @@ "integrity": "sha512-ZoJjft5B+EJBjUyu9C9Hc0OZyPZSSlOF+plzouTrg6UlA8f+e/n8NIgBFG/9tppJtpPWfthHakK7juJdNDODww==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "align-text": { @@ -837,9 +837,9 @@ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" }, "dependencies": { "kind-of": { @@ -848,7 +848,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -885,7 +885,7 @@ "resolved": "https://registry.npmjs.org/angular-dynamic-locale/-/angular-dynamic-locale-0.1.37.tgz", "integrity": "sha512-m5Kyk8W8/mOZSqRxuByOwHBjv8labLBAgvl0Z3iQx2xT/tWCqb94imKUPwumudszdPDjxeopwyucQvm8Sw7ogw==", "requires": { - "@types/angular": "^1.6.25" + "@types/angular": "1.6.51" } }, "angular-i18n": { @@ -928,9 +928,9 @@ "resolved": "https://registry.npmjs.org/angular-ui-sortable/-/angular-ui-sortable-0.19.0.tgz", "integrity": "sha512-u/uc981Nzg4XN1bMU9qKleMTSt7F1XjMWnyGw6gxPLIeQeLZm8jWNy7tj8y2r2HmvzXFbQVq2z6rObznFKAekQ==", "requires": { - "angular": ">=1.2.x", - "jquery": ">=3.1.x", - "jquery-ui-dist": ">=1.12.x" + "angular": "1.7.5", + "jquery": "3.3.1", + "jquery-ui-dist": "1.12.1" } }, "animejs": { @@ -944,7 +944,7 @@ "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { - "ansi-wrap": "^0.1.0" + "ansi-wrap": "0.1.0" } }, "ansi-cyan": { @@ -992,7 +992,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.3" } }, "ansi-wrap": { @@ -1007,8 +1007,8 @@ "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" + "micromatch": "2.3.11", + "normalize-path": "2.1.1" }, "dependencies": { "arr-diff": { @@ -1017,7 +1017,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "^1.0.1" + "arr-flatten": "1.1.0" } }, "array-unique": { @@ -1032,9 +1032,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.3" } }, "expand-brackets": { @@ -1043,7 +1043,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "^0.1.0" + "is-posix-bracket": "0.1.1" } }, "extglob": { @@ -1052,7 +1052,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "is-extglob": { @@ -1067,7 +1067,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "kind-of": { @@ -1076,7 +1076,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } }, "micromatch": { @@ -1085,19 +1085,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.4" } } } @@ -1116,7 +1116,7 @@ "dev": true, "optional": true, "requires": { - "file-type": "^4.2.0" + "file-type": "4.4.0" }, "dependencies": { "file-type": { @@ -1140,7 +1140,7 @@ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "sprintf-js": "1.0.3" } }, "arr-diff": { @@ -1191,7 +1191,7 @@ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "^1.0.1" + "array-uniq": "1.0.3" } }, "array-uniq": { @@ -1231,7 +1231,7 @@ "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "dev": true, "requires": { - "safer-buffer": "~2.1.0" + "safer-buffer": "2.1.2" } }, "assert-plus": { @@ -1282,12 +1282,12 @@ "integrity": "sha512-DY9gOh8z3tnCbJ13JIWaeQsoYncTGdsrgCceBaQSIL4nvdrLxgbRSBPevg2XbX7u4QCSfLheSJEEIUUSlkbx6Q==", "dev": true, "requires": { - "browserslist": "^4.3.3", - "caniuse-lite": "^1.0.30000898", - "normalize-range": "^0.1.2", - "num2fraction": "^1.2.2", - "postcss": "^7.0.5", - "postcss-value-parser": "^3.3.1" + "browserslist": "4.3.5", + "caniuse-lite": "1.0.30000913", + "normalize-range": "0.1.2", + "num2fraction": "1.2.2", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "aws-sign2": { @@ -1320,13 +1320,13 @@ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.2.1", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.1", + "pascalcase": "0.1.1" }, "dependencies": { "define-property": { @@ -1335,7 +1335,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -1344,7 +1344,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -1353,7 +1353,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -1362,9 +1362,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -1399,7 +1399,7 @@ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "beeper": { @@ -1424,11 +1424,11 @@ "dev": true, "optional": true, "requires": { - "decompress": "^4.0.0", - "download": "^6.2.2", - "execa": "^0.7.0", - "p-map-series": "^1.0.0", - "tempfile": "^2.0.0" + "decompress": "4.2.0", + "download": "6.2.5", + "execa": "0.7.0", + "p-map-series": "1.0.0", + "tempfile": "2.0.0" } }, "bin-check": { @@ -1438,8 +1438,8 @@ "dev": true, "optional": true, "requires": { - "execa": "^0.7.0", - "executable": "^4.1.0" + "execa": "0.7.0", + "executable": "4.1.1" } }, "bin-version": { @@ -1449,8 +1449,8 @@ "dev": true, "optional": true, "requires": { - "execa": "^1.0.0", - "find-versions": "^3.0.0" + "execa": "1.0.0", + "find-versions": "3.0.0" }, "dependencies": { "execa": { @@ -1460,13 +1460,13 @@ "dev": true, "optional": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "6.0.5", + "get-stream": "4.1.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "get-stream": { @@ -1476,7 +1476,7 @@ "dev": true, "optional": true, "requires": { - "pump": "^3.0.0" + "pump": "3.0.0" } } } @@ -1488,9 +1488,9 @@ "dev": true, "optional": true, "requires": { - "bin-version": "^3.0.0", - "semver": "^5.6.0", - "semver-truncate": "^1.1.2" + "bin-version": "3.0.0", + "semver": "5.6.0", + "semver-truncate": "1.1.2" } }, "bin-wrapper": { @@ -1500,12 +1500,12 @@ "dev": true, "optional": true, "requires": { - "bin-check": "^4.1.0", - "bin-version-check": "^4.0.0", - "download": "^7.1.0", - "import-lazy": "^3.1.0", - "os-filter-obj": "^2.0.0", - "pify": "^4.0.1" + "bin-check": "4.1.0", + "bin-version-check": "4.0.0", + "download": "7.1.0", + "import-lazy": "3.1.0", + "os-filter-obj": "2.0.0", + "pify": "4.0.1" }, "dependencies": { "download": { @@ -1515,18 +1515,18 @@ "dev": true, "optional": true, "requires": { - "archive-type": "^4.0.0", - "caw": "^2.0.1", - "content-disposition": "^0.5.2", - "decompress": "^4.2.0", - "ext-name": "^5.0.0", - "file-type": "^8.1.0", - "filenamify": "^2.0.0", - "get-stream": "^3.0.0", - "got": "^8.3.1", - "make-dir": "^1.2.0", - "p-event": "^2.1.0", - "pify": "^3.0.0" + "archive-type": "4.0.0", + "caw": "2.0.1", + "content-disposition": "0.5.2", + "decompress": "4.2.0", + "ext-name": "5.0.0", + "file-type": "8.1.0", + "filenamify": "2.1.0", + "get-stream": "3.0.0", + "got": "8.3.2", + "make-dir": "1.3.0", + "p-event": "2.1.0", + "pify": "3.0.0" }, "dependencies": { "pify": { @@ -1545,23 +1545,23 @@ "dev": true, "optional": true, "requires": { - "@sindresorhus/is": "^0.7.0", - "cacheable-request": "^2.1.1", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "into-stream": "^3.1.0", - "is-retry-allowed": "^1.1.0", - "isurl": "^1.0.0-alpha5", - "lowercase-keys": "^1.0.0", - "mimic-response": "^1.0.0", - "p-cancelable": "^0.4.0", - "p-timeout": "^2.0.1", - "pify": "^3.0.0", - "safe-buffer": "^5.1.1", - "timed-out": "^4.0.1", - "url-parse-lax": "^3.0.0", - "url-to-options": "^1.0.1" + "@sindresorhus/is": "0.7.0", + "cacheable-request": "2.1.4", + "decompress-response": "3.3.0", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "into-stream": "3.1.0", + "is-retry-allowed": "1.1.0", + "isurl": "1.0.0", + "lowercase-keys": "1.0.1", + "mimic-response": "1.0.1", + "p-cancelable": "0.4.1", + "p-timeout": "2.0.1", + "pify": "3.0.0", + "safe-buffer": "5.1.2", + "timed-out": "4.0.1", + "url-parse-lax": "3.0.0", + "url-to-options": "1.0.1" }, "dependencies": { "pify": { @@ -1587,7 +1587,7 @@ "dev": true, "optional": true, "requires": { - "p-timeout": "^2.0.1" + "p-timeout": "2.0.1" } }, "p-timeout": { @@ -1596,7 +1596,7 @@ "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", "dev": true, "requires": { - "p-finally": "^1.0.0" + "p-finally": "1.0.0" } }, "pify": { @@ -1620,7 +1620,7 @@ "dev": true, "optional": true, "requires": { - "prepend-http": "^2.0.0" + "prepend-http": "2.0.0" } } } @@ -1637,8 +1637,8 @@ "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "dev": true, "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" + "readable-stream": "2.3.6", + "safe-buffer": "5.1.2" }, "dependencies": { "isarray": { @@ -1653,13 +1653,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -1668,7 +1668,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -1691,10 +1691,10 @@ "integrity": "sha1-5LoM5BCkaTYyM2dgnstOZVMSUGk=", "dev": true, "requires": { - "continuable-cache": "^0.3.1", - "error": "^7.0.0", - "raw-body": "~1.1.0", - "safe-json-parse": "~1.0.1" + "continuable-cache": "0.3.1", + "error": "7.0.2", + "raw-body": "1.1.7", + "safe-json-parse": "1.0.1" } }, "body-parser": { @@ -1704,15 +1704,15 @@ "dev": true, "requires": { "bytes": "3.0.0", - "content-type": "~1.0.4", + "content-type": "1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", + "depd": "1.1.2", + "http-errors": "1.6.3", "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", + "on-finished": "2.3.0", "qs": "6.5.2", "raw-body": "2.3.3", - "type-is": "~1.6.16" + "type-is": "1.6.16" }, "dependencies": { "bytes": { @@ -1736,7 +1736,7 @@ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } }, "ms": { @@ -1781,8 +1781,8 @@ "resolved": "https://registry.npmjs.org/bootstrap-social/-/bootstrap-social-5.1.1.tgz", "integrity": "sha1-dTDGeK31bPj60/qCwp1NPl0CdQE=", "requires": { - "bootstrap": "~3", - "font-awesome": "~4.7" + "bootstrap": "3.3.7", + "font-awesome": "4.7.0" } }, "brace-expansion": { @@ -1791,7 +1791,7 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -1801,16 +1801,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "repeat-element": "1.1.3", + "snapdragon": "0.8.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -1819,7 +1819,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -1830,9 +1830,9 @@ "integrity": "sha512-z9ZhGc3d9e/sJ9dIx5NFXkKoaiQTnrvrMsN3R1fGb1tkWWNSz12UewJn9TNxGo1l7J23h0MRaPmk7jfeTZYs1w==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000912", - "electron-to-chromium": "^1.3.86", - "node-releases": "^1.0.5" + "caniuse-lite": "1.0.30000913", + "electron-to-chromium": "1.3.87", + "node-releases": "1.0.5" } }, "buffer": { @@ -1842,8 +1842,8 @@ "dev": true, "requires": { "base64-js": "0.0.8", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" + "ieee754": "1.1.12", + "isarray": "1.0.0" }, "dependencies": { "isarray": { @@ -1860,8 +1860,8 @@ "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", "dev": true, "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" + "buffer-alloc-unsafe": "1.1.0", + "buffer-fill": "1.0.0" } }, "buffer-alloc-unsafe": { @@ -1894,7 +1894,7 @@ "integrity": "sha1-z7GtlWjTujz+k1upq92VLeiKqyo=", "dev": true, "requires": { - "readable-stream": "^1.0.33" + "readable-stream": "1.1.14" } }, "builtin-modules": { @@ -1915,15 +1915,15 @@ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "collection-visit": "1.0.0", + "component-emitter": "1.2.1", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.0", + "to-object-path": "0.3.0", + "union-value": "1.0.0", + "unset-value": "1.0.0" } }, "cacheable-request": { @@ -1956,9 +1956,9 @@ "dev": true, "optional": true, "requires": { - "prepend-http": "^2.0.0", - "query-string": "^5.0.1", - "sort-keys": "^2.0.0" + "prepend-http": "2.0.0", + "query-string": "5.1.1", + "sort-keys": "2.0.0" } }, "prepend-http": { @@ -1975,7 +1975,7 @@ "dev": true, "optional": true, "requires": { - "is-plain-obj": "^1.0.0" + "is-plain-obj": "1.1.0" } } } @@ -1992,7 +1992,7 @@ "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", "dev": true, "requires": { - "callsites": "^2.0.0" + "callsites": "2.0.0" } }, "caller-path": { @@ -2001,7 +2001,7 @@ "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", "dev": true, "requires": { - "caller-callsite": "^2.0.0" + "caller-callsite": "2.0.0" } }, "callsite": { @@ -2028,8 +2028,8 @@ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" + "camelcase": "2.1.1", + "map-obj": "1.0.1" } }, "caniuse-api": { @@ -2038,10 +2038,10 @@ "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", "dev": true, "requires": { - "browserslist": "^4.0.0", - "caniuse-lite": "^1.0.0", - "lodash.memoize": "^4.1.2", - "lodash.uniq": "^4.5.0" + "browserslist": "4.3.5", + "caniuse-lite": "1.0.30000913", + "lodash.memoize": "4.1.2", + "lodash.uniq": "4.5.0" } }, "caniuse-lite": { @@ -2068,10 +2068,10 @@ "integrity": "sha512-Cg8/ZSBEa8ZVY9HspcGUYaK63d/bN7rqS3CYCzEGUxuYv6UlmcjzDUz2fCFFHyTvUW5Pk0I+3hkA3iXlIj6guA==", "dev": true, "requires": { - "get-proxy": "^2.0.0", - "isurl": "^1.0.0-alpha5", - "tunnel-agent": "^0.6.0", - "url-to-options": "^1.0.1" + "get-proxy": "2.1.0", + "isurl": "1.0.0", + "tunnel-agent": "0.6.0", + "url-to-options": "1.0.1" } }, "center-align": { @@ -2080,8 +2080,8 @@ "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", "dev": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "chalk": { @@ -2090,9 +2090,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, "chardet": { @@ -2107,19 +2107,19 @@ "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.4", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "lodash.debounce": "4.0.8", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.2.1", + "upath": "1.1.0" }, "dependencies": { "anymatch": { @@ -2128,8 +2128,8 @@ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "micromatch": "3.1.10", + "normalize-path": "2.1.1" } }, "is-glob": { @@ -2138,7 +2138,7 @@ "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "dev": true, "requires": { - "is-extglob": "^2.1.1" + "is-extglob": "2.1.1" } } } @@ -2155,10 +2155,10 @@ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" }, "dependencies": { "define-property": { @@ -2167,7 +2167,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -2178,7 +2178,7 @@ "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", "dev": true, "requires": { - "source-map": "~0.6.0" + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -2195,7 +2195,7 @@ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "^2.0.0" + "restore-cursor": "2.0.0" } }, "cli-width": { @@ -2209,9 +2209,9 @@ "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz", "integrity": "sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==", "requires": { - "good-listener": "^1.2.2", - "select": "^1.1.2", - "tiny-emitter": "^2.0.0" + "good-listener": "1.2.2", + "select": "1.1.2", + "tiny-emitter": "2.0.2" } }, "cliui": { @@ -2220,8 +2220,8 @@ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", "dev": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" }, "dependencies": { @@ -2252,7 +2252,7 @@ "dev": true, "optional": true, "requires": { - "mimic-response": "^1.0.0" + "mimic-response": "1.0.1" } }, "clone-stats": { @@ -2267,9 +2267,9 @@ "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", "dev": true, "requires": { - "inherits": "^2.0.1", - "process-nextick-args": "^2.0.0", - "readable-stream": "^2.3.5" + "inherits": "2.0.3", + "process-nextick-args": "2.0.0", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -2284,13 +2284,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -2299,7 +2299,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -2310,7 +2310,7 @@ "integrity": "sha512-5wfTTO8E2/ja4jFSxePXlG5nRu5bBtL/r1HCIpJW/lzT6yDtKl0u0Z4o/Vpz32IpKmBn7HerheEZQgA9N2DarQ==", "dev": true, "requires": { - "q": "^1.1.2" + "q": "1.5.1" } }, "collection-visit": { @@ -2319,8 +2319,8 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "map-visit": "1.0.0", + "object-visit": "1.0.1" } }, "color": { @@ -2329,8 +2329,8 @@ "integrity": "sha512-CwyopLkuRYO5ei2EpzpIh6LqJMt6Mt+jZhO5VI5f/wJLZriXQE32/SSqzmrh+QB+AZT81Cj8yv+7zwToW8ahZg==", "dev": true, "requires": { - "color-convert": "^1.9.1", - "color-string": "^1.5.2" + "color-convert": "1.9.3", + "color-string": "1.5.3" } }, "color-convert": { @@ -2354,8 +2354,8 @@ "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", "dev": true, "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "color-name": "1.1.3", + "simple-swizzle": "0.2.2" } }, "color-support": { @@ -2376,7 +2376,7 @@ "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", "dev": true, "requires": { - "lodash": "^4.5.0" + "lodash": "4.17.11" } }, "combined-stream": { @@ -2385,7 +2385,7 @@ "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", "dev": true, "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "commander": { @@ -2394,7 +2394,7 @@ "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", "dev": true, "requires": { - "graceful-readlink": ">= 1.0.0" + "graceful-readlink": "1.0.1" } }, "component-bind": { @@ -2427,10 +2427,10 @@ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "buffer-from": "1.1.1", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "typedarray": "0.0.6" }, "dependencies": { "isarray": { @@ -2445,13 +2445,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -2460,7 +2460,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -2471,7 +2471,7 @@ "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", "dev": true, "requires": { - "source-map": "^0.6.1" + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -2488,8 +2488,8 @@ "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", "dev": true, "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" + "ini": "1.3.5", + "proto-list": "1.2.4" } }, "connect": { @@ -2500,7 +2500,7 @@ "requires": { "debug": "2.6.9", "finalhandler": "1.1.0", - "parseurl": "~1.3.2", + "parseurl": "1.3.2", "utils-merge": "1.0.1" }, "dependencies": { @@ -2540,7 +2540,7 @@ "integrity": "sha1-WiUEe8dvcwcmZ8jLUsmJiI9JTGM=", "dev": true, "requires": { - "bluebird": "^3.1.1" + "bluebird": "3.5.3" } }, "content-disposition": { @@ -2567,7 +2567,7 @@ "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", "dev": true, "requires": { - "safe-buffer": "~5.1.1" + "safe-buffer": "5.1.2" } }, "cookie": { @@ -2600,10 +2600,10 @@ "integrity": "sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA==", "dev": true, "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0" + "import-fresh": "2.0.0", + "is-directory": "0.3.1", + "js-yaml": "3.12.0", + "parse-json": "4.0.0" } }, "cross-spawn": { @@ -2612,11 +2612,11 @@ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "nice-try": "1.0.5", + "path-key": "2.0.1", + "semver": "5.6.0", + "shebang-command": "1.2.0", + "which": "1.3.1" } }, "css-color-names": { @@ -2631,8 +2631,8 @@ "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", "dev": true, "requires": { - "postcss": "^7.0.1", - "timsort": "^0.3.0" + "postcss": "7.0.6", + "timsort": "0.3.0" } }, "css-select": { @@ -2641,10 +2641,10 @@ "integrity": "sha512-dSpYaDVoWaELjvZ3mS6IKZM/y2PMPa/XYoEfYNZePL4U/XgyxZNroHEHReDx/d+VgXh9VbCTtFqLkFbmeqeaRQ==", "dev": true, "requires": { - "boolbase": "^1.0.0", - "css-what": "^2.1.2", - "domutils": "^1.7.0", - "nth-check": "^1.0.2" + "boolbase": "1.0.0", + "css-what": "2.1.2", + "domutils": "1.7.0", + "nth-check": "1.0.2" } }, "css-select-base-adapter": { @@ -2659,8 +2659,8 @@ "integrity": "sha512-joNNW1gCp3qFFzj4St6zk+Wh/NBv0vM5YbEreZk0SD4S23S+1xBKb6cLDg2uj4P4k/GUMlIm6cKIDqIG+vdt0w==", "dev": true, "requires": { - "mdn-data": "~1.1.0", - "source-map": "^0.5.3" + "mdn-data": "1.1.4", + "source-map": "0.5.7" } }, "css-unit-converter": { @@ -2693,10 +2693,10 @@ "integrity": "sha512-AiXL90l+MDuQmRNyypG2P7ux7K4XklxYzNNUd5HXZCNcH8/N9bHPcpN97v8tXgRVeFL/Ed8iP8mVmAAu0ZpT7A==", "dev": true, "requires": { - "cosmiconfig": "^5.0.0", - "cssnano-preset-default": "^4.0.5", - "is-resolvable": "^1.0.0", - "postcss": "^7.0.0" + "cosmiconfig": "5.0.7", + "cssnano-preset-default": "4.0.5", + "is-resolvable": "1.1.0", + "postcss": "7.0.6" } }, "cssnano-preset-default": { @@ -2705,36 +2705,36 @@ "integrity": "sha512-f1uhya0ZAjPYtDD58QkBB0R+uYdzHPei7cDxJyQQIHt5acdhyGXaSXl2nDLzWHLwGFbZcHxQtkJS8mmNwnxTvw==", "dev": true, "requires": { - "css-declaration-sorter": "^4.0.1", - "cssnano-util-raw-cache": "^4.0.1", - "postcss": "^7.0.0", - "postcss-calc": "^7.0.0", - "postcss-colormin": "^4.0.2", - "postcss-convert-values": "^4.0.1", - "postcss-discard-comments": "^4.0.1", - "postcss-discard-duplicates": "^4.0.2", - "postcss-discard-empty": "^4.0.1", - "postcss-discard-overridden": "^4.0.1", - "postcss-merge-longhand": "^4.0.9", - "postcss-merge-rules": "^4.0.2", - "postcss-minify-font-values": "^4.0.2", - "postcss-minify-gradients": "^4.0.1", - "postcss-minify-params": "^4.0.1", - "postcss-minify-selectors": "^4.0.1", - "postcss-normalize-charset": "^4.0.1", - "postcss-normalize-display-values": "^4.0.1", - "postcss-normalize-positions": "^4.0.1", - "postcss-normalize-repeat-style": "^4.0.1", - "postcss-normalize-string": "^4.0.1", - "postcss-normalize-timing-functions": "^4.0.1", - "postcss-normalize-unicode": "^4.0.1", - "postcss-normalize-url": "^4.0.1", - "postcss-normalize-whitespace": "^4.0.1", - "postcss-ordered-values": "^4.1.1", - "postcss-reduce-initial": "^4.0.2", - "postcss-reduce-transforms": "^4.0.1", - "postcss-svgo": "^4.0.1", - "postcss-unique-selectors": "^4.0.1" + "css-declaration-sorter": "4.0.1", + "cssnano-util-raw-cache": "4.0.1", + "postcss": "7.0.6", + "postcss-calc": "7.0.1", + "postcss-colormin": "4.0.2", + "postcss-convert-values": "4.0.1", + "postcss-discard-comments": "4.0.1", + "postcss-discard-duplicates": "4.0.2", + "postcss-discard-empty": "4.0.1", + "postcss-discard-overridden": "4.0.1", + "postcss-merge-longhand": "4.0.9", + "postcss-merge-rules": "4.0.2", + "postcss-minify-font-values": "4.0.2", + "postcss-minify-gradients": "4.0.1", + "postcss-minify-params": "4.0.1", + "postcss-minify-selectors": "4.0.1", + "postcss-normalize-charset": "4.0.1", + "postcss-normalize-display-values": "4.0.1", + "postcss-normalize-positions": "4.0.1", + "postcss-normalize-repeat-style": "4.0.1", + "postcss-normalize-string": "4.0.1", + "postcss-normalize-timing-functions": "4.0.1", + "postcss-normalize-unicode": "4.0.1", + "postcss-normalize-url": "4.0.1", + "postcss-normalize-whitespace": "4.0.1", + "postcss-ordered-values": "4.1.1", + "postcss-reduce-initial": "4.0.2", + "postcss-reduce-transforms": "4.0.1", + "postcss-svgo": "4.0.1", + "postcss-unique-selectors": "4.0.1" } }, "cssnano-util-get-arguments": { @@ -2755,7 +2755,7 @@ "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", "dev": true, "requires": { - "postcss": "^7.0.0" + "postcss": "7.0.6" } }, "cssnano-util-same-parent": { @@ -2779,8 +2779,8 @@ "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==", "dev": true, "requires": { - "mdn-data": "~1.1.0", - "source-map": "^0.5.3" + "mdn-data": "1.1.4", + "source-map": "0.5.7" } } } @@ -2791,7 +2791,7 @@ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, "requires": { - "array-find-index": "^1.0.1" + "array-find-index": "1.0.2" } }, "custom-event": { @@ -2806,7 +2806,7 @@ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "date-format": { @@ -2827,7 +2827,7 @@ "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.1" } }, "decamelize": { @@ -2848,14 +2848,14 @@ "integrity": "sha1-eu3YVCflqS2s/lVnSnxQXpbQH50=", "dev": true, "requires": { - "decompress-tar": "^4.0.0", - "decompress-tarbz2": "^4.0.0", - "decompress-targz": "^4.0.0", - "decompress-unzip": "^4.0.1", - "graceful-fs": "^4.1.10", - "make-dir": "^1.0.0", - "pify": "^2.3.0", - "strip-dirs": "^2.0.0" + "decompress-tar": "4.1.1", + "decompress-tarbz2": "4.1.1", + "decompress-targz": "4.1.1", + "decompress-unzip": "4.0.1", + "graceful-fs": "4.1.15", + "make-dir": "1.3.0", + "pify": "2.3.0", + "strip-dirs": "2.1.0" }, "dependencies": { "graceful-fs": { @@ -2878,7 +2878,7 @@ "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", "dev": true, "requires": { - "mimic-response": "^1.0.0" + "mimic-response": "1.0.1" } }, "decompress-tar": { @@ -2887,9 +2887,9 @@ "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", "dev": true, "requires": { - "file-type": "^5.2.0", - "is-stream": "^1.1.0", - "tar-stream": "^1.5.2" + "file-type": "5.2.0", + "is-stream": "1.1.0", + "tar-stream": "1.6.2" }, "dependencies": { "file-type": { @@ -2906,11 +2906,11 @@ "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", "dev": true, "requires": { - "decompress-tar": "^4.1.0", - "file-type": "^6.1.0", - "is-stream": "^1.1.0", - "seek-bzip": "^1.0.5", - "unbzip2-stream": "^1.0.9" + "decompress-tar": "4.1.1", + "file-type": "6.2.0", + "is-stream": "1.1.0", + "seek-bzip": "1.0.5", + "unbzip2-stream": "1.3.1" }, "dependencies": { "file-type": { @@ -2927,9 +2927,9 @@ "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", "dev": true, "requires": { - "decompress-tar": "^4.1.1", - "file-type": "^5.2.0", - "is-stream": "^1.1.0" + "decompress-tar": "4.1.1", + "file-type": "5.2.0", + "is-stream": "1.1.0" }, "dependencies": { "file-type": { @@ -2946,10 +2946,10 @@ "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", "dev": true, "requires": { - "file-type": "^3.8.0", - "get-stream": "^2.2.0", - "pify": "^2.3.0", - "yauzl": "^2.4.2" + "file-type": "3.9.0", + "get-stream": "2.3.1", + "pify": "2.3.0", + "yauzl": "2.10.0" }, "dependencies": { "file-type": { @@ -2964,8 +2964,8 @@ "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", "dev": true, "requires": { - "object-assign": "^4.0.1", - "pinkie-promise": "^2.0.0" + "object-assign": "4.1.1", + "pinkie-promise": "2.0.1" } }, "object-assign": { @@ -2994,7 +2994,7 @@ "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "dev": true, "requires": { - "clone": "^1.0.2" + "clone": "1.0.4" } }, "define-properties": { @@ -3003,7 +3003,7 @@ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "object-keys": "1.0.12" } }, "define-property": { @@ -3012,8 +3012,8 @@ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "is-descriptor": "1.0.2", + "isobject": "3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -3022,7 +3022,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -3031,7 +3031,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -3040,9 +3040,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -3099,8 +3099,8 @@ "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", "dev": true, "requires": { - "arrify": "^1.0.1", - "path-type": "^3.0.0" + "arrify": "1.0.1", + "path-type": "3.0.0" } }, "doctrine": { @@ -3109,7 +3109,7 @@ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { - "esutils": "^2.0.2" + "esutils": "2.0.2" } }, "dom-serialize": { @@ -3118,10 +3118,10 @@ "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", "dev": true, "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" + "custom-event": "1.0.1", + "ent": "2.2.0", + "extend": "3.0.2", + "void-elements": "2.0.1" } }, "dom-serializer": { @@ -3130,8 +3130,8 @@ "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "dev": true, "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" + "domelementtype": "1.1.3", + "entities": "1.1.2" }, "dependencies": { "domelementtype": { @@ -3154,8 +3154,8 @@ "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", "dev": true, "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "dom-serializer": "0.1.0", + "domelementtype": "1.2.1" } }, "dot-prop": { @@ -3164,7 +3164,7 @@ "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "dev": true, "requires": { - "is-obj": "^1.0.0" + "is-obj": "1.0.1" } }, "download": { @@ -3174,17 +3174,17 @@ "dev": true, "optional": true, "requires": { - "caw": "^2.0.0", - "content-disposition": "^0.5.2", - "decompress": "^4.0.0", - "ext-name": "^5.0.0", + "caw": "2.0.1", + "content-disposition": "0.5.2", + "decompress": "4.2.0", + "ext-name": "5.0.0", "file-type": "5.2.0", - "filenamify": "^2.0.0", - "get-stream": "^3.0.0", - "got": "^7.0.0", - "make-dir": "^1.0.0", - "p-event": "^1.0.0", - "pify": "^3.0.0" + "filenamify": "2.1.0", + "get-stream": "3.0.0", + "got": "7.1.0", + "make-dir": "1.3.0", + "p-event": "1.3.0", + "pify": "3.0.0" }, "dependencies": { "file-type": { @@ -3208,7 +3208,7 @@ "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", "dev": true, "requires": { - "readable-stream": "~1.1.9" + "readable-stream": "1.1.14" } }, "duplexer3": { @@ -3223,8 +3223,8 @@ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" } }, "ee-first": { @@ -3251,7 +3251,7 @@ "integrity": "sha1-jhdyBsPICDfYVjLouTWd/osvbq8=", "dev": true, "requires": { - "once": "~1.3.0" + "once": "1.3.3" } }, "engine.io": { @@ -3260,12 +3260,12 @@ "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", "dev": true, "requires": { - "accepts": "~1.3.4", + "accepts": "1.3.5", "base64id": "1.0.0", "cookie": "0.3.1", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.0", - "ws": "~3.3.1" + "debug": "3.1.0", + "engine.io-parser": "2.1.3", + "ws": "3.3.3" }, "dependencies": { "debug": { @@ -3293,14 +3293,14 @@ "requires": { "component-emitter": "1.2.1", "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", + "debug": "3.1.0", + "engine.io-parser": "2.1.3", "has-cors": "1.1.0", "indexof": "0.0.1", "parseqs": "0.0.5", "parseuri": "0.0.5", - "ws": "~3.3.1", - "xmlhttprequest-ssl": "~1.5.4", + "ws": "3.3.3", + "xmlhttprequest-ssl": "1.5.5", "yeast": "0.1.2" }, "dependencies": { @@ -3328,10 +3328,10 @@ "dev": true, "requires": { "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", + "arraybuffer.slice": "0.0.7", "base64-arraybuffer": "0.1.5", "blob": "0.0.5", - "has-binary2": "~1.0.2" + "has-binary2": "1.0.3" } }, "ent": { @@ -3353,7 +3353,7 @@ "dev": true, "optional": true, "requires": { - "prr": "~1.0.1" + "prr": "1.0.1" } }, "error": { @@ -3362,8 +3362,8 @@ "integrity": "sha1-pfdf/02ZJhJt2sDqXcOOaJFTywI=", "dev": true, "requires": { - "string-template": "~0.2.1", - "xtend": "~4.0.0" + "string-template": "0.2.1", + "xtend": "4.0.1" } }, "error-ex": { @@ -3372,7 +3372,7 @@ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "requires": { - "is-arrayish": "^0.2.1" + "is-arrayish": "0.2.1" } }, "es-abstract": { @@ -3381,11 +3381,11 @@ "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "dev": true, "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" + "es-to-primitive": "1.2.0", + "function-bind": "1.1.1", + "has": "1.0.3", + "is-callable": "1.1.4", + "is-regex": "1.0.4" } }, "es-to-primitive": { @@ -3394,9 +3394,9 @@ "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "dev": true, "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "1.1.4", + "is-date-object": "1.0.1", + "is-symbol": "1.0.2" } }, "es6-promise": { @@ -3423,11 +3423,11 @@ "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", "dev": true, "requires": { - "esprima": "^3.1.3", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "esprima": "3.1.3", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "optionator": "0.8.2", + "source-map": "0.6.1" }, "dependencies": { "esprima": { @@ -3451,44 +3451,44 @@ "integrity": "sha512-g4KWpPdqN0nth+goDNICNXGfJF7nNnepthp46CAlJoJtC5K/cLu3NgCM3AHu1CkJ5Hzt9V0Y0PBAO6Ay/gGb+w==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.5.3", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^2.1.0", - "eslint-scope": "^4.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^4.0.0", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "imurmurhash": "^0.1.4", - "inquirer": "^6.1.0", - "is-resolvable": "^1.1.0", - "js-yaml": "^3.12.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.5", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "require-uncached": "^1.0.3", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^5.0.2", - "text-table": "^0.2.0" + "@babel/code-frame": "7.0.0", + "ajv": "6.6.1", + "chalk": "2.4.1", + "cross-spawn": "6.0.5", + "debug": "4.1.0", + "doctrine": "2.1.0", + "eslint-scope": "4.0.0", + "eslint-utils": "1.3.1", + "eslint-visitor-keys": "1.0.0", + "espree": "4.1.0", + "esquery": "1.0.1", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", + "glob": "7.1.3", + "globals": "11.9.0", + "ignore": "4.0.6", + "imurmurhash": "0.1.4", + "inquirer": "6.2.1", + "is-resolvable": "1.1.0", + "js-yaml": "3.12.0", + "json-stable-stringify-without-jsonify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.11", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "7.0.0", + "progress": "2.0.1", + "regexpp": "2.0.1", + "require-uncached": "1.0.3", + "semver": "5.6.0", + "strip-ansi": "4.0.0", + "strip-json-comments": "2.0.1", + "table": "5.1.1", + "text-table": "0.2.0" }, "dependencies": { "ansi-regex": { @@ -3503,12 +3503,12 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" } }, "minimatch": { @@ -3517,7 +3517,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "strip-ansi": { @@ -3526,7 +3526,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -3537,8 +3537,8 @@ "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "esrecurse": "4.2.1", + "estraverse": "4.2.0" } }, "eslint-utils": { @@ -3559,9 +3559,9 @@ "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", "dev": true, "requires": { - "acorn": "^6.0.2", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" + "acorn": "6.0.4", + "acorn-jsx": "5.0.1", + "eslint-visitor-keys": "1.0.0" } }, "esprima": { @@ -3576,7 +3576,7 @@ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", "dev": true, "requires": { - "estraverse": "^4.0.0" + "estraverse": "4.2.0" } }, "esrecurse": { @@ -3585,7 +3585,7 @@ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "dev": true, "requires": { - "estraverse": "^4.1.0" + "estraverse": "4.2.0" } }, "estemplate": { @@ -3594,8 +3594,8 @@ "integrity": "sha1-FxSp1GGQc4rJWLyv1J4CnNpWo54=", "dev": true, "requires": { - "esprima": "^2.7.2", - "estraverse": "^4.1.1" + "esprima": "2.7.3", + "estraverse": "4.2.0" }, "dependencies": { "esprima": { @@ -3630,13 +3630,13 @@ "integrity": "sha512-vyibDcu5JL20Me1fP734QBH/kenBGLZap2n0+XXM7mvuUPzJ20Ydqj1aKcIeMdri1p+PU+4yAKugjN8KCVst+g==", "dev": true, "requires": { - "duplexer": "^0.1.1", - "from": "^0.1.7", + "duplexer": "0.1.1", + "from": "0.1.7", "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" + "pause-stream": "0.0.11", + "split": "1.0.1", + "stream-combiner": "0.2.2", + "through": "2.3.8" } }, "eventemitter3": { @@ -3652,11 +3652,11 @@ "dev": true, "optional": true, "requires": { - "execa": "^0.7.0", - "p-finally": "^1.0.0", - "pify": "^3.0.0", - "rimraf": "^2.5.4", - "tempfile": "^2.0.0" + "execa": "0.7.0", + "p-finally": "1.0.0", + "pify": "3.0.0", + "rimraf": "2.6.2", + "tempfile": "2.0.0" } }, "execa": { @@ -3665,13 +3665,13 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" }, "dependencies": { "cross-spawn": { @@ -3680,9 +3680,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.5", + "shebang-command": "1.2.0", + "which": "1.3.1" } }, "lru-cache": { @@ -3691,8 +3691,8 @@ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "dev": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } } } @@ -3704,7 +3704,7 @@ "dev": true, "optional": true, "requires": { - "pify": "^2.2.0" + "pify": "2.3.0" }, "dependencies": { "pify": { @@ -3722,9 +3722,9 @@ "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", "dev": true, "requires": { - "array-slice": "^0.2.3", - "array-unique": "^0.2.1", - "braces": "^0.1.2" + "array-slice": "0.2.3", + "array-unique": "0.2.1", + "braces": "0.1.5" }, "dependencies": { "array-slice": { @@ -3745,7 +3745,7 @@ "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", "dev": true, "requires": { - "expand-range": "^0.1.0" + "expand-range": "0.1.1" } }, "expand-range": { @@ -3754,8 +3754,8 @@ "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", "dev": true, "requires": { - "is-number": "^0.1.1", - "repeat-string": "^0.2.2" + "is-number": "0.1.1", + "repeat-string": "0.2.2" } }, "is-number": { @@ -3778,13 +3778,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "debug": { @@ -3802,7 +3802,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -3811,7 +3811,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "ms": { @@ -3828,7 +3828,7 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "^2.1.0" + "fill-range": "2.2.4" }, "dependencies": { "fill-range": { @@ -3837,11 +3837,11 @@ "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "3.1.1", + "repeat-element": "1.1.3", + "repeat-string": "1.6.1" } }, "is-number": { @@ -3850,7 +3850,7 @@ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "isarray": { @@ -3874,7 +3874,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -3885,7 +3885,7 @@ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "homedir-polyfill": "^1.0.1" + "homedir-polyfill": "1.0.1" } }, "ext-list": { @@ -3894,7 +3894,7 @@ "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", "dev": true, "requires": { - "mime-db": "^1.28.0" + "mime-db": "1.37.0" } }, "ext-name": { @@ -3903,8 +3903,8 @@ "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", "dev": true, "requires": { - "ext-list": "^2.0.0", - "sort-keys-length": "^1.0.0" + "ext-list": "2.2.2", + "sort-keys-length": "1.0.1" } }, "extend": { @@ -3919,8 +3919,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -3929,7 +3929,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -3940,9 +3940,9 @@ "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", "dev": true, "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "chardet": "0.7.0", + "iconv-lite": "0.4.24", + "tmp": "0.0.33" } }, "extglob": { @@ -3951,14 +3951,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -3967,7 +3967,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -3976,7 +3976,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -3985,7 +3985,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -3994,7 +3994,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -4003,9 +4003,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -4037,7 +4037,7 @@ "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", "dev": true, "requires": { - "pend": "~1.2.0" + "pend": "1.2.0" } }, "ms": { @@ -4052,7 +4052,7 @@ "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", "dev": true, "requires": { - "fd-slicer": "~1.0.1" + "fd-slicer": "1.0.1" } } } @@ -4069,10 +4069,10 @@ "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", "dev": true, "requires": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "parse-node-version": "^1.0.0", - "time-stamp": "^1.0.0" + "ansi-gray": "0.1.1", + "color-support": "1.1.3", + "parse-node-version": "1.0.0", + "time-stamp": "1.1.0" } }, "fast-deep-equal": { @@ -4087,12 +4087,12 @@ "integrity": "sha512-FjK2nCGI/McyzgNtTESqaWP3trPvHyRyoyY70hxjc3oKPNmDe8taohLZpoVKoUjW85tbU5txaYUZCNtVzygl1g==", "dev": true, "requires": { - "@mrmlnc/readdir-enhanced": "^2.2.1", - "@nodelib/fs.stat": "^1.1.2", - "glob-parent": "^3.1.0", - "is-glob": "^4.0.0", - "merge2": "^1.2.3", - "micromatch": "^3.1.10" + "@mrmlnc/readdir-enhanced": "2.2.1", + "@nodelib/fs.stat": "1.1.3", + "glob-parent": "3.1.0", + "is-glob": "4.0.0", + "merge2": "1.2.3", + "micromatch": "3.1.10" }, "dependencies": { "is-glob": { @@ -4101,7 +4101,7 @@ "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "dev": true, "requires": { - "is-extglob": "^2.1.1" + "is-extglob": "2.1.1" } } } @@ -4124,7 +4124,7 @@ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "dev": true, "requires": { - "websocket-driver": ">=0.5.1" + "websocket-driver": "0.7.0" } }, "fd-slicer": { @@ -4133,7 +4133,7 @@ "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, "requires": { - "pend": "~1.2.0" + "pend": "1.2.0" } }, "figures": { @@ -4142,7 +4142,7 @@ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5" + "escape-string-regexp": "1.0.5" } }, "file-entry-cache": { @@ -4151,8 +4151,8 @@ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true, "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "1.3.4", + "object-assign": "4.1.1" }, "dependencies": { "object-assign": { @@ -4187,9 +4187,9 @@ "integrity": "sha512-ICw7NTT6RsDp2rnYKVd8Fu4cr6ITzGy3+u4vUujPkabyaz+03F24NWEX7fs5fp+kBonlaqPH8fAO2NM+SXt/JA==", "dev": true, "requires": { - "filename-reserved-regex": "^2.0.0", - "strip-outer": "^1.0.0", - "trim-repeated": "^1.0.0" + "filename-reserved-regex": "2.0.0", + "strip-outer": "1.0.1", + "trim-repeated": "1.0.0" } }, "fill-range": { @@ -4198,10 +4198,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -4210,7 +4210,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -4222,12 +4222,12 @@ "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", - "unpipe": "~1.0.0" + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.2", + "statuses": "1.3.1", + "unpipe": "1.0.0" }, "dependencies": { "debug": { @@ -4259,8 +4259,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" } }, "find-versions": { @@ -4270,8 +4270,8 @@ "dev": true, "optional": true, "requires": { - "array-uniq": "^2.0.0", - "semver-regex": "^2.0.0" + "array-uniq": "2.0.0", + "semver-regex": "2.0.0" }, "dependencies": { "array-uniq": { @@ -4289,10 +4289,10 @@ "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", "dev": true, "requires": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "detect-file": "1.0.0", + "is-glob": "3.1.0", + "micromatch": "3.1.10", + "resolve-dir": "1.0.1" } }, "fined": { @@ -4301,11 +4301,11 @@ "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", "dev": true, "requires": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" + "expand-tilde": "2.0.2", + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0", + "object.pick": "1.3.0", + "parse-filepath": "1.0.2" } }, "first-chunk-stream": { @@ -4326,10 +4326,10 @@ "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==", "dev": true, "requires": { - "circular-json": "^0.3.1", - "graceful-fs": "^4.1.2", - "rimraf": "~2.6.2", - "write": "^0.2.1" + "circular-json": "0.3.3", + "graceful-fs": "4.1.15", + "rimraf": "2.6.2", + "write": "0.2.1" }, "dependencies": { "graceful-fs": { @@ -4351,7 +4351,7 @@ "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", "dev": true, "requires": { - "debug": "=3.1.0" + "debug": "3.1.0" }, "dependencies": { "debug": { @@ -4388,7 +4388,7 @@ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } }, "forever-agent": { @@ -4403,9 +4403,9 @@ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "asynckit": "0.4.0", + "combined-stream": "1.0.7", + "mime-types": "2.1.21" } }, "fragment-cache": { @@ -4414,7 +4414,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "^0.2.2" + "map-cache": "0.2.2" } }, "fresh": { @@ -4436,8 +4436,8 @@ "dev": true, "optional": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "inherits": "2.0.3", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -4454,13 +4454,13 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -4470,7 +4470,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -4487,9 +4487,9 @@ "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0" + "graceful-fs": "4.1.15", + "jsonfile": "2.4.0", + "klaw": "1.3.1" }, "dependencies": { "graceful-fs": { @@ -4506,7 +4506,7 @@ "integrity": "sha1-gAI4I5gfn//+AWCei+Zo9prknnA=", "dev": true, "requires": { - "graceful-fs": "^4.1.2" + "graceful-fs": "4.1.15" }, "dependencies": { "graceful-fs": { @@ -4530,8 +4530,8 @@ "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "2.11.1", + "node-pre-gyp": "0.10.0" }, "dependencies": { "abbrev": { @@ -5070,7 +5070,7 @@ "integrity": "sha1-QLcJU30k0dRXZ9takIaJ3+aaxE8=", "dev": true, "requires": { - "globule": "~0.1.0" + "globule": "0.1.0" } }, "get-proxy": { @@ -5079,7 +5079,7 @@ "integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==", "dev": true, "requires": { - "npm-conf": "^1.1.0" + "npm-conf": "1.1.3" } }, "get-stdin": { @@ -5106,7 +5106,7 @@ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "gifsicle": { @@ -5116,10 +5116,10 @@ "dev": true, "optional": true, "requires": { - "bin-build": "^3.0.0", - "bin-wrapper": "^4.0.0", - "execa": "^1.0.0", - "logalot": "^2.0.0" + "bin-build": "3.0.0", + "bin-wrapper": "4.1.0", + "execa": "1.0.0", + "logalot": "2.1.0" }, "dependencies": { "execa": { @@ -5129,13 +5129,13 @@ "dev": true, "optional": true, "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "6.0.5", + "get-stream": "4.1.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "get-stream": { @@ -5145,7 +5145,7 @@ "dev": true, "optional": true, "requires": { - "pump": "^3.0.0" + "pump": "3.0.0" } } } @@ -5156,10 +5156,10 @@ "integrity": "sha1-xstz0yJsHv7wTePFbQEvAzd+4V8=", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^2.0.1", - "once": "^1.3.0" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "2.0.10", + "once": "1.3.3" } }, "glob-base": { @@ -5168,8 +5168,8 @@ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "glob-parent": "2.0.0", + "is-glob": "2.0.1" }, "dependencies": { "glob-parent": { @@ -5178,7 +5178,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "^2.0.0" + "is-glob": "2.0.1" } }, "is-extglob": { @@ -5193,7 +5193,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } } } @@ -5204,8 +5204,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-glob": "3.1.0", + "path-dirname": "1.0.2" } }, "glob-stream": { @@ -5214,12 +5214,12 @@ "integrity": "sha1-kXCl8St5Awb9/lmPMT+PeVT9FDs=", "dev": true, "requires": { - "glob": "^4.3.1", - "glob2base": "^0.0.12", - "minimatch": "^2.0.1", - "ordered-read-streams": "^0.1.0", - "through2": "^0.6.1", - "unique-stream": "^1.0.0" + "glob": "4.5.3", + "glob2base": "0.0.12", + "minimatch": "2.0.10", + "ordered-read-streams": "0.1.0", + "through2": "0.6.5", + "unique-stream": "1.0.0" }, "dependencies": { "readable-stream": { @@ -5228,10 +5228,10 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "through2": { @@ -5240,8 +5240,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } } } @@ -5258,7 +5258,7 @@ "integrity": "sha1-uVtKjfdLOcgymLDAXJeLTZo7cQs=", "dev": true, "requires": { - "gaze": "^0.5.1" + "gaze": "0.5.2" } }, "glob2base": { @@ -5267,7 +5267,7 @@ "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", "dev": true, "requires": { - "find-index": "^0.1.1" + "find-index": "0.1.1" } }, "global-modules": { @@ -5276,9 +5276,9 @@ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" + "global-prefix": "1.0.2", + "is-windows": "1.0.2", + "resolve-dir": "1.0.1" } }, "global-prefix": { @@ -5287,11 +5287,11 @@ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "dev": true, "requires": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" + "expand-tilde": "2.0.2", + "homedir-polyfill": "1.0.1", + "ini": "1.3.5", + "is-windows": "1.0.2", + "which": "1.3.1" } }, "globals": { @@ -5306,13 +5306,13 @@ "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==", "dev": true, "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "fast-glob": "^2.0.2", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" + "array-union": "1.0.2", + "dir-glob": "2.0.0", + "fast-glob": "2.2.4", + "glob": "7.1.3", + "ignore": "3.3.10", + "pify": "3.0.0", + "slash": "1.0.0" }, "dependencies": { "glob": { @@ -5321,12 +5321,12 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" } }, "ignore": { @@ -5341,7 +5341,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } } } @@ -5352,9 +5352,9 @@ "integrity": "sha1-2cjt3h2nnRJaFRt5UzuXhnY0auU=", "dev": true, "requires": { - "glob": "~3.1.21", - "lodash": "~1.0.1", - "minimatch": "~0.2.11" + "glob": "3.1.21", + "lodash": "1.0.2", + "minimatch": "0.2.14" }, "dependencies": { "glob": { @@ -5363,9 +5363,9 @@ "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", "dev": true, "requires": { - "graceful-fs": "~1.2.0", - "inherits": "1", - "minimatch": "~0.2.11" + "graceful-fs": "1.2.3", + "inherits": "1.0.2", + "minimatch": "0.2.14" } }, "graceful-fs": { @@ -5392,8 +5392,8 @@ "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", "dev": true, "requires": { - "lru-cache": "2", - "sigmund": "~1.0.0" + "lru-cache": "2.7.3", + "sigmund": "1.0.1" } } } @@ -5404,7 +5404,7 @@ "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==", "dev": true, "requires": { - "sparkles": "^1.0.0" + "sparkles": "1.0.1" } }, "good-listener": { @@ -5412,7 +5412,7 @@ "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz", "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", "requires": { - "delegate": "^3.1.2" + "delegate": "3.2.0" } }, "got": { @@ -5422,20 +5422,20 @@ "dev": true, "optional": true, "requires": { - "decompress-response": "^3.2.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-plain-obj": "^1.1.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "isurl": "^1.0.0-alpha5", - "lowercase-keys": "^1.0.0", - "p-cancelable": "^0.3.0", - "p-timeout": "^1.1.1", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "url-parse-lax": "^1.0.0", - "url-to-options": "^1.0.1" + "decompress-response": "3.3.0", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-plain-obj": "1.1.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "isurl": "1.0.0", + "lowercase-keys": "1.0.1", + "p-cancelable": "0.3.0", + "p-timeout": "1.2.1", + "safe-buffer": "5.1.2", + "timed-out": "4.0.1", + "url-parse-lax": "1.0.0", + "url-to-options": "1.0.1" } }, "graceful-fs": { @@ -5444,7 +5444,7 @@ "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=", "dev": true, "requires": { - "natives": "^1.1.0" + "natives": "1.1.6" } }, "graceful-readlink": { @@ -5459,19 +5459,19 @@ "integrity": "sha1-VxzkWSjdQK9lFPxAEYZgFsE4RbQ=", "dev": true, "requires": { - "archy": "^1.0.0", - "chalk": "^1.0.0", - "deprecated": "^0.0.1", - "gulp-util": "^3.0.0", - "interpret": "^1.0.0", - "liftoff": "^2.1.0", - "minimist": "^1.1.0", - "orchestrator": "^0.3.0", - "pretty-hrtime": "^1.0.0", - "semver": "^4.1.0", - "tildify": "^1.0.0", - "v8flags": "^2.0.2", - "vinyl-fs": "^0.3.0" + "archy": "1.0.0", + "chalk": "1.1.3", + "deprecated": "0.0.1", + "gulp-util": "3.0.8", + "interpret": "1.1.0", + "liftoff": "2.5.0", + "minimist": "1.2.0", + "orchestrator": "0.3.8", + "pretty-hrtime": "1.0.3", + "semver": "4.3.6", + "tildify": "1.2.0", + "v8flags": "2.1.1", + "vinyl-fs": "0.3.14" }, "dependencies": { "ansi-styles": { @@ -5486,11 +5486,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "semver": { @@ -5513,10 +5513,10 @@ "integrity": "sha512-oomaIqDXxFkg7lbpBou/gnUkX51/Y/M2ZfSjL2hdqXTAlSWZcgZtd2o0cOH0r/eE8LWD0+Q/PsLsr2DKOoqToQ==", "dev": true, "requires": { - "plugin-error": "^1.0.1", - "replace-ext": "^1.0.0", - "through2": "^2.0.0", - "vinyl-sourcemaps-apply": "^0.2.0" + "plugin-error": "1.0.1", + "replace-ext": "1.0.0", + "through2": "2.0.5", + "vinyl-sourcemaps-apply": "0.2.1" }, "dependencies": { "replace-ext": { @@ -5545,9 +5545,9 @@ "integrity": "sha512-9E1oLoOWfhSXHGv6QlwXJim7uNzd9EVlWK+21tCU9Ju/kR0/p2AZYPz4qSchgO8PlLIH4FpZYfzwS+rEksZjIg==", "dev": true, "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "inherits": "2.0.3", + "string_decoder": "1.2.0", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -5556,7 +5556,7 @@ "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "through2": { @@ -5565,8 +5565,8 @@ "integrity": "sha512-8B+sevlqP4OiCjonI1Zw03Sf8PuV1eRsYQgLad5eonILOdyeRsY27A/2Ze8IlvlMvq31OH+3fz/styI7Ya62yQ==", "dev": true, "requires": { - "readable-stream": "2 || 3", - "xtend": "~4.0.1" + "readable-stream": "3.0.6", + "xtend": "4.0.1" } } } @@ -5577,9 +5577,9 @@ "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", "dev": true, "requires": { - "concat-with-sourcemaps": "^1.0.0", - "through2": "^2.0.0", - "vinyl": "^2.0.0" + "concat-with-sourcemaps": "1.1.0", + "through2": "2.0.5", + "vinyl": "2.2.0" }, "dependencies": { "clone": { @@ -5606,12 +5606,12 @@ "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", "dev": true, "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" + "clone": "2.1.2", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.1.2", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" } } } @@ -5622,15 +5622,15 @@ "integrity": "sha512-FknHeA6smZhiRNrK/3UVH8BHLCFHS7WZdE7Y2VbZHtxye1UTIa5ZY0Cnst6O9n3kL8z7y43QI+acx3nUtJoiHw==", "dev": true, "requires": { - "ansi-colors": "^2.0.5", - "connect": "^3.6.6", - "connect-livereload": "^0.6.0", - "event-stream": "^3.3.4", - "fancy-log": "^1.3.2", - "send": "^0.16.2", - "serve-index": "^1.9.1", - "serve-static": "^1.13.2", - "tiny-lr": "^1.1.1" + "ansi-colors": "2.0.5", + "connect": "3.6.6", + "connect-livereload": "0.6.0", + "event-stream": "3.3.5", + "fancy-log": "1.3.3", + "send": "0.16.2", + "serve-index": "1.9.1", + "serve-static": "1.13.2", + "tiny-lr": "1.1.1" }, "dependencies": { "ansi-colors": { @@ -5647,9 +5647,9 @@ "integrity": "sha512-9GUqCqh85C7rP9120cpxXuZz2ayq3BZc85pCTuPJS03VQYxne0aWPIXWx6LSvsGPa3uRqtSO537vaugOh+5cXg==", "dev": true, "requires": { - "eslint": "^5.0.1", - "fancy-log": "^1.3.2", - "plugin-error": "^1.0.1" + "eslint": "5.9.0", + "fancy-log": "1.3.3", + "plugin-error": "1.0.1" } }, "gulp-imagemin": { @@ -5658,17 +5658,17 @@ "integrity": "sha512-bKJMix4r6EQPVV2u8sUglw6Rn0PSp6i70pSK2ECN7j0dRy0w/Lz5SBbynY3MfGBZ0cTMZlaUq+6LyKlZgP74Ew==", "dev": true, "requires": { - "chalk": "^2.4.1", - "fancy-log": "^1.3.2", - "imagemin": "^6.0.0", - "imagemin-gifsicle": "^6.0.1", - "imagemin-jpegtran": "^6.0.0", - "imagemin-optipng": "^6.0.0", - "imagemin-svgo": "^7.0.0", - "plugin-error": "^1.0.1", - "plur": "^3.0.1", - "pretty-bytes": "^5.1.0", - "through2-concurrent": "^2.0.0" + "chalk": "2.4.1", + "fancy-log": "1.3.3", + "imagemin": "6.0.0", + "imagemin-gifsicle": "6.0.1", + "imagemin-jpegtran": "6.0.0", + "imagemin-optipng": "6.0.0", + "imagemin-svgo": "7.0.0", + "plugin-error": "1.0.1", + "plur": "3.0.1", + "pretty-bytes": "5.1.0", + "through2-concurrent": "2.0.0" } }, "gulp-less": { @@ -5677,13 +5677,13 @@ "integrity": "sha512-hmM2k0FfQp7Ptm3ZaqO2CkMX3hqpiIOn4OHtuSsCeFym63F7oWlEua5v6u1cIjVUKYsVIs9zPg9vbqTEb/udpA==", "dev": true, "requires": { - "accord": "^0.29.0", - "less": "2.6.x || ^3.7.1", - "object-assign": "^4.0.1", - "plugin-error": "^0.1.2", - "replace-ext": "^1.0.0", - "through2": "^2.0.0", - "vinyl-sourcemaps-apply": "^0.2.0" + "accord": "0.29.0", + "less": "3.9.0", + "object-assign": "4.1.1", + "plugin-error": "0.1.2", + "replace-ext": "1.0.0", + "through2": "2.0.5", + "vinyl-sourcemaps-apply": "0.2.1" }, "dependencies": { "arr-diff": { @@ -5692,8 +5692,8 @@ "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", "dev": true, "requires": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" + "arr-flatten": "1.1.0", + "array-slice": "0.2.3" } }, "arr-union": { @@ -5714,7 +5714,7 @@ "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", "dev": true, "requires": { - "kind-of": "^1.1.0" + "kind-of": "1.1.0" } }, "kind-of": { @@ -5735,11 +5735,11 @@ "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", "dev": true, "requires": { - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" + "ansi-cyan": "0.1.1", + "ansi-red": "0.1.1", + "arr-diff": "1.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" } }, "replace-ext": { @@ -5756,8 +5756,8 @@ "integrity": "sha1-J7Z6Ql6zh1ImNFq4lgTXN/Y1fCE=", "dev": true, "requires": { - "angular": "~1.3.1", - "angular-animate": "~1.3.1", + "angular": "1.3.20", + "angular-animate": "1.3.20", "canonical-path": "0.0.2", "extend": "1.3.0", "gulp-util": "3.0.0", @@ -5800,11 +5800,11 @@ "integrity": "sha1-Zjs6ZItotV0EaQ1JFnqoN4WPIXQ=", "dev": true, "requires": { - "ansi-styles": "^1.1.0", - "escape-string-regexp": "^1.0.0", - "has-ansi": "^0.1.0", - "strip-ansi": "^0.3.0", - "supports-color": "^0.2.0" + "ansi-styles": "1.1.0", + "escape-string-regexp": "1.0.5", + "has-ansi": "0.1.0", + "strip-ansi": "0.3.0", + "supports-color": "0.2.0" } }, "clone": { @@ -5819,8 +5819,8 @@ "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", "dev": true, "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.3.0" + "get-stdin": "4.0.1", + "meow": "3.7.0" } }, "extend": { @@ -5835,15 +5835,15 @@ "integrity": "sha1-b+7cR9aXKCO6zplH/F9GvYo0Zuc=", "dev": true, "requires": { - "chalk": "^0.5.0", - "dateformat": "^1.0.7-1.2.3", - "lodash": "^2.4.1", - "lodash._reinterpolate": "^2.4.1", - "lodash.template": "^2.4.1", - "minimist": "^0.2.0", - "multipipe": "^0.1.0", - "through2": "^0.5.0", - "vinyl": "^0.2.1" + "chalk": "0.5.1", + "dateformat": "1.0.12", + "lodash": "2.4.1", + "lodash._reinterpolate": "2.4.1", + "lodash.template": "2.4.1", + "minimist": "0.2.0", + "multipipe": "0.1.2", + "through2": "0.5.1", + "vinyl": "0.2.3" }, "dependencies": { "through2": { @@ -5852,8 +5852,8 @@ "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", "dev": true, "requires": { - "readable-stream": "~1.0.17", - "xtend": "~3.0.0" + "readable-stream": "1.0.34", + "xtend": "3.0.0" } } } @@ -5864,7 +5864,7 @@ "integrity": "sha1-hPJlqujA5qiKEtcCKJS3VoiUxi4=", "dev": true, "requires": { - "ansi-regex": "^0.2.0" + "ansi-regex": "0.2.1" } }, "lodash": { @@ -5885,8 +5885,8 @@ "integrity": "sha1-p+iIXwXmiFEUS24SqPNngCa8TFQ=", "dev": true, "requires": { - "lodash._objecttypes": "~2.4.1", - "lodash.keys": "~2.4.1" + "lodash._objecttypes": "2.4.1", + "lodash.keys": "2.4.1" } }, "lodash.escape": { @@ -5895,9 +5895,9 @@ "integrity": "sha1-LOEsXghNsKV92l5dHu659dF1o7Q=", "dev": true, "requires": { - "lodash._escapehtmlchar": "~2.4.1", - "lodash._reunescapedhtml": "~2.4.1", - "lodash.keys": "~2.4.1" + "lodash._escapehtmlchar": "2.4.1", + "lodash._reunescapedhtml": "2.4.1", + "lodash.keys": "2.4.1" } }, "lodash.keys": { @@ -5906,9 +5906,9 @@ "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", "dev": true, "requires": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" } }, "lodash.template": { @@ -5917,13 +5917,13 @@ "integrity": "sha1-nmEQB+32KRKal0qzxIuBez4c8g0=", "dev": true, "requires": { - "lodash._escapestringchar": "~2.4.1", - "lodash._reinterpolate": "~2.4.1", - "lodash.defaults": "~2.4.1", - "lodash.escape": "~2.4.1", - "lodash.keys": "~2.4.1", - "lodash.templatesettings": "~2.4.1", - "lodash.values": "~2.4.1" + "lodash._escapestringchar": "2.4.1", + "lodash._reinterpolate": "2.4.1", + "lodash.defaults": "2.4.1", + "lodash.escape": "2.4.1", + "lodash.keys": "2.4.1", + "lodash.templatesettings": "2.4.1", + "lodash.values": "2.4.1" } }, "lodash.templatesettings": { @@ -5932,8 +5932,8 @@ "integrity": "sha1-6nbHXRHrhtTb6JqDiTu4YZKaxpk=", "dev": true, "requires": { - "lodash._reinterpolate": "~2.4.1", - "lodash.escape": "~2.4.1" + "lodash._reinterpolate": "2.4.1", + "lodash.escape": "2.4.1" } }, "merge-stream": { @@ -5942,7 +5942,7 @@ "integrity": "sha1-5oIPet267gA/SMpKWMfFolPV4Fw=", "dev": true, "requires": { - "through2": "^0.5.1" + "through2": "0.5.1" }, "dependencies": { "through2": { @@ -5951,8 +5951,8 @@ "integrity": "sha1-390BLrnHAOIyP9M084rGIqs3Lac=", "dev": true, "requires": { - "readable-stream": "~1.0.17", - "xtend": "~3.0.0" + "readable-stream": "1.0.34", + "xtend": "3.0.0" } } } @@ -5969,10 +5969,10 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "strip-ansi": { @@ -5981,7 +5981,7 @@ "integrity": "sha1-JfSOoiynkYfzF0pNuHWTR7sSYiA=", "dev": true, "requires": { - "ansi-regex": "^0.2.1" + "ansi-regex": "0.2.1" } }, "supports-color": { @@ -5996,8 +5996,8 @@ "integrity": "sha1-90KzKJPovSYUbnieT9LMssB6cX4=", "dev": true, "requires": { - "readable-stream": ">=1.0.27-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" }, "dependencies": { "xtend": { @@ -6014,7 +6014,7 @@ "integrity": "sha1-vKk4IJWC7FpJrVOKAPofEl5RMlI=", "dev": true, "requires": { - "clone-stats": "~0.0.1" + "clone-stats": "0.0.1" } }, "vinyl-fs": { @@ -6023,14 +6023,14 @@ "integrity": "sha1-LiXP5t9cgIGPl/9Be/XCGkHkpJs=", "dev": true, "requires": { - "glob-stream": "^3.1.5", - "glob-watcher": "^0.0.6", - "graceful-fs": "^3.0.0", - "lodash": "^2.4.1", - "mkdirp": "^0.5.0", - "strip-bom": "^1.0.0", - "through2": "^0.6.1", - "vinyl": "^0.4.0" + "glob-stream": "3.1.18", + "glob-watcher": "0.0.6", + "graceful-fs": "3.0.11", + "lodash": "2.4.1", + "mkdirp": "0.5.1", + "strip-bom": "1.0.0", + "through2": "0.6.1", + "vinyl": "0.4.6" }, "dependencies": { "vinyl": { @@ -6039,8 +6039,8 @@ "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, "requires": { - "clone": "^0.2.0", - "clone-stats": "^0.0.1" + "clone": "0.2.0", + "clone-stats": "0.0.1" } } } @@ -6059,10 +6059,10 @@ "integrity": "sha512-dohokw+npnt48AsD0hhvCLEHLnDMqM35F+amvIfJlX1H2nNHYUClR0Oy1rI0TvbL1/pHiHGNLmohhk+kvwIKjA==", "dev": true, "requires": { - "colors": "^1.1.2", + "colors": "1.1.2", "opn": "5.2.0", - "plugin-log": "^0.1.0", - "through2": "^2.0.1" + "plugin-log": "0.1.0", + "through2": "2.0.5" } }, "gulp-postcss": { @@ -6071,11 +6071,11 @@ "integrity": "sha512-Wtl6vH7a+8IS/fU5W9IbOpcaLqKxd5L1DUOzaPmlnCbX1CrG0aWdwVnC3Spn8th0m8D59YbysV5zPUe1n/GJYg==", "dev": true, "requires": { - "fancy-log": "^1.3.2", - "plugin-error": "^1.0.1", - "postcss": "^7.0.2", - "postcss-load-config": "^2.0.0", - "vinyl-sourcemaps-apply": "^0.2.1" + "fancy-log": "1.3.3", + "plugin-error": "1.0.1", + "postcss": "7.0.6", + "postcss-load-config": "2.0.0", + "vinyl-sourcemaps-apply": "0.2.1" } }, "gulp-rename": { @@ -6090,7 +6090,7 @@ "integrity": "sha1-xnYqLx8N4KP8WVohWZ0/rI26Gso=", "dev": true, "requires": { - "through2": "^2.0.1" + "through2": "2.0.5" } }, "gulp-util": { @@ -6099,24 +6099,24 @@ "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", "dev": true, "requires": { - "array-differ": "^1.0.0", - "array-uniq": "^1.0.2", - "beeper": "^1.0.0", - "chalk": "^1.0.0", - "dateformat": "^2.0.0", - "fancy-log": "^1.1.0", - "gulplog": "^1.0.0", - "has-gulplog": "^0.1.0", - "lodash._reescape": "^3.0.0", - "lodash._reevaluate": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.template": "^3.0.0", - "minimist": "^1.1.0", - "multipipe": "^0.1.2", - "object-assign": "^3.0.0", + "array-differ": "1.0.0", + "array-uniq": "1.0.3", + "beeper": "1.1.1", + "chalk": "1.1.3", + "dateformat": "2.2.0", + "fancy-log": "1.3.3", + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash._reescape": "3.0.0", + "lodash._reevaluate": "3.0.0", + "lodash._reinterpolate": "3.0.0", + "lodash.template": "3.6.2", + "minimist": "1.2.0", + "multipipe": "0.1.2", + "object-assign": "3.0.0", "replace-ext": "0.0.1", - "through2": "^2.0.0", - "vinyl": "^0.5.0" + "through2": "2.0.5", + "vinyl": "0.5.3" }, "dependencies": { "ansi-styles": { @@ -6131,11 +6131,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "supports-color": { @@ -6153,17 +6153,17 @@ "dev": true, "requires": { "ansi-colors": "1.1.0", - "anymatch": "^1.3.0", - "chokidar": "^2.0.0", + "anymatch": "1.3.2", + "chokidar": "2.0.4", "fancy-log": "1.3.2", - "glob-parent": "^3.0.1", - "object-assign": "^4.1.0", - "path-is-absolute": "^1.0.1", + "glob-parent": "3.1.0", + "object-assign": "4.1.1", + "path-is-absolute": "1.0.1", "plugin-error": "1.0.1", - "readable-stream": "^2.2.2", - "slash": "^1.0.0", - "vinyl": "^2.1.0", - "vinyl-file": "^2.0.0" + "readable-stream": "2.3.6", + "slash": "1.0.0", + "vinyl": "2.2.0", + "vinyl-file": "2.0.0" }, "dependencies": { "clone": { @@ -6184,9 +6184,9 @@ "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", "dev": true, "requires": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "time-stamp": "^1.0.0" + "ansi-gray": "0.1.1", + "color-support": "1.1.3", + "time-stamp": "1.1.0" } }, "isarray": { @@ -6207,13 +6207,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "replace-ext": { @@ -6228,7 +6228,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "vinyl": { @@ -6237,12 +6237,12 @@ "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", "dev": true, "requires": { - "clone": "^2.1.1", - "clone-buffer": "^1.0.0", - "clone-stats": "^1.0.0", - "cloneable-readable": "^1.0.0", - "remove-trailing-separator": "^1.0.1", - "replace-ext": "^1.0.0" + "clone": "2.1.2", + "clone-buffer": "1.0.0", + "clone-stats": "1.0.0", + "cloneable-readable": "1.1.2", + "remove-trailing-separator": "1.1.0", + "replace-ext": "1.0.0" } } } @@ -6253,16 +6253,16 @@ "integrity": "sha1-FaXCBI4nIecFOaYbrxw0oLxfJyk=", "dev": true, "requires": { - "consolidate": "^0.14.1", - "es6-promise": "^3.1.2", - "fs-readfile-promise": "^2.0.1", - "js-yaml": "^3.2.6", - "lodash": "^4.11.1", - "node.extend": "^1.1.2", - "plugin-error": "^0.1.2", - "through2": "^2.0.1", - "tryit": "^1.0.1", - "vinyl-bufferstream": "^1.0.1" + "consolidate": "0.14.5", + "es6-promise": "3.3.1", + "fs-readfile-promise": "2.0.1", + "js-yaml": "3.12.0", + "lodash": "4.17.11", + "node.extend": "1.1.8", + "plugin-error": "0.1.2", + "through2": "2.0.5", + "tryit": "1.0.3", + "vinyl-bufferstream": "1.0.1" }, "dependencies": { "arr-diff": { @@ -6271,8 +6271,8 @@ "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", "dev": true, "requires": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" + "arr-flatten": "1.1.0", + "array-slice": "0.2.3" } }, "arr-union": { @@ -6293,7 +6293,7 @@ "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", "dev": true, "requires": { - "kind-of": "^1.1.0" + "kind-of": "1.1.0" } }, "kind-of": { @@ -6308,11 +6308,11 @@ "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", "dev": true, "requires": { - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" + "ansi-cyan": "0.1.1", + "ansi-red": "0.1.1", + "arr-diff": "1.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" } } } @@ -6323,12 +6323,12 @@ "integrity": "sha1-3uYqpISqupVHqT0f9c0MPQvtwDE=", "dev": true, "requires": { - "escodegen": "^1.6.1", - "esprima": "^2.3.0", - "estemplate": "*", - "gulp-util": "~3.0.5", - "through2": "*", - "vinyl-sourcemaps-apply": "^0.1.4" + "escodegen": "1.11.0", + "esprima": "2.7.3", + "estemplate": "0.5.1", + "gulp-util": "3.0.8", + "through2": "2.0.5", + "vinyl-sourcemaps-apply": "0.1.4" }, "dependencies": { "esprima": { @@ -6343,7 +6343,7 @@ "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } }, "vinyl-sourcemaps-apply": { @@ -6352,7 +6352,7 @@ "integrity": "sha1-xfy9Q+LyOEI8LcmL3db3m3K8NFs=", "dev": true, "requires": { - "source-map": "^0.1.39" + "source-map": "0.1.43" } } } @@ -6363,7 +6363,7 @@ "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", "dev": true, "requires": { - "glogg": "^1.0.0" + "glogg": "1.0.1" } }, "har-schema": { @@ -6378,8 +6378,8 @@ "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "dev": true, "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" + "ajv": "6.6.1", + "har-schema": "2.0.0" } }, "has": { @@ -6388,7 +6388,7 @@ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "^1.1.1" + "function-bind": "1.1.1" } }, "has-ansi": { @@ -6397,7 +6397,7 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "has-binary2": { @@ -6435,7 +6435,7 @@ "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", "dev": true, "requires": { - "sparkles": "^1.0.0" + "sparkles": "1.0.1" } }, "has-symbol-support-x": { @@ -6456,7 +6456,7 @@ "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", "dev": true, "requires": { - "has-symbol-support-x": "^1.4.1" + "has-symbol-support-x": "1.4.2" } }, "has-value": { @@ -6465,9 +6465,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" } }, "has-values": { @@ -6476,8 +6476,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "kind-of": { @@ -6486,7 +6486,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6497,8 +6497,8 @@ "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", "dev": true, "requires": { - "is-stream": "^1.0.1", - "pinkie-promise": "^2.0.0" + "is-stream": "1.1.0", + "pinkie-promise": "2.0.1" } }, "hex-color-regex": { @@ -6513,7 +6513,7 @@ "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", "dev": true, "requires": { - "parse-passwd": "^1.0.0" + "parse-passwd": "1.0.0" } }, "hosted-git-info": { @@ -6553,10 +6553,10 @@ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { - "depd": "~1.1.2", + "depd": "1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "statuses": "1.5.0" }, "dependencies": { "statuses": { @@ -6579,9 +6579,9 @@ "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", "dev": true, "requires": { - "eventemitter3": "^3.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "eventemitter3": "3.1.0", + "follow-redirects": "1.5.10", + "requires-port": "1.0.0" } }, "http-signature": { @@ -6590,9 +6590,9 @@ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.15.2" } }, "iconv-lite": { @@ -6601,7 +6601,7 @@ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } }, "ieee754": { @@ -6629,12 +6629,12 @@ "integrity": "sha512-m4Mxwt2QvCp1F85HXoTungXk0Y6XzuvQGqrK9qEddQfo/7x4aZjRENmyXXfc29ei4Mk55rW002bORG86YM3/aQ==", "dev": true, "requires": { - "file-type": "^8.1.0", - "globby": "^8.0.1", - "make-dir": "^1.0.0", - "p-pipe": "^1.1.0", - "pify": "^3.0.0", - "replace-ext": "^1.0.0" + "file-type": "8.1.0", + "globby": "8.0.1", + "make-dir": "1.3.0", + "p-pipe": "1.2.0", + "pify": "3.0.0", + "replace-ext": "1.0.0" }, "dependencies": { "replace-ext": { @@ -6652,9 +6652,9 @@ "dev": true, "optional": true, "requires": { - "exec-buffer": "^3.0.0", - "gifsicle": "^4.0.0", - "is-gif": "^3.0.0" + "exec-buffer": "3.2.0", + "gifsicle": "4.0.1", + "is-gif": "3.0.0" } }, "imagemin-jpegtran": { @@ -6664,9 +6664,9 @@ "dev": true, "optional": true, "requires": { - "exec-buffer": "^3.0.0", - "is-jpg": "^2.0.0", - "jpegtran-bin": "^4.0.0" + "exec-buffer": "3.2.0", + "is-jpg": "2.0.0", + "jpegtran-bin": "4.0.0" } }, "imagemin-optipng": { @@ -6676,9 +6676,9 @@ "dev": true, "optional": true, "requires": { - "exec-buffer": "^3.0.0", - "is-png": "^1.0.0", - "optipng-bin": "^5.0.0" + "exec-buffer": "3.2.0", + "is-png": "1.1.0", + "optipng-bin": "5.0.0" } }, "imagemin-svgo": { @@ -6688,8 +6688,8 @@ "dev": true, "optional": true, "requires": { - "is-svg": "^3.0.0", - "svgo": "^1.0.5" + "is-svg": "3.0.0", + "svgo": "1.1.1" } }, "import-cwd": { @@ -6698,7 +6698,7 @@ "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", "dev": true, "requires": { - "import-from": "^2.1.0" + "import-from": "2.1.0" } }, "import-fresh": { @@ -6707,8 +6707,8 @@ "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", "dev": true, "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" + "caller-path": "2.0.0", + "resolve-from": "3.0.0" } }, "import-from": { @@ -6717,7 +6717,7 @@ "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", "dev": true, "requires": { - "resolve-from": "^3.0.0" + "resolve-from": "3.0.0" } }, "import-lazy": { @@ -6739,7 +6739,7 @@ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "indexes-of": { @@ -6766,8 +6766,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.3.3", + "wrappy": "1.0.2" } }, "inherits": { @@ -6788,19 +6788,19 @@ "integrity": "sha512-088kl3DRT2dLU5riVMKKr1DlImd6X7smDhpXUCkJDCKvTEJeRiXh0G132HG9u5a+6Ylw9plFRY7RuTnwohYSpg==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.0", - "figures": "^2.0.0", - "lodash": "^4.17.10", + "ansi-escapes": "3.1.0", + "chalk": "2.4.1", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "3.0.3", + "figures": "2.0.0", + "lodash": "4.17.11", "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.1.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.0.0", - "through": "^2.3.6" + "run-async": "2.3.0", + "rxjs": "6.3.3", + "string-width": "2.1.1", + "strip-ansi": "5.0.0", + "through": "2.3.8" }, "dependencies": { "ansi-regex": { @@ -6815,7 +6815,7 @@ "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==", "dev": true, "requires": { - "ansi-regex": "^4.0.0" + "ansi-regex": "4.0.0" } } } @@ -6833,8 +6833,8 @@ "dev": true, "optional": true, "requires": { - "from2": "^2.1.1", - "p-is-promise": "^1.1.0" + "from2": "2.3.0", + "p-is-promise": "1.1.0" } }, "invariant": { @@ -6843,7 +6843,7 @@ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.4.0" } }, "irregular-plurals": { @@ -6864,8 +6864,8 @@ "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" + "is-relative": "1.0.0", + "is-windows": "1.0.2" } }, "is-absolute-url": { @@ -6880,7 +6880,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -6889,7 +6889,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6906,7 +6906,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "1.12.0" } }, "is-buffer": { @@ -6921,7 +6921,7 @@ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "^1.0.0" + "builtin-modules": "1.1.1" } }, "is-callable": { @@ -6936,12 +6936,12 @@ "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=", "dev": true, "requires": { - "css-color-names": "^0.0.4", - "hex-color-regex": "^1.1.0", - "hsl-regex": "^1.0.0", - "hsla-regex": "^1.0.0", - "rgb-regex": "^1.0.1", - "rgba-regex": "^1.0.0" + "css-color-names": "0.0.4", + "hex-color-regex": "1.1.0", + "hsl-regex": "1.0.0", + "hsla-regex": "1.0.0", + "rgb-regex": "1.0.1", + "rgba-regex": "1.0.0" } }, "is-data-descriptor": { @@ -6950,7 +6950,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -6959,7 +6959,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -6976,9 +6976,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -7007,7 +7007,7 @@ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "^2.0.0" + "is-primitive": "2.0.0" } }, "is-extendable": { @@ -7028,7 +7028,7 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-fullwidth-code-point": { @@ -7044,7 +7044,7 @@ "dev": true, "optional": true, "requires": { - "file-type": "^10.4.0" + "file-type": "10.6.0" }, "dependencies": { "file-type": { @@ -7062,7 +7062,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } }, "is-jpg": { @@ -7084,7 +7084,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7093,7 +7093,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -7122,7 +7122,7 @@ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "is-png": { @@ -7156,7 +7156,7 @@ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "has": "^1.0.1" + "has": "1.0.3" } }, "is-relative": { @@ -7165,7 +7165,7 @@ "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { - "is-unc-path": "^1.0.0" + "is-unc-path": "1.0.0" } }, "is-resolvable": { @@ -7192,7 +7192,7 @@ "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", "dev": true, "requires": { - "html-comment-regex": "^1.1.0" + "html-comment-regex": "1.1.2" } }, "is-symbol": { @@ -7201,7 +7201,7 @@ "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", "dev": true, "requires": { - "has-symbols": "^1.0.0" + "has-symbols": "1.0.0" } }, "is-typedarray": { @@ -7216,7 +7216,7 @@ "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { - "unc-path-regex": "^0.1.2" + "unc-path-regex": "0.1.2" } }, "is-utf8": { @@ -7249,7 +7249,7 @@ "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", "dev": true, "requires": { - "buffer-alloc": "^1.2.0" + "buffer-alloc": "1.2.0" } }, "isexe": { @@ -7276,8 +7276,8 @@ "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", "dev": true, "requires": { - "has-to-string-tag-x": "^1.2.0", - "is-object": "^1.0.1" + "has-to-string-tag-x": "1.4.1", + "is-object": "1.0.1" } }, "jasmine-core": { @@ -7293,9 +7293,9 @@ "dev": true, "optional": true, "requires": { - "bin-build": "^3.0.0", - "bin-wrapper": "^4.0.0", - "logalot": "^2.0.0" + "bin-build": "3.0.0", + "bin-wrapper": "4.1.0", + "logalot": "2.1.0" } }, "jquery": { @@ -7331,8 +7331,8 @@ "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "1.0.10", + "esprima": "4.0.1" } }, "jsbn": { @@ -7390,7 +7390,7 @@ "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "1.2.0" } }, "jsonfile": { @@ -7399,7 +7399,7 @@ "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "4.1.15" }, "dependencies": { "graceful-fs": { @@ -7429,31 +7429,31 @@ "integrity": "sha512-NetT3wPCQMNB36uiL9LLyhrOt8SQwrEKt0xD3+KpTCfm0VxVyUJdPL5oTq2Ic5ouemgL/Iz4wqXEbF3zea9kQQ==", "dev": true, "requires": { - "bluebird": "^3.3.0", - "body-parser": "^1.16.1", - "chokidar": "^2.0.3", - "colors": "^1.1.0", - "combine-lists": "^1.0.0", - "connect": "^3.6.0", - "core-js": "^2.2.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.0", - "expand-braces": "^0.1.1", - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "http-proxy": "^1.13.0", - "isbinaryfile": "^3.0.0", - "lodash": "^4.17.4", - "log4js": "^3.0.0", - "mime": "^2.3.1", - "minimatch": "^3.0.2", - "optimist": "^0.6.1", - "qjobs": "^1.1.4", - "range-parser": "^1.2.0", - "rimraf": "^2.6.0", - "safe-buffer": "^5.0.1", + "bluebird": "3.5.3", + "body-parser": "1.18.3", + "chokidar": "2.0.4", + "colors": "1.1.2", + "combine-lists": "1.0.1", + "connect": "3.6.6", + "core-js": "2.5.7", + "di": "0.0.1", + "dom-serialize": "2.2.1", + "expand-braces": "0.1.2", + "glob": "7.1.3", + "graceful-fs": "4.1.15", + "http-proxy": "1.17.0", + "isbinaryfile": "3.0.3", + "lodash": "4.17.11", + "log4js": "3.0.6", + "mime": "2.4.0", + "minimatch": "3.0.4", + "optimist": "0.6.1", + "qjobs": "1.2.0", + "range-parser": "1.2.0", + "rimraf": "2.6.2", + "safe-buffer": "5.1.2", "socket.io": "2.1.1", - "source-map": "^0.6.1", + "source-map": "0.6.1", "tmp": "0.0.33", "useragent": "2.2.1" }, @@ -7464,12 +7464,12 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" } }, "graceful-fs": { @@ -7490,7 +7490,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "source-map": { @@ -7507,7 +7507,7 @@ "integrity": "sha512-iuC0hmr9b+SNn1DaUD2QEYtUxkS1J+bSJSn7ejdEexs7P8EYvA1CWkEdrDQ+8jVH3AgWlCNwjYsT1chjcNW9lA==", "dev": true, "requires": { - "jasmine-core": "^3.3" + "jasmine-core": "3.3.0" } }, "karma-phantomjs-launcher": { @@ -7516,8 +7516,8 @@ "integrity": "sha1-0jyjSAG9qYY60xjju0vUBisTrNI=", "dev": true, "requires": { - "lodash": "^4.0.1", - "phantomjs-prebuilt": "^2.1.7" + "lodash": "4.17.11", + "phantomjs-prebuilt": "2.1.16" } }, "kew": { @@ -7548,7 +7548,7 @@ "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", "dev": true, "requires": { - "graceful-fs": "^4.1.9" + "graceful-fs": "4.1.15" }, "dependencies": { "graceful-fs": { @@ -7577,15 +7577,15 @@ "integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==", "dev": true, "requires": { - "clone": "^2.1.2", - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "mime": "^1.4.1", - "mkdirp": "^0.5.0", - "promise": "^7.1.1", - "request": "^2.83.0", - "source-map": "~0.6.0" + "clone": "2.1.2", + "errno": "0.1.7", + "graceful-fs": "4.1.15", + "image-size": "0.5.5", + "mime": "1.4.1", + "mkdirp": "0.5.1", + "promise": "7.3.1", + "request": "2.88.0", + "source-map": "0.6.1" }, "dependencies": { "clone": { @@ -7616,8 +7616,8 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "1.1.2", + "type-check": "0.3.2" } }, "liftoff": { @@ -7626,14 +7626,14 @@ "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", "dev": true, "requires": { - "extend": "^3.0.0", - "findup-sync": "^2.0.0", - "fined": "^1.0.1", - "flagged-respawn": "^1.0.0", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" + "extend": "3.0.2", + "findup-sync": "2.0.0", + "fined": "1.1.0", + "flagged-respawn": "1.0.0", + "is-plain-object": "2.0.4", + "object.map": "1.0.1", + "rechoir": "0.6.2", + "resolve": "1.8.1" } }, "livereload-js": { @@ -7648,11 +7648,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "graceful-fs": "4.1.15", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" }, "dependencies": { "graceful-fs": { @@ -7667,7 +7667,7 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "^1.2.0" + "error-ex": "1.3.2" } }, "pify": { @@ -7682,7 +7682,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } } } @@ -7717,7 +7717,7 @@ "integrity": "sha1-32fDu2t+jh6DGrSL+geVuSr+iZ0=", "dev": true, "requires": { - "lodash._htmlescapes": "~2.4.1" + "lodash._htmlescapes": "2.4.1" } }, "lodash._escapestringchar": { @@ -7780,8 +7780,8 @@ "integrity": "sha1-dHxPxAED6zu4oJduVx96JlnpO6c=", "dev": true, "requires": { - "lodash._htmlescapes": "~2.4.1", - "lodash.keys": "~2.4.1" + "lodash._htmlescapes": "2.4.1", + "lodash.keys": "2.4.1" }, "dependencies": { "lodash.keys": { @@ -7790,9 +7790,9 @@ "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", "dev": true, "requires": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" } } } @@ -7809,7 +7809,7 @@ "integrity": "sha1-bpzJZm/wgfC1psl4uD4kLmlJ0gM=", "dev": true, "requires": { - "lodash._objecttypes": "~2.4.1" + "lodash._objecttypes": "2.4.1" } }, "lodash.clone": { @@ -7836,7 +7836,7 @@ "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", "dev": true, "requires": { - "lodash._root": "^3.0.0" + "lodash._root": "3.0.1" } }, "lodash.flatten": { @@ -7863,7 +7863,7 @@ "integrity": "sha1-Wi5H/mmVPx7mMafrof5k0tBlWPU=", "dev": true, "requires": { - "lodash._objecttypes": "~2.4.1" + "lodash._objecttypes": "2.4.1" } }, "lodash.keys": { @@ -7872,9 +7872,9 @@ "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", "dev": true, "requires": { - "lodash._getnative": "^3.0.0", - "lodash.isarguments": "^3.0.0", - "lodash.isarray": "^3.0.0" + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" } }, "lodash.memoize": { @@ -7913,15 +7913,15 @@ "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", "dev": true, "requires": { - "lodash._basecopy": "^3.0.0", - "lodash._basetostring": "^3.0.0", - "lodash._basevalues": "^3.0.0", - "lodash._isiterateecall": "^3.0.0", - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0", - "lodash.keys": "^3.0.0", - "lodash.restparam": "^3.0.0", - "lodash.templatesettings": "^3.0.0" + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" } }, "lodash.templatesettings": { @@ -7930,8 +7930,8 @@ "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", "dev": true, "requires": { - "lodash._reinterpolate": "^3.0.0", - "lodash.escape": "^3.0.0" + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" } }, "lodash.uniq": { @@ -7946,7 +7946,7 @@ "integrity": "sha1-q/UUQ2s8twUAFieXjLzzCxKA7qQ=", "dev": true, "requires": { - "lodash.keys": "~2.4.1" + "lodash.keys": "2.4.1" }, "dependencies": { "lodash.keys": { @@ -7955,9 +7955,9 @@ "integrity": "sha1-SN6kbfj/djKxDXBrissmWR4rNyc=", "dev": true, "requires": { - "lodash._isnative": "~2.4.1", - "lodash._shimkeys": "~2.4.1", - "lodash.isobject": "~2.4.1" + "lodash._isnative": "2.4.1", + "lodash._shimkeys": "2.4.1", + "lodash.isobject": "2.4.1" } } } @@ -7968,10 +7968,10 @@ "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", "dev": true, "requires": { - "circular-json": "^0.5.5", - "date-format": "^1.2.0", - "debug": "^3.1.0", - "rfdc": "^1.1.2", + "circular-json": "0.5.9", + "date-format": "1.2.0", + "debug": "3.2.6", + "rfdc": "1.1.2", "streamroller": "0.7.0" }, "dependencies": { @@ -7987,7 +7987,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.1" } } } @@ -7999,8 +7999,8 @@ "dev": true, "optional": true, "requires": { - "figures": "^1.3.5", - "squeak": "^1.0.0" + "figures": "1.7.0", + "squeak": "1.3.0" }, "dependencies": { "figures": { @@ -8010,8 +8010,8 @@ "dev": true, "optional": true, "requires": { - "escape-string-regexp": "^1.0.5", - "object-assign": "^4.1.0" + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" } }, "object-assign": { @@ -8035,7 +8035,7 @@ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dev": true, "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" + "js-tokens": "4.0.0" } }, "loud-rejection": { @@ -8044,8 +8044,8 @@ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true, "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" } }, "lowercase-keys": { @@ -8061,10 +8061,10 @@ "dev": true, "optional": true, "requires": { - "get-stdin": "^4.0.1", - "indent-string": "^2.1.0", - "longest": "^1.0.0", - "meow": "^3.3.0" + "get-stdin": "4.0.1", + "indent-string": "2.1.0", + "longest": "1.0.1", + "meow": "3.7.0" } }, "lru-cache": { @@ -8079,7 +8079,7 @@ "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" } }, "make-iterator": { @@ -8088,7 +8088,7 @@ "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", "dev": true, "requires": { - "kind-of": "^6.0.2" + "kind-of": "6.0.2" } }, "map-cache": { @@ -8115,7 +8115,7 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "^1.0.0" + "object-visit": "1.0.1" } }, "marked": { @@ -8148,16 +8148,16 @@ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.0", + "normalize-package-data": "2.4.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" }, "dependencies": { "object-assign": { @@ -8174,7 +8174,7 @@ "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", "dev": true, "requires": { - "readable-stream": "^2.0.1" + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -8189,13 +8189,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -8204,7 +8204,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -8221,19 +8221,19 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "mime": { @@ -8254,7 +8254,7 @@ "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", "dev": true, "requires": { - "mime-db": "~1.37.0" + "mime-db": "1.37.0" } }, "mimic-fn": { @@ -8275,7 +8275,7 @@ "integrity": "sha1-jQh8OcazjAAbl/ynzm0OHoCvusc=", "dev": true, "requires": { - "brace-expansion": "^1.0.0" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -8290,8 +8290,8 @@ "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "dev": true, "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "for-in": "1.0.2", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -8300,7 +8300,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } @@ -8361,17 +8361,17 @@ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-windows": "1.0.2", + "kind-of": "6.0.2", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "natives": { @@ -8409,7 +8409,7 @@ "integrity": "sha512-Ky7q0BO1BBkG/rQz6PkEZ59rwo+aSfhczHP1wwq8IowoVdN/FpiP7qp0XW0P2+BVCWe5fQUBozdbVd54q1RbCQ==", "dev": true, "requires": { - "semver": "^5.3.0" + "semver": "5.6.0" } }, "node.extend": { @@ -8418,8 +8418,8 @@ "integrity": "sha512-L/dvEBwyg3UowwqOUTyDsGBU6kjBQOpOhshio9V3i3BMPv5YUb9+mWNN8MK0IbWqT0AqaTSONZf0aTuMMahWgA==", "dev": true, "requires": { - "has": "^1.0.3", - "is": "^3.2.1" + "has": "1.0.3", + "is": "3.2.1" } }, "normalize-package-data": { @@ -8428,10 +8428,10 @@ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.7.1", + "is-builtin-module": "1.0.0", + "semver": "5.6.0", + "validate-npm-package-license": "3.0.4" } }, "normalize-path": { @@ -8440,7 +8440,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.1.0" } }, "normalize-range": { @@ -8465,132 +8465,132 @@ "resolved": "https://registry.npmjs.org/npm/-/npm-6.4.1.tgz", "integrity": "sha512-mXJL1NTVU136PtuopXCUQaNWuHlXCTp4McwlSW8S9/Aj8OEPAlSBgo8og7kJ01MjCDrkmqFQTvN5tTEhBMhXQg==", "requires": { - "JSONStream": "^1.3.4", - "abbrev": "~1.1.1", - "ansicolors": "~0.3.2", - "ansistyles": "~0.1.3", - "aproba": "~1.2.0", - "archy": "~1.0.0", - "bin-links": "^1.1.2", - "bluebird": "~3.5.1", - "byte-size": "^4.0.3", - "cacache": "^11.2.0", - "call-limit": "~1.1.0", - "chownr": "~1.0.1", - "ci-info": "^1.4.0", - "cli-columns": "^3.1.2", - "cli-table3": "^0.5.0", - "cmd-shim": "~2.0.2", - "columnify": "~1.5.4", - "config-chain": "~1.1.11", - "debuglog": "*", - "detect-indent": "~5.0.0", - "detect-newline": "^2.1.0", - "dezalgo": "~1.0.3", - "editor": "~1.0.0", - "figgy-pudding": "^3.4.1", - "find-npm-prefix": "^1.0.2", - "fs-vacuum": "~1.2.10", - "fs-write-stream-atomic": "~1.0.10", - "gentle-fs": "^2.0.1", - "glob": "~7.1.2", - "graceful-fs": "~4.1.11", - "has-unicode": "~2.0.1", - "hosted-git-info": "^2.7.1", - "iferr": "^1.0.2", - "imurmurhash": "*", - "inflight": "~1.0.6", - "inherits": "~2.0.3", - "ini": "^1.3.5", - "init-package-json": "^1.10.3", - "is-cidr": "^2.0.6", - "json-parse-better-errors": "^1.0.2", - "lazy-property": "~1.0.0", - "libcipm": "^2.0.2", - "libnpmhook": "^4.0.1", - "libnpx": "^10.2.0", - "lock-verify": "^2.0.2", - "lockfile": "^1.0.4", - "lodash._baseindexof": "*", - "lodash._baseuniq": "~4.6.0", - "lodash._bindcallback": "*", - "lodash._cacheindexof": "*", - "lodash._createcache": "*", - "lodash._getnative": "*", - "lodash.clonedeep": "~4.5.0", - "lodash.restparam": "*", - "lodash.union": "~4.6.0", - "lodash.uniq": "~4.5.0", - "lodash.without": "~4.4.0", - "lru-cache": "^4.1.3", - "meant": "~1.0.1", - "mississippi": "^3.0.0", - "mkdirp": "~0.5.1", - "move-concurrently": "^1.0.1", - "node-gyp": "^3.8.0", - "nopt": "~4.0.1", - "normalize-package-data": "~2.4.0", - "npm-audit-report": "^1.3.1", - "npm-cache-filename": "~1.0.2", - "npm-install-checks": "~3.0.0", - "npm-lifecycle": "^2.1.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.11", - "npm-pick-manifest": "^2.1.0", - "npm-profile": "^3.0.2", - "npm-registry-client": "^8.6.0", - "npm-registry-fetch": "^1.1.0", - "npm-user-validate": "~1.0.0", - "npmlog": "~4.1.2", - "once": "~1.4.0", - "opener": "^1.5.0", - "osenv": "^0.1.5", - "pacote": "^8.1.6", - "path-is-inside": "~1.0.2", - "promise-inflight": "~1.0.1", - "qrcode-terminal": "^0.12.0", - "query-string": "^6.1.0", - "qw": "~1.0.1", - "read": "~1.0.7", - "read-cmd-shim": "~1.0.1", - "read-installed": "~4.0.3", - "read-package-json": "^2.0.13", - "read-package-tree": "^5.2.1", - "readable-stream": "^2.3.6", - "readdir-scoped-modules": "*", - "request": "^2.88.0", - "retry": "^0.12.0", - "rimraf": "~2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.5.0", - "sha": "~2.0.1", - "slide": "~1.1.6", - "sorted-object": "~2.0.1", - "sorted-union-stream": "~2.1.3", - "ssri": "^6.0.0", - "stringify-package": "^1.0.0", - "tar": "^4.4.6", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", + "JSONStream": "1.3.4", + "abbrev": "1.1.1", + "ansicolors": "0.3.2", + "ansistyles": "0.1.3", + "aproba": "1.2.0", + "archy": "1.0.0", + "bin-links": "1.1.2", + "bluebird": "3.5.1", + "byte-size": "4.0.3", + "cacache": "11.2.0", + "call-limit": "1.1.0", + "chownr": "1.0.1", + "ci-info": "1.4.0", + "cli-columns": "3.1.2", + "cli-table3": "0.5.0", + "cmd-shim": "2.0.2", + "columnify": "1.5.4", + "config-chain": "1.1.11", + "debuglog": "1.0.1", + "detect-indent": "5.0.0", + "detect-newline": "2.1.0", + "dezalgo": "1.0.3", + "editor": "1.0.0", + "figgy-pudding": "3.4.1", + "find-npm-prefix": "1.0.2", + "fs-vacuum": "1.2.10", + "fs-write-stream-atomic": "1.0.10", + "gentle-fs": "2.0.1", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "has-unicode": "2.0.1", + "hosted-git-info": "2.7.1", + "iferr": "1.0.2", + "imurmurhash": "0.1.4", + "inflight": "1.0.6", + "inherits": "2.0.3", + "ini": "1.3.5", + "init-package-json": "1.10.3", + "is-cidr": "2.0.6", + "json-parse-better-errors": "1.0.2", + "lazy-property": "1.0.0", + "libcipm": "2.0.2", + "libnpmhook": "4.0.1", + "libnpx": "10.2.0", + "lock-verify": "2.0.2", + "lockfile": "1.0.4", + "lodash._baseindexof": "3.1.0", + "lodash._baseuniq": "4.6.0", + "lodash._bindcallback": "3.0.1", + "lodash._cacheindexof": "3.0.2", + "lodash._createcache": "3.1.2", + "lodash._getnative": "3.9.1", + "lodash.clonedeep": "4.5.0", + "lodash.restparam": "3.6.1", + "lodash.union": "4.6.0", + "lodash.uniq": "4.5.0", + "lodash.without": "4.4.0", + "lru-cache": "4.1.3", + "meant": "1.0.1", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "node-gyp": "3.8.0", + "nopt": "4.0.1", + "normalize-package-data": "2.4.0", + "npm-audit-report": "1.3.1", + "npm-cache-filename": "1.0.2", + "npm-install-checks": "3.0.0", + "npm-lifecycle": "2.1.0", + "npm-package-arg": "6.1.0", + "npm-packlist": "1.1.11", + "npm-pick-manifest": "2.1.0", + "npm-profile": "3.0.2", + "npm-registry-client": "8.6.0", + "npm-registry-fetch": "1.1.0", + "npm-user-validate": "1.0.0", + "npmlog": "4.1.2", + "once": "1.4.0", + "opener": "1.5.0", + "osenv": "0.1.5", + "pacote": "8.1.6", + "path-is-inside": "1.0.2", + "promise-inflight": "1.0.1", + "qrcode-terminal": "0.12.0", + "query-string": "6.1.0", + "qw": "1.0.1", + "read": "1.0.7", + "read-cmd-shim": "1.0.1", + "read-installed": "4.0.3", + "read-package-json": "2.0.13", + "read-package-tree": "5.2.1", + "readable-stream": "2.3.6", + "readdir-scoped-modules": "1.0.2", + "request": "2.88.0", + "retry": "0.12.0", + "rimraf": "2.6.2", + "safe-buffer": "5.1.2", + "semver": "5.5.0", + "sha": "2.0.1", + "slide": "1.1.6", + "sorted-object": "2.0.1", + "sorted-union-stream": "2.1.3", + "ssri": "6.0.0", + "stringify-package": "1.0.0", + "tar": "4.4.6", + "text-table": "0.2.0", + "tiny-relative-date": "1.3.0", "uid-number": "0.0.6", - "umask": "~1.1.0", - "unique-filename": "~1.1.0", - "unpipe": "~1.0.0", - "update-notifier": "^2.5.0", - "uuid": "^3.3.2", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "~3.0.0", - "which": "^1.3.1", - "worker-farm": "^1.6.0", - "write-file-atomic": "^2.3.0" + "umask": "1.1.0", + "unique-filename": "1.1.0", + "unpipe": "1.0.0", + "update-notifier": "2.5.0", + "uuid": "3.3.2", + "validate-npm-package-license": "3.0.4", + "validate-npm-package-name": "3.0.0", + "which": "1.3.1", + "worker-farm": "1.6.0", + "write-file-atomic": "2.3.0" }, "dependencies": { "JSONStream": { "version": "1.3.4", "bundled": true, "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" + "jsonparse": "1.3.1", + "through": "2.3.8" } }, "abbrev": { @@ -8601,31 +8601,31 @@ "version": "4.2.0", "bundled": true, "requires": { - "es6-promisify": "^5.0.0" + "es6-promisify": "5.0.0" } }, "agentkeepalive": { "version": "3.4.1", "bundled": true, "requires": { - "humanize-ms": "^1.2.1" + "humanize-ms": "1.2.1" } }, "ajv": { "version": "5.5.2", "bundled": true, "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } }, "ansi-align": { "version": "2.0.0", "bundled": true, "requires": { - "string-width": "^2.0.0" + "string-width": "2.1.1" } }, "ansi-regex": { @@ -8636,7 +8636,7 @@ "version": "3.2.1", "bundled": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.1" } }, "ansicolors": { @@ -8659,8 +8659,8 @@ "version": "1.1.4", "bundled": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "delegates": "1.0.0", + "readable-stream": "2.3.6" } }, "asap": { @@ -8671,7 +8671,7 @@ "version": "0.2.4", "bundled": true, "requires": { - "safer-buffer": "~2.1.0" + "safer-buffer": "2.1.2" } }, "assert-plus": { @@ -8699,25 +8699,25 @@ "bundled": true, "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "bin-links": { "version": "1.1.2", "bundled": true, "requires": { - "bluebird": "^3.5.0", - "cmd-shim": "^2.0.2", - "gentle-fs": "^2.0.0", - "graceful-fs": "^4.1.11", - "write-file-atomic": "^2.3.0" + "bluebird": "3.5.1", + "cmd-shim": "2.0.2", + "gentle-fs": "2.0.1", + "graceful-fs": "4.1.11", + "write-file-atomic": "2.3.0" } }, "block-stream": { "version": "0.0.9", "bundled": true, "requires": { - "inherits": "~2.0.0" + "inherits": "2.0.3" } }, "bluebird": { @@ -8728,20 +8728,20 @@ "version": "1.3.0", "bundled": true, "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" + "ansi-align": "2.0.0", + "camelcase": "4.1.0", + "chalk": "2.4.1", + "cli-boxes": "1.0.0", + "string-width": "2.1.1", + "term-size": "1.2.0", + "widest-line": "2.0.0" } }, "brace-expansion": { "version": "1.1.11", "bundled": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -8769,20 +8769,20 @@ "version": "11.2.0", "bundled": true, "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "figgy-pudding": "^3.1.0", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.3", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^6.0.0", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" + "bluebird": "3.5.1", + "chownr": "1.0.1", + "figgy-pudding": "3.4.1", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "lru-cache": "4.1.3", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.2", + "ssri": "6.0.0", + "unique-filename": "1.1.0", + "y18n": "4.0.0" } }, "call-limit": { @@ -8805,9 +8805,9 @@ "version": "2.4.1", "bundled": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" } }, "chownr": { @@ -8822,7 +8822,7 @@ "version": "2.0.9", "bundled": true, "requires": { - "ip-regex": "^2.1.0" + "ip-regex": "2.1.0" } }, "cli-boxes": { @@ -8833,26 +8833,26 @@ "version": "3.1.2", "bundled": true, "requires": { - "string-width": "^2.0.0", - "strip-ansi": "^3.0.1" + "string-width": "2.1.1", + "strip-ansi": "3.0.1" } }, "cli-table3": { "version": "0.5.0", "bundled": true, "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^2.1.1" + "colors": "1.1.2", + "object-assign": "4.1.1", + "string-width": "2.1.1" } }, "cliui": { "version": "4.1.0", "bundled": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" }, "dependencies": { "ansi-regex": { @@ -8863,7 +8863,7 @@ "version": "4.0.0", "bundled": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -8876,8 +8876,8 @@ "version": "2.0.2", "bundled": true, "requires": { - "graceful-fs": "^4.1.2", - "mkdirp": "~0.5.0" + "graceful-fs": "4.1.11", + "mkdirp": "0.5.1" } }, "co": { @@ -8892,7 +8892,7 @@ "version": "1.9.1", "bundled": true, "requires": { - "color-name": "^1.1.1" + "color-name": "1.1.3" } }, "color-name": { @@ -8908,15 +8908,15 @@ "version": "1.5.4", "bundled": true, "requires": { - "strip-ansi": "^3.0.0", - "wcwidth": "^1.0.0" + "strip-ansi": "3.0.1", + "wcwidth": "1.0.1" } }, "combined-stream": { "version": "1.0.6", "bundled": true, "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "concat-map": { @@ -8927,30 +8927,30 @@ "version": "1.6.2", "bundled": true, "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "buffer-from": "1.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "typedarray": "0.0.6" } }, "config-chain": { "version": "1.1.11", "bundled": true, "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" + "ini": "1.3.5", + "proto-list": "1.2.4" } }, "configstore": { "version": "3.1.2", "bundled": true, "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" + "dot-prop": "4.2.0", + "graceful-fs": "4.1.11", + "make-dir": "1.3.0", + "unique-string": "1.0.0", + "write-file-atomic": "2.3.0", + "xdg-basedir": "3.0.0" } }, "console-control-strings": { @@ -8961,12 +8961,12 @@ "version": "1.0.5", "bundled": true, "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" + "aproba": "1.2.0", + "fs-write-stream-atomic": "1.0.10", + "iferr": "0.1.5", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" }, "dependencies": { "iferr": { @@ -8983,16 +8983,16 @@ "version": "3.0.2", "bundled": true, "requires": { - "capture-stack-trace": "^1.0.0" + "capture-stack-trace": "1.0.0" } }, "cross-spawn": { "version": "5.1.0", "bundled": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.3", + "shebang-command": "1.2.0", + "which": "1.3.1" } }, "crypto-random-string": { @@ -9007,7 +9007,7 @@ "version": "1.14.1", "bundled": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "debug": { @@ -9043,7 +9043,7 @@ "version": "1.0.3", "bundled": true, "requires": { - "clone": "^1.0.2" + "clone": "1.0.4" } }, "delayed-stream": { @@ -9066,15 +9066,15 @@ "version": "1.0.3", "bundled": true, "requires": { - "asap": "^2.0.0", - "wrappy": "1" + "asap": "2.0.6", + "wrappy": "1.0.2" } }, "dot-prop": { "version": "4.2.0", "bundled": true, "requires": { - "is-obj": "^1.0.0" + "is-obj": "1.0.1" } }, "dotenv": { @@ -9089,10 +9089,10 @@ "version": "3.6.0", "bundled": true, "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.1", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "stream-shift": "1.0.0" } }, "ecc-jsbn": { @@ -9100,8 +9100,8 @@ "bundled": true, "optional": true, "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" } }, "editor": { @@ -9112,14 +9112,14 @@ "version": "0.1.12", "bundled": true, "requires": { - "iconv-lite": "~0.4.13" + "iconv-lite": "0.4.23" } }, "end-of-stream": { "version": "1.4.1", "bundled": true, "requires": { - "once": "^1.4.0" + "once": "1.4.0" } }, "err-code": { @@ -9130,7 +9130,7 @@ "version": "0.1.7", "bundled": true, "requires": { - "prr": "~1.0.1" + "prr": "1.0.1" } }, "es6-promise": { @@ -9141,7 +9141,7 @@ "version": "5.0.0", "bundled": true, "requires": { - "es6-promise": "^4.0.3" + "es6-promise": "4.2.4" } }, "escape-string-regexp": { @@ -9152,13 +9152,13 @@ "version": "0.7.0", "bundled": true, "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "extend": { @@ -9189,15 +9189,15 @@ "version": "2.1.0", "bundled": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } }, "flush-write-stream": { "version": "1.0.3", "bundled": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" + "inherits": "2.0.3", + "readable-stream": "2.3.6" } }, "forever-agent": { @@ -9208,43 +9208,43 @@ "version": "2.3.2", "bundled": true, "requires": { - "asynckit": "^0.4.0", + "asynckit": "0.4.0", "combined-stream": "1.0.6", - "mime-types": "^2.1.12" + "mime-types": "2.1.19" } }, "from2": { "version": "2.3.0", "bundled": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "inherits": "2.0.3", + "readable-stream": "2.3.6" } }, "fs-minipass": { "version": "1.2.5", "bundled": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.3.3" } }, "fs-vacuum": { "version": "1.2.10", "bundled": true, "requires": { - "graceful-fs": "^4.1.2", - "path-is-inside": "^1.0.1", - "rimraf": "^2.5.2" + "graceful-fs": "4.1.11", + "path-is-inside": "1.0.2", + "rimraf": "2.6.2" } }, "fs-write-stream-atomic": { "version": "1.0.10", "bundled": true, "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" + "graceful-fs": "4.1.11", + "iferr": "0.1.5", + "imurmurhash": "0.1.4", + "readable-stream": "2.3.6" }, "dependencies": { "iferr": { @@ -9261,33 +9261,33 @@ "version": "1.0.11", "bundled": true, "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" } }, "gauge": { "version": "2.7.4", "bundled": true, "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" }, "dependencies": { "string-width": { "version": "1.0.2", "bundled": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -9300,14 +9300,14 @@ "version": "2.0.1", "bundled": true, "requires": { - "aproba": "^1.1.2", - "fs-vacuum": "^1.2.10", - "graceful-fs": "^4.1.11", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "path-is-inside": "^1.0.2", - "read-cmd-shim": "^1.0.1", - "slide": "^1.1.6" + "aproba": "1.2.0", + "fs-vacuum": "1.2.10", + "graceful-fs": "4.1.11", + "iferr": "0.1.5", + "mkdirp": "0.5.1", + "path-is-inside": "1.0.2", + "read-cmd-shim": "1.0.1", + "slide": "1.1.6" }, "dependencies": { "iferr": { @@ -9328,43 +9328,43 @@ "version": "0.1.7", "bundled": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "glob": { "version": "7.1.2", "bundled": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "global-dirs": { "version": "0.1.1", "bundled": true, "requires": { - "ini": "^1.3.4" + "ini": "1.3.5" } }, "got": { "version": "6.7.1", "bundled": true, "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" + "create-error-class": "3.0.2", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-redirect": "1.0.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "lowercase-keys": "1.0.1", + "safe-buffer": "5.1.2", + "timed-out": "4.0.1", + "unzip-response": "2.0.1", + "url-parse-lax": "1.0.0" } }, "graceful-fs": { @@ -9379,8 +9379,8 @@ "version": "5.1.0", "bundled": true, "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" + "ajv": "5.5.2", + "har-schema": "2.0.0" } }, "has-flag": { @@ -9403,7 +9403,7 @@ "version": "2.1.0", "bundled": true, "requires": { - "agent-base": "4", + "agent-base": "4.2.0", "debug": "3.1.0" } }, @@ -9411,31 +9411,31 @@ "version": "1.2.0", "bundled": true, "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.14.2" } }, "https-proxy-agent": { "version": "2.2.1", "bundled": true, "requires": { - "agent-base": "^4.1.0", - "debug": "^3.1.0" + "agent-base": "4.2.0", + "debug": "3.1.0" } }, "humanize-ms": { "version": "1.2.1", "bundled": true, "requires": { - "ms": "^2.0.0" + "ms": "2.1.1" } }, "iconv-lite": { "version": "0.4.23", "bundled": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } }, "iferr": { @@ -9446,7 +9446,7 @@ "version": "3.0.1", "bundled": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "import-lazy": { @@ -9461,8 +9461,8 @@ "version": "1.0.6", "bundled": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -9477,14 +9477,14 @@ "version": "1.10.3", "bundled": true, "requires": { - "glob": "^7.1.1", - "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", - "promzard": "^0.3.0", - "read": "~1.0.1", - "read-package-json": "1 || 2", - "semver": "2.x || 3.x || 4 || 5", - "validate-npm-package-license": "^3.0.1", - "validate-npm-package-name": "^3.0.0" + "glob": "7.1.2", + "npm-package-arg": "6.1.0", + "promzard": "0.3.0", + "read": "1.0.7", + "read-package-json": "2.0.13", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.4", + "validate-npm-package-name": "3.0.0" } }, "invert-kv": { @@ -9503,36 +9503,36 @@ "version": "1.0.0", "bundled": true, "requires": { - "builtin-modules": "^1.0.0" + "builtin-modules": "1.1.1" } }, "is-ci": { "version": "1.1.0", "bundled": true, "requires": { - "ci-info": "^1.0.0" + "ci-info": "1.4.0" } }, "is-cidr": { "version": "2.0.6", "bundled": true, "requires": { - "cidr-regex": "^2.0.8" + "cidr-regex": "2.0.9" } }, "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-installed-globally": { "version": "0.1.0", "bundled": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "global-dirs": "0.1.1", + "is-path-inside": "1.0.1" } }, "is-npm": { @@ -9547,7 +9547,7 @@ "version": "1.0.1", "bundled": true, "requires": { - "path-is-inside": "^1.0.1" + "path-is-inside": "1.0.2" } }, "is-redirect": { @@ -9617,7 +9617,7 @@ "version": "3.1.0", "bundled": true, "requires": { - "package-json": "^4.0.0" + "package-json": "4.0.1" } }, "lazy-property": { @@ -9628,46 +9628,46 @@ "version": "1.0.0", "bundled": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "libcipm": { "version": "2.0.2", "bundled": true, "requires": { - "bin-links": "^1.1.2", - "bluebird": "^3.5.1", - "find-npm-prefix": "^1.0.2", - "graceful-fs": "^4.1.11", - "lock-verify": "^2.0.2", - "mkdirp": "^0.5.1", - "npm-lifecycle": "^2.0.3", - "npm-logical-tree": "^1.2.1", - "npm-package-arg": "^6.1.0", - "pacote": "^8.1.6", - "protoduck": "^5.0.0", - "read-package-json": "^2.0.13", - "rimraf": "^2.6.2", - "worker-farm": "^1.6.0" + "bin-links": "1.1.2", + "bluebird": "3.5.1", + "find-npm-prefix": "1.0.2", + "graceful-fs": "4.1.11", + "lock-verify": "2.0.2", + "mkdirp": "0.5.1", + "npm-lifecycle": "2.1.0", + "npm-logical-tree": "1.2.1", + "npm-package-arg": "6.1.0", + "pacote": "8.1.6", + "protoduck": "5.0.0", + "read-package-json": "2.0.13", + "rimraf": "2.6.2", + "worker-farm": "1.6.0" } }, "libnpmhook": { "version": "4.0.1", "bundled": true, "requires": { - "figgy-pudding": "^3.1.0", - "npm-registry-fetch": "^3.0.0" + "figgy-pudding": "3.4.1", + "npm-registry-fetch": "3.1.1" }, "dependencies": { "npm-registry-fetch": { "version": "3.1.1", "bundled": true, "requires": { - "bluebird": "^3.5.1", - "figgy-pudding": "^3.1.0", - "lru-cache": "^4.1.2", - "make-fetch-happen": "^4.0.0", - "npm-package-arg": "^6.0.0" + "bluebird": "3.5.1", + "figgy-pudding": "3.4.1", + "lru-cache": "4.1.3", + "make-fetch-happen": "4.0.1", + "npm-package-arg": "6.1.0" } } } @@ -9676,37 +9676,37 @@ "version": "10.2.0", "bundled": true, "requires": { - "dotenv": "^5.0.1", - "npm-package-arg": "^6.0.0", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.0", - "update-notifier": "^2.3.0", - "which": "^1.3.0", - "y18n": "^4.0.0", - "yargs": "^11.0.0" + "dotenv": "5.0.1", + "npm-package-arg": "6.1.0", + "rimraf": "2.6.2", + "safe-buffer": "5.1.2", + "update-notifier": "2.5.0", + "which": "1.3.1", + "y18n": "4.0.0", + "yargs": "11.0.0" } }, "locate-path": { "version": "2.0.0", "bundled": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" } }, "lock-verify": { "version": "2.0.2", "bundled": true, "requires": { - "npm-package-arg": "^5.1.2 || 6", - "semver": "^5.4.1" + "npm-package-arg": "6.1.0", + "semver": "5.5.0" } }, "lockfile": { "version": "1.0.4", "bundled": true, "requires": { - "signal-exit": "^3.0.2" + "signal-exit": "3.0.2" } }, "lodash._baseindexof": { @@ -9717,8 +9717,8 @@ "version": "4.6.0", "bundled": true, "requires": { - "lodash._createset": "~4.0.0", - "lodash._root": "~3.0.0" + "lodash._createset": "4.0.3", + "lodash._root": "3.0.1" } }, "lodash._bindcallback": { @@ -9733,7 +9733,7 @@ "version": "3.1.2", "bundled": true, "requires": { - "lodash._getnative": "^3.0.0" + "lodash._getnative": "3.9.1" } }, "lodash._createset": { @@ -9776,32 +9776,32 @@ "version": "4.1.3", "bundled": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "make-dir": { "version": "1.3.0", "bundled": true, "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" } }, "make-fetch-happen": { "version": "4.0.1", "bundled": true, "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^11.0.1", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "lru-cache": "^4.1.2", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^4.0.0", - "ssri": "^6.0.0" + "agentkeepalive": "3.4.1", + "cacache": "11.2.0", + "http-cache-semantics": "3.8.1", + "http-proxy-agent": "2.1.0", + "https-proxy-agent": "2.2.1", + "lru-cache": "4.1.3", + "mississippi": "3.0.0", + "node-fetch-npm": "2.0.2", + "promise-retry": "1.1.1", + "socks-proxy-agent": "4.0.1", + "ssri": "6.0.0" } }, "meant": { @@ -9812,7 +9812,7 @@ "version": "1.1.0", "bundled": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "mime-db": { @@ -9823,7 +9823,7 @@ "version": "2.1.19", "bundled": true, "requires": { - "mime-db": "~1.35.0" + "mime-db": "1.35.0" } }, "mimic-fn": { @@ -9834,7 +9834,7 @@ "version": "3.0.4", "bundled": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -9845,8 +9845,8 @@ "version": "2.3.3", "bundled": true, "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" + "safe-buffer": "5.1.2", + "yallist": "3.0.2" }, "dependencies": { "yallist": { @@ -9859,23 +9859,23 @@ "version": "1.1.0", "bundled": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.3.3" } }, "mississippi": { "version": "3.0.0", "bundled": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "concat-stream": "1.6.2", + "duplexify": "3.6.0", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.3", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "3.0.0", + "pumpify": "1.5.1", + "stream-each": "1.2.2", + "through2": "2.0.3" } }, "mkdirp": { @@ -9889,12 +9889,12 @@ "version": "1.0.1", "bundled": true, "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" + "aproba": "1.2.0", + "copy-concurrently": "1.0.5", + "fs-write-stream-atomic": "1.0.10", + "mkdirp": "0.5.1", + "rimraf": "2.6.2", + "run-queue": "1.0.3" } }, "ms": { @@ -9909,34 +9909,34 @@ "version": "2.0.2", "bundled": true, "requires": { - "encoding": "^0.1.11", - "json-parse-better-errors": "^1.0.0", - "safe-buffer": "^5.1.1" + "encoding": "0.1.12", + "json-parse-better-errors": "1.0.2", + "safe-buffer": "5.1.2" } }, "node-gyp": { "version": "3.8.0", "bundled": true, "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" + "fstream": "1.0.11", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "npmlog": "4.1.2", + "osenv": "0.1.5", + "request": "2.88.0", + "rimraf": "2.6.2", + "semver": "5.3.0", + "tar": "2.2.1", + "which": "1.3.1" }, "dependencies": { "nopt": { "version": "3.0.6", "bundled": true, "requires": { - "abbrev": "1" + "abbrev": "1.1.1" } }, "semver": { @@ -9947,9 +9947,9 @@ "version": "2.2.1", "bundled": true, "requires": { - "block-stream": "*", - "fstream": "^1.0.2", - "inherits": "2" + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" } } } @@ -9958,26 +9958,26 @@ "version": "4.0.1", "bundled": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1.1.1", + "osenv": "0.1.5" } }, "normalize-package-data": { "version": "2.4.0", "bundled": true, "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.7.1", + "is-builtin-module": "1.0.0", + "semver": "5.5.0", + "validate-npm-package-license": "3.0.4" } }, "npm-audit-report": { "version": "1.3.1", "bundled": true, "requires": { - "cli-table3": "^0.5.0", - "console-control-strings": "^1.1.0" + "cli-table3": "0.5.0", + "console-control-strings": "1.1.0" } }, "npm-bundled": { @@ -9992,21 +9992,21 @@ "version": "3.0.0", "bundled": true, "requires": { - "semver": "^2.3.0 || 3.x || 4 || 5" + "semver": "5.5.0" } }, "npm-lifecycle": { "version": "2.1.0", "bundled": true, "requires": { - "byline": "^5.0.0", - "graceful-fs": "^4.1.11", - "node-gyp": "^3.8.0", - "resolve-from": "^4.0.0", - "slide": "^1.1.6", + "byline": "5.0.0", + "graceful-fs": "4.1.11", + "node-gyp": "3.8.0", + "resolve-from": "4.0.0", + "slide": "1.1.6", "uid-number": "0.0.6", - "umask": "^1.1.0", - "which": "^1.3.1" + "umask": "1.1.0", + "which": "1.3.1" } }, "npm-logical-tree": { @@ -10017,52 +10017,52 @@ "version": "6.1.0", "bundled": true, "requires": { - "hosted-git-info": "^2.6.0", - "osenv": "^0.1.5", - "semver": "^5.5.0", - "validate-npm-package-name": "^3.0.0" + "hosted-git-info": "2.7.1", + "osenv": "0.1.5", + "semver": "5.5.0", + "validate-npm-package-name": "3.0.0" } }, "npm-packlist": { "version": "1.1.11", "bundled": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.5" } }, "npm-pick-manifest": { "version": "2.1.0", "bundled": true, "requires": { - "npm-package-arg": "^6.0.0", - "semver": "^5.4.1" + "npm-package-arg": "6.1.0", + "semver": "5.5.0" } }, "npm-profile": { "version": "3.0.2", "bundled": true, "requires": { - "aproba": "^1.1.2 || 2", - "make-fetch-happen": "^2.5.0 || 3 || 4" + "aproba": "1.2.0", + "make-fetch-happen": "4.0.1" } }, "npm-registry-client": { "version": "8.6.0", "bundled": true, "requires": { - "concat-stream": "^1.5.2", - "graceful-fs": "^4.1.6", - "normalize-package-data": "~1.0.1 || ^2.0.0", - "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", - "npmlog": "2 || ^3.1.0 || ^4.0.0", - "once": "^1.3.3", - "request": "^2.74.0", - "retry": "^0.10.0", - "safe-buffer": "^5.1.1", - "semver": "2 >=2.2.1 || 3.x || 4 || 5", - "slide": "^1.1.3", - "ssri": "^5.2.4" + "concat-stream": "1.6.2", + "graceful-fs": "4.1.11", + "normalize-package-data": "2.4.0", + "npm-package-arg": "6.1.0", + "npmlog": "4.1.2", + "once": "1.4.0", + "request": "2.88.0", + "retry": "0.10.1", + "safe-buffer": "5.1.2", + "semver": "5.5.0", + "slide": "1.1.6", + "ssri": "5.3.0" }, "dependencies": { "retry": { @@ -10073,7 +10073,7 @@ "version": "5.3.0", "bundled": true, "requires": { - "safe-buffer": "^5.1.1" + "safe-buffer": "5.1.2" } } } @@ -10082,47 +10082,47 @@ "version": "1.1.0", "bundled": true, "requires": { - "bluebird": "^3.5.1", - "figgy-pudding": "^2.0.1", - "lru-cache": "^4.1.2", - "make-fetch-happen": "^3.0.0", - "npm-package-arg": "^6.0.0", - "safe-buffer": "^5.1.1" + "bluebird": "3.5.1", + "figgy-pudding": "2.0.1", + "lru-cache": "4.1.3", + "make-fetch-happen": "3.0.0", + "npm-package-arg": "6.1.0", + "safe-buffer": "5.1.2" }, "dependencies": { "cacache": { "version": "10.0.4", "bundled": true, "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" + "bluebird": "3.5.1", + "chownr": "1.0.1", + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "lru-cache": "4.1.3", + "mississippi": "2.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.2", + "ssri": "5.3.0", + "unique-filename": "1.1.0", + "y18n": "4.0.0" }, "dependencies": { "mississippi": { "version": "2.0.0", "bundled": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^2.0.1", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "concat-stream": "1.6.2", + "duplexify": "3.6.0", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.3", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "2.0.1", + "pumpify": "1.5.1", + "stream-each": "1.2.2", + "through2": "2.0.3" } } } @@ -10135,25 +10135,25 @@ "version": "3.0.0", "bundled": true, "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^10.0.4", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.0", - "lru-cache": "^4.1.2", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^3.0.1", - "ssri": "^5.2.4" + "agentkeepalive": "3.4.1", + "cacache": "10.0.4", + "http-cache-semantics": "3.8.1", + "http-proxy-agent": "2.1.0", + "https-proxy-agent": "2.2.1", + "lru-cache": "4.1.3", + "mississippi": "3.0.0", + "node-fetch-npm": "2.0.2", + "promise-retry": "1.1.1", + "socks-proxy-agent": "3.0.1", + "ssri": "5.3.0" } }, "pump": { "version": "2.0.1", "bundled": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.4.0" } }, "smart-buffer": { @@ -10164,23 +10164,23 @@ "version": "1.1.10", "bundled": true, "requires": { - "ip": "^1.1.4", - "smart-buffer": "^1.0.13" + "ip": "1.1.5", + "smart-buffer": "1.1.15" } }, "socks-proxy-agent": { "version": "3.0.1", "bundled": true, "requires": { - "agent-base": "^4.1.0", - "socks": "^1.1.10" + "agent-base": "4.2.0", + "socks": "1.1.10" } }, "ssri": { "version": "5.3.0", "bundled": true, "requires": { - "safe-buffer": "^5.1.1" + "safe-buffer": "5.1.2" } } } @@ -10189,7 +10189,7 @@ "version": "2.0.2", "bundled": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "npm-user-validate": { @@ -10200,10 +10200,10 @@ "version": "4.1.2", "bundled": true, "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" } }, "number-is-nan": { @@ -10222,7 +10222,7 @@ "version": "1.4.0", "bundled": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "opener": { @@ -10237,9 +10237,9 @@ "version": "2.1.0", "bundled": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "os-tmpdir": { @@ -10250,8 +10250,8 @@ "version": "0.1.5", "bundled": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "p-finally": { @@ -10262,14 +10262,14 @@ "version": "1.2.0", "bundled": true, "requires": { - "p-try": "^1.0.0" + "p-try": "1.0.0" } }, "p-locate": { "version": "2.0.0", "bundled": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.2.0" } }, "p-try": { @@ -10280,50 +10280,50 @@ "version": "4.0.1", "bundled": true, "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "got": "6.7.1", + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0", + "semver": "5.5.0" } }, "pacote": { "version": "8.1.6", "bundled": true, "requires": { - "bluebird": "^3.5.1", - "cacache": "^11.0.2", - "get-stream": "^3.0.0", - "glob": "^7.1.2", - "lru-cache": "^4.1.3", - "make-fetch-happen": "^4.0.1", - "minimatch": "^3.0.4", - "minipass": "^2.3.3", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.10", - "npm-pick-manifest": "^2.1.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.0", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.5.0", - "ssri": "^6.0.0", - "tar": "^4.4.3", - "unique-filename": "^1.1.0", - "which": "^1.3.0" + "bluebird": "3.5.1", + "cacache": "11.2.0", + "get-stream": "3.0.0", + "glob": "7.1.2", + "lru-cache": "4.1.3", + "make-fetch-happen": "4.0.1", + "minimatch": "3.0.4", + "minipass": "2.3.3", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "normalize-package-data": "2.4.0", + "npm-package-arg": "6.1.0", + "npm-packlist": "1.1.11", + "npm-pick-manifest": "2.1.0", + "osenv": "0.1.5", + "promise-inflight": "1.0.1", + "promise-retry": "1.1.1", + "protoduck": "5.0.0", + "rimraf": "2.6.2", + "safe-buffer": "5.1.2", + "semver": "5.5.0", + "ssri": "6.0.0", + "tar": "4.4.6", + "unique-filename": "1.1.0", + "which": "1.3.1" } }, "parallel-transform": { "version": "1.1.0", "bundled": true, "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" + "cyclist": "0.2.2", + "inherits": "2.0.3", + "readable-stream": "2.3.6" } }, "path-exists": { @@ -10366,8 +10366,8 @@ "version": "1.1.1", "bundled": true, "requires": { - "err-code": "^1.0.0", - "retry": "^0.10.0" + "err-code": "1.1.2", + "retry": "0.10.1" }, "dependencies": { "retry": { @@ -10380,7 +10380,7 @@ "version": "0.3.0", "bundled": true, "requires": { - "read": "1" + "read": "1.0.7" } }, "proto-list": { @@ -10391,7 +10391,7 @@ "version": "5.0.0", "bundled": true, "requires": { - "genfun": "^4.0.1" + "genfun": "4.0.1" } }, "prr": { @@ -10410,25 +10410,25 @@ "version": "3.0.0", "bundled": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.4.0" } }, "pumpify": { "version": "1.5.1", "bundled": true, "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" + "duplexify": "3.6.0", + "inherits": "2.0.3", + "pump": "2.0.1" }, "dependencies": { "pump": { "version": "2.0.1", "bundled": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.4.0" } } } @@ -10449,8 +10449,8 @@ "version": "6.1.0", "bundled": true, "requires": { - "decode-uri-component": "^0.2.0", - "strict-uri-encode": "^2.0.0" + "decode-uri-component": "0.2.0", + "strict-uri-encode": "2.0.0" } }, "qw": { @@ -10461,10 +10461,10 @@ "version": "1.2.7", "bundled": true, "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -10477,113 +10477,113 @@ "version": "1.0.7", "bundled": true, "requires": { - "mute-stream": "~0.0.4" + "mute-stream": "0.0.7" } }, "read-cmd-shim": { "version": "1.0.1", "bundled": true, "requires": { - "graceful-fs": "^4.1.2" + "graceful-fs": "4.1.11" } }, "read-installed": { "version": "4.0.3", "bundled": true, "requires": { - "debuglog": "^1.0.1", - "graceful-fs": "^4.1.2", - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "slide": "~1.1.3", - "util-extend": "^1.0.1" + "debuglog": "1.0.1", + "graceful-fs": "4.1.11", + "read-package-json": "2.0.13", + "readdir-scoped-modules": "1.0.2", + "semver": "5.5.0", + "slide": "1.1.6", + "util-extend": "1.0.3" } }, "read-package-json": { "version": "2.0.13", "bundled": true, "requires": { - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "json-parse-better-errors": "^1.0.1", - "normalize-package-data": "^2.0.0", - "slash": "^1.0.0" + "glob": "7.1.2", + "graceful-fs": "4.1.11", + "json-parse-better-errors": "1.0.2", + "normalize-package-data": "2.4.0", + "slash": "1.0.0" } }, "read-package-tree": { "version": "5.2.1", "bundled": true, "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "once": "^1.3.0", - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0" + "debuglog": "1.0.1", + "dezalgo": "1.0.3", + "once": "1.4.0", + "read-package-json": "2.0.13", + "readdir-scoped-modules": "1.0.2" } }, "readable-stream": { "version": "2.3.6", "bundled": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "readdir-scoped-modules": { "version": "1.0.2", "bundled": true, "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" + "debuglog": "1.0.1", + "dezalgo": "1.0.3", + "graceful-fs": "4.1.11", + "once": "1.4.0" } }, "registry-auth-token": { "version": "3.3.2", "bundled": true, "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "rc": "1.2.7", + "safe-buffer": "5.1.2" } }, "registry-url": { "version": "3.1.0", "bundled": true, "requires": { - "rc": "^1.0.1" + "rc": "1.2.7" } }, "request": { "version": "2.88.0", "bundled": true, "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "aws-sign2": "0.7.0", + "aws4": "1.8.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.1.0", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.19", + "oauth-sign": "0.9.0", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.4.3", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" } }, "require-directory": { @@ -10606,14 +10606,14 @@ "version": "2.6.2", "bundled": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.2" } }, "run-queue": { "version": "1.0.3", "bundled": true, "requires": { - "aproba": "^1.1.1" + "aproba": "1.2.0" } }, "safe-buffer": { @@ -10632,7 +10632,7 @@ "version": "2.1.0", "bundled": true, "requires": { - "semver": "^5.0.3" + "semver": "5.5.0" } }, "set-blocking": { @@ -10643,15 +10643,15 @@ "version": "2.0.1", "bundled": true, "requires": { - "graceful-fs": "^4.1.2", - "readable-stream": "^2.0.2" + "graceful-fs": "4.1.11", + "readable-stream": "2.3.6" } }, "shebang-command": { "version": "1.2.0", "bundled": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -10678,16 +10678,16 @@ "version": "2.2.0", "bundled": true, "requires": { - "ip": "^1.1.5", - "smart-buffer": "^4.0.1" + "ip": "1.1.5", + "smart-buffer": "4.0.1" } }, "socks-proxy-agent": { "version": "4.0.1", "bundled": true, "requires": { - "agent-base": "~4.2.0", - "socks": "~2.2.0" + "agent-base": "4.2.0", + "socks": "2.2.0" } }, "sorted-object": { @@ -10698,16 +10698,16 @@ "version": "2.1.3", "bundled": true, "requires": { - "from2": "^1.3.0", - "stream-iterate": "^1.1.0" + "from2": "1.3.0", + "stream-iterate": "1.2.0" }, "dependencies": { "from2": { "version": "1.3.0", "bundled": true, "requires": { - "inherits": "~2.0.1", - "readable-stream": "~1.1.10" + "inherits": "2.0.3", + "readable-stream": "1.1.14" } }, "isarray": { @@ -10718,10 +10718,10 @@ "version": "1.1.14", "bundled": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "string_decoder": { @@ -10734,8 +10734,8 @@ "version": "3.0.0", "bundled": true, "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" } }, "spdx-exceptions": { @@ -10746,8 +10746,8 @@ "version": "3.0.0", "bundled": true, "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" } }, "spdx-license-ids": { @@ -10758,15 +10758,15 @@ "version": "1.14.2", "bundled": true, "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" + "asn1": "0.2.4", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.2", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" } }, "ssri": { @@ -10777,16 +10777,16 @@ "version": "1.2.2", "bundled": true, "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.1", + "stream-shift": "1.0.0" } }, "stream-iterate": { "version": "1.2.0", "bundled": true, "requires": { - "readable-stream": "^2.1.5", - "stream-shift": "^1.0.0" + "readable-stream": "2.3.6", + "stream-shift": "1.0.0" } }, "stream-shift": { @@ -10801,8 +10801,8 @@ "version": "2.1.1", "bundled": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "ansi-regex": { @@ -10817,7 +10817,7 @@ "version": "4.0.0", "bundled": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -10826,7 +10826,7 @@ "version": "1.1.1", "bundled": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "stringify-package": { @@ -10837,7 +10837,7 @@ "version": "3.0.1", "bundled": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-eof": { @@ -10852,20 +10852,20 @@ "version": "5.4.0", "bundled": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } }, "tar": { "version": "4.4.6", "bundled": true, "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.3", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" + "chownr": "1.0.1", + "fs-minipass": "1.2.5", + "minipass": "2.3.3", + "minizlib": "1.1.0", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.2", + "yallist": "3.0.2" }, "dependencies": { "yallist": { @@ -10878,7 +10878,7 @@ "version": "1.2.0", "bundled": true, "requires": { - "execa": "^0.7.0" + "execa": "0.7.0" } }, "text-table": { @@ -10893,8 +10893,8 @@ "version": "2.0.3", "bundled": true, "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" + "readable-stream": "2.3.6", + "xtend": "4.0.1" } }, "timed-out": { @@ -10909,15 +10909,15 @@ "version": "2.4.3", "bundled": true, "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" + "psl": "1.1.29", + "punycode": "1.4.1" } }, "tunnel-agent": { "version": "0.6.0", "bundled": true, "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.2" } }, "tweetnacl": { @@ -10941,21 +10941,21 @@ "version": "1.1.0", "bundled": true, "requires": { - "unique-slug": "^2.0.0" + "unique-slug": "2.0.0" } }, "unique-slug": { "version": "2.0.0", "bundled": true, "requires": { - "imurmurhash": "^0.1.4" + "imurmurhash": "0.1.4" } }, "unique-string": { "version": "1.0.0", "bundled": true, "requires": { - "crypto-random-string": "^1.0.0" + "crypto-random-string": "1.0.0" } }, "unpipe": { @@ -10970,23 +10970,23 @@ "version": "2.5.0", "bundled": true, "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" + "boxen": "1.3.0", + "chalk": "2.4.1", + "configstore": "3.1.2", + "import-lazy": "2.1.0", + "is-ci": "1.1.0", + "is-installed-globally": "0.1.0", + "is-npm": "1.0.0", + "latest-version": "3.1.0", + "semver-diff": "2.1.0", + "xdg-basedir": "3.0.0" } }, "url-parse-lax": { "version": "1.0.0", "bundled": true, "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "1.0.4" } }, "util-deprecate": { @@ -11005,38 +11005,38 @@ "version": "3.0.4", "bundled": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" } }, "validate-npm-package-name": { "version": "3.0.0", "bundled": true, "requires": { - "builtins": "^1.0.3" + "builtins": "1.0.3" } }, "verror": { "version": "1.10.0", "bundled": true, "requires": { - "assert-plus": "^1.0.0", + "assert-plus": "1.0.0", "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "extsprintf": "1.3.0" } }, "wcwidth": { "version": "1.0.1", "bundled": true, "requires": { - "defaults": "^1.0.3" + "defaults": "1.0.3" } }, "which": { "version": "1.3.1", "bundled": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-module": { @@ -11047,16 +11047,16 @@ "version": "1.1.2", "bundled": true, "requires": { - "string-width": "^1.0.2" + "string-width": "1.0.2" }, "dependencies": { "string-width": { "version": "1.0.2", "bundled": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -11065,31 +11065,31 @@ "version": "2.0.0", "bundled": true, "requires": { - "string-width": "^2.1.1" + "string-width": "2.1.1" } }, "worker-farm": { "version": "1.6.0", "bundled": true, "requires": { - "errno": "~0.1.7" + "errno": "0.1.7" } }, "wrap-ansi": { "version": "2.1.0", "bundled": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" }, "dependencies": { "string-width": { "version": "1.0.2", "bundled": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -11102,9 +11102,9 @@ "version": "2.3.0", "bundled": true, "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "signal-exit": "3.0.2" } }, "xdg-basedir": { @@ -11127,18 +11127,18 @@ "version": "11.0.0", "bundled": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "9.0.2" }, "dependencies": { "y18n": { @@ -11151,7 +11151,7 @@ "version": "9.0.2", "bundled": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" } } } @@ -11162,8 +11162,8 @@ "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", "dev": true, "requires": { - "config-chain": "^1.1.11", - "pify": "^3.0.0" + "config-chain": "1.1.12", + "pify": "3.0.0" } }, "npm-run-path": { @@ -11172,7 +11172,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "nth-check": { @@ -11181,7 +11181,7 @@ "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", "dev": true, "requires": { - "boolbase": "~1.0.0" + "boolbase": "1.0.0" } }, "num2fraction": { @@ -11220,9 +11220,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" }, "dependencies": { "define-property": { @@ -11231,7 +11231,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "kind-of": { @@ -11240,7 +11240,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -11257,7 +11257,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "^3.0.0" + "isobject": "3.0.1" } }, "object.defaults": { @@ -11266,10 +11266,10 @@ "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", "dev": true, "requires": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" + "array-each": "1.0.1", + "array-slice": "1.1.0", + "for-own": "1.0.0", + "isobject": "3.0.1" } }, "object.getownpropertydescriptors": { @@ -11278,8 +11278,8 @@ "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "define-properties": "1.1.3", + "es-abstract": "1.12.0" } }, "object.map": { @@ -11288,8 +11288,8 @@ "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", "dev": true, "requires": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" + "for-own": "1.0.0", + "make-iterator": "1.0.1" } }, "object.omit": { @@ -11298,8 +11298,8 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "for-own": "0.1.5", + "is-extendable": "0.1.1" }, "dependencies": { "for-own": { @@ -11308,7 +11308,7 @@ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } } } @@ -11319,7 +11319,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" } }, "object.values": { @@ -11328,10 +11328,10 @@ "integrity": "sha1-5STaCbT2b/Bd9FdUbscqyZ8TBpo=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.6.1", - "function-bind": "^1.1.0", - "has": "^1.0.1" + "define-properties": "1.1.3", + "es-abstract": "1.12.0", + "function-bind": "1.1.1", + "has": "1.0.3" } }, "on-finished": { @@ -11349,7 +11349,7 @@ "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "onetime": { @@ -11358,7 +11358,7 @@ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "opn": { @@ -11367,7 +11367,7 @@ "integrity": "sha512-Jd/GpzPyHF4P2/aNOVmS3lfMSWV9J7cOhCG1s08XCEAsPkB7lp6ddiU0J7XzyQRDUh8BqJ7PchfINjR8jyofRQ==", "dev": true, "requires": { - "is-wsl": "^1.1.0" + "is-wsl": "1.1.0" } }, "optimist": { @@ -11376,8 +11376,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "minimist": "0.0.10", + "wordwrap": "0.0.3" }, "dependencies": { "minimist": { @@ -11400,12 +11400,12 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" } }, "optipng-bin": { @@ -11415,9 +11415,9 @@ "dev": true, "optional": true, "requires": { - "bin-build": "^3.0.0", - "bin-wrapper": "^4.0.0", - "logalot": "^2.0.0" + "bin-build": "3.0.0", + "bin-wrapper": "4.1.0", + "logalot": "2.1.0" } }, "orchestrator": { @@ -11426,9 +11426,9 @@ "integrity": "sha1-FOfp4nZPcxX7rBhOUGx6pt+UrX4=", "dev": true, "requires": { - "end-of-stream": "~0.1.5", - "sequencify": "~0.0.7", - "stream-consume": "~0.1.0" + "end-of-stream": "0.1.5", + "sequencify": "0.0.7", + "stream-consume": "0.1.1" } }, "ordered-read-streams": { @@ -11444,7 +11444,7 @@ "dev": true, "optional": true, "requires": { - "arch": "^2.1.0" + "arch": "2.1.1" } }, "os-homedir": { @@ -11473,7 +11473,7 @@ "dev": true, "optional": true, "requires": { - "p-timeout": "^1.1.1" + "p-timeout": "1.2.1" } }, "p-finally": { @@ -11496,7 +11496,7 @@ "dev": true, "optional": true, "requires": { - "p-reduce": "^1.0.0" + "p-reduce": "1.0.0" } }, "p-pipe": { @@ -11518,7 +11518,7 @@ "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", "dev": true, "requires": { - "p-finally": "^1.0.0" + "p-finally": "1.0.0" } }, "parse-filepath": { @@ -11527,9 +11527,9 @@ "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", "dev": true, "requires": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" + "is-absolute": "1.0.0", + "map-cache": "0.2.2", + "path-root": "0.1.1" } }, "parse-glob": { @@ -11538,10 +11538,10 @@ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" }, "dependencies": { "is-extglob": { @@ -11556,7 +11556,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } } } @@ -11567,8 +11567,8 @@ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "error-ex": "1.3.2", + "json-parse-better-errors": "1.0.2" } }, "parse-node-version": { @@ -11589,7 +11589,7 @@ "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", "dev": true, "requires": { - "better-assert": "~1.0.0" + "better-assert": "1.0.2" } }, "parseuri": { @@ -11598,7 +11598,7 @@ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", "dev": true, "requires": { - "better-assert": "~1.0.0" + "better-assert": "1.0.2" } }, "parseurl": { @@ -11631,7 +11631,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } }, "path-is-absolute": { @@ -11664,7 +11664,7 @@ "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", "dev": true, "requires": { - "path-root-regex": "^0.1.0" + "path-root-regex": "0.1.2" } }, "path-root-regex": { @@ -11679,7 +11679,7 @@ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" } }, "pause-stream": { @@ -11688,7 +11688,7 @@ "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", "dev": true, "requires": { - "through": "~2.3" + "through": "2.3.8" } }, "pend": { @@ -11709,15 +11709,15 @@ "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=", "dev": true, "requires": { - "es6-promise": "^4.0.3", - "extract-zip": "^1.6.5", - "fs-extra": "^1.0.0", - "hasha": "^2.2.0", - "kew": "^0.7.0", - "progress": "^1.1.8", - "request": "^2.81.0", - "request-progress": "^2.0.1", - "which": "^1.2.10" + "es6-promise": "4.2.5", + "extract-zip": "1.6.7", + "fs-extra": "1.0.0", + "hasha": "2.2.0", + "kew": "0.7.0", + "progress": "1.1.8", + "request": "2.88.0", + "request-progress": "2.0.1", + "which": "1.3.1" }, "dependencies": { "es6-promise": { @@ -11752,7 +11752,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "plugin-error": { @@ -11761,10 +11761,10 @@ "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", "dev": true, "requires": { - "ansi-colors": "^1.0.1", - "arr-diff": "^4.0.0", - "arr-union": "^3.1.0", - "extend-shallow": "^3.0.2" + "ansi-colors": "1.1.0", + "arr-diff": "4.0.0", + "arr-union": "3.1.0", + "extend-shallow": "3.0.2" } }, "plugin-log": { @@ -11773,8 +11773,8 @@ "integrity": "sha1-hgSc9qsQgzOYqTHzaJy67nteEzM=", "dev": true, "requires": { - "chalk": "^1.1.1", - "dateformat": "^1.0.11" + "chalk": "1.1.3", + "dateformat": "1.0.12" }, "dependencies": { "ansi-styles": { @@ -11789,11 +11789,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "dateformat": { @@ -11802,8 +11802,8 @@ "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", "dev": true, "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.3.0" + "get-stdin": "4.0.1", + "meow": "3.7.0" } }, "supports-color": { @@ -11820,7 +11820,7 @@ "integrity": "sha512-lJl0ojUynAM1BZn58Pas2WT/TXeC1+bS+UqShl0x9+49AtOn7DixRXVzaC8qrDOIxNDmepKnLuMTH7NQmkX0PA==", "dev": true, "requires": { - "irregular-plurals": "^2.0.0" + "irregular-plurals": "2.0.0" } }, "pluralize": { @@ -11841,9 +11841,9 @@ "integrity": "sha512-Nq/rNjnHFcKgCDDZYO0lNsl6YWe6U7tTy+ESN+PnLxebL8uBtYX59HZqvrj7YLK5UCyll2hqDsJOo3ndzEW8Ug==", "dev": true, "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.5.0" + "chalk": "2.4.1", + "source-map": "0.6.1", + "supports-color": "5.5.0" }, "dependencies": { "source-map": { @@ -11860,10 +11860,10 @@ "integrity": "sha512-oXqx0m6tb4N3JGdmeMSc/i91KppbYsFZKdH0xMOqK8V1rJlzrKlTdokz8ozUXLVejydRN6u2IddxpcijRj2FqQ==", "dev": true, "requires": { - "css-unit-converter": "^1.1.1", - "postcss": "^7.0.5", - "postcss-selector-parser": "^5.0.0-rc.4", - "postcss-value-parser": "^3.3.1" + "css-unit-converter": "1.1.1", + "postcss": "7.0.6", + "postcss-selector-parser": "5.0.0-rc.4", + "postcss-value-parser": "3.3.1" } }, "postcss-colormin": { @@ -11872,11 +11872,11 @@ "integrity": "sha512-1QJc2coIehnVFsz0otges8kQLsryi4lo19WD+U5xCWvXd0uw/Z+KKYnbiNDCnO9GP+PvErPHCG0jNvWTngk9Rw==", "dev": true, "requires": { - "browserslist": "^4.0.0", - "color": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "browserslist": "4.3.5", + "color": "3.1.0", + "has": "1.0.3", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-convert-values": { @@ -11885,8 +11885,8 @@ "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", "dev": true, "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-discard-comments": { @@ -11895,7 +11895,7 @@ "integrity": "sha512-Ay+rZu1Sz6g8IdzRjUgG2NafSNpp2MSMOQUb+9kkzzzP+kh07fP0yNbhtFejURnyVXSX3FYy2nVNW1QTnNjgBQ==", "dev": true, "requires": { - "postcss": "^7.0.0" + "postcss": "7.0.6" } }, "postcss-discard-duplicates": { @@ -11904,7 +11904,7 @@ "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", "dev": true, "requires": { - "postcss": "^7.0.0" + "postcss": "7.0.6" } }, "postcss-discard-empty": { @@ -11913,7 +11913,7 @@ "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", "dev": true, "requires": { - "postcss": "^7.0.0" + "postcss": "7.0.6" } }, "postcss-discard-overridden": { @@ -11922,7 +11922,7 @@ "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", "dev": true, "requires": { - "postcss": "^7.0.0" + "postcss": "7.0.6" } }, "postcss-load-config": { @@ -11931,8 +11931,8 @@ "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", "dev": true, "requires": { - "cosmiconfig": "^4.0.0", - "import-cwd": "^2.0.0" + "cosmiconfig": "4.0.0", + "import-cwd": "2.1.0" }, "dependencies": { "cosmiconfig": { @@ -11941,10 +11941,10 @@ "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", "dev": true, "requires": { - "is-directory": "^0.3.1", - "js-yaml": "^3.9.0", - "parse-json": "^4.0.0", - "require-from-string": "^2.0.1" + "is-directory": "0.3.1", + "js-yaml": "3.12.0", + "parse-json": "4.0.0", + "require-from-string": "2.0.2" } } } @@ -11956,9 +11956,9 @@ "dev": true, "requires": { "css-color-names": "0.0.4", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "stylehacks": "^4.0.0" + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1", + "stylehacks": "4.0.1" } }, "postcss-merge-rules": { @@ -11967,12 +11967,12 @@ "integrity": "sha512-UiuXwCCJtQy9tAIxsnurfF0mrNHKc4NnNx6NxqmzNNjXpQwLSukUxELHTRF0Rg1pAmcoKLih8PwvZbiordchag==", "dev": true, "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "cssnano-util-same-parent": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0", - "vendors": "^1.0.0" + "browserslist": "4.3.5", + "caniuse-api": "3.0.0", + "cssnano-util-same-parent": "4.0.1", + "postcss": "7.0.6", + "postcss-selector-parser": "3.1.1", + "vendors": "1.0.2" }, "dependencies": { "postcss-selector-parser": { @@ -11981,9 +11981,9 @@ "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", "dev": true, "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" + "dot-prop": "4.2.0", + "indexes-of": "1.0.1", + "uniq": "1.0.1" } } } @@ -11994,8 +11994,8 @@ "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", "dev": true, "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-minify-gradients": { @@ -12004,10 +12004,10 @@ "integrity": "sha512-pySEW3E6Ly5mHm18rekbWiAjVi/Wj8KKt2vwSfVFAWdW6wOIekgqxKxLU7vJfb107o3FDNPkaYFCxGAJBFyogA==", "dev": true, "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "is-color-stop": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "cssnano-util-get-arguments": "4.0.0", + "is-color-stop": "1.1.0", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-minify-params": { @@ -12016,12 +12016,12 @@ "integrity": "sha512-h4W0FEMEzBLxpxIVelRtMheskOKKp52ND6rJv+nBS33G1twu2tCyurYj/YtgU76+UDCvWeNs0hs8HFAWE2OUFg==", "dev": true, "requires": { - "alphanum-sort": "^1.0.0", - "browserslist": "^4.0.0", - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "uniqs": "^2.0.0" + "alphanum-sort": "1.0.2", + "browserslist": "4.3.5", + "cssnano-util-get-arguments": "4.0.0", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1", + "uniqs": "2.0.0" } }, "postcss-minify-selectors": { @@ -12030,10 +12030,10 @@ "integrity": "sha512-8+plQkomve3G+CodLCgbhAKrb5lekAnLYuL1d7Nz+/7RANpBEVdgBkPNwljfSKvZ9xkkZTZITd04KP+zeJTJqg==", "dev": true, "requires": { - "alphanum-sort": "^1.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" + "alphanum-sort": "1.0.2", + "has": "1.0.3", + "postcss": "7.0.6", + "postcss-selector-parser": "3.1.1" }, "dependencies": { "postcss-selector-parser": { @@ -12042,9 +12042,9 @@ "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", "dev": true, "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" + "dot-prop": "4.2.0", + "indexes-of": "1.0.1", + "uniq": "1.0.1" } } } @@ -12055,7 +12055,7 @@ "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", "dev": true, "requires": { - "postcss": "^7.0.0" + "postcss": "7.0.6" } }, "postcss-normalize-display-values": { @@ -12064,9 +12064,9 @@ "integrity": "sha512-R5mC4vaDdvsrku96yXP7zak+O3Mm9Y8IslUobk7IMP+u/g+lXvcN4jngmHY5zeJnrQvE13dfAg5ViU05ZFDwdg==", "dev": true, "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "cssnano-util-get-match": "4.0.0", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-normalize-positions": { @@ -12075,10 +12075,10 @@ "integrity": "sha512-GNoOaLRBM0gvH+ZRb2vKCIujzz4aclli64MBwDuYGU2EY53LwiP7MxOZGE46UGtotrSnmarPPZ69l2S/uxdaWA==", "dev": true, "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "cssnano-util-get-arguments": "4.0.0", + "has": "1.0.3", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-normalize-repeat-style": { @@ -12087,10 +12087,10 @@ "integrity": "sha512-fFHPGIjBUyUiswY2rd9rsFcC0t3oRta4wxE1h3lpwfQZwFeFjXFSiDtdJ7APCmHQOnUZnqYBADNRPKPwFAONgA==", "dev": true, "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "cssnano-util-get-arguments": "4.0.0", + "cssnano-util-get-match": "4.0.0", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-normalize-string": { @@ -12099,9 +12099,9 @@ "integrity": "sha512-IJoexFTkAvAq5UZVxWXAGE0yLoNN/012v7TQh5nDo6imZJl2Fwgbhy3J2qnIoaDBrtUP0H7JrXlX1jjn2YcvCQ==", "dev": true, "requires": { - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "has": "1.0.3", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-normalize-timing-functions": { @@ -12110,9 +12110,9 @@ "integrity": "sha512-1nOtk7ze36+63ONWD8RCaRDYsnzorrj+Q6fxkQV+mlY5+471Qx9kspqv0O/qQNMeApg8KNrRf496zHwJ3tBZ7w==", "dev": true, "requires": { - "cssnano-util-get-match": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "cssnano-util-get-match": "4.0.0", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-normalize-unicode": { @@ -12121,9 +12121,9 @@ "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", "dev": true, "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "browserslist": "4.3.5", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-normalize-url": { @@ -12132,10 +12132,10 @@ "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", "dev": true, "requires": { - "is-absolute-url": "^2.0.0", - "normalize-url": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "is-absolute-url": "2.1.0", + "normalize-url": "3.3.0", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-normalize-whitespace": { @@ -12144,8 +12144,8 @@ "integrity": "sha512-U8MBODMB2L+nStzOk6VvWWjZgi5kQNShCyjRhMT3s+W9Jw93yIjOnrEkKYD3Ul7ChWbEcjDWmXq0qOL9MIAnAw==", "dev": true, "requires": { - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-ordered-values": { @@ -12154,9 +12154,9 @@ "integrity": "sha512-PeJiLgJWPzkVF8JuKSBcylaU+hDJ/TX3zqAMIjlghgn1JBi6QwQaDZoDIlqWRcCAI8SxKrt3FCPSRmOgKRB97Q==", "dev": true, "requires": { - "cssnano-util-get-arguments": "^4.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "cssnano-util-get-arguments": "4.0.0", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-reduce-initial": { @@ -12165,10 +12165,10 @@ "integrity": "sha512-epUiC39NonKUKG+P3eAOKKZtm5OtAtQJL7Ye0CBN1f+UQTHzqotudp+hki7zxXm7tT0ZAKDMBj1uihpPjP25ug==", "dev": true, "requires": { - "browserslist": "^4.0.0", - "caniuse-api": "^3.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0" + "browserslist": "4.3.5", + "caniuse-api": "3.0.0", + "has": "1.0.3", + "postcss": "7.0.6" } }, "postcss-reduce-transforms": { @@ -12177,10 +12177,10 @@ "integrity": "sha512-sZVr3QlGs0pjh6JAIe6DzWvBaqYw05V1t3d9Tp+VnFRT5j+rsqoWsysh/iSD7YNsULjq9IAylCznIwVd5oU/zA==", "dev": true, "requires": { - "cssnano-util-get-match": "^4.0.0", - "has": "^1.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0" + "cssnano-util-get-match": "4.0.0", + "has": "1.0.3", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1" } }, "postcss-selector-parser": { @@ -12189,9 +12189,9 @@ "integrity": "sha512-0XvfYuShrKlTk1ooUrVzMCFQRcypsdEIsGqh5IxC5rdtBi4/M/tDAJeSONwC2MTqEFsmPZYAV7Dd4X8rgAfV0A==", "dev": true, "requires": { - "cssesc": "^2.0.0", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" + "cssesc": "2.0.0", + "indexes-of": "1.0.1", + "uniq": "1.0.1" } }, "postcss-svgo": { @@ -12200,10 +12200,10 @@ "integrity": "sha512-YD5uIk5NDRySy0hcI+ZJHwqemv2WiqqzDgtvgMzO8EGSkK5aONyX8HMVFRFJSdO8wUWTuisUFn/d7yRRbBr5Qw==", "dev": true, "requires": { - "is-svg": "^3.0.0", - "postcss": "^7.0.0", - "postcss-value-parser": "^3.0.0", - "svgo": "^1.0.0" + "is-svg": "3.0.0", + "postcss": "7.0.6", + "postcss-value-parser": "3.3.1", + "svgo": "1.1.1" } }, "postcss-unique-selectors": { @@ -12212,9 +12212,9 @@ "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", "dev": true, "requires": { - "alphanum-sort": "^1.0.0", - "postcss": "^7.0.0", - "uniqs": "^2.0.0" + "alphanum-sort": "1.0.2", + "postcss": "7.0.6", + "uniqs": "2.0.0" } }, "postcss-value-parser": { @@ -12279,7 +12279,7 @@ "dev": true, "optional": true, "requires": { - "asap": "~2.0.3" + "asap": "2.0.6" } }, "proto-list": { @@ -12313,8 +12313,8 @@ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.3.3" }, "dependencies": { "end-of-stream": { @@ -12323,7 +12323,7 @@ "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { - "once": "^1.4.0" + "once": "1.4.0" }, "dependencies": { "once": { @@ -12332,7 +12332,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } } } @@ -12370,9 +12370,9 @@ "dev": true, "optional": true, "requires": { - "decode-uri-component": "^0.2.0", - "object-assign": "^4.1.0", - "strict-uri-encode": "^1.0.0" + "decode-uri-component": "0.2.0", + "object-assign": "4.1.1", + "strict-uri-encode": "1.1.0" }, "dependencies": { "object-assign": { @@ -12390,9 +12390,9 @@ "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", "dev": true, "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" + "is-number": "4.0.0", + "kind-of": "6.0.2", + "math-random": "1.0.1" }, "dependencies": { "is-number": { @@ -12415,8 +12415,8 @@ "integrity": "sha1-HQJ8K/oRasxmI7yo8AAWVyqH1CU=", "dev": true, "requires": { - "bytes": "1", - "string_decoder": "0.10" + "bytes": "1.0.0", + "string_decoder": "0.10.31" } }, "read-pkg": { @@ -12425,9 +12425,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" }, "dependencies": { "graceful-fs": { @@ -12442,9 +12442,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.1.15", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pify": { @@ -12461,8 +12461,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" } }, "readable-stream": { @@ -12471,10 +12471,10 @@ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "readdirp": { @@ -12483,9 +12483,9 @@ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "graceful-fs": "4.1.15", + "micromatch": "3.1.10", + "readable-stream": "2.3.6" }, "dependencies": { "graceful-fs": { @@ -12506,13 +12506,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -12521,7 +12521,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -12532,7 +12532,7 @@ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, "requires": { - "resolve": "^1.1.6" + "resolve": "1.8.1" } }, "redent": { @@ -12541,8 +12541,8 @@ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" + "indent-string": "2.1.0", + "strip-indent": "1.0.1" } }, "regenerate": { @@ -12557,7 +12557,7 @@ "integrity": "sha512-s5NGghCE4itSlUS+0WUj88G6cfMVMmH8boTPNvABf8od+2dhT9WDlWu8n01raQAJZMOK8Ch6jSexaRO7swd6aw==", "dev": true, "requires": { - "regenerate": "^1.4.0" + "regenerate": "1.4.0" } }, "regenerator-transform": { @@ -12566,7 +12566,7 @@ "integrity": "sha512-5ipTrZFSq5vU2YoGoww4uaRVAK4wyYC4TSICibbfEPOruUu8FFP7ErV0BjmbIOEpn3O/k9na9UEdYR/3m7N6uA==", "dev": true, "requires": { - "private": "^0.1.6" + "private": "0.1.8" } }, "regex-cache": { @@ -12575,7 +12575,7 @@ "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { - "is-equal-shallow": "^0.1.3" + "is-equal-shallow": "0.1.3" } }, "regex-not": { @@ -12584,8 +12584,8 @@ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" } }, "regexpp": { @@ -12600,12 +12600,12 @@ "integrity": "sha512-Z835VSnJJ46CNBttalHD/dB+Sj2ezmY6Xp38npwU87peK6mqOzOpV8eYktdkLTEkzzD+JsTcxd84ozd8I14+rw==", "dev": true, "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^7.0.0", - "regjsgen": "^0.4.0", - "regjsparser": "^0.3.0", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.0.2" + "regenerate": "1.4.0", + "regenerate-unicode-properties": "7.0.0", + "regjsgen": "0.4.0", + "regjsparser": "0.3.0", + "unicode-match-property-ecmascript": "1.0.4", + "unicode-match-property-value-ecmascript": "1.0.2" } }, "regjsgen": { @@ -12620,7 +12620,7 @@ "integrity": "sha512-zza72oZBBHzt64G7DxdqrOo/30bhHkwMUoT0WqfGu98XLd7N+1tsy5MJ96Bk4MD0y74n629RhmrGW6XlnLLwCA==", "dev": true, "requires": { - "jsesc": "~0.5.0" + "jsesc": "0.5.0" }, "dependencies": { "jsesc": { @@ -12655,7 +12655,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "^1.0.0" + "is-finite": "1.0.2" } }, "replace-ext": { @@ -12670,26 +12670,26 @@ "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "dev": true, "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "aws-sign2": "0.7.0", + "aws4": "1.8.0", + "caseless": "0.12.0", + "combined-stream": "1.0.7", + "extend": "3.0.2", + "forever-agent": "0.6.1", + "form-data": "2.3.3", + "har-validator": "5.1.3", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.21", + "oauth-sign": "0.9.0", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.1.2", + "tough-cookie": "2.4.3", + "tunnel-agent": "0.6.0", + "uuid": "3.3.2" }, "dependencies": { "qs": { @@ -12706,7 +12706,7 @@ "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", "dev": true, "requires": { - "throttleit": "^1.0.0" + "throttleit": "1.0.0" } }, "require-from-string": { @@ -12721,8 +12721,8 @@ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" + "caller-path": "0.1.0", + "resolve-from": "1.0.1" }, "dependencies": { "caller-path": { @@ -12731,7 +12731,7 @@ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, "requires": { - "callsites": "^0.2.0" + "callsites": "0.2.0" } }, "callsites": { @@ -12760,7 +12760,7 @@ "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==", "dev": true, "requires": { - "path-parse": "^1.0.5" + "path-parse": "1.0.6" } }, "resolve-dir": { @@ -12769,8 +12769,8 @@ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "dev": true, "requires": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "expand-tilde": "2.0.2", + "global-modules": "1.0.0" } }, "resolve-from": { @@ -12792,7 +12792,7 @@ "dev": true, "optional": true, "requires": { - "lowercase-keys": "^1.0.0" + "lowercase-keys": "1.0.1" } }, "restore-cursor": { @@ -12801,8 +12801,8 @@ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" + "onetime": "2.0.1", + "signal-exit": "3.0.2" } }, "ret": { @@ -12835,7 +12835,7 @@ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", "dev": true, "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rimraf": { @@ -12844,7 +12844,7 @@ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.3" }, "dependencies": { "glob": { @@ -12853,12 +12853,12 @@ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.3.3", + "path-is-absolute": "1.0.1" } }, "minimatch": { @@ -12867,7 +12867,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } } } @@ -12878,7 +12878,7 @@ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", "dev": true, "requires": { - "is-promise": "^2.1.0" + "is-promise": "2.1.0" } }, "run-sequence": { @@ -12887,9 +12887,9 @@ "integrity": "sha512-qkzZnQWMZjcKbh3CNly2srtrkaO/2H/SI5f2eliMCapdRD3UhMrwjfOAZJAnZ2H8Ju4aBzFZkBGXUqFs9V0yxw==", "dev": true, "requires": { - "chalk": "^1.1.3", - "fancy-log": "^1.3.2", - "plugin-error": "^0.1.2" + "chalk": "1.1.3", + "fancy-log": "1.3.3", + "plugin-error": "0.1.2" }, "dependencies": { "ansi-styles": { @@ -12904,8 +12904,8 @@ "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", "dev": true, "requires": { - "arr-flatten": "^1.0.1", - "array-slice": "^0.2.3" + "arr-flatten": "1.1.0", + "array-slice": "0.2.3" } }, "arr-union": { @@ -12926,11 +12926,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "extend-shallow": { @@ -12939,7 +12939,7 @@ "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", "dev": true, "requires": { - "kind-of": "^1.1.0" + "kind-of": "1.1.0" } }, "kind-of": { @@ -12954,11 +12954,11 @@ "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", "dev": true, "requires": { - "ansi-cyan": "^0.1.1", - "ansi-red": "^0.1.1", - "arr-diff": "^1.0.1", - "arr-union": "^2.0.1", - "extend-shallow": "^1.1.2" + "ansi-cyan": "0.1.1", + "ansi-red": "0.1.1", + "arr-diff": "1.1.0", + "arr-union": "2.1.0", + "extend-shallow": "1.1.4" } }, "supports-color": { @@ -12975,7 +12975,7 @@ "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.9.3" } }, "safe-buffer": { @@ -12996,7 +12996,7 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "~0.1.10" + "ret": "0.1.15" } }, "safer-buffer": { @@ -13017,7 +13017,7 @@ "integrity": "sha1-z+kXyz0nS8/6x5J1ivUxc+sfq9w=", "dev": true, "requires": { - "commander": "~2.8.1" + "commander": "2.8.1" } }, "select": { @@ -13045,7 +13045,7 @@ "dev": true, "optional": true, "requires": { - "semver": "^5.3.0" + "semver": "5.6.0" } }, "send": { @@ -13055,18 +13055,18 @@ "dev": true, "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", "fresh": "0.5.2", - "http-errors": "~1.6.2", + "http-errors": "1.6.3", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" + "on-finished": "2.3.0", + "range-parser": "1.2.0", + "statuses": "1.4.0" }, "dependencies": { "debug": { @@ -13104,13 +13104,13 @@ "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", "dev": true, "requires": { - "accepts": "~1.3.4", + "accepts": "1.3.5", "batch": "0.6.1", "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" + "escape-html": "1.0.3", + "http-errors": "1.6.3", + "mime-types": "2.1.21", + "parseurl": "1.3.2" }, "dependencies": { "debug": { @@ -13136,9 +13136,9 @@ "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "dev": true, "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.2", "send": "0.16.2" } }, @@ -13148,10 +13148,10 @@ "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" }, "dependencies": { "extend-shallow": { @@ -13160,7 +13160,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -13177,7 +13177,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -13203,7 +13203,7 @@ "resolved": "https://registry.npmjs.org/signalr/-/signalr-2.4.0.tgz", "integrity": "sha512-GPJHb3pcNk3IUui5/WG8lMuarEn+Vpc8wEvJ60w0KQ43W9FHnJcuNcF8dkZePr81eBslzicsRdyEunKNF7KjZQ==", "requires": { - "jquery": ">=1.6.4" + "jquery": "3.3.1" } }, "simple-swizzle": { @@ -13212,7 +13212,7 @@ "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", "dev": true, "requires": { - "is-arrayish": "^0.3.1" + "is-arrayish": "0.3.2" }, "dependencies": { "is-arrayish": { @@ -13235,9 +13235,9 @@ "integrity": "sha512-4j2WTWjp3GsZ+AOagyzVbzp4vWGtZ0hEZ/gDY/uTvm6MTxUfTUIsnMIFb1bn8o0RuXiqUw15H1bue8f22Vw2oQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" + "ansi-styles": "3.2.1", + "astral-regex": "1.0.0", + "is-fullwidth-code-point": "2.0.0" } }, "snapdragon": { @@ -13246,14 +13246,14 @@ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" + "base": "0.11.2", + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "map-cache": "0.2.2", + "source-map": "0.5.7", + "source-map-resolve": "0.5.2", + "use": "3.1.1" }, "dependencies": { "debug": { @@ -13271,7 +13271,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -13280,7 +13280,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "ms": { @@ -13297,9 +13297,9 @@ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" }, "dependencies": { "define-property": { @@ -13308,7 +13308,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -13317,7 +13317,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -13326,7 +13326,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -13335,9 +13335,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } } } @@ -13348,7 +13348,7 @@ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { - "kind-of": "^3.2.0" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -13357,7 +13357,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -13368,12 +13368,12 @@ "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", "dev": true, "requires": { - "debug": "~3.1.0", - "engine.io": "~3.2.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", + "debug": "3.1.0", + "engine.io": "3.2.1", + "has-binary2": "1.0.3", + "socket.io-adapter": "1.1.1", "socket.io-client": "2.1.1", - "socket.io-parser": "~3.2.0" + "socket.io-parser": "3.2.0" }, "dependencies": { "debug": { @@ -13409,15 +13409,15 @@ "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.2.0", - "has-binary2": "~1.0.2", + "debug": "3.1.0", + "engine.io-client": "3.2.1", + "has-binary2": "1.0.3", "has-cors": "1.1.0", "indexof": "0.0.1", "object-component": "0.0.3", "parseqs": "0.0.5", "parseuri": "0.0.5", - "socket.io-parser": "~3.2.0", + "socket.io-parser": "3.2.0", "to-array": "0.1.4" }, "dependencies": { @@ -13445,7 +13445,7 @@ "dev": true, "requires": { "component-emitter": "1.2.1", - "debug": "~3.1.0", + "debug": "3.1.0", "isarray": "2.0.1" }, "dependencies": { @@ -13478,7 +13478,7 @@ "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", "dev": true, "requires": { - "is-plain-obj": "^1.0.0" + "is-plain-obj": "1.1.0" } }, "sort-keys-length": { @@ -13487,7 +13487,7 @@ "integrity": "sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg=", "dev": true, "requires": { - "sort-keys": "^1.0.0" + "sort-keys": "1.1.2" } }, "source-map": { @@ -13502,11 +13502,11 @@ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "atob": "2.1.2", + "decode-uri-component": "0.2.0", + "resolve-url": "0.2.1", + "source-map-url": "0.4.0", + "urix": "0.1.0" } }, "source-map-url": { @@ -13527,8 +13527,8 @@ "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==", "dev": true, "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.2" } }, "spdx-exceptions": { @@ -13543,8 +13543,8 @@ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "dev": true, "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "spdx-exceptions": "2.2.0", + "spdx-license-ids": "3.0.2" } }, "spdx-license-ids": { @@ -13564,7 +13564,7 @@ "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", "dev": true, "requires": { - "through": "2" + "through": "2.3.8" } }, "split-string": { @@ -13573,7 +13573,7 @@ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { - "extend-shallow": "^3.0.0" + "extend-shallow": "3.0.2" } }, "sprintf-js": { @@ -13589,9 +13589,9 @@ "dev": true, "optional": true, "requires": { - "chalk": "^1.0.0", - "console-stream": "^0.1.1", - "lpad-align": "^1.0.1" + "chalk": "1.1.3", + "console-stream": "0.1.1", + "lpad-align": "1.1.2" }, "dependencies": { "ansi-styles": { @@ -13608,11 +13608,11 @@ "dev": true, "optional": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "supports-color": { @@ -13630,15 +13630,15 @@ "integrity": "sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA==", "dev": true, "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" + "asn1": "0.2.4", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.2", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" } }, "stable": { @@ -13653,8 +13653,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "define-property": "0.2.5", + "object-copy": "0.1.0" }, "dependencies": { "define-property": { @@ -13663,7 +13663,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -13680,8 +13680,8 @@ "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=", "dev": true, "requires": { - "duplexer": "~0.1.1", - "through": "~2.3.4" + "duplexer": "0.1.1", + "through": "2.3.8" } }, "stream-consume": { @@ -13696,10 +13696,10 @@ "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", "dev": true, "requires": { - "date-format": "^1.2.0", - "debug": "^3.1.0", - "mkdirp": "^0.5.1", - "readable-stream": "^2.3.0" + "date-format": "1.2.0", + "debug": "3.2.6", + "mkdirp": "0.5.1", + "readable-stream": "2.3.6" }, "dependencies": { "debug": { @@ -13708,7 +13708,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.1" } }, "isarray": { @@ -13723,13 +13723,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -13738,7 +13738,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -13762,8 +13762,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "ansi-regex": { @@ -13778,7 +13778,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -13795,7 +13795,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-bom": { @@ -13804,8 +13804,8 @@ "integrity": "sha1-hbiGLzhEtabV7IRnqTWYFzo295Q=", "dev": true, "requires": { - "first-chunk-stream": "^1.0.0", - "is-utf8": "^0.2.0" + "first-chunk-stream": "1.0.0", + "is-utf8": "0.2.1" } }, "strip-bom-stream": { @@ -13814,8 +13814,8 @@ "integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=", "dev": true, "requires": { - "first-chunk-stream": "^2.0.0", - "strip-bom": "^2.0.0" + "first-chunk-stream": "2.0.0", + "strip-bom": "2.0.0" }, "dependencies": { "first-chunk-stream": { @@ -13824,7 +13824,7 @@ "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", "dev": true, "requires": { - "readable-stream": "^2.0.2" + "readable-stream": "2.3.6" } }, "isarray": { @@ -13839,13 +13839,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -13854,7 +13854,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "strip-bom": { @@ -13863,7 +13863,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } } } @@ -13874,7 +13874,7 @@ "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", "dev": true, "requires": { - "is-natural-number": "^4.0.1" + "is-natural-number": "4.0.1" } }, "strip-eof": { @@ -13889,7 +13889,7 @@ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, "requires": { - "get-stdin": "^4.0.1" + "get-stdin": "4.0.1" } }, "strip-json-comments": { @@ -13904,7 +13904,7 @@ "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", "dev": true, "requires": { - "escape-string-regexp": "^1.0.2" + "escape-string-regexp": "1.0.5" } }, "stylehacks": { @@ -13913,9 +13913,9 @@ "integrity": "sha512-TK5zEPeD9NyC1uPIdjikzsgWxdQQN/ry1X3d1iOz1UkYDCmcr928gWD1KHgyC27F50UnE0xCTrBOO1l6KR8M4w==", "dev": true, "requires": { - "browserslist": "^4.0.0", - "postcss": "^7.0.0", - "postcss-selector-parser": "^3.0.0" + "browserslist": "4.3.5", + "postcss": "7.0.6", + "postcss-selector-parser": "3.1.1" }, "dependencies": { "postcss-selector-parser": { @@ -13924,9 +13924,9 @@ "integrity": "sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=", "dev": true, "requires": { - "dot-prop": "^4.1.1", - "indexes-of": "^1.0.1", - "uniq": "^1.0.1" + "dot-prop": "4.2.0", + "indexes-of": "1.0.1", + "uniq": "1.0.1" } } } @@ -13937,7 +13937,7 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } }, "svgo": { @@ -13946,20 +13946,20 @@ "integrity": "sha512-GBkJbnTuFpM4jFbiERHDWhZc/S/kpHToqmZag3aEBjPYK44JAN2QBjvrGIxLOoCyMZjuFQIfTO2eJd8uwLY/9g==", "dev": true, "requires": { - "coa": "~2.0.1", - "colors": "~1.1.2", - "css-select": "^2.0.0", - "css-select-base-adapter": "~0.1.0", + "coa": "2.0.1", + "colors": "1.1.2", + "css-select": "2.0.2", + "css-select-base-adapter": "0.1.1", "css-tree": "1.0.0-alpha.28", - "css-url-regex": "^1.1.0", - "csso": "^3.5.0", - "js-yaml": "^3.12.0", - "mkdirp": "~0.5.1", - "object.values": "^1.0.4", - "sax": "~1.2.4", - "stable": "~0.1.6", - "unquote": "~1.1.1", - "util.promisify": "~1.0.0" + "css-url-regex": "1.1.0", + "csso": "3.5.1", + "js-yaml": "3.12.0", + "mkdirp": "0.5.1", + "object.values": "1.0.4", + "sax": "1.2.4", + "stable": "0.1.8", + "unquote": "1.1.1", + "util.promisify": "1.0.0" } }, "table": { @@ -13968,10 +13968,10 @@ "integrity": "sha512-NUjapYb/qd4PeFW03HnAuOJ7OMcBkJlqeClWxeNlQ0lXGSb52oZXGzkO0/I0ARegQ2eUT1g2VDJH0eUxDRcHmw==", "dev": true, "requires": { - "ajv": "^6.6.1", - "lodash": "^4.17.11", + "ajv": "6.6.1", + "lodash": "4.17.11", "slice-ansi": "2.0.0", - "string-width": "^2.1.1" + "string-width": "2.1.1" } }, "tar-stream": { @@ -13980,13 +13980,13 @@ "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "dev": true, "requires": { - "bl": "^1.0.0", - "buffer-alloc": "^1.2.0", - "end-of-stream": "^1.0.0", - "fs-constants": "^1.0.0", - "readable-stream": "^2.3.0", - "to-buffer": "^1.1.1", - "xtend": "^4.0.0" + "bl": "1.2.2", + "buffer-alloc": "1.2.0", + "end-of-stream": "1.4.1", + "fs-constants": "1.0.0", + "readable-stream": "2.3.6", + "to-buffer": "1.1.1", + "xtend": "4.0.1" }, "dependencies": { "end-of-stream": { @@ -13995,7 +13995,7 @@ "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { - "once": "^1.4.0" + "once": "1.4.0" } }, "isarray": { @@ -14010,7 +14010,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "readable-stream": { @@ -14019,13 +14019,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -14034,7 +14034,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -14051,8 +14051,8 @@ "integrity": "sha1-awRGhWqbERTRhW/8vlCczLCXcmU=", "dev": true, "requires": { - "temp-dir": "^1.0.0", - "uuid": "^3.0.1" + "temp-dir": "1.0.0", + "uuid": "3.3.2" } }, "text-table": { @@ -14079,8 +14079,8 @@ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "readable-stream": "2.3.6", + "xtend": "4.0.1" }, "dependencies": { "isarray": { @@ -14095,13 +14095,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -14110,7 +14110,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } } } @@ -14121,7 +14121,7 @@ "integrity": "sha512-R5/jLkfMvdmDD+seLwN7vB+mhbqzWop5fAjx5IX8/yQq7VhBhzDmhXgaHAOnhnWkCpRMM7gToYHycB0CS/pd+A==", "dev": true, "requires": { - "through2": "^2.0.0" + "through2": "2.0.5" } }, "tildify": { @@ -14130,7 +14130,7 @@ "integrity": "sha1-3OwD9V3Km3qj5bBPIYF+tW5jWIo=", "dev": true, "requires": { - "os-homedir": "^1.0.0" + "os-homedir": "1.0.2" } }, "time-stamp": { @@ -14162,12 +14162,12 @@ "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", "dev": true, "requires": { - "body": "^5.1.0", - "debug": "^3.1.0", - "faye-websocket": "~0.10.0", - "livereload-js": "^2.3.0", - "object-assign": "^4.1.0", - "qs": "^6.4.0" + "body": "5.1.0", + "debug": "3.2.6", + "faye-websocket": "0.10.0", + "livereload-js": "2.4.0", + "object-assign": "4.1.1", + "qs": "6.6.0" }, "dependencies": { "debug": { @@ -14176,7 +14176,7 @@ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "2.1.1" } }, "object-assign": { @@ -14198,7 +14198,7 @@ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "os-tmpdir": "1.0.2" } }, "to-array": { @@ -14225,7 +14225,7 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -14234,7 +14234,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.6" } } } @@ -14245,10 +14245,10 @@ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" } }, "to-regex-range": { @@ -14257,8 +14257,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "3.0.0", + "repeat-string": "1.6.1" } }, "tough-cookie": { @@ -14267,8 +14267,8 @@ "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "dev": true, "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" + "psl": "1.1.29", + "punycode": "1.4.1" }, "dependencies": { "punycode": { @@ -14291,7 +14291,7 @@ "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", "dev": true, "requires": { - "escape-string-regexp": "^1.0.2" + "escape-string-regexp": "1.0.5" } }, "trim-right": { @@ -14318,7 +14318,7 @@ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.2" } }, "tweetnacl": { @@ -14333,7 +14333,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "1.1.2" } }, "type-is": { @@ -14343,7 +14343,7 @@ "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "2.1.21" } }, "typeahead.js": { @@ -14351,7 +14351,7 @@ "resolved": "https://registry.npmjs.org/typeahead.js/-/typeahead.js-0.11.1.tgz", "integrity": "sha1-TmTmcbIjEKhgb0rsgFkkuoSwFbg=", "requires": { - "jquery": ">=1.7" + "jquery": "3.3.1" } }, "typedarray": { @@ -14366,9 +14366,9 @@ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.7", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" } }, "uglify-to-browserify": { @@ -14390,8 +14390,8 @@ "integrity": "sha512-fIZnvdjblYs7Cru/xC6tCPVhz7JkYcVQQkePwMLyQELzYTds2Xn8QefPVnvdVhhZqubxNA1cASXEH5wcK0Bucw==", "dev": true, "requires": { - "buffer": "^3.0.1", - "through": "^2.3.6" + "buffer": "3.6.0", + "through": "2.3.8" } }, "unc-path-regex": { @@ -14417,8 +14417,8 @@ "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", "dev": true, "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" + "unicode-canonical-property-names-ecmascript": "1.0.4", + "unicode-property-aliases-ecmascript": "1.0.4" } }, "unicode-match-property-value-ecmascript": { @@ -14439,10 +14439,10 @@ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "0.4.3" }, "dependencies": { "extend-shallow": { @@ -14451,7 +14451,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "set-value": { @@ -14460,10 +14460,10 @@ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "to-object-path": "0.3.0" } } } @@ -14504,8 +14504,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "has-value": "0.3.1", + "isobject": "3.0.1" }, "dependencies": { "has-value": { @@ -14514,9 +14514,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" }, "dependencies": { "isobject": { @@ -14556,7 +14556,7 @@ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" } }, "urix": { @@ -14572,7 +14572,7 @@ "dev": true, "optional": true, "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "1.0.4" } }, "url-to-options": { @@ -14599,8 +14599,8 @@ "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", "dev": true, "requires": { - "lru-cache": "2.2.x", - "tmp": "0.0.x" + "lru-cache": "2.2.4", + "tmp": "0.0.33" }, "dependencies": { "lru-cache": { @@ -14623,8 +14623,8 @@ "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" + "define-properties": "1.1.3", + "object.getownpropertydescriptors": "2.0.3" } }, "utils-merge": { @@ -14645,7 +14645,7 @@ "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", "dev": true, "requires": { - "user-home": "^1.1.1" + "user-home": "1.1.1" } }, "validate-npm-package-license": { @@ -14654,8 +14654,8 @@ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "spdx-correct": "3.0.2", + "spdx-expression-parse": "3.0.0" } }, "vendors": { @@ -14670,9 +14670,9 @@ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { - "assert-plus": "^1.0.0", + "assert-plus": "1.0.0", "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "extsprintf": "1.3.0" } }, "vinyl": { @@ -14681,8 +14681,8 @@ "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } }, @@ -14701,12 +14701,12 @@ "integrity": "sha1-p+v1/779obfRjRQPyweyI++2dRo=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.3.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0", - "strip-bom-stream": "^2.0.0", - "vinyl": "^1.1.0" + "graceful-fs": "4.1.15", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0", + "strip-bom-stream": "2.0.0", + "vinyl": "1.2.0" }, "dependencies": { "graceful-fs": { @@ -14727,7 +14727,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } }, "vinyl": { @@ -14736,8 +14736,8 @@ "integrity": "sha1-XIgDbPVl5d8FVYv8kR+GVt8hiIQ=", "dev": true, "requires": { - "clone": "^1.0.0", - "clone-stats": "^0.0.1", + "clone": "1.0.4", + "clone-stats": "0.0.1", "replace-ext": "0.0.1" } } @@ -14749,14 +14749,14 @@ "integrity": "sha1-mmhRzhysHBzqX+hsCTHWIMLPqeY=", "dev": true, "requires": { - "defaults": "^1.0.0", - "glob-stream": "^3.1.5", - "glob-watcher": "^0.0.6", - "graceful-fs": "^3.0.0", - "mkdirp": "^0.5.0", - "strip-bom": "^1.0.0", - "through2": "^0.6.1", - "vinyl": "^0.4.0" + "defaults": "1.0.3", + "glob-stream": "3.1.18", + "glob-watcher": "0.0.6", + "graceful-fs": "3.0.11", + "mkdirp": "0.5.1", + "strip-bom": "1.0.0", + "through2": "0.6.5", + "vinyl": "0.4.6" }, "dependencies": { "clone": { @@ -14771,10 +14771,10 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "through2": { @@ -14783,8 +14783,8 @@ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=", "dev": true, "requires": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" + "readable-stream": "1.0.34", + "xtend": "4.0.1" } }, "vinyl": { @@ -14793,8 +14793,8 @@ "integrity": "sha1-LzVsh6VQolVGHza76ypbqL94SEc=", "dev": true, "requires": { - "clone": "^0.2.0", - "clone-stats": "^0.0.1" + "clone": "0.2.0", + "clone-stats": "0.0.1" } } } @@ -14805,7 +14805,7 @@ "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", "dev": true, "requires": { - "source-map": "^0.5.1" + "source-map": "0.5.7" } }, "void-elements": { @@ -14820,8 +14820,8 @@ "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "dev": true, "requires": { - "http-parser-js": ">=0.4.0", - "websocket-extensions": ">=0.1.1" + "http-parser-js": "0.5.0", + "websocket-extensions": "0.1.3" } }, "websocket-extensions": { @@ -14842,7 +14842,7 @@ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "window-size": { @@ -14869,7 +14869,7 @@ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { - "mkdirp": "^0.5.1" + "mkdirp": "0.5.1" } }, "ws": { @@ -14878,9 +14878,9 @@ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", "dev": true, "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" + "async-limiter": "1.0.0", + "safe-buffer": "5.1.2", + "ultron": "1.1.1" } }, "xmlhttprequest-ssl": { @@ -14907,9 +14907,9 @@ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", "dev": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" }, "dependencies": { @@ -14927,8 +14927,8 @@ "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", "dev": true, "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" + "buffer-crc32": "0.2.13", + "fd-slicer": "1.1.0" } }, "yeast": { diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index 88a6cfb33d..99972a27cf 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.PropertyEditors : base(logger) { } - public override IPropertyIndexValues PropertyIndexValues => new GridPropertyIndexValues(); + public override IPropertyIndexValueFactory PropertyIndexValueFactory => new GridPropertyIndexValueFactory(); /// /// Overridden to ensure that the value is validated diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValues.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs similarity index 97% rename from src/Umbraco.Web/PropertyEditors/GridPropertyIndexValues.cs rename to src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs index 04876ec118..c8235d1d11 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValues.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyIndexValueFactory.cs @@ -16,7 +16,7 @@ namespace Umbraco.Web.PropertyEditors /// /// Parses the grid value into indexable values /// - public class GridPropertyIndexValues : IPropertyIndexValues + public class GridPropertyIndexValueFactory : IPropertyIndexValueFactory { public IEnumerable>> GetIndexValues(Property property, string culture, string segment, bool published) { diff --git a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs index c4978e62bc..07c5aac5bb 100644 --- a/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/NestedContentPropertyEditor.cs @@ -42,7 +42,7 @@ namespace Umbraco.Web.PropertyEditors : Current.Services.ContentTypeService.Get(contentTypeAlias); } - //fixme: Need to add a custom IValueIndexer for this editor + //fixme: Need to add a custom IPropertyIndexValueFactory for this editor #region Pre Value Editor diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 6dd63e6123..2deb8b8444 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -32,7 +32,7 @@ namespace Umbraco.Web.PropertyEditors protected override IConfigurationEditor CreateConfigurationEditor() => new RichTextConfigurationEditor(); - public override IPropertyIndexValues PropertyIndexValues => new RichTextPropertyIndexValues(); + public override IPropertyIndexValueFactory PropertyIndexValueFactory => new RichTextPropertyIndexValueFactory(); /// /// A custom value editor to ensure that macro syntax is parsed when being persisted and formatted correctly for display in the editor @@ -93,7 +93,7 @@ namespace Umbraco.Web.PropertyEditors } } - internal class RichTextPropertyIndexValues : IPropertyIndexValues + internal class RichTextPropertyIndexValueFactory : IPropertyIndexValueFactory { public IEnumerable>> GetIndexValues(Property property, string culture, string segment, bool published) { diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 1a842525a4..c45d470571 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -63,6 +63,7 @@ namespace Umbraco.Web.Search //composition.Container.RegisterSingleton(); //composition.Container.RegisterSingleton(); + // fixme -- CHANGE THIS WHEN THE DI PR IS MERGED //fixme: Instead i have to do this, but this means that developers adding their own will also need to do this which isn't ideal composition.Container.RegisterMany(new[] { diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index e8c76a1f45..ce39f51baf 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -161,7 +161,7 @@ - + From c3d0bee4d325eaf19304805df1a2afe92c7842c5 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 5 Dec 2018 17:34:34 +0100 Subject: [PATCH 41/44] Fix boot issue --- .../PropertyEditors/DefaultPropertyIndexValueFactory.cs | 2 +- src/Umbraco.Core/PropertyEditors/IDataEditor.cs | 2 +- .../PropertyEditors/IPropertyIndexValueFactory.cs | 2 +- .../DatabaseServerRegistrarAndMessengerComponent.cs | 5 +++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs b/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs index 86e1621441..413f31d79e 100644 --- a/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs +++ b/src/Umbraco.Core/PropertyEditors/DefaultPropertyIndexValueFactory.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Models; namespace Umbraco.Core.PropertyEditors { /// - /// Provides a default implementation for , returning a single field to index containing the property value. + /// Provides a default implementation for , returning a single field to index containing the property value. /// public class DefaultPropertyIndexValueFactory : IPropertyIndexValueFactory { diff --git a/src/Umbraco.Core/PropertyEditors/IDataEditor.cs b/src/Umbraco.Core/PropertyEditors/IDataEditor.cs index 917cd20dfc..f109620ad9 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataEditor.cs @@ -67,7 +67,7 @@ namespace Umbraco.Core.PropertyEditors IConfigurationEditor GetConfigurationEditor(); /// - /// Gets the value indexer for the editor. + /// Gets the index value factory for the editor. /// IPropertyIndexValueFactory PropertyIndexValueFactory { get; } } diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs index bb2f2aacf3..e303a53f5f 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Models; namespace Umbraco.Core.PropertyEditors { /// - /// Represents a property value indexer. + /// Represents a property index value factory. /// public interface IPropertyIndexValueFactory { diff --git a/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs b/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs index 6d35580afa..990aa32ddd 100644 --- a/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs +++ b/src/Umbraco.Web/Components/DatabaseServerRegistrarAndMessengerComponent.cs @@ -102,8 +102,6 @@ namespace Umbraco.Web.Components _messenger = serverMessenger as BatchedDatabaseServerMessenger; if (_messenger == null) throw new Exception("panic: messenger"); - _messenger.Startup(); - _runtime = runtime; _logger = logger; _registrationService = registrationService; @@ -116,6 +114,9 @@ namespace Umbraco.Web.Components //We will start the whole process when a successful request is made UmbracoModule.RouteAttempt += RegisterBackgroundTasksOnce; + + // must come last, as it references some _variables + _messenger.Startup(); } /// From 62d74f07bf58083dbf766684690f09630d2607df Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 7 Dec 2018 15:06:07 +1100 Subject: [PATCH 42/44] fixes boolean change mistake to make sure that the Internal/external index has the correct PublishedOnlyFlag, latest examine update, fixes issue with queuing deletions before updates --- src/Umbraco.Examine/Umbraco.Examine.csproj | 2 +- src/Umbraco.Examine/UmbracoContentIndexer.cs | 14 +++++++++++--- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 2 +- .../Editors/ExamineManagementController.cs | 11 ++++++----- src/Umbraco.Web/Search/ExamineComponent.cs | 4 ++-- src/Umbraco.Web/Search/UmbracoIndexesCreator.cs | 4 ++-- src/Umbraco.Web/Umbraco.Web.csproj | 2 +- 8 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Examine/Umbraco.Examine.csproj b/src/Umbraco.Examine/Umbraco.Examine.csproj index 0b0e02df49..9194d8332d 100644 --- a/src/Umbraco.Examine/Umbraco.Examine.csproj +++ b/src/Umbraco.Examine/Umbraco.Examine.csproj @@ -48,7 +48,7 @@ - + diff --git a/src/Umbraco.Examine/UmbracoContentIndexer.cs b/src/Umbraco.Examine/UmbracoContentIndexer.cs index e39a9d5990..0501fe90c6 100644 --- a/src/Umbraco.Examine/UmbracoContentIndexer.cs +++ b/src/Umbraco.Examine/UmbracoContentIndexer.cs @@ -156,24 +156,32 @@ namespace Umbraco.Examine : 1; }); + var hasDeletes = false; + var hasUpdates = false; foreach (var group in invalidOrValid.OrderBy(x => x.Key)) { if (group.Key == 0) { + hasDeletes = true; //these are the invalid items so we'll delete them //since the path is not valid we need to delete this item in case it exists in the index already and has now //been moved to an invalid parent. foreach (var i in group) - { - PerformDeleteFromIndex(i.Id, x => { /*noop*/ }); - } + QueueIndexOperation(new IndexOperation(new ValueSet(i.Id), IndexOperationType.Delete)); } else { + hasUpdates = true; //these are the valid ones, so just index them all at once base.PerformIndexItems(group, onComplete); } } + + if (hasDeletes && !hasUpdates || !hasDeletes && !hasUpdates) + { + //we need to manually call the completed method + onComplete(new IndexOperationEventArgs(this, 0)); + } } /// diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 3f9ab2ddbd..7147cc8453 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.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index d0433b1a77..ad3dcd91e6 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -88,7 +88,7 @@ - + diff --git a/src/Umbraco.Web/Editors/ExamineManagementController.cs b/src/Umbraco.Web/Editors/ExamineManagementController.cs index c72b4439f0..67209c91bd 100644 --- a/src/Umbraco.Web/Editors/ExamineManagementController.cs +++ b/src/Umbraco.Web/Editors/ExamineManagementController.cs @@ -167,16 +167,15 @@ namespace Umbraco.Web.Editors //now add a single handler index.IndexOperationComplete += Indexer_IndexOperationComplete; - var cacheKey = "temp_indexing_op_" + index.Name; - - //put temp val in cache which is used as a rudimentary way to know when the indexing is done - ApplicationCache.RuntimeCache.InsertCacheItem(cacheKey, () => "tempValue", TimeSpan.FromMinutes(5)); - try { //clear and replace index.CreateIndex(); + var cacheKey = "temp_indexing_op_" + index.Name; + //put temp val in cache which is used as a rudimentary way to know when the indexing is done + ApplicationCache.RuntimeCache.InsertCacheItem(cacheKey, () => "tempValue", TimeSpan.FromMinutes(5)); + _indexRebuilder.RebuildIndex(indexName); ////populate it @@ -281,6 +280,8 @@ namespace Umbraco.Web.Editors { var indexer = (LuceneIndex)sender; + _logger.Debug("Logging operation completed for index {IndexName}", indexer.Name); + //ensure it's not listening anymore indexer.IndexOperationComplete -= Indexer_IndexOperationComplete; diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index c45d470571..d8c1016c3e 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -80,13 +80,13 @@ namespace Umbraco.Web.Search factory.GetInstance(), factory.GetInstance>(), factory.GetInstance(), - false)); + true)); composition.Container.Register(factory => new ContentValueSetBuilder( factory.GetInstance(), factory.GetInstance>(), factory.GetInstance(), - true)); + false)); composition.Container.RegisterSingleton, MediaValueSetBuilder>(); composition.Container.RegisterSingleton, MemberValueSetBuilder>(); } diff --git a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs index dd64d9b142..623bd5b75f 100644 --- a/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs +++ b/src/Umbraco.Web/Search/UmbracoIndexesCreator.cs @@ -112,12 +112,12 @@ namespace Umbraco.Web.Search public virtual IContentValueSetValidator GetContentValueSetValidator() { - return new ContentValueSetValidator(true, true, PublicAccessService); + return new ContentValueSetValidator(false, true, PublicAccessService); } public virtual IContentValueSetValidator GetPublishedContentValueSetValidator() { - return new ContentValueSetValidator(false, false, PublicAccessService); + return new ContentValueSetValidator(true, false, PublicAccessService); } /// diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index ce39f51baf..1a31160f20 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -62,7 +62,7 @@ - + 2.6.2.25 From 04b2765d72f4200da79869414442e949f1b30898 Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 7 Dec 2018 09:03:36 +0100 Subject: [PATCH 43/44] Document --- .../PropertyEditors/IPropertyIndexValueFactory.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs index e303a53f5f..fd4e272f08 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyIndexValueFactory.cs @@ -11,6 +11,14 @@ namespace Umbraco.Core.PropertyEditors /// /// Gets the index values for a property. /// + /// + /// Returns key-value pairs, where keys are indexed field names. By default, that would be the property alias, + /// and there would be only one pair, but some implementations (see for instance the grid one) may return more than + /// one pair, with different indexed field names. + /// And then, values are an enumerable of objects, because each indexed field can in turn have multiple + /// values. By default, there would be only one object: the property value. But some implementations may return + /// more than one value for a given field. + /// IEnumerable>> GetIndexValues(Property property, string culture, string segment, bool published); } } From 0095429ff09338609c57a717320791f43597962f Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 7 Dec 2018 09:24:15 +0100 Subject: [PATCH 44/44] Fix package.json after merge --- src/Umbraco.Web.UI.Client/package.json | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index 0974432a15..a4ea212ece 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -44,7 +44,7 @@ "autoprefixer": "9.3.1", "gulp-clean-css": "4.0.0", "cssnano": "4.1.7", - "gulp-connect": "5.6.1", + "gulp": "3.9.1", "gulp-babel": "8.0.0", "gulp-concat": "2.6.1", "gulp-connect": "5.6.1", @@ -60,14 +60,13 @@ "gulp-wrap": "0.14.0", "gulp-wrap-js": "0.4.1", "jasmine-core": "3.3.0", - "karma": "^3.1.3", + "karma": "3.1.1", "karma-jasmine": "2.0.1", "karma-phantomjs-launcher": "1.0.4", - "less": "^3.9.0", + "less": "3.9.0", "lodash": "4.17.11", "merge-stream": "1.0.1", - "run-sequence": "^2.2.1", - "marked": "^0.5.2", - "event-stream": "3.3.4" + "run-sequence": "2.2.1", + "marked": "^0.5.2" } }